跳至内容
GitTree

课程 21 · 历史与融合

合并与变基:通过图表进行选择

从相同分歧的两个相同副本运行合并和变基,比较两个图、父级和文件树,并了解在它们之间做出决定的一条规则:还有谁已经拥有您的提交。

进度已保存在此浏览器中。

播放时会加载来自 YouTube 的视频(隐私增强模式)。

英文旁白 · English and Arabic 字幕

打开视频页面在 YouTube 上观看

你将学到什么

  • 从相同的副本开始合并和变基,因此比较是公平的。
  • 读取合并提交的两个父级和重新基址提交的单个新父级。
  • 解释为什么两个副本可以以相同的文件结尾,但仍然具有不同的历史记录。
  • 选择一条规则:仅重写其他人没有的提交。

开始之前

您需要第 20 课中的分歧的两个单独副本,名为 gittree-merge 和 gittree-rebase。在每个副本中,分支 feature/signup-form 保存您的一个提交(称为 S),分支 updated-main 保存一个不同的提交(称为 N),两者都从同一个提交 M 开始,并且 rescue/signup-before-rebase 指向 S。主题分支已签出,工作树是干净的。不要根据第一个结果制作第二个副本。

这里的一切都是本地的并且采用免费计划(GitTree要求您登录):没有托管帐户,没有提供商,没有互联网。每个身份都是虚构的(example.com),您永远不应该在真实的项目中运行本课程。

在 GitTree 中或使用本机 Git 命令进行更改,而不是在同一副本中进行更改。之后读取结果总是安全的。菜单标签来自GitTree 2.0自己的字符串;本机命令在一次性副本中使用 Git 2.43 运行。 GitTree 步骤未在 macOS、Windows 或 Linux 上运行;应用程序的快捷方式使用 macOS 上的 Command,其中此页面写入 Ctrl。

概念和状态效应

只有当两个副本都从相同的引用和相同的文件开始时,比较才是公平的。如果您先合并然后重新设置该结果,您会问一个不同的问题:重播现在在其范围内有一个合并提交。

Merge 保留两条线的工作。它添加了一个新提交 K,有两个父提交:首先是 S,然后是 N。S 和 N 保留其 ID。

Rebase 在 N 之上重播您的提交。结果 S′ 与 S 具有相同的更改,但有一个新的父级,并且提交的 ID 包括其父级,因此 S′ 有一个新 ID。旧的 S 幸存下来只是因为救援分支仍然指向它。

当没有冲突时,两个副本都以相同的文件树结束。相同的树并不意味着相同的历史:一个图保留菱形,另一个图保留直线,并且提交 ID 不同。

哪一个是正确的并不是品味问题。如果其他人已经有您的提交,则重播它们会破坏他们的副本,因此请合并。如果提交是私有的,则变基是安全的,并且如果您的团队愿意的话,可以提供线性历史记录。两者都不会让代码变得更好。

在每个副本中,仅主题分支移动。服务器、updated-main 和原来的 main 没有被触及。

准备准确的项目状态

做一个分歧,然后复制两次。从第 18 课留下的项目的单独普通副本开始(主要是合并提交 M)。保持原始的 gittree-features 不变,并且永远不要在源签出中运行它。演示身份为 GitTree Demo,demo@example.com,仅在此存储库中关闭签名。

完整的文件输入

仅在下面指定的阶段保存。这些是完整的 UTF-8/LF 正文,最后有换行符;永远不要粘贴 Markdown 栅栏。未更改的可运行文件继承命名输入。单独变体中的版本不得覆盖原始版本。

index.html — 注册

文件摘录 · html
<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>GitTree Features — course demo</title>
  <link rel="stylesheet" href="styles.css">
</head>
<body>
  <header class="site-header">
    <a href="#main">GitTree Features</a>
  </header>
  <main id="main" tabindex="-1">
    <section class="hero" aria-labelledby="hero-title">
      <p class="eyebrow">GitTree Learn demo project</p>
      <h1 id="hero-title">See your work clearly.</h1>
      <p id="hero-copy">Choose one change, then check what Git saved.</p>
    </section>
    <section class="features" aria-labelledby="features-title">
      <h2 id="features-title">Follow your work</h2>
      <div class="cards">
        <article class="feature-card">
          <h3>History</h3>
          <p>Read commits and branches together.</p>
        </article>
        <article class="feature-card">
          <h3>Changes</h3>
          <p>Review the exact content selected for a commit.</p>
        </article>
        <article class="feature-card">
          <h3>Recovery</h3>
          <p>Inspect the scope of a recovery point before using it.</p>
        </article>
      </div>
    </section>
    <section aria-labelledby="plans-title">
      <h2 id="plans-title">Choose the workflow you need</h2>
      <div class="cards">
        <article class="feature-card">
          <h3>Local Git</h3>
          <p>Local Git workflows work without signing in.</p>
        </article>
        <article class="feature-card">
          <h3>Connected workflows</h3>
          <p>Provider integrations and Nour require Pro or an applicable trial.</p>
        </article>
      </div>
    </section>
    <section aria-labelledby="signup-title">
      <h2 id="signup-title">Try the demo form</h2>
      <form id="signup-form" novalidate>
        <label for="demo-email">Demo email</label>
        <input id="demo-email" name="email" type="email" autocomplete="off" aria-describedby="signup-hint">
        <p id="signup-hint">Use a fictional example.com address. Nothing is sent.</p>
        <button type="submit">Check the address locally</button>
        <p id="signup-result" role="status"></p>
      </form>
    </section>
  </main>
  <footer><p>Built by a fictional team for GitTree Learn. Review one change at a time.</p></footer>
  <script type="module" src="site.js"></script>
