简体   繁体   English

git 分支高级用法,从上游分支变基

[英]git branching advanced usage, rebase from upstream branch

Following up on Git: rebase onto development branch from upstream , basically the same ask as:跟进Git:从上游 rebase 到 development branch ,基本上和 ask 一样:

I have local master and develop branches.我有本地masterdevelop分支机构。 I do all my work on develop and then merge them into master for releases.我在develop上完成所有工作,然后将它们合并到master中以进行发布。 There is a remote branch, upstream/master which has changes I want, but I want to rebase my changes in develop (which shares a common ancestor) on top of its changes and put them back into develop .有一个远程分支upstream/master有我想要的更改,但我想在其更改之上重新设置develop (它共享一个共同的祖先)中的更改并将它们放回develop I've already done git fetch upstream .我已经完成了git fetch upstream

Here is the (a bit more complicated/advanced) situation I'm facing with, starting from scratch.这是我从头开始面临的(有点复杂/高级)情况。

  1. I forked an upstream, and made my own changes from its development branches ( dev/br-1 ), in my own new branch ( dev/br-2 ), and git push to my own repo.我分叉了一个上游,并在我自己的新分支 ( dev/br-2 ) 中从其开发分支( dev/br-1 ) 进行了自己的更改,并将git push送到我自己的 repo。
  2. Now, upstream has advanced, both in its master and develop branches.现在,upstream 在其masterdevelop分支中都取得了进步。
  3. The way upstream advanced its develop branch is via rebase , ie, all its own changes are put back on top after rebase .上游推进其develop分支的方式是通过rebase ,即它自己的所有更改都在 rebase 之后放回顶部
  4. My local repo is gone with my old machine, and I need to pick-up/git-clone from my own repo to continue with my customization.我的本地存储库随我的旧机器一起消失了,我需要从我自己的存储库中获取/git-clone 以继续我的定制。
  5. I want to rebase my changes in my dev/br-2 branch (which shares a common ancestor) on top of all the upstream changes.我想在所有上游更改之上重新设置我在dev/br-2分支(共享一个共同祖先)中的更改。
  6. I've already done git fetch upstream and was able to rebase my master with upstream's.我已经完成了git fetch upstream并且能够将我的master与上游的联系起来。
  7. It is how to rebase my current dev/br-1 from upstream, then dev/br-2 from dev/br-1 branch that makes my head spinning nonstop.它是如何从上游重新设置我当前的dev/br-1 ,然后从dev/br-1分支重新dev/br-2 ,这让我头晕目眩。

Although it looks like a very specific case, but the principle of how to do git branching and rebasing still applies, and the answer would be very educational to the general public.虽然看起来是一个非常具体的案例,但是git branching and rebasing 的原理仍然适用,答案对一般人来说很有教育意义。 Please help.请帮忙。

UPDATE: So I looked at @torek suggested git rebase --onto command, like How to git rebase a branch with the onto command?更新:所以我查看了@torek 建议的git rebase --onto命令,例如How to git rebase a branch with the onto command? , and all their refed docs, and think my problem is still one or two levels up than what I've read (because there are two repos and 5 branches involved). ,以及他们所有参考的文档,并认为我的问题仍然比我读过的问题高一两个级别(因为涉及两个回购和 5 个分支)。 Here is the summary:这是摘要:

Situation at point#1:第 1 点的情况:

A---B---C---D  master (upstream)
    \
     E---F---G  dev/br-1 (upstream)
             \
              H---I---J dev/br-2 (myown)

Situation at point#2&3:第 2 点和第 3 点的情况:

A---B---C---D  master (upstream)
            \
              E'---F'---G'  dev/br-1 (upstream)

And I don't even know where should I draw my myown branch.而且我什至不知道我应该在哪里画我myown分支。 Here is my current situation (which may have already been messed up, as I see HEAD might be in a weird place):这是我目前的情况(可能已经搞砸了,因为我看到HEAD可能在一个奇怪的地方):

