课程 22 · 历史与融合
重新排序、压缩、重写和删除本地提交
将五个粗糙的私有提交变成两个干净的提交:移动依赖修复、重写消息、折叠和压缩步骤、仅删除噪音,并检查结果是否完全包含您想要的文件。
- 级别: 高级
- 阅读 5 分钟
- 7 分钟的视频
- 已更新
- 在这节课之前: 将未发表的作品重新建立到新的基础上
进度已保存在此浏览器中。
播放时会加载来自 YouTube 的视频(隐私增强模式)。
你将学到什么
- 阅读重播计划:最旧的步骤优先,从上到下应用。
- 选择正确的操作:重写、压缩、修复或删除。
- 移动一个步骤而不破坏它所依赖的步骤。
- 之后验证新的提交、文件和救援分支。
开始之前
您的分支 private/signup-polish 有五个提交,仅存在于您的计算机上,位于提交 M 之上。工作树是干净的,并且 rescue/before-polish 指向第五个提交。按顺序,最旧的在前:
| 步骤 | 提交 | 它的作用 |
|---|---|---|
| C1 | 壮举:添加注册草稿 | 添加注册表单 |
| C2 | 文档:解释注册审核 | 添加 docs/replay-a.md |
| C3 | 文档:解释验证 | 添加 docs/replay-b.md |
| C4 | 修复!壮举:添加注册草稿 | 修复表单的标签 |
| C5 | 琐事:临时笔记 | 添加 docs/temporary-review.md |
一切都是本地的并且采用免费套餐(GitTree 要求您登录):没有托管帐户、没有提供商、没有互联网。每个身份都是虚构的(example.com)。切勿在真实项目中运行本课程,也切勿重写其他人已经拥有的提交。
在 GitTree 中或使用本机 Git 命令进行更改,而不是在同一副本上进行更改。读取结果总是安全的。菜单标签来自 GitTree 2.0 自己的字符串,本机命令使用 Git 2.43 在一次性副本中运行。 GitTree 步骤未在 macOS、Windows 或 Linux 上运行。
概念和状态效应
交互式变基是一个计划,而不是排序游戏。 Git 从列表顶部到底部逐个重播列出的提交,因此最早的步骤排在最前面。每个操作都告诉 Git 在一个步骤中要做什么:
- Pick 保持提交原样。
- Reword 保留其内容并允许您编写新消息。
- Squash 将提交折叠到其上方保留的提交中,并允许您编写一条组合消息。
- Fixup 也将其折叠起来,但丢弃了它自己的消息。
- Drop 完全保留提交的更改。
顺序很重要。依赖于另一个步骤的步骤(此处标签修复需要 C1 中的表格)必须位于其下方。编辑器会让你将其移到上面,然后重播会因冲突而停止。 GitTree 还拒绝 Git 无法运行的计划:删除每个提交的计划、上面没有任何内容可折叠的挤压以及空消息。
每个重播的提交都会获得一个新的 ID,因为它的父级发生了变化。仅重写该分支;救援部门保留了老五。已经在远程上的提交被标记为“在远程上”,并且重写它们需要明确的确认。这个教训从未证实这一点。
您将制定的计划将五个提交变成两个:
| 步骤 | 提交 | 行动 | 结果 |
|---|---|---|---|
| 1 | C1 | 改写 | 壮举:填写本地注册表单 |
| 2 | C4 | 修正 | 折叠到步骤1中 |
| 3 | C3 | 挑选 | 保留;挤压目标 |
| 4 | C2 | 壁球 | 文档:解释注册审核和验证 |
| 5 | C5 | 掉落 | 消失了 |
准备准确的项目状态
从第 18 课留下的项目的单独普通副本开始。保持原来的 gittree-features 不变。在一个新分支上构建下面的五个提交,一次一个文件,并仅在其指定的步骤中保存每个完整的正文。
完整的文件输入
仅在下面指定的阶段保存。这些是完整的 UTF-8/LF 正文,最后有换行符;永远不要粘贴 Markdown 栅栏。未更改的可运行文件继承命名输入。单独变体中的版本不得覆盖原始版本。
index.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>文档/replay-a.md — DOC_A
# Local signup review
Review the label and hint together.文档/replay-b.md — DOC_B
# Local validation review
No address leaves this demonstration.index.html — SIGNUP_LABEL
<!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">Fictional email address</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/temporary-review.md — DOC_NOISE
Temporary rehearsal note, deliberately dropped.在GitTree
使用 Command+Shift+P(在 Ubuntu 上为 Ctrl+Shift+P)打开调色板。提交上的操作使用该提交的右键单击菜单。
- 构建五个提交和救援分支,如下一节所示,其中
private/signup-polish已签出并清理。注意M和五个科目。 - 右键单击 M(五个正下方的提交),然后选择 Edit History → Interactive Rebase of the Commits Above…。编辑器以五个私人步骤开始,最旧的在前。
- 在 Options for this rebase 下,保留所有三个选项,包括 Arrange fixup!, squash! and amend! commits。您将自行移动修复程序。
- 聚焦 C4 行并按 Alt+↑ 两次,然后聚焦 C3 行并按 Alt+↑ 一次。现在的顺序是 C1、C4、C3、C2、C5。在焦点行上按 p、r、e、s、f 或 d 可设置“拾取”、“改写”、“编辑”、“挤压”、“修复”或“删除”。
- 将 C1 设置为 Reword 并在其消息字段中输入
feat: complete the local signup form。将 C4 设置为 Fixup。 - 将 C3 保留为 Pick,将 C2 设置为 Squash。在 Squashed commits 下,“2 提交到文档:解释验证”组显示一条组合消息。将其替换为
docs: explain signup review and validation。 - 将 C5 设置为 Drop。再次阅读每一行和组合消息。没有任何内容被标记为“在远程”,因此不需要确认。
- 选择 Start Rebase 一次并等待。如果停止,请使用横幅继续;不要再次开始整个计划。
- 验证两个新提交、页面中的标签修复、两个文档且没有临时注释。保留
rescue/before-polish,并保持工作树干净。
对于课程中的每个普通提交:Summary 是规定的主题,Description 为空,并且选项 Amend Previous Commit、Skip Hooks、Add Signed-off-by、Different Author、Push After Commit 和 Allow Empty Commit 保持关闭状态。
在航站楼
在新分支上构建五个提交,并在其步骤中保存每个完整的正文:
git switch --create private/signup-polish将 SIGNUP 保存为 index.html,然后:
git add -- index.html
git commit --quiet -m "feat: add signup draft"将 DOC_A 保存为 docs/replay-a.md,然后:
git add -- docs/replay-a.md
git commit --quiet -m "docs: explain signup review"将 DOC_B 保存为 docs/replay-b.md,然后:
git add -- docs/replay-b.md
git commit --quiet -m "docs: explain validation"将 SIGNUP_LABEL 保存为 index.html,然后:
git add -- index.html
git commit --quiet -m "fixup! feat: add signup draft"将 DOC_NOISE 保存为 docs/temporary-review.md,然后:
git add -- docs/temporary-review.md
git commit --quiet -m "chore: temporary note"保护旧的提示并列出范围,最旧的在前:
git branch rescue/before-polish
BASE_SHA="$(git rev-parse HEAD~5)"
git log --reverse --format=%s "$BASE_SHA"..HEAD现在开始交互式变基(-i 是 --interactive 的缩写形式)。 Git 打开带有待办事项列表的编辑器(您的 ID 会有所不同):
git rebase --interactive --no-autosquash --no-update-refs --no-autostash "$BASE_SHA"pick 1a2b3c4 feat: add signup draft
pick 2b3c4d5 docs: explain signup review
pick 3c4d5e6 docs: explain validation
pick 4d5e6f7 fixup! feat: add signup draft
pick 5e6f708 chore: temporary note将五行重写到计划中,保留每行自己的 ID,然后保存:
reword 1a2b3c4 feat: add signup draft
fixup 4d5e6f7 fixup! feat: add signup draft
pick 3c4d5e6 docs: explain validation
squash 2b3c4d5 docs: explain signup review
drop 5e6f708 chore: temporary note然后 Git 再打开编辑器两次。对于改写,请将消息替换为 feat: complete the local signup form。对于壁球,它显示这两条消息;将它们替换为一行 docs: explain signup review and validation。检查结果:
git rev-list --count "$BASE_SHA"..HEAD
git log --reverse --format=%s "$BASE_SHA"..HEAD
git status --short2
feat: complete the local signup form
docs: explain signup review and validation状态为空。 index.html 是 SIGNUP_LABEL,两个文档都存在,而 docs/temporary-review.md 不存在。
如果您在第一行放置一个挤压,Git 会立即停止并显示 error: cannot 'squash' without a previous commit。使用 git rebase --edit-todo 和 git rebase --continue 修复列表,或使用 git rebase --abort 离开。使用 --autosquash,Git 会为您执行移动和提交 fixup! 的操作;本课将其关闭以显示计划。
结果
Where you end: M 之上的两个重写提交,“feat:完成本地注册表单”和“docs:解释注册审核和验证”。该页面有标签修复,两个文档都在那里,但临时注释不在那里。旧的五个提交由救援分支保存,工作树是干净的。
常见错误
- 使用 Drop 隐藏您可能需要的提交。这种变化根本不在新历史中。
- 将修复移至其修复的提交之上。
- 压扁第一步,上面没有任何东西可以折叠。
- 计划完成后重新开始,而不是先检查结果。
如果出现问题
在开始之前,Cancel 会保持分支不变。如果 GitTree 表示分支自编辑器打开以来发生了更改,请关闭编辑器并再次打开它。如果变基停止,请修复冲突或消息并从横幅继续,或故意中止它。完成后,请先与 rescue/before-polish 进行比较,然后再尝试恢复任何内容。
检查你的理解情况
回答每个问题,然后阅读原因。标记为“必填”的问题将有助于完成本课程。
0 个(共 6 个)正确
正确回答所需的问题以完成课程。
在此浏览器中检查。
练习
解释计划 C1、C4、C3、C2、C5 以及保留下来的两条消息。检查旧范围 5、新范围 2、预期文件,但没有注释。然后在不运行的情况下预测为什么将 C4 移动到 C1 上方或压缩步骤 1 不起作用。
您自己的确认:该网站无法看到您的计算机。
要完成本课程,请正确回答所需的问题并确认实践练习。
回顾
- 重播计划具有顺序和依赖性。
- Squash 会保留这两条消息供您组合; fixup 会丢弃自己的。
- 丢弃会删除更改本身,因此只丢弃真正的噪音。
有关的
术语表介绍
改写 — ¡¡¡¡¡¡¡壁球 — жмّ;修复 — 修复并修复;掉落 — ¡重播计划 — 重播计划。
视频文字记录
视频的旁白,逐条提示。
显示成绩单
- 五个粗糙的提交变成两个干净的提交, 安全。
- 交互式变基是一个计划:一个列表 步骤,最旧的在前。
- Git 从上到下重播列表 一个基地。
- 每个步骤都有一个动作:选择、改写、 挤压、修复或丢弃。
- 首先,构建五个私有提交和一个 救援分会。
- 三是实实在在的工作。一个是修复,一个是 是暂时的噪音。
- 右键单击它们下面的提交,然后 打开编辑历史记录。
- 选择提交的交互式变基 以上。
- 小编列出了五个步骤,最古老的 首先。
- 关闭所有三个选项。我们会搬家 我们自己修复。
- 修复程序在提交之后立即生效 它修复了。将其向上移动两次。
- 现在将验证注释移至 复习笔记。
- 现在的顺序是一、四、三、二、 五.
- 将第一步设置为Reword,然后写入 新消息。
- 将修复步骤设置为 Fixup。它的信息 被扔掉了。
- 保留验证记录,并压扁 查看注释。
- Squash 显示这两条消息。写一个 给这对的消息。
- 放下临时笔记。它的改变将 无法生存。
- 再次阅读每一行。上面什么都没有 远程。
- 选择一次Start Rebase,然后等待 完成。
- 该图现在在上面有两个提交 基地。
- 两人都有新的身份证。老五都在 救援分会。
- 打开页面:标签修复就在那里。的 噪音消失了。
- 现在航站楼也有同样的计划, git rebase -i。
- Git 打开待办事项列表。最旧的第一个,全部 设置为选择。
- 将这些内容重写到我们的计划中:重写, 修复、拾取、挤压、掉落。
- Git 要求提供这两条消息。写一个 每条线。
- 计算基础之上的提交:两次。
- 列出科目。这是我们两个 写道。
- 状态为空,临时备注为 走了。
- 更改输入:将南瓜放在 第一步。
- 它上面没有任何东西可以折叠,所以 吉特拒绝了。
- 或者将修复程序移至其提交之上: 重播因冲突而停止。
- 如果停止,请修复它并继续。或者 中止,并且没有任何变化。
- 救援部门仍然保留着你 原来的五次提交。
- 永远不要重写别人的提交 已经有了。
- 练习:解释计划以及为什么要采取步骤 一个不可能是南瓜。
- 接下来,解决合并或合并时的冲突 变基停止。