</body>
</html>

docs/integration-note.md — DOC_MERGE

输出
# Integration review

This note was written on main while signup work was private.

在GitTree

使用 Command+Shift+P(在 Ubuntu 上为 Ctrl+Shift+P)打开调色板,输入准确的命令名称并选择它。对指定分支的操作​​使用该分支的右键单击菜单。

  1. 打开gittree-merge。然后选择 New Tab 并从其 Recent 列表中打开 gittree-rebase。每个选项卡都以其文件夹名称为标题,因此您可以区分这两个选项卡。
  2. 在 gittree-merge 选项卡中,保持 feature/signup-form 处于选中状态。右键单击 updated-main 并选择 Merge updated-main into feature/signup-form。
  3. 在 Record it as 下选择 Always a merge commit,然后选择 Merge 一次。新提交有两个父提交:首先是 S,然后是 N。
  4. 切换到 gittree-rebase 选项卡。它仍然位于 S 和 N,与您保存时一模一样。不要使用合并副本。
  5. 右键单击 updated-main 并选择 Rebase feature/signup-form onto updated-main。阅读 Options for this rebase 并保留两个选项,然后选择 Rebase feature/signup-form 一次。
  6. 选择重播的提交。它的父级是N并且它的ID是新的。老S仍以rescue/signup-before-rebase命名。
  7. 在每个选项卡中打开 Show Files → index.html 和 docs/integration-note.md。两份副本中的页面和注释相同。然后用 Back to Graph 返回。
  8. 比较两个图,然后在选择结果之前说出哪个规则决定该分支的选择。保留两份副本原样。

对于课程中的每个普通提交:Summary 是规定的主题,Description 为空,并且选项 Amend Previous Commit、Skip Hooks、Add Signed-off-by、Different Author、Push After Commit 和 Allow Empty Commit 保持关闭状态。本课的承诺不是普通的。

在航站楼

在第 18 课结果的单独干净副本中构建分歧(您在应用程序中所做的工作,以本机命令编写):

Command · bash
git switch --create feature/signup-form

将完整的注册页面保存为 index.html,然后:

Command · bash
git add -- index.html
git commit --quiet -m "feat: add the local signup demonstration"
git branch rescue/signup-before-rebase
BASE_SHA="$(git rev-parse HEAD~1)"
git switch --quiet --create updated-main "$BASE_SHA"

在 updated-main 将完整的 DOC_MERGE 注释保存为 docs/integration-note.md,然后:

Command · bash
git add -- docs/integration-note.md
git commit --quiet -m "docs: add the integration review"
git switch --quiet feature/signup-form

BASE_SHA 是两条线都源自的提交 M。现在从这个确切的状态 (cd .. && cp -R gittree-features gittree-merge && cp -R gittree-features gittree-rebase) 制作两个独立的副本。在合并副本中:

Command · bash
git merge --quiet --no-ff --no-edit updated-main
git rev-list --parents -n 1 HEAD
git rev-parse 'HEAD^{tree}'
git status --short

在未触及的变基副本中:

Command · bash
git rebase --quiet --no-autostash --no-update-refs --no-autosquash updated-main
git rev-list --parents -n 1 HEAD
git rev-parse 'HEAD^{tree}'
git status --short

git rev-list --parents -n 1 HEAD 打印提交的 ID,后跟其父项:合并副本中的三个 ID,变基副本中的两个 ID。两个 HEAD^{tree} 行具有相同的 ID,并且 git status --short 在这两行中不打印任何内容。两个 HEAD 提交 ID 不同。您的 ID 与其他人的 ID 不同;将您的内容相互比较,而不是与此页面进行比较。

如果主题没有自己的提交,并且 updated-main 位于其前面,则 git merge 将默认快进并且不创建合并提交。那就是不同的输入,不同的结果;有目的地选择它,切勿伪造双亲合并。

结果

Where you end: 合并副本有一个新提交 K,其父提交为 S 和 N; rebase 副本有 S' 和父 N,旧的 S 由救援分支保留。文件树是相等的,两个状态都是干净的,并且原始的main、M和服务器未受影响。

常见错误

  • 假设直图意味着旧的提交 ID 仍然存在。变基总是会创建新的提交。
  • 从合并结果开始变基。这是一个不同的实验,重播可以包括合并提交。
  • 比较两个 HEAD ID 并期望它们一致。相反,比较树木。
  • 完成后重复合并或变基。

