用 Git 重新定基

Rebasing with Git

我看了很多关于如何使用 Git 变基的文章,这很有意义……或者至少我认为是这样。但是,我正在努力解决以下问题...

对于这个场景,我们有以下分支:

好的,现在假设 origin/master 领先 1 次提交。因此,文章告诉我,我所在的开发团队将在 jheigs 分支上执行以下操作:

$ git add ...
$ git commit ...
$ git status (ensure everything is up-to-date locally)
$ git pull (again, check and ensure that everything is ready to go)
$ git pull --rebase origin master
$ git push (defaulting to origin/jheigs)
  1. 当变基 运行 没问题时,我 运行 进入的是 origin/jheigs 和本地 jheigs 的 HEAD 提交不匹配(给定上面的变基),所以有时我可能不得不先拉然后再推,这可能会导致冲突。我感到困惑的是......我应该改为使用:

    $ git push --force origin jheigs (?)
    
  2. 第二个问题...现在,假设我已经推送并且 jheigs 已使用 origin/master 正确地变基。不存在冲突,我的 jheigs 和 origin/jheigs 现在比 master 领先 1 次提交。好的,一天过去了,我需要做更多的改变。所以我对 jheigs 和 add/commit 进行了这些更改。但是,origin/master 没有其他更新。本质上我仍然领先于 origin/master 1 次提交(很快是 2 次)。我是否仍然遵循上述相同的过程?还是我只是 add/commit 并推到 origin/jheigs 而没有变基,因为我已经领先于 origin/master?

很抱歉,这太长了,但我认为我已经弄明白了,但它并不像我想象的那么顺利。我想小心变基,所以任何帮助都将不胜感激!

嗯,首先,我会(并且确实)完全或大部分避免 git pull:它本来是为了方便,但结果是 in方便的。它只是 运行s git fetch 后跟第二个 Git 命令,它会影响您检出的任何 b运行ch。有时我不想或不需要取回;其他时候,我想看看发生了什么 when fetch 运行, before 我执行任何第二个命令来影响当前b运行ch.

您可能已经读到,git rebase 实际上是关于 复制(一些)提交。您的存储库中有一些提交集合,现在,无论出于何种原因,一些提交已经失去了光彩,您想要更新、更好、更漂亮、更闪亮的提交。

使所有这些都有意义的方法是绘制提交图。请记住,有多个克隆 — 不同的存储库包含 大部分 相同的提交集,但不完全相同的集 — 涉及!

绘制您自己的存储库的提交

您可以让 Git 为您做这件事:git log --all --decorate --oneline --graph,或 git 记录(与)一只狗。不过,一开始用手做是很好的练习。另外,当你手工画的时候,你可以把它们水平地画出来,这往往更有意义。

请记住,每个由其唯一哈希 ID 标识的提交都是 read-only。每个提交都指向它的 parent 提交。像 masterjheigs 这样的 b运行ch 名称,或者像 origin/masterorigin/jheigs 这样的 remote-tracking 名称, 指向(记录哈希 ID)提示 提交,Git 从这些提示向后工作:

...--C--D--E   <-- master
         \
          F--G   <-- jheigs (HEAD)

这可能是存储库的图形片段,其中提交 Emaster 的尖端,而 E 指向 D,而您添加了您自己的两个提交:G,这是 jheigs 的提示,指向 F,之前,您添加了 F 指向 [=28] =].

请注意,这里的提交 D 和更早的提交都在 both b运行ches.

A git rebase 将复制 1 一些提交到 new-and-improved 提交。例如,在这里您可能想要将 F 复制到 new-and-improved F',其中主要区别在于 F' 附加到 E,而不是 D:

             F'   [new and improved!]
            /
...--C--D--E   <-- master
         \
          F--G   <-- jheigs (HEAD)

F 复制到 F' 后,您现在可以将 G 复制到 new-and-improved G':

             F'-G'   [new and improved!]
            /
...--C--D--E   <-- master
         \
          F--G   <-- jheigs (HEAD)

现在所有"on"(可从)jheigs不是master的提交已经复制到 new-and-improved 版本,你可以让 Git 剥离提交 G 上的 jheigs 标签并将其粘贴到 G':

             F'-G'  <-- jheigs (HEAD)
            /
...--C--D--E   <-- master
         \
          F--G   [abandoned]

旧的、沉闷的 FG 昨天还闪亮漂亮的提交现在变成了垃圾,取而代之的是闪亮的新 F'G'。这是因为 your b运行ch name jheigs 现在指向最后一个新复制的提交。


