为什么“rebase - 在ABC”不同于“rebase ABC”? [英] Why is "rebase --onto ABC" different than "rebase ABC"?

查看:92
本文介绍了为什么“rebase - 在ABC”不同于“rebase ABC”?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

使用git 2.11, git rebase 文档说:


当前分支被重置为< upstream>或< newbase>如果提供了 -
选项。这与git reset
--hard(或)具有完全相同的效果。


我理解它就好像 upstream(上游)一样,ORIG_HEAD被设置为指向分支的顶端。 newbase 指向相同的基本引用,那么这意味着下面的两个rebase语法是等价的:

  git rebase ABC 
git rebase --onto ABC

这是我设置的演示。假设当前分支是 FeatureABC ,它完全与远程分支同步。

 #---创建两个完全相同的分支,位于当前分支后面5个提交
(FeatureABC)git分支Demo1- Rebase-ABC HEAD〜4
(FeatureABC)git分支Demo2-Rebase-to-ABC HEAD〜4

#---在分支Demo1中进行新的提交
git checkout Demo1-Rebase-ABC
echo演示:git rebase FeatureABC Demo1-Rebase-ABC> ./Demo1_BogusFile.txt
git add ./Demo1_BogusFile.txt
git commit -m创建文件Demo1_BogusFile.txt

git rebase FeatureABC


首先,倒带头重播你的作品 -
...申请:创建文件Demo1_BogusFile.txt

git log --oneline -3 显示分支 Demo1-Rebase-ABC 与FeatureABC的HEAD同步。并提交创建文件Demo1_BogusFile.txt正确应用在它上面。

 #---做一个新的提交在分支Demo2 
git checkout Demo2-Rebase-to-ABC
echoDemo:git rebase --onto FeatureABC Demo2-Rebase-to-ABC> ./Demo2_Onto_BogusFile.txt
git add ./Demo2_Onto_BogusFile.txt
git commit -m创建文件Demo2_Onto_BogusFile.txt

git rebase --ont FeatureABC




当前分支没有跟踪信息。请
指定您想要反转的分支。有关
的详细信息,请参阅git-rebase(1)。

  git rebase< branch> 

如果您希望为这个分支设置跟踪信息,您可以这样做

  git branch --set-upstream-to = origin /< branch> Demo2-Rebase-onto-ABC 


我误解了警告信息。当使用--onto时,认为git在默认情况下被混淆了。所以我只是想通过告诉git当前的分支来帮助我要分配的数据

  git rebase --onto FeatureABC Demo2- Rebase-to-ABC 




首先,回头重放你的作品顶部...


git log --oneline -3 显示分支 Demo2-Rebase-to-ABC FeatureABC 成为相同。最后一个提交创建文件Demo2_Onto_BogusFile.txt消失了,并且文件 ./ Demo2_Onto_BogusFile.txt 被删除。



问题 git rebase --on FeatureABC Demo2-Rebase-to-ABC 不适用新提交 Demo2-Rebase-to-ABC 分支

解决方案

不一样,这也可以通过 - fork-point 选项变得复杂。我认为这可能是你的一点,尽管无法确定,就像你所描述的那样,因为你列出的步骤之一只会产生一个错误。



我从一个合理的猜测开始,但它是猜测



要看看真正发生的事情,绘制(部分)提交图,特别注意标签,因为你使用的多个名称都指向一次提交。


让我们假设当前分支是 FeatureABC 它完全与远程分支同步。


因此,我们有这样的东西 - 但是 这样的东西不够好; 有存储库,所以应该绘制图形;我必须猜测:

  ...-- o  -  A  -  B-- C  -  D  -  E < -  FeatureABC(HEAD),origin / FeatureABC 

运行:


 #---在当前分支后面创建两个完全相同的分支5次提交
(FeatureABC)git分支Demo1-Rebase-ABC HEAD〜4
(FeatureABC)git分支Demo2-Rebase-to-ABC HEAD〜4
HEAD〜4
名称提交 A

c $ c>( HEAD〜1 D HEAD〜2 C ,等等),我们需要做一些事情来标记这两个新名称指向提交 A 。尽管如此,我将缩短名称为 Demo1 Demo2 。 (我创建了一个仓库,此时只提交 o E ,并且实际运行 git branch Demo1 HEAD〜4; git branch Demo2 HEAD〜4 here。)

 。 ...  -  o  -  A<  -  Demo1,Demo2 
\
B - C - D - E < - FeatureABC(HEAD),origin / FeatureABC

顺便提一句, git log --all --decorate --oneline --graph code>(从A DOG获得帮助,就像有人说的那样)以这种方式显示了这个测试库(在我的例子中没有 origin / 分支):

  * c4a0671(HEAD  - > master)E 
* a7b8ae4 D
* 3deea72 C
* b11828d B
* ffc29b5(Demo2,Demo1)A
* 3309a8d初始

接下来,您检查Demo1,移动 HEAD


  git checkout Demo1-Rebase-ABC 




  ...-- o  -  A< ;  -  Demo1(HEAD),Demo2 
\
B - C - D - E < - FeatureABC,origin / FeatureABC

并修改工作树,将修改后的文件添加到索引中,并提交,以创建一个新的提交,我将调用 F ,它更新 HEAD 分支,并因此分隔 Demo1 DEMO2

  $ git checkout Demo1 
切换到分支'Demo1 '
$ echo demo1> demo1.txt&& git add demo1.txt&& git commit -m F
[Demo1 89773b6] F
1文件已更改,1个插入(+)
创建模式100644 demo1.txt

绘制图形有点困难;

  F < -  Demo1(HEAD)
/
... - o - A< - Demo2
\
B - C - D - E < - FeatureABC,origin / FeatureABC

现在我们得到您的第一个 git rebase 命令。当然,我必须使用 master

  $ git rebase master 
首先,回头重播你的作品...
申请:F

这适用于当前分支( HEAD Demo1 )。它发现不在 FeatureABC FeatureABC .. )上的提交< HEAD code>在 gitrevisions 语法中)。这是提交 F 。这些提交会被放入可能副本的提交列表中 - git rebase 将检查具有相同的 git补丁的提交-id 并跳过它们,虽然很明显在这里没有发生。因此,现在提交 F 被复制到具有不同哈希ID和不同基础的新提交 F'

  F [弃用] 
/
...-- o - A< - Demo2
\
B - C - D - E < - FeatureABC,origin / FeatureABC
\
F'< - Demo1(HEAD)

(下面是实际的 git log 输出,显示了新的提交除非我添加了 Demo1 @ {1} ,否则不会显示原始的,现在已被废弃的 F 这是我在这里所做的命令,原来的是 second F 显示的,也就是前面的commit:

  $ git log --all --decorate --oneline --graph Demo1 @ {1} 
* c1d0896(HEAD - &> Demo1)F
* c4a0671(主)E
* a7b8ae4 D
* 3deea72 C
* b11828d B
| * 89773b6 F
| /
* ffc29b5(Demo2)A
* 3309a8d初始

我更喜欢横向图,但是这个有更多的信息,特别是缩写)

复制器失败,我必须再次猜测



现在我们试着重复这与 Demo2 ,但它失败。这是我的实际命令,剪切粘贴。第一步工作正常:

  $ git checkout Demo2 
转换到分支'Demo2'
$ echo demo2> demo2.txt&& git add demo2.txt&& git commit -m G
[Demo2 ae30665] G
1文件已更改,1个插入(+)
创建模式100644 demo2.txt

不再绘制原始的 F ,这里是新的图形。我把 G 其中 F 曾经是,尽管我可以将它画成。 。 - o - A - G

  G < -  Demo2(HEAD )
/
... - o - A
\
B - C - D - E < - FeatureABC,origin / FeatureABC
\
F < - Demo1

然而,rebase工作。再次,我必须使用 master 而不是 FeatureABC ,但这在您的示例中的表现方式相同,因为 git branch 命令没有设置上游(跟踪)名称:

  $ git rebase --onto master 
当前分支没有跟踪信息。
请指定您想要反转的分支。
有关详细信息,请参阅git-rebase(1)。

git rebase< branch>

如果你想为这个分支设置跟踪信息,你可以这样做:

git branch --set-upstream-to =< remote> /< branch> ; Demo2

原因 git rebase 失败,因此错误消息是 - 到吸收了< newtarget> 这个参数,让我们没有< upstream> <上游> ,在分支中配置上游< name> .remote 分支。< name> .merge 选项将会(请参阅 git-config(1) a>以获取详细信息),并假定 - 分支点选项。如果您目前没有在任何分支上,或者当前分支没有配置上游,那么rebase会中止。

这是我的,但它也是我认为的关键。我假设你运行了一个 c ,而 没有失败。因为它没有失败,你的分支必须有一个上游设置。上游可能 origin / FeatureABC 或类似,这意味着就Git而言,您正在运行:

  git rebase --onto FeatureABC  - 伪点原点/ FeatureABC 

不是:



  git rebase  - 到FeatureABC  - 无叉点起源/ FeatureABC 

部分进一步阅读(在我看来,过分神秘) git rebase


如果< / p> 上游> - root 在命令行中给出,那么
默认为 - no -fork-point ,否则默认为
- 分叉点




换句话说:

  git rebase FeatureABC 

关闭 - fork-point 选项,如下所示:

  git rebase --ont FeatureABC FeatureABC 

但是:

  git rebase 


或$:

  git rebase  -  -  FeatureABC 

离开 - 叉点



什么 - 分叉点 h3>

- fork-point 目标特指 drop 过去,曾经是在你的上游,但不再在你的上游。有关示例,请参阅 Git rebase - 以fork-point模式提交选择具体的机制很复杂,依赖于上游分支的reflog。因为我没有仓库或reflog,我无法测试你的具体情况 - 但这是一个原因,也可能是你的问题提示中最可能的原因,即会影响rebase 树结果的提交。会掉线。由于拥有相同的而被放弃的提交修补程序ID 作为上游提交是[编辑:]经常 不会影响最后一次修改的最终树的修改,复制提交:它们只会导致合并冲突和/或强制您使用 git rebase --skip 跳过它们(如果包含它们)。






1 在写完这篇文章后,我想到了一个重要的异常(这可能与原来的问题,但我应该提到)。将特性或主题分支重新映射到更主线分支上时,如果首次将提交选项提交到主线,然后在主线中将还原提交,将会造成问题。考虑,例如:

  ...  -  o  -  *  -  P  -  Q  -  C'-R- -S  -  X  -  T  -   - 主线
\
A - B - C - D - E - - 主题

其中 C'是commit C X 是提交 C 时不应该放入主线。做:

  git checkout topic 
git rebase mainline

将指示Git将提交 A 通过 E 进入候选人复制列表,还可以看看 P T ,看看是否有人被采用。提交 C 采用,如 C'。如果 C C'具有相同的补丁ID,通常它们会 -Git将从列表中删除 C 作为已复制。但是,在提交 X 时显式还原了 C



无论是谁都需要注意,并仔细恢复 C ,如果需要和适当。



这个特殊的行为不是 git merge (因为合并忽略了中间提交)的问题,只有 git rebase


Using git 2.11, git rebase documentation says:

The current branch is reset to <upstream>, or <newbase> if the --onto option was supplied. This has the exact same effect as git reset --hard (or ). ORIG_HEAD is set to point at the tip of the branch before the reset.

I understand it as if upstream and newbase points to the same "base reference", then this means the two rebase syntaxes below are equivalent:

git rebase ABC
git rebase --onto ABC

Here is the demo I setup. Let's assume the current branch is FeatureABC it is perfectly in sync with the remote branch.

#---create two identical branches, behind current branch by 5 commits
(FeatureABC) git branch Demo1-Rebase-ABC      HEAD~4
(FeatureABC) git branch Demo2-Rebase-onto-ABC HEAD~4

#---Make a new commit in branch Demo1
git checkout Demo1-Rebase-ABC
echo "Demo of: git rebase FeatureABC Demo1-Rebase-ABC" > ./Demo1_BogusFile.txt
git add ./Demo1_BogusFile.txt
git commit -m "Create file Demo1_BogusFile.txt"

git rebase FeatureABC

First, rewinding head to replay your work on top of it
... Applying: Create file Demo1_BogusFile.txt

git log --oneline -3 shows that the branch Demo1-Rebase-ABC is sync'ed with the HEAD of FeatureABC. And the commit "Create file Demo1_BogusFile.txt" was correctly applied on top of it.

#---Make a new commit in branch Demo2
git checkout Demo2-Rebase-onto-ABC
echo "Demo of: git rebase --onto FeatureABC Demo2-Rebase-onto-ABC" > ./Demo2_Onto_BogusFile.txt
git add ./Demo2_Onto_BogusFile.txt
git commit -m "Create file Demo2_Onto_BogusFile.txt"

git rebase --onto FeatureABC

There is no tracking information for the current branch. Please specify which branch you want to rebase against. See git-rebase(1) for details.

git rebase <branch>

If you wish to set tracking information for this branch you can do so with:

git branch --set-upstream-to=origin/<branch> Demo2-Rebase-onto-ABC

I misunderstood the warning message. Thinking that git was confused in the defaults when --onto is used. So I just want to "help" by telling git the current branch I want to rebase

git rebase --onto FeatureABC Demo2-Rebase-onto-ABC

First, rewinding head to replay your work on top of it...

git log --oneline -3 shows that the branch Demo2-Rebase-onto-ABC becomes identical than FeatureABC. The last commit "Create file Demo2_Onto_BogusFile.txt" has vanished and the file ./Demo2_Onto_BogusFile.txt is deleted.

Question: what is the reason the git rebase --onto FeatureABC Demo2-Rebase-onto-ABC didn't apply the new commits made on the Demo2-Rebase-onto-ABC branch?

解决方案

They are not the same, and this can get complicated by the --fork-point option as well. I think this may be what bit you, although it's not possible to be sure, just from what you have described, as one of the steps you outlined merely produces an error.

I start with a reasonable guess, but it is a guess

To see what's really happening it is very helpful to draw (part of) the commit graph, with special attention to labeling since you are using multiple names that all point to a single commit.

Let's assume the current branch is FeatureABC it is perfectly in sync with the remote branch.

Hence we have something like this—but something like is not really good enough; you have the repository, so you should draw the graph; I have to guess:

...--o--A--B--C--D--E   <-- FeatureABC (HEAD), origin/FeatureABC

Now you run:

#---create two identical branches, behind current branch by 5 commits
(FeatureABC) git branch Demo1-Rebase-ABC      HEAD~4
(FeatureABC) git branch Demo2-Rebase-onto-ABC HEAD~4

Since HEAD~4 names commit A (HEAD~1 is D, HEAD~2 is C, and so on), we need to do something to mark the fact that these two new names point to commit A. I'm going to shorten the names to just Demo1 and Demo2 though. (I've created a repository with only commits o through E at this point, and actually run git branch Demo1 HEAD~4; git branch Demo2 HEAD~4 here.)

...--o--A              <-- Demo1, Demo2
         \
          B--C--D--E   <-- FeatureABC (HEAD), origin/FeatureABC

Incidentally, git log --all --decorate --oneline --graph ("get help from A DOG" as someone put it) shows this test repository this way (there is no origin/ branch in my case):

* c4a0671 (HEAD -> master) E
* a7b8ae4 D
* 3deea72 C
* b11828d B
* ffc29b5 (Demo2, Demo1) A
* 3309a8d initial

Next, you check out Demo1, moving HEAD:

git checkout Demo1-Rebase-ABC

...--o--A              <-- Demo1 (HEAD), Demo2
         \
          B--C--D--E   <-- FeatureABC, origin/FeatureABC

and modify the work-tree, add the modified file to the index, and commit, to make a new commit which I will call F, which updates the HEAD branch and therefore separates Demo1 and Demo2. I'll now use my own commands and their output here:

$ git checkout Demo1
Switched to branch 'Demo1'
$ echo demo1 > demo1.txt && git add demo1.txt && git commit -m F
[Demo1 89773b6] F
 1 file changed, 1 insertion(+)
 create mode 100644 demo1.txt

Drawing the graph gets a bit harder; I'll use a row up:

          F            <-- Demo1 (HEAD)
         /
...--o--A              <-- Demo2
         \
          B--C--D--E   <-- FeatureABC, origin/FeatureABC

Now we get to your first git rebase command. I have to use master, of course:

$ git rebase master
First, rewinding head to replay your work on top of it...
Applying: F

This works on the current branch (HEAD or Demo1). It finds commits that are on HEAD that are not on FeatureABC (FeatureABC.. in gitrevisions syntax). That's commit F. These commits get put into a list of commits to maybe copy—git rebase will check for commits with the same git patch-id and skip them, although clearly that did not happen here. So now commit F is copied to new commit F', with different hash ID and different base:

          F              [abandoned]
         /
...--o--A                <-- Demo2
         \
          B--C--D--E     <-- FeatureABC, origin/FeatureABC
                    \
                     F'  <-- Demo1 (HEAD)

(Here's the actual git log output, showing the new commit hash for the copy. The original, now-abandoned F is not shown unless I add Demo1@{1} to the command, which I did here. The original is the second F shown, i.e., the earlier commit:

$ git log --all --decorate --oneline --graph Demo1@{1}
* c1d0896 (HEAD -> Demo1) F
* c4a0671 (master) E
* a7b8ae4 D
* 3deea72 C
* b11828d B
| * 89773b6 F
|/  
* ffc29b5 (Demo2) A
* 3309a8d initial

I like the horizontal graph better, but this one has more information, specifically the abbreviated hash IDs.)

Reproducer fails, and I'll have to guess again

Now we try to repeat this with Demo2, but it fails. Here are my actual commands, cut-and-pasted. The first step works fine:

$ git checkout Demo2
Switched to branch 'Demo2'
$ echo demo2 > demo2.txt && git add demo2.txt && git commit -m G
[Demo2 ae30665] G
 1 file changed, 1 insertion(+)
 create mode 100644 demo2.txt

No longer drawing the original F, here is the new graph. I put G where F used to be, although I could draw this as just ...--o--A--G:

          G              <-- Demo2 (HEAD)
         /
...--o--A
         \
          B--C--D--E     <-- FeatureABC, origin/FeatureABC
                    \
                     F   <-- Demo1

The rebase, however, does not work. Again I have to use master instead of FeatureABC, but this would behave the same way in your example, given that the git branch command did not set an upstream ("tracking") name:

$ git rebase --onto master
There is no tracking information for the current branch.
Please specify which branch you want to rebase against.
See git-rebase(1) for details.

    git rebase <branch>

If you wish to set tracking information for this branch you can do so with:

    git branch --set-upstream-to=<remote>/<branch> Demo2

The reason git rebase failed with this error message is that --onto has absorbed the argument as <newtarget>, leaving us with no <upstream>:

If <upstream> is not specified, the upstream configured in branch.<name>.remote and branch.<name>.merge options will be used (see git-config(1) for details) and the --fork-point option is assumed. If you are currently not on any branch or if the current branch does not have a configured upstream, the rebase will abort.

The boldface here is mine, but it's also, I think, the key. I assume you ran a git rebase --onto <somename> that did not fail. For it to have not-failed, your branch must have had an upstream set. That upstream probably was origin/FeatureABC or similar, and that meant that as far as Git was concerned, you were running:

git rebase --onto FeatureABC --fork-point origin/FeatureABC

and not:

git rebase --onto FeatureABC --no-fork-point origin/FeatureABC

Some further reading in the (overly cryptic, in my opinion) git rebase documentation will turn up this sentence:

If either <upstream> or --root is given on the command line, then the default is --no-fork-point, otherwise the default is --fork-point.

In other words:

git rebase FeatureABC

turns off the --fork-point option, as does:

git rebase --onto FeatureABC FeatureABC

but:

git rebase

or:

git rebase --onto FeatureABC

leaves the --fork-point option on.

What --fork-point is about

The goal of --fork-point is to specifically drop commits that used to be, at one time, in your upstream, but are no longer in your upstream. See Git rebase - commit select in fork-point mode for an example. The specific mechanism is complicated and relies on the upstream branch's reflog. Since I don't have either your repository or your reflog, I cannot test out your specific case—but that's one reason, and probably the most likely reason given the hints in your question, that a commit that would affect the rebase tree result would get dropped. The commits that are dropped due to having the same patch ID as an upstream commit are ones that [edit:] often1 will not affect the final tree of the last-copied commit: they would just cause merge conflicts and/or force you to use git rebase --skip to skip over them, if they were included.


1It occurred to me after writing this that there is an important exception (which probably has nothing to do with the original question, but which I should mention). Rebasing a feature or topic branch onto a more mainline branch, when a commit was first cherry-picked out of the feature into the mainline, and then reverted in the mainline, will cause a problem. Consider, e.g.:

...--o--*--P--Q--C'-R--S--X--T   <-- mainline
         \
          A--B--C--D--E          <-- topic

where C' is a copy of commit C, and X is a revert of commit C that should not have been put into mainline yet. Doing:

git checkout topic
git rebase mainline

will instruct Git to put commits A through E into the "candidates to copy" list, but also look at P through T to see if any were already adopted. Commit C was adopted, as C'. If C and C' have the same patch ID—usually, they will—Git will drop C from the list as "already copied". However, C was explicitly reverted in commit X.

Whoever does the rebase needs to notice, and carefully restore C if it is required and appropriate.

This particular behavior is not a problem with git merge (since merge ignores intermediate commits), only with git rebase.

这篇关于为什么“rebase - 在ABC”不同于“rebase ABC”?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