如果出现问题

不要丢弃任何一个结果来强制它们匹配。如果树不同,请比较起始引用、任何冲突解决方案和任何额外提交,然后从保存的分歧重新开始。救援分支仅保留提交的历史记录;它不保留未提交的文件。

检查你的理解情况

回答每个问题,然后阅读原因。标记为“必填”的问题将有助于完成本课程。

  1. 第 1 题,共 6 题

    从同一开始,您将一个副本中的 updated-main 合并到 feature/signup-form 中,并将另一个副本中的 feature/signup-form 变基到 updated-main 上。每个副本中该主题的新提示提交有多少个父项?

    选择一个答案。

  2. 第 2 题,共 6 题

    一旦合并和变基完成且没有冲突,两个副本中哪些是相同的?

    选择每个适用的答案。

  3. 第 3 题,共 6 题
    必填

    rebase之后,你原来的主题commit S发生了什么?

    选择一个答案。

  4. 第 4 题,共 6 题

    更改输入:您的主题没有自己的提交,而 updated-main 就位于其前面。您使用默认模式进行合并,“如果可能的话快进,否则合并提交”。会发生什么?

    选择一个答案。

  5. 第 5 题,共 6 题
    必填

    一位同事已经拉取了 feature/signup-form 并正在其基础上进行构建。您希望 main 历史记录保持整洁。什么是安全的选择?

    选择一个答案。

  6. 第 6 题,共 6 题
    必填

    哪些回读表明比较是公平的并显示结果如何?

    选择每个适用的答案。

0 个(共 6 个)正确

正确回答所需的问题以完成课程。

在此浏览器中检查。

练习

报告两个父列表、两个 HEAD ID 和两个树 ID。解释为什么不同 HEAD ID 旁边的相同树 ID 是预期的结果。然后预测当主题没有自己的提交时默认合并会做什么,但不这样做。

您自己的确认:该网站无法看到您的计算机。

要完成本课程,请正确回答所需的问题并确认实践练习。

回顾

  • 相同的开始,不同的历史。
  • 同等的树木并不意味着同等的历史。
  • 当其他人有你的提交时合并。仅对私有的进行变基。

术语表介绍

Rebase — 重新设置;重播——重播;救援部门 — فف فٸ。

视频文字记录

视频的旁白,逐条提示。

显示成绩单
  1. 合并还是变基?同样的工作可以离开 两个不同的历史。
  2. 我们将从同一个起点运行两者 点并比较。
  3. 您的分支有一项提交。更新后的 分行还有另一个。
  4. 两者都来自同一个提交,所以两者都不是 包含另一个。
  5. 首先,准备分歧并制作两个 相同的副本。
  6. 公平的测试需要相同的参考和文件 在两份副本中。
  7. 这是第一个副本。我们将并入 这个。
  8. 这是第二份。我们保留它 变基未受影响。
  9. 右键单击 update-main 并选择其 合并命令。
  10. 在记录为下,选择始终合并 提交。
  11. 选择合并一次,等待真正的合并 结果。
  12. 新的提交有两个父母:你的 提交,然后更新主的。
  13. 现在是第二个副本。还是有这两个 原始提交。
  14. 再次右键单击updated-main,然后选择 这次重新设定基准。
  15. 该对话框命名分支和 目标。将这两个选项都关闭。
  16. 变基一次。 Git 重放您的提交 更新主的顶部。
  17. 重放的提交有一个新的父提交,所以 它得到一个新的ID。
  18. 旧的提交没有丢失:救援 分支仍然指向它。
  19. 现在比较文件。该页面是 两份副本相同。
  20. 集成说明也是如此。相同的文件, 不同的历史。
  21. 看这两个图:一个菱形,一个 合并提交和一条直线。
  22. 那么您应该使用哪一个呢?问还有谁 有你的承诺。
  23. 如果其他人有,则合并。变基会 改写他们的历史。
  24. 如果提交是私有的,则 rebase 是 安全,并给出一条直线。
  25. 两者都不会让你的代码变得更好。规则 是关于所有权。
  26. 现在在终端中证明这一点。首先, 每个小费的父母。
  27. 合并副本的三个 ID:提交、 和它的两个父母。
  28. 变基副本的两个 ID:提交、 和它的一个父母。
  29. 然后是文件树:一个 ID,打印出来 两次。相同的文件。
  30. 但两个提交 ID 不同。平等 树木,不同的历史。
  31. 更改输入:假设您的分支有 没有自己的提交。
  32. 默认合并将快进, 没有合并提交。
  33. 永远不要对别人的分支进行变基 已经有了。
  34. 如果树不同,则比较起始树 首先参考。
  35. 不要删除一个结果来强制两个结果 比赛。
  36. 练习:报告父母、身份证和 树木来自您自己的副本。
  37. 接下来,重新排序、压缩和重新措辞 本地提交。