Git:拉一个重新分支

Ita*_*ski 45 git push pull rebase

让我来描述我的情况:

Blond先生和Orange先生正在研究分支A,它在提交M1的主分支上分支出来.分支A有2个提交:A1和A2.

M1  
   \
    \
     A1 - A2
Run Code Online (Sandbox Code Playgroud)

与此同时,奥兰治先生承诺并在主分公司M2和M3上多推了两笔.

M1  - M2 - M3
   \
    \
     A1 - A2
Run Code Online (Sandbox Code Playgroud)

布朗先生从遥控器拉出来,过了一会儿决定重新进入主分支:

M1  - M2 - M3
   \         \
    \         \
     A1 - A2   A1` - A2`
Run Code Online (Sandbox Code Playgroud)

现在A1`和A2`是在先生金发的本地存在的衍合的提交,以及A1和A2远程存在.金发先生推动他的提交,使用-f强制他的改变并"重写"历史.现在远程存储库看起来像这样:

M1  - M2 - M3
             \
              \
               A1` - A2`
Run Code Online (Sandbox Code Playgroud)

但奥兰治先生也在A分公司工作.他的本地存储库仍然如下所示:

M1  - M2 - M3
   \
    \
     A1 - A2
Run Code Online (Sandbox Code Playgroud)

Orange先生需要做什么才能与远程存储库中的A分支同步?

正常拉动不起作用.请拉-f强制远程本地的更改?我知道删除本地版本的A并从远程存储库再次提供它将会起到作用,但这似乎并不是实现这一目标的好方法.

Gar*_*ler 51

如果Orange先生不介意丢失他的更改,他可以从服务器获取,然后git checkout A2进入他的本地A2分支,然后(假设遥控器被命名为"origin")git reset --hard origin/A2将他重置A2到遥控器的A2位置.

如果他担心丢失更改,他可以合并服务器的更改以解决它们(从他自己的A2分支,并再次假设该遥控器被命名为"origin")git merge origin/A2.这将使得一个新的提交位于他和远程的A2分支之上,并且两者的更改都合并在一起.然后可以将其推回遥控器.

  • 我认为这个答案已经过时了。“git pull --rebase” 可以很好地处理这种情况。 (9认同)
  • 注意:` - `和`hard`之间没有空格 - 我花了一段时间,因为答案在我的屏幕上正好包裹在这两个之间:) (2认同)

Fid*_*tix 36

总长DR

除非我误解了这个问题,否则奥兰治先生的答案是…… git pull --rebase

读得更长

简单案例

如果我们假设 A1 和 A2 所做的更改与 A1' 和 A2' 相同(即使它们的提交哈希值不同),那么 Orange 先生可以从

M1  - M2 - M3
   \
    \
     A1 - A2
Run Code Online (Sandbox Code Playgroud)

当地有

M1  - M2 - M3
             \
              \
               A1` - A2`
Run Code Online (Sandbox Code Playgroud)

通过做任何事

M1  - M2 - M3
   \
    \
     A1 - A2
Run Code Online (Sandbox Code Playgroud)

或者

M1  - M2 - M3
             \
              \
               A1` - A2`
Run Code Online (Sandbox Code Playgroud)

一个更有趣的案例

如果我们假设,如上所述,A1 和 A2 所做的更改是相同的,并且 Orange 先生做了额外的提交,那么他将在本地拥有

M1  - M2 - M3
   \
    \
     A1 - A2 - A3O
Run Code Online (Sandbox Code Playgroud)

原点将是

M1  - M2 - M3
             \
              \
               A1` - A2`
Run Code Online (Sandbox Code Playgroud)

再次,奥兰治先生可以到达

M1  - M2 - M3
             \
              \
               A1` - A2` - A3O
Run Code Online (Sandbox Code Playgroud)

git pull --rebase
Run Code Online (Sandbox Code Playgroud)

更现实的案例

如果我们假设,如上所述,A1 和 A2 所做的更改是相同的,Orange 先生进行了额外的提交,Blonde 先生进行了额外的提交,那么 Orange 先生将在本地进行此更改

M1  - M2 - M3
   \
    \
     A1 - A2 - A3O
Run Code Online (Sandbox Code Playgroud)

并且,假设金发先生已经推动,原点将是

M1  - M2 - M3
             \
              \
               A1` - A2` - A3B
Run Code Online (Sandbox Code Playgroud)

橙色先生可以到达

M1  - M2 - M3
             \
              \
               A1` - A2` - A3B - A3O
Run Code Online (Sandbox Code Playgroud)

通过做

git branch -d A
git checkout A
Run Code Online (Sandbox Code Playgroud)

不过,这一次,他可能需要解决冲突。

  • 这应该是公认的答案。我不知道自 2013 年 Gary Fixler 的回答以来 Git 是否有了很大的改进,但现在对已发布的分支进行变基似乎不再是问题了。 (3认同)

