Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Merge and rollback gracefully in Git

Yibo
January 16, 2021

Merge and rollback gracefully in Git

Yibo

January 16, 2021
Tweet

More Decks by Yibo

Other Decks in Technology

Transcript

  1. 1.1 三个区域 ⼯作区、暂存区和提交区 • ⼯作区(Working Directory):没有修改、修 改后未使⽤ git add 的⽂件

    • 暂存区(Staged/Index):新增/修改后的⽂件 调⽤ git add 后都会被添加这⾥ • 提交区(Commit/Local Repository):所有添 加到暂存区⾥的⽂件通过 git commit 之后会被 统⼀添加这⾥
  2. 1.1 三个区域 ⼯作区、暂存区和提交区 • ⼯作区(Working Directory):没有修改、修 改后未使⽤ git add 的⽂件

    • 暂存区(Staged/Index):新增/修改后的⽂件 调⽤ git add 后都会被添加这⾥ • 提交区(Commit/Local Repository):所有添 加到暂存区⾥的⽂件通过 git commit 之后会被 统⼀添加这⾥ • /refs/remotes:远程仓库的引⽤(不可修改) • remote:远程仓库
  3. 最简单的⽅式是通过以下命令将 master 分⽀合并到 feature 分⽀中: 这会在 feature 分⽀中创建⼀个新的 merge commit,它将两个分⽀的历史联系在⼀

    起。 思考:这⾥没有指定 --no-ff 参数,为什么 不是默认的 Fast-Forward Merge 呢? 2.1.1 Merge git checkout feature git merge master
  4. 可以使⽤以下命令将 master 分⽀合并到 feature分⽀上: 这会将整个 feature 分⽀移动到 master 分⽀的顶端,从⽽有效地整合了所有 master

    分 ⽀上的提交。 但是,与 merge 提交⽅式不同,rebase 通过为原始分⽀中的每个提交创建全新的 commits 来 重写 项⽬历史记录。 2.1.2 Rebase git checkout feature git rebase master
  5. 2.1.3 简单对⽐ Merge vs Rebase • 好处 • 保持源分⽀的原始上下⽂ •

    源分⽀上的提交与其他分⽀的提交是分开的 • 可以保留提交历史
  6. 2.1.3 简单对⽐ Merge vs Rebase • 好处 • 保持源分⽀的原始上下⽂ •

    源分⽀上的提交与其他分⽀的提交是分开的 • 可以保留提交历史 • 弊端 • 乱 feature 分⽀每次需要合并上游更改时,它都将产 ⽣⼀个额外的合并提交。如果master 提交⾮常活 跃,这可能会严重污染你的 feature 分⽀历史记 录,它可能使其他开发⼈员难以理解项⽬的历史 记录。
  7. 2.1.3 简单对⽐ Merge vs Rebase • 好处 • 保持源分⽀的原始上下⽂ •

    源分⽀上的提交与其他分⽀的提交是分开的 • 可以保留提交历史 • 弊端 • 乱 feature 分⽀每次需要合并上游更改时,它都将产 ⽣⼀个额外的合并提交。如果master 提交⾮常活 跃,这可能会严重污染你的 feature 分⽀历史记 录,它可能使其他开发⼈员难以理解项⽬的历史 记录。 • 好处 • 代码历史是简化的、线性的、可读的 • 与许多独⽴的特性分⽀的提交历史相⽐,操作单个 提交历史更容易 • ⼲净、清晰的提交信息可以更好地跟踪⼀个 bug 或何时引⼊的某个功能,可以避免众多的单⾏提交 污染历史
  8. 2.1.3 简单对⽐ Merge vs Rebase • 好处 • 保持源分⽀的原始上下⽂ •

    源分⽀上的提交与其他分⽀的提交是分开的 • 可以保留提交历史 • 弊端 • 乱 feature 分⽀每次需要合并上游更改时,它都将产 ⽣⼀个额外的合并提交。如果master 提交⾮常活 跃,这可能会严重污染你的 feature 分⽀历史记 录,它可能使其他开发⼈员难以理解项⽬的历史 记录。 • 好处 • 代码历史是简化的、线性的、可读的 • 与许多独⽴的特性分⽀的提交历史相⽐,操作单个 提交历史更容易 • ⼲净、清晰的提交信息可以更好地跟踪⼀个 bug 或何时引⼊的某个功能,可以避免众多的单⾏提交 污染历史 • 弊端 • 可能会丢失合并提交的上下⽂(也就⽆法看到上游 更改是何时合并到 feature 中的) • 如果不正确地重写了历史,可能会导致严重的问题
  9. 2.1.3 简单对⽐ Merge vs Rebase • 好处 • 保持源分⽀的原始上下⽂ •

    源分⽀上的提交与其他分⽀的提交是分开的 • 可以保留提交历史 • 弊端 • 乱 feature 分⽀每次需要合并上游更改时,它都将产 ⽣⼀个额外的合并提交。如果master 提交⾮常活 跃,这可能会严重污染你的 feature 分⽀历史记 录,它可能使其他开发⼈员难以理解项⽬的历史 记录。 • 好处 • 代码历史是简化的、线性的、可读的 • 与许多独⽴的特性分⽀的提交历史相⽐,操作单个 提交历史更容易 • ⼲净、清晰的提交信息可以更好地跟踪⼀个 bug 或何时引⼊的某个功能,可以避免众多的单⾏提交 污染历史 • 弊端 • 可能会丢失合并提交的上下⽂(也就⽆法看到上游 更改是何时合并到 feature 中的) • 如果不正确地重写了历史,可能会导致严重的问题
  10. 由于 master 分⽀从 C2 开始与 feat 分叉以后就再也没 有新的提交了,所以 Git 只是简单地把

    master 的 head 指针向前移动到 C4,合并就完成了。 这就是所谓的 Fast-Forward Merge。Fast-Forward Merge 要求参与合并的两个分⽀上的提交必须是“⼀脉 相承”的⽗⼦或祖孙关系。 因为不涉及内容变更的⽐较,所以这种合并⽅式效率 很⾼。 2.2.1 Fast-Forward Merge
  11. 特点: 由于 master 分⽀从 C2 开始与 feat 分叉以后就再也没 有新的提交了,所以 Git

    只是简单地把 master 的 head 指针向前移动到 C4,合并就完成了。 这就是所谓的 Fast-Forward Merge。Fast-Forward Merge 要求参与合并的两个分⽀上的提交必须是“⼀脉 相承”的⽗⼦或祖孙关系。 因为不涉及内容变更的⽐较,所以这种合并⽅式效率 很⾼。 2.2.1 Fast-Forward Merge
  12. 特点: • 是 git merge ⽆参数时的默认合并⽅式 由于 master 分⽀从 C2

    开始与 feat 分叉以后就再也没 有新的提交了,所以 Git 只是简单地把 master 的 head 指针向前移动到 C4,合并就完成了。 这就是所谓的 Fast-Forward Merge。Fast-Forward Merge 要求参与合并的两个分⽀上的提交必须是“⼀脉 相承”的⽗⼦或祖孙关系。 因为不涉及内容变更的⽐较,所以这种合并⽅式效率 很⾼。 2.2.1 Fast-Forward Merge
  13. 特点: • 是 git merge ⽆参数时的默认合并⽅式 • 如果删除被合并分⽀(通常被称为 topic 分⽀),会丢掉

    topic 分⽀信息 由于 master 分⽀从 C2 开始与 feat 分叉以后就再也没 有新的提交了,所以 Git 只是简单地把 master 的 head 指针向前移动到 C4,合并就完成了。 这就是所谓的 Fast-Forward Merge。Fast-Forward Merge 要求参与合并的两个分⽀上的提交必须是“⼀脉 相承”的⽗⼦或祖孙关系。 因为不涉及内容变更的⽐较,所以这种合并⽅式效率 很⾼。 2.2.1 Fast-Forward Merge
  14. 特点: • 是 git merge ⽆参数时的默认合并⽅式 • 如果删除被合并分⽀(通常被称为 topic 分⽀),会丢掉

    topic 分⽀信息 缺点:作为被合并的 topic 分⽀,它的提交历史在合并以后会和 master 分⽀的提交历史重合 由于 master 分⽀从 C2 开始与 feat 分叉以后就再也没 有新的提交了,所以 Git 只是简单地把 master 的 head 指针向前移动到 C4,合并就完成了。 这就是所谓的 Fast-Forward Merge。Fast-Forward Merge 要求参与合并的两个分⽀上的提交必须是“⼀脉 相承”的⽗⼦或祖孙关系。 因为不涉及内容变更的⽐较,所以这种合并⽅式效率 很⾼。 2.2.1 Fast-Forward Merge
  15. 通过⼀个例⼦来说明: 假设有⼀个⽂本⽂件,Alice 和 Bob 同时做了改动,节点如下: 如果只对 Alice 和 Bob 各⾃的⽂件进⾏对⽐,也就是所谓的“diff”(或者也有⼈称之为“Two-Way

    Merge”),那么⼯具在帮我们做合并时,只知道两个⽂件在同⼀⾏上有差异,却没办法知道在合 并后的版本⾥,到底该保留谁的版本,所以只能交给⽤户⾃⼰⼿⼯来决定。 这就是普通的 Merge ⼯具所能做的。
  16. 和普通的 Merge ⼯具相⽐,Git 最⼤的不同在于它记录了⽂件的提交历史,因此可以向前回溯⽂件 修改前的“原件”。它在合并时不仅会看两⼈各⾃的⽂件内容,还会看之前的原件。 通过和原来版本的对⽐,就可以清楚地看出, • base/Alice 的对⽐,可以知道 Alice

    的改动是删除了 3; • base/Bob 的对⽐,可以知道 Bob 的改动是增加了 1; 这⼀过程可以由⼯具⾃动完成,⽽不⽤像 Two-Way Merge 那样,需要交给⽤户⼿⼯来决定,这就 是 Three-Way Merge。
  17. 这⾥边的 • 1 是公共祖先 • 2 是 HEAD,git ⽂档中有时候也称作 ours

    • 3 是 MERGE_HEAD,git ⽂档中有时候也称作 theirs 在 git 的官⽅⽂档⾥是有提到 Three-Way Merge 的 参考: • git 官⽅关于 Three-Way Merge 的介绍:http://git-scm.com/docs/git-merge#_true_merge • Wikipedia 中关于 Three-Way Merge 的介绍:https://en.wikipedia.org/wiki/Merge_(version_control) • 这篇论⽂介绍了 diff3 的算法:https://www.cis.upenn.edu/~bcpierce/papers/diff3-short.pdf
  18. 所谓 Squash Merge,是指 Git 在做两个分⽀间的合并时,会把被合并分⽀上的所 有变更“压缩(squash)”成⼀个提交,追加到当前分⽀的后⾯,作为“合并提交” (merge commit)。 从参与合并的⽂件变更上来说,Squash Merge

    和普通 Merge 并没有任何区别,效 果完全⼀样。 唯⼀的区别体现在提交历史上:对于普通的 Merge ⽽⾔,在当前分⽀上的合并提 交通常会有两个 parent;⽽ Squash Merge 却只有⼀个。 2.2.3 Squash Merge
  19. ⽐如⽬前有两个分⽀,master 和 feature * 098be39 (HEAD -> master) c5 |

    * 0f029c3 (feature) c4 | * c594fa9 c3 |/ * 9229adb c2 * cfbff28 c1 git log --oneline --graph --all
  20. ⽐如⽬前有两个分⽀,master 和 feature * 098be39 (HEAD -> master) c5 |

    * 0f029c3 (feature) c4 | * c594fa9 c3 |/ * 9229adb c2 * cfbff28 c1 现在执⾏⼀次 Squash Merge git log --oneline --graph --all git merge --squash feature
  21. ⽐如⽬前有两个分⽀,master 和 feature * 098be39 (HEAD -> master) c5 |

    * 0f029c3 (feature) c4 | * c594fa9 c3 |/ * 9229adb c2 * cfbff28 c1 现在执⾏⼀次 Squash Merge 然后⽣成新的提交 git log --oneline --graph --all git merge --squash feature git commit -m c6
  22. ⽐如⽬前有两个分⽀,master 和 feature * 098be39 (HEAD -> master) c5 |

    * 0f029c3 (feature) c4 | * c594fa9 c3 |/ * 9229adb c2 * cfbff28 c1 * 0b7fd35 (HEAD -> master) c6 * 098be39 c5 | * 0f029c3 (feature) c4 | * c594fa9 c3 |/ * 9229adb c2 * cfbff28 c1 现在执⾏⼀次 Squash Merge 然后⽣成新的提交 此时再看提交记录 git log --oneline --graph --all git merge --squash feature git commit -m c6 git log --oneline --graph --all 作为合并提交的 c6,只有⼀个 parent,即:c5。 ⽽且,如果这个时候我们把 feature 分⽀删掉,整个 提交历史就会变得⾮常⼲净。
  23. ⽐如⽬前有两个分⽀,master 和 feature * 098be39 (HEAD -> master) c5 |

    * 0f029c3 (feature) c4 | * c594fa9 c3 |/ * 9229adb c2 * cfbff28 c1 * 0b7fd35 (HEAD -> master) c6 * 098be39 c5 | * 0f029c3 (feature) c4 | * c594fa9 c3 |/ * 9229adb c2 * cfbff28 c1 现在执⾏⼀次 Squash Merge 然后⽣成新的提交 此时再看提交记录 git log --oneline --graph --all git merge --squash feature git commit -m c6 git log --oneline --graph --all 作为合并提交的 c6,只有⼀个 parent,即:c5。 ⽽且,如果这个时候我们把 feature 分⽀删掉,整个 提交历史就会变得⾮常⼲净。
  24. 如果在 topic 分⽀与 merge 分⽀⽆“分叉”的时候,可以使⽤ Fast-Forward Merge(默认,--ff)。(仅仅是可以,不建议) 如果在 topic 分⽀上,完整的提交历史⾥包含了很多中间提交(intermediate

    commit),⽐如:改正⼀个⼩⼩的拼写错误可能也会成为⼀个独⽴的提交,⽽我 们并不希望在合并时把这些细节都反映在 merge 分⽀的提交历史⾥,这时就选择 Squash Merge(--squash)。 如果在 topic 分⽀上,有多次不同类型的提交,⽐如:有和功能相关的 feat 的提 交,也有⽂档相关的 docs 的提交,我们需要把这些提交都反映在 merge 分⽀的 提交历史⾥,这时我们就选择 Three-Way Merge(--no-ff)。 2.2.1 三种 Merge 分别何时使⽤?
  25. git checkout feature git rebase -i master 这将打开⼀个⽂本编辑器,列出即将移动的所有提交: pick 33d5b7a

    Message for commit #1 pick 9480b3d Message for commit #2 pick 5c67e61 Message for commit #3 要使⽤交互式 rebase,需要使⽤ git rebase 和 -i 选项: 通过更改 pick 命令或重新排序条⽬,你可以使分⽀ 的历史记录看起来像你想要的任何内容。
  26. git checkout feature git rebase -i master 这将打开⼀个⽂本编辑器,列出即将移动的所有提交: pick 33d5b7a

    Message for commit #1 pick 9480b3d Message for commit #2 pick 5c67e61 Message for commit #3 要使⽤交互式 rebase,需要使⽤ git rebase 和 -i 选项: 通过更改 pick 命令或重新排序条⽬,你可以使分⽀ 的历史记录看起来像你想要的任何内容。 例如,如果第⼆次提交 fix 了第⼀次提交中的⼀个⼩问 题,可以使⽤以下 fixup 命令将它们“浓缩”为⼀个提交:
  27. git checkout feature git rebase -i master 这将打开⼀个⽂本编辑器,列出即将移动的所有提交: pick 33d5b7a

    Message for commit #1 pick 9480b3d Message for commit #2 pick 5c67e61 Message for commit #3 要使⽤交互式 rebase,需要使⽤ git rebase 和 -i 选项: 通过更改 pick 命令或重新排序条⽬,你可以使分⽀ 的历史记录看起来像你想要的任何内容。 pick 33d5b7a Message for commit #1 9480b3d Message for commit #2 pick 5c67e61 Message for commit #3 fixup 例如,如果第⼆次提交 fix 了第⼀次提交中的⼀个⼩问 题,可以使⽤以下 fixup 命令将它们“浓缩”为⼀个提交:
  28. git checkout feature git rebase -i master 这将打开⼀个⽂本编辑器,列出即将移动的所有提交: pick 33d5b7a

    Message for commit #1 pick 9480b3d Message for commit #2 pick 5c67e61 Message for commit #3 要使⽤交互式 rebase,需要使⽤ git rebase 和 -i 选项: 通过更改 pick 命令或重新排序条⽬,你可以使分⽀ 的历史记录看起来像你想要的任何内容。 pick 33d5b7a Message for commit #1 9480b3d Message for commit #2 pick 5c67e61 Message for commit #3 保存并关闭⽂件时,Git 将根据指示执⾏ rebase, 从⽽产⽣如下所示的项⽬历史记录 fixup 例如,如果第⼆次提交 fix 了第⼀次提交中的⼀个⼩问 题,可以使⽤以下 fixup 命令将它们“浓缩”为⼀个提交:
  29. git checkout feature git rebase -i master 这将打开⼀个⽂本编辑器,列出即将移动的所有提交: pick 33d5b7a

    Message for commit #1 pick 9480b3d Message for commit #2 pick 5c67e61 Message for commit #3 要使⽤交互式 rebase,需要使⽤ git rebase 和 -i 选项: 通过更改 pick 命令或重新排序条⽬,你可以使分⽀ 的历史记录看起来像你想要的任何内容。 pick 33d5b7a Message for commit #1 9480b3d Message for commit #2 pick 5c67e61 Message for commit #3 保存并关闭⽂件时,Git 将根据指示执⾏ rebase, 从⽽产⽣如下所示的项⽬历史记录 fixup 例如,如果第⼆次提交 fix 了第⼀次提交中的⼀个⼩问 题,可以使⽤以下 fixup 命令将它们“浓缩”为⼀个提交: 消除这种⽆意义的提交使你的功能历史更容易理 解。这是 git merge 根本⽆法做到的事情。
  30. 操作 说明 pick 采⽤该提交(默认⾏为) reword 采⽤该提交,但要求修改 commit message edit 采⽤该提交,但要求修改提交记录的信息,如:作者名称,邮箱地址等

    squash 采⽤该提交,但它会被并⼊前⼀条提交 fixup 类似 squash,但是会丢弃这条提交记录的⽇志信息 exec 执⾏指定的 shell 脚本或命令 drop 丢弃该提交 除了 fixup 外,rebase 还有以下可供选择的操作
  31. 操作 说明 pick 采⽤该提交(默认⾏为) reword 采⽤该提交,但要求修改 commit message edit 采⽤该提交,但要求修改提交记录的信息,如:作者名称,邮箱地址等

    squash 采⽤该提交,但它会被并⼊前⼀条提交 fixup 类似 squash,但是会丢弃这条提交记录的⽇志信息 exec 执⾏指定的 shell 脚本或命令 drop 丢弃该提交 除了 fixup 外,rebase 还有以下可供选择的操作 ⼀旦我们理解了什么是 rebase,最重要的是要学习什么时候不能使⽤它。
  32. ⼀旦你理解了什么是 rebase,最重要的是要学习什么时候不能使⽤它。 git rebase 的⻩⾦法则有三: 1. 永远不要 在 公共分⽀ 上使⽤它

    2.4 Rebase 的⻩⾦法则 2. 永远不要 在 公共分⽀ 上使⽤它 3. 永远不要 在 公共分⽀ 上使⽤它
  33. 试想⼀下,如果你将 master 分⽀ rebase 到 feature 分⽀之上会发⽣什么 rebase 将所有 master

    分⽀上的提交移动 feature 分⽀ 的顶端。 问题是这只发⽣在 你⾃⼰ 的存储库中,所有其他开发 ⼈员仍在使⽤原始版本的 master。 由于 rebase 导致全新 commit,Git 会认为你的 master 分⽀历史与其他⼈的历史不同。
  34. 试想⼀下,如果你将 master 分⽀ rebase 到 feature 分⽀之上会发⽣什么 rebase 将所有 master

    分⽀上的提交移动 feature 分⽀ 的顶端。 问题是这只发⽣在 你⾃⼰ 的存储库中,所有其他开发 ⼈员仍在使⽤原始版本的 master。 由于 rebase 导致全新 commit,Git 会认为你的 master 分⽀历史与其他⼈的历史不同。 此时,同步两个 master 分⽀的唯⼀⽅法是将它们 merge 在⼀ 起,但是这样会产⽣额外的合并提交和两组包含相同更改的提交 (原始提交和通过 rebase 更改的提交)。 这是⼀个令⼈⾮常困惑的情况。
  35. 试想⼀下,如果你将 master 分⽀ rebase 到 feature 分⽀之上会发⽣什么 rebase 将所有 master

    分⽀上的提交移动 feature 分⽀ 的顶端。 问题是这只发⽣在 你⾃⼰ 的存储库中,所有其他开发 ⼈员仍在使⽤原始版本的 master。 由于 rebase 导致全新 commit,Git 会认为你的 master 分⽀历史与其他⼈的历史不同。 此时,同步两个 master 分⽀的唯⼀⽅法是将它们 merge 在⼀ 起,但是这样会产⽣额外的合并提交和两组包含相同更改的提交 (原始提交和通过 rebase 更改的提交)。 这是⼀个令⼈⾮常困惑的情况。 因此,在运⾏ git rebase 命令之前,总是问⾃⼰,还有其他⼈在⽤这个分⽀吗? 如果答案是肯定的,那就把你的⼿从键盘上移开,开始考虑采⽤⾮破坏性的⽅式进 ⾏改变(例如 git merge)。
  36. 永远不要 在 公共分⽀ 上使⽤ Rebase • 本地 commit 整理(压缩、修改) git

    checkout feature git rebase -i HEAD~3 2.5 Rebase 的⼏种场景
  37. 永远不要 在 公共分⽀ 上使⽤ Rebase • 本地 commit 整理(压缩、修改) •

    将上游更改合并到功能分⽀中 git checkout feature git rebase -i HEAD~3 2.5 Rebase 的⼏种场景
  38. 永远不要 在 公共分⽀ 上使⽤ Rebase • 本地 commit 整理(压缩、修改) •

    将上游更改合并到功能分⽀中 • 在提交 PR 之前清理代码 git checkout feature git rebase -i HEAD~3 2.5 Rebase 的⼏种场景
  39. git checkout feature git revert --no-commit HEAD~2..HEAD 假如 V2、V3 的修改不需要了

    m..n 表示范围的话,开闭类似 (m, n] ,等价的写法 git revert --no-commit HEAD HEAD~1 git revert --no-commit V3 V2
  40. 对于由 Three-Way Merge 产⽣的 merge commit, 在 revert 时需要加 -m

    参数,例如: 某个功能不上了,我们要从 master ⾥剔除已合并 的 feature 分⽀的代码
  41. 对于由 Three-Way Merge 产⽣的 merge commit, 在 revert 时需要加 -m

    参数,例如: 某个功能不上了,我们要从 master ⾥剔除已合并 的 feature 分⽀的代码
  42. 对于由 Three-Way Merge 产⽣的 merge commit, 在 revert 时需要加 -m

    参数,例如: 某个功能不上了,我们要从 master ⾥剔除已合并 的 feature 分⽀的代码 git revert -m 1 M1 对 M1 进⾏ revert (此时 M1 有两个 parent,所以我们需要使⽤ -m 或 --mainline 告诉 git 我们保留那个分⽀)
  43. 对于由 Three-Way Merge 产⽣的 merge commit, 在 revert 时需要加 -m

    参数,例如: 某个功能不上了,我们要从 master ⾥剔除已合并 的 feature 分⽀的代码 git revert -m 1 M1 如果回滚后,⼜要上该功能(或者发现回滚错了) 此时再 git merge feature 你会发现 feature 分⽀代码并没有回来 对 M1 进⾏ revert (此时 M1 有两个 parent,所以我们需要使⽤ -m 或 --mainline 告诉 git 我们保留那个分⽀)
  44. 对于由 Three-Way Merge 产⽣的 merge commit, 在 revert 时需要加 -m

    参数,例如: 某个功能不上了,我们要从 master ⾥剔除已合并 的 feature 分⽀的代码 git revert -m 1 M1 git revert -m 1 M1’ 如果回滚后,⼜要上该功能(或者发现回滚错了) 此时再 git merge feature 你会发现 feature 分⽀代码并没有回来 正确的操作是对 M1’ 再次 revert(负负得正) 对 M1 进⾏ revert (此时 M1 有两个 parent,所以我们需要使⽤ -m 或 --mainline 告诉 git 我们保留那个分⽀)
  45. 3.2 总结 在运⾏ git reset 改写历史之前,和使⽤ git rebase ⼀样,总是问⾃⼰: 还有其他⼈在⽤这个分⽀吗?

    如果答案是肯定的,那不要敲下回⻋键,⽽是优先考虑采⽤ git revert。 注:git reset 和 git rebase 在改写历史的时候均需要配合 force push 使⽤,所以公共分⽀均应开启分⽀保护以规 避被改写历史的问题。
  46. 练练⼿ 有这么⼀段提交记录,现在想只保留 1、2 的提交,在不改写历史的情况下,如何做? git reset --hard cfbe7f6 #2 git

    reset --soft 7ca8127 #7 git commit -m "Reverted 3 4 5 6 7" Revert :注意 merge commit,另外需解冲突 Reset:也可以不改写历史 text.txt commits
  47. 4.1 WIP MR Work In Progress Merge Request 含义:在⼯作过程中的合并请求 作⽤:避免

    MR 在准备就绪前被合并 使⽤: • 在 MR 的标题开头添加 WIP:
  48. 4.1 WIP MR Work In Progress Merge Request 含义:在⼯作过程中的合并请求 作⽤:避免

    MR 在准备就绪前被合并 使⽤: • 在 MR 的标题开头添加 WIP: • 当已经准备好被合并,编辑⼯单来⼿动删除 WIP: 或者使⽤“Resolve WIP status”按钮
  49. 4.3 git pull --rebase git pull = git pull --merge

    = git fetch + git merge FETCH_HEAD git pull --rebase = git fetch + git rebase FETCH_HEAD ⼤家都基于 master 拉出分⽀进⾏并⾏开发,这⾥的分⽀可能是多到数⼗个。然后彼此在进⾏⾃⼰的逻 辑编写,时间可能需要⼏天或者⼏周。在这期间你可能需要时不时的需要 pull 下远程 master 分⽀上的 同事的提交。这是个好的习惯,这样下去就可以避免你在⼀个⽆⽤的代码上进⾏⻓期的开发,回头来看 这些代码不是新的代码。 如果使⽤默认的 git pull(默认是 merge 模式),那么往往在 pull 的时候会多⼀条 merge commit,是 在 git pull --merge 的时候⾃动⽣成的。如果多⼈多次如此操作,那么提交记录就会出现很多条这种⾃ 从⽣成的 merge commit,⾮常难看。
  50. 4.3 git pull --rebase --rebase 的本意是想让事情的发展看起来很连续和优美,⽽不是多出很多⽆⽤的 merge commit 。 git

    pull = git pull --merge = git fetch + git merge FETCH_HEAD git pull --rebase = git fetch + git rebase FETCH_HEAD ⼤家都基于 master 拉出分⽀进⾏并⾏开发,这⾥的分⽀可能是多到数⼗个。然后彼此在进⾏⾃⼰的逻 辑编写,时间可能需要⼏天或者⼏周。在这期间你可能需要时不时的需要 pull 下远程 master 分⽀上的 同事的提交。这是个好的习惯,这样下去就可以避免你在⼀个⽆⽤的代码上进⾏⻓期的开发,回头来看 这些代码不是新的代码。 如果使⽤默认的 git pull(默认是 merge 模式),那么往往在 pull 的时候会多⼀条 merge commit,是 在 git pull --merge 的时候⾃动⽣成的。如果多⼈多次如此操作,那么提交记录就会出现很多条这种⾃ 从⽣成的 merge commit,⾮常难看。
  51. Alice 基于 master 创建了⼀个 topic 分⽀ A,Bob 基于 master 创建了⼀个不相⼲的

    topic 分⽀ B Alice:git checkout master && git pull(Master is already up to date) Bob:git checkout master && git pull(Master is already up to date) Alice:git merge topic-branch-A Bob:git merge topic-branch-B Bob:git push origin master(在 Alice 之前) Alice:git push origin master(会被拒绝,因为这不是⼀个 fast-forward merge) Alice:git pull --rebase origin master(这时 Bob 的提交被拉下来,Alice 的提交放到 Bob 的后边) Alice:git push origin master(不会产⽣⽆⽤的 merge commit) 使⽤ git pull --rebase 的⼀个典型场景:
  52. 4.4 Cherry Pick 摘樱桃 使⽤ cherry-pick,可以从其他分⽀复制指定的提交,然后导⼊到现在的分⽀。 主要使⽤的场景: • 把弄错分⽀的提交移动到正确的地⽅ •

    把其他分⽀的提交添加到现在的分⽀ git checkout master git cherry-pick f 例如: a - b - c - d - f Master \ e - f - g Feature
  53. Q&A