2016-11-15 112 views
3

在这个例子中,git并不认为它需要重定位,但很明显。为什么这个git rebase认为没有什么可做的?

下面是一个简单的脚本在这之后运行,以创建一个小型测试git仓库

#!/bin/bash 
rm -rf testgit 
mkdir testgit 
cd testgit 
git init 
echo -e "a\nb\nc" > file 
git add file 
git commit -am 'first' 

git checkout -b lineA 
echo -e "A\nb\nc" > file 
git commit -am 'A' 

git checkout -b lineB 
echo -e "a\nB\nc" > file 
git commit -am 'B' 

git checkout -b lineC 
echo -e "a\nb\nC" > file 
git commit -am 'C' 

,这里是file看起来像每个分支

master | lineA | lineB | lineC 
------------------------------ 

a  A  a  a 
b  b  B  b 
c  c  c  C 

现在,让我们所有的分支机构合并成高手,之后file中的每个字母应该大写

$ git checkout master 
$ git merge lineA 

好的。

$ git checkout lineB 
$ git rebase master 
Current branch lineB is up to date. 

什么?不,主人改变了。

$ git checkout master 
$ cat file 
A 
b 
c 

$ git checkout lineB 
$ cat file 
a 
B 
c 

在我看来,分支lineB需要重新绑定,以将合并lineA所做的更改合并到master中。

为什么不git认为这需要做?

此外,如果你这样做

$ git checkout master 
$ git merge lineA 
Already up-to-date. 
$ git merge lineB 
... 
1 file changed, 2 insertions(+), 2 deletions(-) 
$ cat file 
a 
B 
c 
$ git merge lineC 
... 
1 file changed, 2 insertions(+), 2 deletions(-) 
$ cat file 
a 
b 
C 

这里的合并应该发生冲突,但Git是悄悄重挫他们。我不知道这是否相关,但似乎很奇怪。

回答

2

的第一个命令

我们的脚本后的结果(缩写支线的名字......):

master lA lB lC 
    ↓  ↓ ↓ ↓ 
first----A----B----C 

这些命令:

$ git checkout master 
$ git merge lineA 

结果一个快进合并,它只是将master指向与lineA相同的提交。

,并提供:

 master 
     lA lB lC 
     ↓ ↓ ↓ 
first----A----B----C 

没有工作的底垫

因此命令序列:

$ git checkout lineB 
$ git rebase master 

是说移动lineB分支上的master顶部。

...但它已经在master之上。

master在其历史记录中必须有一些提交lineB没有让rebase命令有任何工作要做。

没有合并冲突

当Git会创建一个合并提交它认为正在进入合并父母的历史。在这种情况下,两个分支都指向提交一个公共链上的提交。 Git可以看到A提交发生在C提交之前的B提交之前。因为Git认为后面的承诺是在早期承诺之上增加的,所以它们不会发生冲突。

相反,如果你有一个像史:

 master 
     ↓ 
     ---A 
    /
    | lB 
    | ↓ 
first----B 

然后混帐会看到他们作为发展的平行线。在这种情况下,合并lBmaster或反之亦然会发生冲突。这是因为B不是历史上的A的顶部的附加项,因此Git需要用户输入以了解要保留哪些更改。

+0

我想也一样。使用git扩展来查看你的分支树形状。对于我来说,所有你创建的分支都有他们的主人,所以git以它的方式告诉你,不需要rebase。在这一点上,你只需要合并 –

1

我认为你的困惑之处还在后面。当您执行

git commit -am 'first' 
git checkout -b lineA 

你没有创建一个名为lineA新的分支,但分叉点被提交你只是做了(“first”)。正如Git所说的,first是两个分支历史的一部分,并且在您的脚本中,这也是两个分支中最近的提交。 Git的日志有一些选项显示两个分支名称指向第一个承诺,在本例中是提交ID 58b7dcf:

git log --graph --oneline --decorate 
* 58b7dcf (HEAD, master, lineA) first 

继续:

echo -e "A\nb\nc" > file 
git commit -am 'A' 
git log --graph --oneline --decorate 
* ed40ed2 (HEAD, lineA) A 
* 58b7dcf (master) first 

日志命令显示提交A(ed40ed2 )现在是lineA上的最新提交,它的父提交是first,它仍然是master上的最新提交。如果您现在将lineA合并回master这将是一个快进(微不足道)合并,因为没有对master进行提交,因为从lineA分支出来。同样,重订lineAmaster什么都不做,除了报告

Current branch lineA is up to date. 

因为最近提交的分支master(主分支的末端)仍然是分支lineA的历史的一部分。持续:

git checkout -b lineB 
echo -e "a\nB\nc" > file 
git commit -am 'B' 
git checkout -b lineC 
echo -e "a\nb\nC" > file 
git commit -am 'C' 
git log --graph --oneline --decorate 
* 4033067 (HEAD, lineC) C 
* f742aed (lineB) B 
* ed40ed2 (lineA) A 
* 58b7dcf (master) first 

每个新分支都建立在最近一次提交的上一个分支上,没有进行并行开发。继续:

git checkout master 
git merge lineA 
Updating 58b7dcf..ed40ed2 
Fast-forward 

请注意这是一个快进合并,所以它所作的只是移动master到ed40ed2:

git log --graph --oneline --decorate 
* ed40ed2 (HEAD, master, lineA) A 
* 58b7dcf first 

最后:

git checkout lineB 
git rebase master 
Current branch lineB is up to date. 
git log --graph --oneline --decorate 
* f742aed (HEAD, lineB) B 
* ed40ed2 (master, lineA) A 
* 58b7dcf first 

您写道:

什么?不,主人改变了。

是,master改变了,但在某种程度上影响了lineBlineC历史。 master快速提交ed40ed2,它已经是lineB的父项。确切地说,它是f742aed的父级,它是我为提交B获得的提交ID。

对于习惯于其他版本控制系统的人来说,混淆是Git为提交记录记录了一个树状数据结构(技术上说是DAG),而不是分支名称。分支是短暂的;它们只是标签,就像可移动的版本标签。提交之间的父子关系是您在和其他可视化工具中看到的内容。