1这个复制就像git cherry-pick完成的一样。根据您使用的变基命令,它实际上可能 git cherry-pick 完成的。


涉及另一个存储库:fetchpush

上图适用于您的 存储库,但此处涉及第二个存储库。他们有自己的 b运行ch 名称和自己的提交。您拥有的提交,他们也拥有,共享提交哈希 ID。可能有一些您拥有但他们没有的承诺;并且可能有一些他们拥有而您没有的提交。

如果您认为他们可能有您没有的承诺,您应该 运行 git fetchgit fetch origin。这让你的 Git 调用他们的 Git,在 URL 你已经列在名字 origin 下。您的 Git 调用他们的 Git 并让他们列出他们的所有提交(通过哈希 ID),这些提交由 他们的 b运行ch 名称给出。

如果他们有您没有的提交,您的 Git 现在会下载这些提交。您的 Git 还 更改 他们的 b运行ch 名称,例如 masterjheigs,以便他们阅读 origin/masterorigin/jheigs。这些新名称不会干扰您的 b运行ch 名称。

现在您已经拥有他们拥有的所有提交,加上您之前拥有的所有提交,您的存储库可能看起来像这样——假设您还没有完成git rebase还:

             H   <-- origin/master
            /
...--C--D--E   <-- master
         \
          F--G   <-- jheigs (HEAD), origin/jheigs

你的origin/*是你Git对他们b运行ch名字的记忆。这意味着 他们的 master 标识提交 H。感谢 git fetch,您现在已经提交了 H他们的 jheigs 标识提交 G,就像你的一样。

如果您现在 运行 git rebase master,您的 Git 会将您的 FG 复制到新的 F'G',在提交 E 之后构建,这是您的 master 点。您可能希望它们在提交 H 之后出现。

在这里,你可以很容易地做到这一点:你可以 运行 git rebase origin/master。这告诉您的 Git 找到您拥有的提交(即 FG),这些提交不是 可从 访问的 origin/master (即,他们的 master,就你能记住的程度而言)并在提交 H 之后放置副本。结果如下所示:

               F'-G'  <-- jheigs (HEAD)
              /
             H   <-- origin/master
            /
...--C--D--E   <-- master
         \
          F--G   <-- origin/jheigs

请注意,origin/* 个名称中的 none 个已移动,您自己的 master 也没有移动。然而,他们的 jheigs 你称之为origin/jheigs 仍然记得提交G.

现在你需要git push --force jheigs,告诉他们:丢弃提交FG支持新的闪亮 F'G'. 一旦他们同意这样做,您的 Git 就会记住他们的 jheigs 指向 G'

               F'-G'  <-- jheigs (HEAD), origin/jheigs
              /
             H   <-- origin/master
            /
...--C--D--E   <-- master

由于找不到名字,提交 FG 似乎完全消失了。

为什么你可能永远不想变基

请注意,那里可能有一个 third Git 存储库,还有第四个或更多存储库,其中还有另一个 origin/jheigs。如果是这样,所有这些 other 存储库必须采取一些行动(例如 运行ning git fetch)来获取新的提交并更新他们自己的 origin/jheigs名字。

此外,在您决定放弃您的提交以支持 new-and-improved 之前,他们可能已经在您的提交之上构建了自己的提交。如果是这样,他们可能会被迫复制 他们的 提交,就像您复制自己的一样。这甚至可能是一件好事,也可能会让他们恼火。

因此,如果您打算对 其他人 已经或可能已经完成的提交进行变基,您应该有理由相信可以为他们工作。如果没有其他人拥有原始提交,显然 copy-and-replace 他们是安全的。如果其他人都同意这种 copy-replace 是 应该 发生,那也没关系。因此,如果可以这样做,并且这样做有意义,那么就进行变基。

When the rebase runs fine, what I have run into is that origin/jheigs and local jheigs have HEAD commits that don't match (given the above rebase)

是的,但不同之处在于您的额外 un-pushed 工作。在 git pull --rebase 之后,上游 HEAD 将是您本地 HEAD 的 祖先

so at times I may have to pull and then push

这通常是正确的做法

which can cause conflicts.

不应该,除非有人在你的拉和推之间插入(在这种情况下,你需要再做一次,以合并他们的工作)。

Ok, a day goes by and I need to make more changes. So I make those changes on jheigs and add/commit. However, origin/master has no additional updates. Essentially I'm still ahead of origin/master by 1 commits (soon to be 2). Do I still follow the same process above? Or do I just add/commit and push to origin/jheigs without rebasing since I'm already ahead of origin/master?

两者都可以。如果上游没有移动,git pull --rebase 什么都不做。