$ git log --all --decorate --oneline --graph
* 7aec18c (upstream/dev/br-1) more updates
* b83c3f8 more updates
* 4cf241f update file-a
* 200959c update file-a from main
* dc45a94 (upstream/master, master) update file-a from main
| * ce2a804 (origin/dev/br-2) update file-a
| * 0006c5e (HEAD -> dev/br-1, origin/dev/br-1) more updates
| * cdee8bb more updates
| * 85afa56 update file-a
|/  
* 2f5eaaf (origin/master, origin/HEAD) add file-a

The ultimate goal is to place myown最终目标是放置我自己的

H---I---J dev/br-2

branch on top of the newly rebased G' , in my own repo, after catching up with upstream.在赶上上游之后,在我自己的回购协议中,在新重新定位的G'之上分支。 Ie, in my own repo, in the end, it should look like this:也就是说,在我自己的 repo 中,最后应该是这样的:

A---B---C---D  rebased master (upstream)
            \
             E'---F'---G'  rebased dev/br-1 (upstream)
                       \
                        H'---I'---J' rebased dev/br-2

How to do that?怎么做?

More explain by command:通过命令进行更多解释:

 cd /tmp
 mkdir upstream
 cd upstream
 # prepare its `master` and `dev/br-1` branches

 cd /tmp
 git clone upstream myfork
 # prepare my own changes based on the `dev/br-1` branch into `dev/br-2`

 cd /tmp/upstream
 # advance its `master` 
 . . .
 # and its`dev/br-1` branches
 git checkout dev/br-1
 git rebase -X theirs master dev/br-1
 . . .

Now upstream has advanced, both in its master and its develop branches (via rebase ), and I need to pick-up from my own repo to continue with my customization.现在 upstream 在它的master和它的develop分支(通过rebase )都取得了进展,我需要从我自己的 repo 中获取以继续我的定制。

 cd /tmp
 mv myfork myfork0
 git clone myfork0 myfork1
 cd myfork1
 git remote -v
 git remote add upstream /tmp/upstream
 git remote -v
 git fetch upstream
 git rebase upstream/master

 git checkout --track origin/dev/br-1

$ git remote -v
origin  /tmp/myfork0 (fetch)
origin  /tmp/myfork0 (push)
upstream        /tmp/upstream (fetch)
upstream        /tmp/upstream (push)

$ git branch -avv
* dev/br-1                  0006c5e [origin/dev/br-1] more updates
  master                    dc45a94 [origin/master: ahead 1] update file-a from main
  remotes/origin/HEAD       -> origin/master
  remotes/origin/dev/br-1   0006c5e more updates
  remotes/origin/dev/br-2   ce2a804 update file-a
  remotes/origin/master     2f5eaaf add file-a
  remotes/upstream/dev/br-1 7aec18c more updates
  remotes/upstream/master   dc45a94 update file-a from main

$ git log --all --decorate --oneline --graph
* 7aec18c (upstream/dev/br-1) more updates
* b83c3f8 more updates
* 4cf241f update file-a
* 200959c update file-a from main
* dc45a94 (upstream/master, master) update file-a from main
| * ce2a804 (origin/dev/br-2) update file-a
| * 0006c5e (HEAD -> dev/br-1, origin/dev/br-1) more updates
| * cdee8bb more updates
| * 85afa56 update file-a
|/  
* 2f5eaaf (origin/master, origin/HEAD) add file-a

UPDATE2:更新2:

With above status, when I tried --rebase-merges as suggested by @VonC, I'm getting:在上述状态下,当我按照@VonC 的建议尝试--rebase-merges merges 时,我得到:

$ git rebase --rebase-merges --onto master $(git merge-base dev/br-2 master) dev/br2
fatal: Not a valid object name dev/br-2
fatal: invalid upstream 'dev/br2'

$ git checkout --track origin/dev/br-2
Branch 'dev/br-2' set up to track remote branch 'dev/br-2' from 'origin' by rebasing.
Switched to a new branch 'dev/br-2'

$ git rebase --rebase-merges --onto master $(git merge-base dev/br-2 master) dev/br-2
Successfully rebased and updated refs/heads/dev/br-2.

$ git log --all --decorate --oneline --graph
* 344418c (HEAD -> dev/br-2) update file-a
* 4de3dec more updates
* 81af2ac more updates
* 1e3f9fb update file-a
| * 7aec18c (upstream/dev/br-1) more updates
| * b83c3f8 more updates
| * 4cf241f update file-a
| * 200959c update file-a from main
|/  
* dc45a94 (upstream/master, master) update file-a from main
| * ce2a804 (origin/dev/br-2) update file-a
| * 0006c5e (origin/dev/br-1, dev/br-1) more updates
| * cdee8bb more updates
| * 85afa56 update file-a
|/  
* 2f5eaaf (origin/master, origin/HEAD) add file-a

Here, how to rebase my current dev/br-1 from upstream, then dev/br-2 from dev/br-1 branch please (detailed preparation can be found at https://pastebin.com/Df8VbCp2 , if necessary).在这里,请如何从上游 rebase 我当前的dev/br-1 ,然后从dev/br-1分支 rebase dev/br-2 (详细准备工作可以在https://pastebin.com/Df8VbCp2找到,如有必要)。

WARNING: This is an extremely long answer as it includes every step's command, output and status.警告:这是一个非常长的答案,因为它包括每个步骤的命令、output 和状态。 It is a log of how I achieved my goal, which was:它记录了我如何实现我的目标,即:

======== ========

  1. I forked an upstream, and made my own changes from its development branches ( dev/br-1 ), in my own new branch ( dev/br-2 ), and git push to my own repo.我分叉了一个上游,并在我自己的新分支 ( dev/br-2 ) 中从其开发分支( dev/br-1 ) 进行了自己的更改,并将git push送到我自己的 repo。

    Situation at point#1:第 1 点的情况:

     A---B---C---D master (upstream) \ E---F---G dev/br-1 (upstream) \ H---I---J dev/br-2 (myown)
  2. Now, upstream has advanced, both in its master and develop branches.现在,upstream 在其masterdevelop分支中都取得了进步。

  3. The way upstream advanced its develop branch is via rebase , ie, all its own changes are put back on top after rebase .上游推进其develop分支的方式是通过rebase ,即它自己的所有更改都在 rebase 之后放回顶部

    Situation at point#2&3:第 2 点和第 3 点的情况:

     A---B---C---D master (upstream) \ E'---F'---G' dev/br-1 (upstream)
  4. My local repo is gone with my old machine, and I need to pick-up/git-clone from my own repo to continue with my customization.我的本地存储库随我的旧机器一起消失了,我需要从我自己的存储库中获取/git-clone 以继续我的定制。

  5. I want to rebase my changes in my dev/br-2 branch (which shares a common ancestor) on top of all the upstream changes.我想在所有上游更改之上重新设置我在dev/br-2分支(共享一个共同祖先)中的更改。

Ie, the ultimate goal is to place myown即,最终目标是放置我自己的

H---I---J dev/br-2

branch on top of the newly rebased G' , in my own repo, after catching up with upstream.在赶上上游之后,在我自己的回购协议中,在新重新定位的G'之上分支。 Ie, in my own repo, in the end, it should look like this:也就是说,在我自己的 repo 中,最后应该是这样的:

A---B---C---D  rebased master (upstream)
            \
             E'---F'---G'  rebased dev/br-1 (upstream)
                       \
                        H'---I'---J' rebased dev/br-2

======== ========

Solution:解决方案:

Do it with three git rebase operations.使用三个 git 变基操作来完成。

Again, The details of the preparation steps can be found at pastebin.com/Df8VbCp2, included below to make a full story:同样,准备步骤的详细信息可以在 pastebin.com/Df8VbCp2 上找到,下面包含一个完整的故事:

$ cd /tmp

$ rm -rf upstream/ myfork/

$  mkdir upstream

$  cd upstream

# == prepare its `master` and `dev/br-1` branches

$  git init

seq 12 | tee file-a
git commit -am 'add file-a'

$  git checkout -b dev/br-1
Branch 'dev/br-1' set up to track local branch 'master' by rebasing.
Switched to a new branch 'dev/br-1'

sed -i 's/^1/7/' file-a

git commit -am 'update file-a'

seq 5 6 | tee -a file-a
git commit -am 'more updates'
seq 12 | tee file-b
git commit -am 'more updates'

 cd /tmp
 git clone upstream myfork
 # == prepare my own changes based on the `dev/br-1` branch into `dev/br-2`

$  git branch -avv
* master                  2f5eaaf [origin/master] add file-a
  remotes/origin/HEAD     -> origin/master
  remotes/origin/dev/br-1 0006c5e more updates
  remotes/origin/master   2f5eaaf add file-a

$  git checkout --track origin/dev/br-1
Branch 'dev/br-1' set up to track remote branch 'dev/br-1' from 'origin' by rebasing.
Switched to a new branch 'dev/br-1'

$ git checkout -b dev/br-2
Branch 'dev/br-2' set up to track local branch 'dev/br-1' by rebasing.
Switched to a new branch 'dev/br-2'

sed -i '/9/{ N; N; s/^.*$/3\n4/; }' file-a 

$ cat file-a
7
2
3
4
5
6
7
8
3
4
72
5
6

git commit -am 'update file-a in dev/br-2'

 cd /tmp/upstream
 # advance its `master` 

 sed -i 's/^5/55/' file-a
 git commit -am 'update file-a from main'

 # and its`dev/br-1` branches
 git checkout dev/br-1
 git rebase -X theirs master dev/br-1

$ cat file-a
7
2
3
4
55
6
7
8
9
70
71
72
5
6

# Now upstream has advanced, both in its master and its develop branches (via rebase)

Continuing from there -- Start from point#4, pick-up/git-clone from my own repo to continue with my customization, and then first rebase master, then br-1, then br-2, using从那里继续——从第 4 点开始,从我自己的 repo 中获取/git-clone 以继续我的定制,然后首先 rebase master,然后是 br-1,然后是 br-2,使用

git rebase upstream/master

git switch dev/br-1
git rebase upstream/master

git switch dev/br-2
git rebase --onto dev/br-1 upstream/dev/br-1

Details:细节:

# I need to pick-up from my own repo to continue with my customization.
 cd /tmp
 mv myfork myfork0
 git clone myfork0 myfork1
 cd myfork1
 git remote -v
 git remote add upstream /tmp/upstream
 git remote -v
 git fetch upstream

$ git log --all --decorate --oneline --graph
* 7aec18c (upstream/dev/br-1) more updates
* b83c3f8 more updates
* 4cf241f update file-a
* 200959c update file-a from main
* dc45a94 (upstream/master) update file-a from main
| * ce2a804 (origin/dev/br-2) update file-a
| * 0006c5e (origin/dev/br-1) more updates
| * cdee8bb more updates
| * 85afa56 update file-a
|/  
* 2f5eaaf (HEAD -> master, origin/master, origin/HEAD) add file-a

$ git rebase upstream/master
Successfully rebased and updated refs/heads/master.

$ git log --all --decorate --oneline --graph
* 7aec18c (upstream/dev/br-1) more updates
* b83c3f8 more updates
* 4cf241f update file-a
* 200959c update file-a from main
* dc45a94 (HEAD -> master, upstream/master) update file-a from main
| * ce2a804 (origin/dev/br-2) update file-a
| * 0006c5e (origin/dev/br-1) more updates
| * cdee8bb more updates
| * 85afa56 update file-a
|/  
* 2f5eaaf (origin/master, origin/HEAD) add file-a

$ git switch dev/br-1
fatal: 'dev/br-1' matched multiple (2) remote tracking branches
$ git checkout --track origin/dev/br-1
Branch 'dev/br-1' set up to track remote branch 'dev/br-1' from 'origin' by rebasing.
Switched to a new branch 'dev/br-1'

$ git log --all --decorate --oneline --graph
* 7aec18c (upstream/dev/br-1) more updates
* b83c3f8 more updates
* 4cf241f update file-a
* 200959c update file-a from main
* dc45a94 (upstream/master, master) update file-a from main
| * ce2a804 (origin/dev/br-2) update file-a
| * 0006c5e (HEAD -> dev/br-1, origin/dev/br-1) more updates
| * cdee8bb more updates
| * 85afa56 update file-a
|/  
* 2f5eaaf (origin/master, origin/HEAD) add file-a


$ git rebase upstream/master
Successfully rebased and updated refs/heads/dev/br-1.

$ git log --all --decorate --oneline --graph
* 53d26a9 (HEAD -> dev/br-1) more updates
* 3ad83f2 more updates
* c18eab7 update file-a
| * 7aec18c (upstream/dev/br-1) more updates
| * b83c3f8 more updates
| * 4cf241f update file-a
| * 200959c update file-a from main
|/  
* dc45a94 (upstream/master, master) update file-a from main
| * ce2a804 (origin/dev/br-2) update file-a
| * 0006c5e (origin/dev/br-1) more updates
| * cdee8bb more updates
| * 85afa56 update file-a
|/  
* 2f5eaaf (origin/master, origin/HEAD) add file-a



$ git switch dev/br-2
Branch 'dev/br-2' set up to track remote branch 'dev/br-2' from 'origin' by rebasing.
Switched to a new branch 'dev/br-2'

$ git log --all --decorate --oneline --graph
* 53d26a9 (dev/br-1) more updates
* 3ad83f2 more updates
* c18eab7 update file-a
| * 7aec18c (upstream/dev/br-1) more updates
| * b83c3f8 more updates
| * 4cf241f update file-a
| * 200959c update file-a from main
|/  
* dc45a94 (upstream/master, master) update file-a from main
| * ce2a804 (HEAD -> dev/br-2, origin/dev/br-2) update file-a
| * 0006c5e (origin/dev/br-1) more updates
| * cdee8bb more updates
| * 85afa56 update file-a
|/  
* 2f5eaaf (origin/master, origin/HEAD) add file-a

$ git rebase --onto dev/br-1 upstream/dev/br-1
Auto-merging file-a
CONFLICT (content): Merge conflict in file-a
error: could not apply 85afa56... update file-a
Resolve all conflicts manually, mark them as resolved with
"git add/rm <conflicted_files>", then run "git rebase --continue".
. . .

# fix it, then
git add file-a

$ git rebase --continue
Successfully rebased and updated refs/heads/dev/br-2.

$ git log --all --decorate --oneline --graph
* 76d524a (HEAD -> dev/br-2) update file-a
* 53d26a9 (dev/br-1) more updates
* 3ad83f2 more updates
* c18eab7 update file-a
| * 7aec18c (upstream/dev/br-1) more updates
| * b83c3f8 more updates
| * 4cf241f update file-a
| * 200959c update file-a from main
|/  
* dc45a94 (upstream/master, master) update file-a from main
| * ce2a804 (origin/dev/br-2) update file-a
| * 0006c5e (origin/dev/br-1) more updates
| * cdee8bb more updates
| * 85afa56 update file-a
|/  
* 2f5eaaf (origin/master, origin/HEAD) add file-a

$ cat file-a
7
2
3
4
55
6
7
8
3
4
72
5
6

Here is my current situation (which may have already been messed up, as I see HEAD might be in a weird place):这是我目前的情况(可能已经搞砸了,因为我看到 HEAD 可能在一个奇怪的地方):

 $ git log --all --decorate --oneline --graph * 7aec18c (upstream/dev/br-1) more updates * b83c3f8 more updates * 4cf241f update file-a * 200959c update file-a from main * dc45a94 (upstream/master, master) update file-a from main | * ce2a804 (origin/dev/br-2) update file-a | * 0006c5e (HEAD -> dev/br-1, origin/dev/br-1) more updates | * cdee8bb more updates | * 85afa56 update file-a |/ * 2f5eaaf (origin/master, origin/HEAD) add file-a

OK, I might draw this horizontally as:好的,我可以将其水平绘制为:

A   <-- origin/master
|\
| B--C--D   <-- dev/br-1 (HEAD), origin/dev/br-1
 \       \
  \       E   <-- origin/dev/br-2
   \
    F   <-- master, upstream/master
     \
      G--B'-C'-H--I   <-- upstream/dev/br1

A = 2f5eaaf add file-a ; A = 2f5eaaf add file-a ; B = 85afa56 update file-a ; B = 85afa56 update file-a ; and so on.等等。 I picked the later ones to be the "primes" based on subject lines, although that might be backwards: perhaps the one labeled B should be B' for instance.我根据主题行选择了后面的作为“素数”,尽管这可能是倒退的:例如,标有B的可能应该是B' It seems likely that despite the same subject line for commits B and E , they have different patch-IDs (do different things to file-a ), and it's hard to say whether matching up the two "more updates" commits here (calling the second one on the bottom row C' ) was right.尽管提交BE的主题行相同,但它们似乎有不同的补丁 ID(对file-a做不同的事情),而且很难说这里是否匹配两个“更多更新”提交(调用最后一行的第二个C' ) 是正确的。

(Note that there's no dev/br2 here.) (注意这里没有dev/br2 。)

But let's go back to this:但是让我们 go 回到这个:

[We start with] [我们开始]

 A--B--C--D <-- master (upstream) \ E--F--G <-- dev/br-1 (upstream) \ H--I--J <-- dev/br-2 (myown)

I reformatted this slightly into my own preferred style (two dashes between commits, or one if using a prime marker to indicate that it's a copy).我将其稍微重新格式化为我自己喜欢的样式(提交之间有两个破折号,如果使用主要标记表示它是一个副本,则为一个破折号)。

The ultimate goal is to place myown最终目标是放置我自己的

H--I--J <-- dev/br-2

branch on top of the newly rebased G', in my own repo, after catching up with upstream.在赶上上游之后,在我自己的回购协议中,在新重新定位的 G' 之上分支。 Ie, in my own repo, in the end, it should look like this:也就是说,在我自己的 repo 中,最后应该是这样的:

 A--B--C--D <-- rebased master (upstream) \ E'-F'-G' <-- rebased dev/br-1 (upstream) \ H'-I'-J' <-- rebased dev/br-2

The commits that must be copied are precisely EFGHIJ (in that order).必须复制的提交恰好是EFGHIJ (按此顺序)。

If you have all six commits in your own repository (plus of course the four ABCD commits), then—ignoring the desired labels for a moment—all we need to do is convince Git to copy, as by cherry-picking, the six commits in question.如果您在自己的存储库中拥有所有六个提交(当然还有四个ABCD提交),那么 - 暂时忽略所需的标签- 我们需要做的就是说服 Git 复制,就像通过樱桃挑选一样,六个提交有问题。

The two options for doing this are:执行此操作的两个选项是:

  • git cherry-pick , which leaves both the originals and the copies for us to find easily; git cherry-pick ,它把原件和副本都留给我们很容易找到; or要么
  • git rebase , which does the copying, then moves one (1) branch name around for us. git rebase进行复制,然后为我们移动一 (1) 个分支名称。

Moving one branch name is insufficient.移动一个分支名称是不够的。 It's not particularly harmful and we could allow Git to do it, but let's just do this directly with cherry-pick.它并不是特别有害,我们可以允许 Git 这样做,但让我们直接用 cherry-pick 来做。 We'll start by checking out the commit onto which everything should land, creating a new branch name:我们将首先检查所有内容都应该登陆的提交,创建一个新的分支名称:

git switch -c copied <hash-of-D>

or:要么:

git switch -c copied --no-track upstream/master

(assuming upstream/master names commit D ). (假设upstream/master名称提交D )。 Then:然后:

git cherry-pick <hash-of-D>..<hash-of-J>

or:要么:

git cherry-pick upstream/master..dev/br2

(again the two names used here are just ways of typing in the raw commit hash IDs without having to type in raw commit hash IDs). (同样,这里使用的两个名称只是输入原始提交 hash ID 而无需输入原始提交 hash ID 的方法)。 The two-dot syntax here means any commits reachable from the second specifier, excluding all commits reachable from the first specifier , so this means commits from J all the way back to the root, minus commits from D all the way back to the root which means EFGHIJ .这里的双点语法表示从第二个说明符可到达的任何提交,不包括从第一个说明符可到达的所有提交,因此这意味着J一直提交到根,减去从D一直到根的提交意味着EFGHIJ

The result of this copying would be:此复制的结果将是:

           E'-F'-G'-H'-I'-J'  <-- copied (HEAD)
          /
A--B--C--D   <-- master (upstream)
    \
     E--F--G   <-- dev/br-1 (upstream)
            \
             H--I--J   <-- dev/br-2 (myown)

Now that we have the commits we want, we merely need to place the particular labels.现在我们有了我们想要的提交,我们只需要放置特定的标签。 Since one or more of these labels will point to commit G' , it helps to redraw the above with H'-I'-J' on a row of its own:由于这些标签中的一个或多个将指向提交G' ,因此有助于在其自己的一行上用H'-I'-J'重绘上面的内容:

                   H'-I'-J'  <-- copied (HEAD)
                  /
           E'-F'-G'
          /
A--B--C--D   <-- master (upstream)
    \
     E--F--G   <-- dev/br-1 (upstream)
            \
             H--I--J   <-- dev/br-2 (myown)

The labels we want moved are:我们要移动的标签是:

  • dev/br1 , maybe; dev/br1 ,也许; it should point to G' ;它应该指向G'
  • upstream/dev/br1 —but this is our copy of upstream 's dev/br1 , so we don't move it directly, we have the remote named upstream move theirs so that our Git updates our memory of their name; upstream/dev/br1 — 但这是我们upstreamdev/br1的副本,所以我们不直接移动它,我们让远程命名为upstream移动他们的,以便我们的 Git 更新我们的 memory 他们的名字;
  • origin/dev/br1 : this is just like upstream/dev/br1 ; origin/dev/br1 :这就像upstream/dev/br1
  • dev/br2 : this should point to J' ; dev/br2 :这应该指向J' and
  • myown/dev/br2 or maybe origin/dev/br2 , but once again this is our Git's memory of some other repository's branch name, so we have to convince that other Git repository to move their name. myown/dev/br2或可能是origin/dev/br2 ,但这又是我们 Git 的memory一些其他存储库的分支名称,因此我们必须说服其他 Git 存储库移动他们的名称。

To move our own dev/br1 , we can simply use git branch -f now:要移动我们自己的dev/br1 ,我们现在可以简单地使用git branch -f

git branch -f dev/br1 copied~3

for instance.例如。 Since the name copied selects commit J' , and the ~3 suffix means "move back three first-parents", that will select commit G' .由于copied的名称选择提交J' ,并且~3后缀表示“移回三个第一父母”,因此将 select 提交G' The -f means force and causes our Git to move our dev/br1 . -f表示强制并导致我们的 Git 移动我们的dev/br1

To move upstream 's dev/br1 and cause our upstream/dev/br1 to move, we now need to git push --force-with-lease or similar to upstream , which also assumes that we have permission (on whatever system hosts upstream : Git itself doesn't "do" permissions, but sites like GitHub and others do, for obvious reasons).要移动upstreamdev/br1并导致我们的upstream/dev/br1移动,我们现在需要git push --force-with-lease或类似于upstream ,这也假设我们有权限(在upstream主机上的任何系统上: Git 本身不“执行”权限,但出于显而易见的原因,GitHub 等网站会执行)。 The --force-with-lease tells our Git to verify that their dev/br1 still points where we expect; --force-with-lease告诉我们的 Git 验证他们的dev/br1仍然指向我们期望的地方; if we're sure that this will be the case, we can use plain --force .如果我们确定会是这种情况,我们可以使用普通的--force Either way the command is, or resembles:无论哪种方式,该命令都是或类似于:

git push --force-with-lease upstream dev/br1
git push --force-with-lease origin dev/br1

which uses the fact that we forced our br1 to point to G' .它使用了我们强制br1指向G'的事实。

The same process applies to making the various names point to H' , except that now we can use the name copied :相同的过程适用于使各种名称指向H' ,除了现在我们可以使用copied的名称:

git branch -f dev/br2 copied
git push --force-with-lease origin dev/br2

Once we have done all this, we can switch to dev/br2 or whatever and delete the extra branch name copied .完成所有这些后,我们可以切换到dev/br2或其他任何地方并删除copied的额外分支名称。 It existed only so that we had a nice simple way to find commit H' after all the copying.它的存在只是为了让我们有一个很好的简单方法来在所有复制之后找到提交H'

The key here is to understand that the names are largely irrelevant.这里的关键是要明白名称在很大程度上是无关紧要的。 All that actually matters are the commits , which are identified by their hash IDs.真正重要的是提交,它们由它们的 hash ID 标识。 The trick is that we find the commits using the names, so that makes the names relevant.诀窍是我们找到使用名称的提交,因此使名称相关。

Alternatives备择方案

If you like, you can still do this with two git rebase operations.如果愿意,您仍然可以使用两个git rebase操作来完成此操作。 Since things are relatively simple, we don't need the fancy --onto for the first one (but we will need it for the second):由于事情相对简单,我们不需要花哨的--onto一个(但我们需要第二个):

git switch dev/br-1
git rebase upstream/master

This takes our starting point, which looks like this—note that I'm assuming things about the remote-tracking names this time:这需要我们的起点,它看起来像这样——请注意,我这次假设了关于远程跟踪名称的事情:

A--B--C--D   <-- upstream/master
    \
     E--F--G   <-- dev/br-1 (HEAD), upstream/dev/br-1
            \
             H--I--J   <-- dev/br-2, origin/dev/br-2

and copies EFG to new-and-improved E'-F'-G' , placing them after commit D , as named by upstream/master :并将EFG复制到新的和改进E'-F'-G' ,将它们放在提交D之后,如upstream/master所命名:

           E'-F'-G'  <-- dev/br-1 (HEAD)
          /
A--B--C--D   <-- upstream/master
    \
     E--F--G   <-- upstream/dev/br-1
            \
             H--I--J   <-- dev/br-2, origin/dev/br-2

Having made the three copies, git rebase yanked the name dev/br-1 off commit G and made it point to commit G' instead.制作了三个副本后, git rebase名称dev/br-1从提交G中删除,并使其指向提交G'

Now we will separately copy HIJ :现在我们将单独复制HIJ

git switch dev/br-2
git rebase --onto dev/br-1 upstream/dev/br-1

Here we needed the --onto to tell Git:这里我们需要--onto来告诉 Git:

  • Don't copy commits from upstream/dev/br-1 backwards, ie, don't copy commits G and earlier, but that's not where we want to put the copies!不要从upstream/dev/br-1向后复制提交,即不要复制提交G和更早的提交,但这不是我们想要放置副本的地方!
  • Put the copies after commit G' , ie, the newly updated dev/br-1 .将副本放在提交G'之后,即新更新的dev/br-1

The result of this is:结果是:

                   H'-I'-J'  <-- dev/br-2 (HEAD)
                  /
           E'-F'-G'  <-- dev/br-1
          /
A--B--C--D   <-- upstream/master
    \
     E--F--G   <-- upstream/dev/br-1
            \
             H--I--J   <-- origin/dev/br-2

As with the first git rebase , Git did the copying of the commits found from the current branch name (ie, J and back), excluding the commits we said not to copy (ie, G and back), placing the copies wherever they go—this time that's separate from the "what not to copy" part—and then, having made the copies, git rebase yanked the name dev/br-2 to point to the final copied commit ( J' ).与第一个git rebase一样,Git 复制了从当前分支名称(即J和 back)找到的提交,排除了我们说不复制的提交(即G和 back),将副本放在任何地方—这次与“不复制的内容”部分分开—然后,在制作副本后, git rebase猛拉名称dev/br-2以指向最终复制的提交 ( J' )。

With the (local) repository's names now pointing to the copies, it's once again just a matter of using git push --force-with-lease or git push --force to get the other Git software, working with the two other repositories, to update their branch names, so that our own Git's memories in our remote-tracking names get updated.现在(本地)存储库的名称指向副本,这再次只是使用git push --force-with-leasegit push --force获取其他Git 软件的问题,与其他两个存储库一起工作,更新他们的分支名称,以便我们自己的 Git 在我们的远程跟踪名称中的记忆得到更新。

(If you can't literally force-push to upstream or origin , you can still send the updated commits , eg, via pull requests, but someone else will have to convince the other repositories to actually move their branch names to take in the new commits.) (如果你不能从字面上强制推送到upstreamorigin ,你仍然可以发送更新的提交,例如,通过拉取请求,但其他人将不得不说服其他存储库实际移动他们的分支名称以接受新的提交。)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM