为什么这个git rebase认为没什么可做的?

时间:2016-11-15 15:02:09

标签: git

在这个例子中,git认为它不需要重新绑定,但显然确实如此。

这是一个创建迷你测试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

现在让我们将所有分支合并为master,之后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

在我看来,分支行B需要通过合并lineA来修改主变量。

为什么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 个答案:

答案 0 :(得分:2)

第一个命令的结果

在我们拥有的脚本之后(缩写行分支名称......):

The application environment is not set correctly.

这些命令:

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

会导致快进合并,只会将$ git checkout master $ git merge lineA 移至指向与master相同的提交。

,并提供:

lineA

无法使用Rebase

所以这个命令序列:

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

表示将$ git checkout lineB $ git rebase master 分支移到lineB之上。

...但它已经位于master之上。

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

无合并冲突

当Git创建合并提交时,它会考虑进入合并的父项的历史记录。在这种情况下,两个分支都指向共同提交链上的提交。 Git可以看到lineB提交来自A提交之前的B提交。因此,Git认为后来的提交是在早期提交之上添加的,因此它们不会发生冲突。

如果您有以下历史记录:

C
然后Git会将它们视为平行的开发线。在这种情况下,将 master ↓ ---A / | lB | ↓ first----B 合并到lB或反之亦然会发生冲突。这是因为master不是历史记录中B之上的附加内容,因此Git需要用户输入才能了解要保留哪些更改。

答案 1 :(得分:1)

我认为你的困惑又回来了。执行时

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

你确实创建了一个名为lineA的新分支,但分支点就是你刚刚做过的提交(" first")。正如Git所说,first是两个分支历史的一部分,在你的脚本中,它也是两个分支中最近的提交。带有一些选项的Git日志显示两个分支名称都指向第一个提交,在此示例中为commit 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

log命令显示提交A(ed40ed2)现在是lineA上的最新提交,其父提交是first,它仍然是master上的最新提交。 {1}}。如果您此时将lineA合并回master,那么这将是一个快速(简单)合并,因为master已经lineA未提交任何提交离开它。同样,将lineA重新定位到master除了报告

之外什么都不做
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)。分支是短暂的;它们只是标签,如可移动版本标签。提交之间的父子关系就是您在git log --graph和其他可视化工具中看到的。