跟进Git: rebase onto development branch from upstream,基本上与以下问题相同:
我有本地分支master
和develop
。我在develop
上做了所有的工作,然后将它们合并到master
中发布。有一个远程分支upstream/master
,它有我想要的修改,但我想在develop
中重定更改的基(它共享一个共同的祖先),并将它们放回develop
中。
这是我面临的(有点复杂/高级)情况,从头开始。
1.我分叉了一个上游,并在我自己的新分支(dev/br-2
)中从它的开发分支 (dev/br-1
)和git push
对我自己的repo进行了自己的更改。
1.如今,上游在其master
和develop
两个分支上都取得了进步。
1.向上游推进其develop
分支的方式是通过rebase
,即,在rebase * 之后,它自己的所有更改都被 * 放回顶部。
1.我的本地repo和我的旧机器一起消失了,我需要从我自己的repo中提取/git-clone来继续我的定制。
1.我想在所有上游变更的基础上对dev/br-2
分支(共享一个共同的祖先)中的变更进行重定基。
1.我已经完成了git fetch upstream
,并且能够用上游的master
重新定基。
1.这是如何从上游重定我当前的dev/br-1
,然后从dev/br-1
分支重定dev/br-2
,使我的头不停地旋转。
虽然这看起来像是一个非常具体的案例,但是如何进行git分支和重定基的原则仍然适用,并且这个答案对普通大众来说是非常有教育意义的。
- 更新:* 所以我看了@torek建议的
git rebase --onto
命令,比如How to git rebase a branch with the onto command?,以及他们所有的refed文档,我认为我的问题仍然比我读到的要高出一两个层次(因为涉及到两个repos和5个分支)。
1号点的情况:
A---B---C---D master (upstream)
\
E---F---G dev/br-1 (upstream)
\
H---I---J dev/br-2 (myown)
第2点和第3点的情况:
A---B---C---D master (upstream)
\
E'---F'---G' dev/br-1 (upstream)
我甚至不知道应该在哪里画myown
分支,下面是我目前的情况(可能已经被搞砸了,因为我看到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
最终的目标是把我自己的H---I---J dev/br-2
在我自己的回购协议中,在赶上上游之后,在新重定基的G'
之上的分支。即,在我自己的回购协议中,最后,它应该看起来像这样:
A---B---C---D rebased master (upstream)
\
E'---F'---G' rebased dev/br-1 (upstream)
\
H'---I'---J' rebased dev/br-2
如何做到这一点?
按命令进行更多解释:
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
. . .
现在上游已经进步了,无论是在它的master
还是develop
分支(通过rebase
),我需要从我自己的回购中挑选,以 * 继续 * 我的定制。
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
更新2:
在上述状态下,当我按照@VonC的建议尝试--rebase-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
在这里,如何从上游重定我当前的dev/br-1
,然后从dev/br-1
分支重定dev/br-2
(如果需要,可以在https://pastebin.com/Df8VbCp2找到详细的准备工作)。
2条答案
按热度按时间o0lyfsai1#
以下是我目前的情况(可能已经被搞砸了,因为我看到HEAD可能在一个奇怪的地方):
好的,我可以把它水平画成:
x一个m一个n一个x = x一个m一个n一个x x~1米~2米~1 x = x~1米~3米~1 x;等等。我选择了后面的那些作为基于主题行的"质数",尽管这可能是倒过来的:例如,标记为
B
的应该是B'
。看起来尽管提交B
和E
的主题行相同,但它们有不同的补丁ID(对file-a
做不同的事情),很难说匹配这里的两个"更多更新"提交(调用最下面一行的第二个C'
)是否正确。(Note这里没有
dev/br2
。)但让我们回到这一点:
[We开头为]
我将其稍微重新格式化为我自己喜欢的样式(两个破折号在提交之间,或者一个破折号,如果使用主要标记来指示它是一个副本)。
最终的目标是把我自己的
在我自己的回购协议中,在赶上上游之后,在新重定基的G '上的分支。也就是说,在我自己的回购协议中,最后,它应该看起来像这样:
必须复制的提交正好是
E-F-G-H-I-J
(按此顺序)。如果你在自己的仓库里有所有的6个提交(当然还有4个
A-B-C-D
提交),那么-暂时忽略你想要的 * 标签 *-我们所要做的就是说服Git复制,就像摘樱桃一样,有问题的6个提交。执行此操作的两个选项是:
git cherry-pick
,这使得原件和副本都很容易找到;或git rebase
执行复制,然后为我们移动一(1)个分支名称。移动一个分支名是不够的,它不是特别有害,我们可以允许Git这样做,但让我们直接用cherry-pick来做。我们将从检查所有内容应该到达的提交开始,创建一个新的分支名:
或:
(假设
upstream/master
名称提交D
)。则:或:
(再次,这里使用的两个 * 名称 * 仅仅是键入原始提交散列ID而不必键入原始提交散列ID的方式).这里的两点语法意味着 * 从第二说明符可达的任何提交,排除从第一说明符可达的所有提交 *,因此这意味着 * 从
J
一直回到根的提交,减去从D
一直返回到根 * 的提交,这意味着E-F-G-H-I-J
。此复制的结果将是:
现在我们已经有了想要的 * commits *,我们只需要放置特定的标签,因为这些标签中的一个或多个将指向commit
G'
,所以将上面的标签重新绘制成H'-I'-J'
所在的行就很有帮助了:要移动的标签为:
dev/br1
,也许是;它应该指向G'
;upstream/dev/br1
-但这是upstream
的dev/br1
的 * 我们的副本 *,所以 * 我们 * 不直接移动它,我们让名为upstream
的远程设备移动 * 他们的 *,以便我们的Git更新我们的内存中的名称;origin/dev/br1
:这就像upstream/dev/br1
一样;dev/br2
:此应指向J'
;以及myown/dev/br2
或origin/dev/br2
,但这又是我们的Git对其他仓库的分支名称的 * 内存 *,所以我们必须说服 * 其他Git仓库 * 移动 * 他们的 * 名称。要移动我们自己的
dev/br1
,现在可以简单地使用git branch -f
:例如,由于名称
copied
选择提交J'
,后缀~3
表示"向后移动三个第一父节点",因此将选择提交G'
。-f
表示 * 强制 *,并导致Git移动dev/br1
。要移动
upstream
的dev/br1
并使我们的upstream/dev/br1
移动,我们现在需要git push --force-with-lease
或类似于upstream
,这也假设我们有权限(在任何托管upstream
的系统上:Git本身并不"执行"权限,但像GitHub这样的网站会执行,原因很明显)。--force-with-lease
告诉我们的Git验证他们的dev/br1
是否仍然指向我们期望的位置;如果我们确信这是真的,我们可以使用普通的--force
。无论哪种方式,命令都是,或者类似于:它利用了我们强制
br1
指向G'
的事实。同样的过程也适用于使不同的名称指向
H'
,只是现在我们可以使用名称copied
:一旦我们完成了所有这些,我们就可以切换到
dev/br2
或者其他什么,并删除额外的分支名称copied
。它的存在只是为了让我们有一个很好的简单方法来在所有复制之后 * 查找 * 提交H'
。备选方案
如果你愿意,你仍然可以用两个
git rebase
操作来完成这个任务,因为事情相对简单,我们不需要为 * 第一个 * 使用花哨的--onto
(但是我们需要为第二个):这是我们的起点,看起来像这样--注意,这次我假设的是远程跟踪名称:
并且将
E-F-G
复制到新的和改进的E'-F'-G'
,将它们放置在提交D
之后,如由upstream/master
命名的:创建了三个副本后,
git rebase
将 namedev/br-1
从提交G
中删除,并使其指向提交G'
。现在我们将单独复制
H-I-J
:这里我们需要
--onto
来告诉Git:upstream/dev/br-1
的提交,也就是说,* don 't * copy commitsG
及更早的提交,但那不是我们想要放置副本的地方!G'
后放置副本,即新更新的dev/br-1
。与第一个
git rebase
一样,Git复制了从当前分支名称中找到的提交(即J
及其后),不包括我们声明 not 要复制的提交(即,G
和背面),将副本放置在它们所到的任何地方-这次与“什么不要复制”部分分开-然后,在制作了副本之后,git rebase
将 namedev/br-2
拉到指向最终复制的提交(J'
)。(本地)仓库的名称现在指向副本,再一次只需要使用
git push --force-with-lease
或git push --force
获取 other Git软件,与两个 other 仓库一起更新 * 它们的 * 分支名称,这样我们自己的Git内存中的远程跟踪名称也会得到更新。(If你不能直接强制推送到
upstream
或origin
,你仍然可以发送更新后的提交,例如通过拉取请求,但是其他人必须说服其他仓库移动他们的分支名称来接收新的提交。lymnna712#
***警告:***这是一个 * 非常 * 长的答案,因为它包括了每个步骤的命令、输出和状态。它是我如何实现目标的日志,即:
========
1.我分叉了一个上游,并在我自己的新分支(
dev/br-2
)中,从**它的开发分支 *(dev/br-1
)和git push
中对我自己的repo进行了自己的更改。1号点的情况:
1.现在,上游已经取得了进步,无论是在
master
还是develop
分支。1.向上游推进其
develop
分支的方式是通过rebase
,即,在rebase之后,它自己的所有更改都被 * 放回***顶部***。第2点和第3点的情况:
1.我的本地repo和我的旧机器一起消失了,我需要从我自己的repo中提取/git-clone来继续我的定制。
1.我想在所有上游变更的基础上对
dev/br-2
分支(共享一个共同的祖先)中的变更进行重定基。也就是说,最终的目标是把我自己的
H---I---J dev/br-2
在我自己的repo中,在赶上上游之后,在新重定基的
G'
之上的分支。也就是说,在我自己的repo中,最后,它应该看起来像这样:========
用三个git rebase操作来完成。
再次,准备步骤的细节可以在pastebin.com/Df8VbCp2上找到,包括下面做一个完整的故事:
从这里继续--从第4点开始,从我自己的repo中提取/git-clone以继续我的定制,然后使用
详情: