我只能告诉你一个小实验,Git并不比Subversion好(同样的问题)。
I was wondering about this case:
You start with two branches "mytest1" and "mytest2" both based on the same commit.
You have got a C file which contains a function blub().
In branch mytest1 you move "blub()" to a different position in the file and commit.
In branch mytest2 you modify blub() and commit.
On branch mytest2 you try to use "git merge mytest1".
Seems to give a merge conflict.
I hoped that Git would recognize that "blub()" was moved in mytest1 and then be able to auto-merge the modification in mytest2 with the move in mytest1. But at least when I tried this did not work automatically...
因此,虽然我完全理解Git在跟踪已经合并和尚未合并的内容方面要好得多,但我也想知道是否存在Git比SVN更好的“纯”合并情况。。。
现在,因为这个问题困扰了我很长一段时间,我真的试图创建一个具体的例子,其中Git<strong>更好,而在SVN中合并失败。
我在这里找到了一个https://stackoverflow.com/a/2486662/1917520,但这包括重命名,这里的问题是针对没有重命名的情况。
这里有一个SVN的例子,它基本上是这样尝试的:
bob +-----r3----r5---r6---+
/ /
anna / +-r2----r4----+--+
/ /
trunk r1-+-------------------r7-- Conflict
这里的想法是:
- Anna and Bob are both developers with their own branches (created in r2,r3).
- Anna does some modifications (r4),
- Bob does some modifications (r5).
- Bob merges the modifications from Anna into his branch; this gives conflicts, which Bob fixes and then commits (r6).
- Annas modifications are merged back into the trunk (r7).
- Bob tries to merge his modification back into the trunk and this again gives a conflict.
这是Bash脚本,它会产生此冲突(使用SVN 1.6.17和SVN 1.7.9):
#!/bin/bash
cd /tmp
rm -rf rep2 wk2
svnadmin create rep2
svn co file:///tmp/rep2 wk2
cd wk2
mkdir trunk
mkdir branches
echo -e "A
A
B
B" > trunk/f.txt
svn add trunk branches
svn commit -m "Initial file"
svn copy ^/trunk ^/branches/anna -m "Created branch anna"
svn copy ^/trunk ^/branches/bob -m "Created branch bob"
svn up
echo -e "A
MA
A
B
B" > branches/anna/f.txt
svn commit -m "anna added text"
echo -e "A
MB
A
B
MB
B" > branches/bob/f.txt
svn commit -m "bob added text"
svn up
svn merge --accept postpone ^/branches/anna branches/bob
echo -e "A
MAB
A
B
MB
B" > branches/bob/f.txt
svn resolved branches/bob/f.txt
svn commit -m "anna merged into bob with conflict"
svn up
svn merge --reintegrate ^/branches/anna trunk
svn commit -m "anna reintegrated into trunk"
svn up
svn merge --reintegrate --dry-run ^/branches/bob trunk
最后一个“干式运行”告诉你,将会发生冲突。如果你首先尝试将Anna的重新融入Bob的分支,那么你也会遇到冲突;因此,如果将最后一个svn-merge
替换为
svn merge ^/trunk branches/bob
这也显示出一种矛盾。
这里与Git 1.7.9.5相同:
#!/bin/bash
cd /tmp
rm -rf rep2
mkdir rep2
cd rep2
git init .
echo -e "A
A
B
B" > f.txt
git add f.txt
git commit -m "Initial file"
git branch anna
git branch bob
git checkout anna
echo -e "A
MA
A
B
B" > f.txt
git commit -a -m "anna added text"
git checkout bob
echo -e "A
MB
A
B
MB
B" > f.txt
git commit -a -m "bob added text"
git merge anna
echo -e "A
MAB
A
B
MB
B" > f.txt
git commit -a -m "anna merged into bob with conflict"
git checkout master
git merge anna
git merge bob
f.txt的内容是这样变化的。
初始版本
A
A
B
B
安娜的修改
A
MA
A
B
B
Bob的修改
A
MB
A
B
MB
B
安娜的分店并入鲍勃的分店后
A
MAB
A
B
MB
B
正如许多人已经指出的那样:问题是,颠覆者不记得鲍勃已经解决了冲突。因此,当您现在尝试将Bob的分支合并到主干中时,必须重新解决冲突。
Git的工作方式完全不同。下面是git的一些图形表示
bob +--s1----s3------s4---+
/ /
anna / +-s1----s2----+--+
/ /
master s1-+-------------------s2----s4
s1/s2/s3/s4是gittake工作目录的快照。
注意事项:
- When anna and bob create their development branches, this will
NOT create any commits under git. git will just remember that
both branches initially refer to the same commit object as
the master branch. (This commit in turn will refer to the s1 snapshot).
- When anna implements her modification, this will create a
new snapshot "s2" + a commit object.
A commit object includes:
- A reference to the snapshot (s2 here)
- A commit message
- Information about ancestors (other commit objects)
- When bob implements his modification, this will create another snapshot
s3 + a commit object
- When bob merges annas modifications into his development branch
this will create yet another snapshot s4 (containing a merge of his
changes and anna s changes) + yet another commit object
- When anna merges her changes back into the master branch, this will
be a "fast-forward" merge in the shown example, because the master
has not changed in the meantime. What "fast-forward" here means is,
that the master will simply point to the s2 snapshot from anna
without merging anything. With such a "fast-forward" there will
not even be another commit object. The "master" branch will just
directly now refer to the last commit from the "anna" branch
- When bob now merges his changes into the trunk, the following will happen:
- git will find out that the commit from anna which created
the s2 snapshot is a (direct) ancestor for bobs commit,
which created the s4 snapshot.
- because of this git will again "fast-forward" the master branch to
the last commit of the "bob" branch.
- again this will not even create a new commit object.
The "master" branch will simply be pointed to the last commit
of the "bob" branch.
下面是“git-ref-log”的输出,它显示了所有这些:
88807ab HEAD@{0}: merge bob: Fast-forward
346ce9f HEAD@{1}: merge anna: Fast-forward
15e91e2 HEAD@{2}: checkout: moving from bob to master
88807ab HEAD@{3}: commit (merge): anna merged into bob with conflict
83db5d7 HEAD@{4}: commit: bob added text
15e91e2 HEAD@{5}: checkout: moving from anna to bob
346ce9f HEAD@{6}: commit: anna added text
15e91e2 HEAD@{7}: checkout: moving from master to anna
15e91e2 HEAD@{8}: commit (initial): Initial file
从中可以看出:
- when we go to anna s development branch (HEAD@{7}) we do not change
to a different commit, we keep the commit; git just remembers that
we are now on a different branch
- At HEAD@{5} we move to bob s initial branch; this will move the working
copy to the same state as the master branch, because bob has not changed
anything yet
- At HEAD@{2} we move back to the master branch, so to the same commit
object everything started from.
- Head@{1},HEAD@{0} show the "fast-forward" merges, which do not create
new commit objects.
使用“git-cat-fileHEAD@{8}-p”,您可以检查初始提交对象的完整细节。对于上面的例子,我得到:
tree b634f7c9c819bb524524bcada067a22d1c33737f
author Ingo <***> 1475066831 +0200
committer Ingo <***> 1475066831 +0200
Initial file
The "tree" line identifies the
snapshot s1 (==b634f7c9c819bb524524bcada067a22d1c33737f)
to which this commit refers.
如果我做“git-cat文件HEAD@{3}-p”,我得到:
tree f8e16dfd2deb7b99e6c8c12d9fe39eda5fe677a3
parent 83db5d741678908d76dabb5fbb0100fb81484302
parent 346ce9fe2b613c8a41c47117b6f4e5a791555710
author Ingo <***> 1475066831 +0200
committer Ingo <***> 1475066831 +0200
anna merged into bob with conflict
上面显示了bob在合并anna的开发分支时创建的commit对象。同样,“树”行指的是创建的快照(此处为s3)。此外,请注意“父”行。第二个以“parent 346ce9f”开头,稍后告诉git,当您试图将bob的开发分支合并回master分支时,bob的最后一个提交将anna的最后一次提交作为祖先。这就是为什么git知道将bob的开发分支合并到master分支是一种“快进”。