tor*_*rek 25

我的建议(或者,"如果我是奥兰治先生,我将会做什么")的开头是git fetch.现在我将在我的回购中拥有这个,这就是金发先生在他的篮板之后以及在他执行"git push -f"之前所拥有的.

M1  - M2 - M3
   \         \
    \         \
     A1 - A2   A1' - A2'
Run Code Online (Sandbox Code Playgroud)

一个重要的区别是,我的本地标签A指向rev A2,远程标签remotes/origin/A指向A2'(金色先生反过来,本地标签A指向A2'并remotes/origin/A指向A2).

如果我一直在处理名为"A"的分支副本,我将改为:

M1  ---- M2 ---- M3
   \               \
    \               \
     A1 - A2 - A3    A1' - A2'
Run Code Online (Sandbox Code Playgroud)

(我的本地标签指向A3而不是A2;或A4或A5等,取决于我已经应用了多少更改.)现在我所要做的就是将我的A3(和A4,如果需要等)转换到A2' .一个明显的直接方式:

$ git branch -a
  master
* A
  remotes/origin/master
  remotes/origin/A
$ git branch new_A remotes/origin/A
$ git rebase -i new_A
Run Code Online (Sandbox Code Playgroud)

然后完全降低转速A1和A2,因为修改后的转换器在new_A中为A1'和A2'.要么:

$ git checkout -b new_A remotes/origin/A
$ git format-patch -k --stdout A3..A | git am -3 -k
Run Code Online (Sandbox Code Playgroud)

(该git am -3 -k方法在git-format-patch手册页中描述).

这些确实需要弄清楚金发先生在他做之前没有做过的事情rebase,即识别A1,A2,A3等.

如果第二种方法成功,我最终得到:

M1  ---- M2 ---- M3
   \               \
    \               \
     A1 - A2 - A3    A1' - A2' - A3'
Run Code Online (Sandbox Code Playgroud)

我的分支名称new_A指向A3'(我现有的A分支仍然指向旧的A3).如果我使用第一种方法并且它成功,我最终会得到同样的结果,只是我现有的分支名称A现在将指向A3'(并且我没有使用A1-A2-A3的旧分支的名称,即使它仍然在我的回购中;找到它需要通过reflogs或类似的).

(如果我的A3需要修改成为A3',当然,交互式rebase和"git am"方法都需要我的工作.)

当然也可以git merge(就像在Gary Fixler的回答中那样),但这将创建一个合并提交("M"没有数字,下面)并保持转速A1和A2可见,给出:

M1  ---- M2 ---- M3
   \               \
    \               \
     A1 - A2 - A3    A1' - A2' -- M
                 \_______________/
Run Code Online (Sandbox Code Playgroud)

如果你想保留原来的A1和A2,这是一件好事; 如果你想摆脱它们,这是一件坏事.所以"做什么"取决于"你想要的结果是什么".

编辑添加:我更喜欢格式补丁方法,因为它保留了我的旧A分支名称,同时我确保一切都很好.假设所有的工作,并好,这里是最后几个步骤:

$ git branch -m A old_A
$ git branch -m new_A A
Run Code Online (Sandbox Code Playgroud)

然后,如果old_A可以完全放弃:

$ git branch -D old_A
Run Code Online (Sandbox Code Playgroud)

或者,等效地,从分支删除开始,然后将new_A重命名为A.

(编辑:另请参阅git rebase --onto文档,了解将A3等转换到new_A分支的目标.)


the*_*yer 19

我在两台虚拟机上同时开发,用于配置目的。因此,我经常在一台机器上变基,并且需要毫无困难地将更改显示在另一台机器上。

假设我的分支名为feature/my-feature-branch。在第一个 VM 上完成 rebase 后,我在第二个 VM 上执行 git fetch。出现以下消息:

$ git status
On branch feature/my-feature-branch
Your branch and 'origin/feature/my-feature-branch' have diverged,
and have 21 and 24 different commits each, respectively.
  (use "git pull" to merge the remote branch into yours)
Run Code Online (Sandbox Code Playgroud)

好吧,不要执行 git pull,因为在经过一番大惊小怪之后,您最终会得到一个毫无意义的合并提交。

相反,运行

git rebase -i origin/feature/my-feature-branch

一旦文本编辑器弹出,删除所有提交,并将其替换为以下内容(这使得 rebase 完成而不保留任何提交)。

exec echo test

如果您确实有需要保留的提交,那么可以在此处应用这些提交。在任何一种情况下,rebase 都会完成,现在两台机器再次同步,如下所示:

$ git pull
Already up-to-date.
$ git push
Everything up-to-date
Run Code Online (Sandbox Code Playgroud)