我有一个我在不同项目中使用过的库,我根据需要一直在更新。因为我忘记复制到某些更改,所以我最终得到了两个添加了不同内容的文件,我只添加了它。
因为我打算为这个库制作一个git repo,我做了一个,添加了第一个文件,在分支上添加了第二个文件并合并它们。这给我留下了第二个文件的副本,而不是合并在一起的文件。
所以我最后做的是创建一个包含空白文件的新repo,然后将第一个文件复制到新的提交中,然后检出空白文件提交,然后复制第二个文件并创建一个新文件分支,然后合并。这导致了合并冲突,但在我简单地删除之后
git插入了<<<<<<< HEAD
,=======
和>>>>>>> <branch name>
行我有我想要的文件。
我想知道是否有更好的方法来做到这一点?特别是如果这又发生了多个文件。我尝试了一个可视化合并工具,但它在功能边界上拆分了块,所以我不得不手动完成它。
编辑:我拥有的和我想要的例子。
我有两个文件(伪代码): 档案1
defmodule foo
foo.bar = ...
foo.baz = ...
return foo
文件2
defmodule foo
foo.bar = ...
foo.qux = ...
return foo
然后我想将这些合并到一个文件中。由于新功能(baz和qux)没有相互调用,我可以使用
defmodule foo
foo.bar = ...
foo.baz = ...
foo.qux = ...
return foo
或
defmodule foo
foo.bar = ...
foo.qux = ...
foo.baz = ...
return foo
由于我只添加到这些文件,似乎我应该能够获得我想要的文件,而无需手动剪切和粘贴,并且使用空文件和两个分支的技巧感觉过于复杂并且没有用多个文件很好地扩展。所以我想知道是否有更好的方法。
答案 0 :(得分:0)
好的,所以我把它作为SSCCE / MVCE。我们从README
中的master
存储库开始:
$ mkdir mergesamp
$ cd mergesamp
$ git init
Initialized empty Git repository in .../mergesamp/.git/
$ cat << END > README
> We put a dummy README file into the base,
> to get an initial commit.
> END
$ git add README && git commit -m initial
[master (root-commit) 40d9565] initial
1 file changed, 2 insertions(+)
create mode 100644 README
现在我们创建一个新分支b1
并创建file1
:
$ git checkout -b b1
Switched to a new branch 'b1'
$ cat << END > file1
> defmodule foo
>
> # We'll put a bunch of stuff in here
> # so that git can detect renames.
> # Files that are very short, that are
> # in fact renamed and modified, can
> # get missed because git does not
> # see that they are at least 50% matching
> # between the "before" and "after" versions.
>
> foo.bar = ...
>
> foo.baz = ...
>
> return foo
> END
$ git add file1 && git commit -m 'add file1'
[b1 f603e15] add file1
1 file changed, 15 insertions(+)
create mode 100644 file1
然后我们回到主人并创建一个新分支b2
来保持file2
(我在这里使用一个小捷径,从{{1}标识的提交开始创建b2
而不是使用两个单独的git命令):
master
让我们看看我们现在的位置:
$ git checkout -b b2 master
Switched to a new branch 'b2'
$ cat << END > file2
> defmodule foo
>
> # This is the b2 version of file1.
> # We'll put a bunch of stuff in here
> # so that git can detect renames.
> # Files that are very short, that are
> # in fact renamed and modified, can
> # get missed because git does not
> # see that they are at least 50% matching
> # between the "before" and "after" versions.
> defmodule foo
>
> foo.bar = ...
>
> foo.qux = ...
>
> return foo
> END
$ git add file2 && git commit -m 'add file2'
[b2 01ef1ca] add file2
1 file changed, 17 insertions(+)
create mode 100644 file2
此时我们希望返回$ git log --graph --oneline --decorate --all
* 01ef1ca (HEAD -> b2) add file2
| * f603e15 (b1) add file1
|/
* 40d9565 (master) initial
$
并制作git merge master
和file1
。但当然file2
中没有该文件的常见基本版本,因此简单master
无法正常工作。
(旁白:也许我们只想将git merge
合并到b2
,或b1
合并到b1
。我没有从原文中找到这一点,这就是为什么这种SSCCE / MVCE是一个好主意。幸运的是,这对于终极解决方案来说无关紧要。)
有多种方法可以解决这个问题。也许是最简单的蓝天&#34;思考它&#34;观点是回到master,创建一个公共基本文件,然后将两个分支重新绑定到这一点,以便它们做有一个共同的基本文件。
然而,这是错误的方式。尽管如此,让我们展示一下......b2
请注意,$ git checkout master
Switched to branch 'master'
$ git checkout b1 -- file1
$ git commit -m 'copy b1 file1 to common base'
[master 71b0f5e] copy b1 file1 to common base
1 file changed, 15 insertions(+)
create mode 100644 file1
中的这个新提交与master
中的file1
完全相同,因此当我执行rebase时,我必须保留空提交。 (如果分支b1
中有其他更改,我将不需要b1
以及下面其他内容。)
-k
我想要空提交(为了说明),所以我按照说明(添加$ git rebase -k master b1
First, rewinding head to replay your work on top of it...
HEAD detached from 40d9565
You are currently cherry-picking commit f603e15.
nothing to commit, working directory clean
The previous cherry-pick is now empty, possibly due to conflict resolution.
If you wish to commit it anyway, use:
git commit --allow-empty
If you wish to skip this commit, use:
git reset
Then "git cherry-pick --continue" will resume cherry-picking
the remaining commits.
,因为我不想编辑提交消息):
--no-edit
唉,我遇到了一个git bug!我没有时间来修复这个例子,所以我只是强行完成了变形,让git做它应该在这里做的事情。 (如果你不理解这一点,不要担心它,git不应该做它刚刚做的事情而且我正在解决它。)
$ git commit --allow-empty --no-edit
[detached HEAD 3080d64] add file1
Date: Thu Mar 17 11:18:51 2016 -0700
$ git cherry-pick --continue
$ git status
$ git status
HEAD detached from 40d9565
nothing to commit, working directory clean
现在我将重复$ git branch -f b1 HEAD && git checkout b1
Switched to branch 'b1'
$ git log --graph --oneline --decorate --all
* 3080d64 (HEAD -> b1) add file1
* 71b0f5e (master) copy b1 file1 to common base
| * 01ef1ca (b2) add file2
|/
* 40d9565 initial
。在变基础之后,我需要将file2重命名为file1。
b2
让我们查看提交图:
$ git rebase master b2
First, rewinding head to replay your work on top of it...
Applying: add file2
$ git rm file1 && git mv file2 file1 && git commit -m 'rename file2->file1'
rm 'file1'
[b2 435e6e7] rename file2->file1
2 files changed, 3 insertions(+), 18 deletions(-)
delete mode 100644 file2
现在我可以在master上进行合并了(分两步,我需要$ git log --graph --oneline --decorate --all
* 435e6e7 (HEAD -> b2) rename file2->file1
* 63a19b5 add file2
| * 3080d64 (b1) add file1
|/
* 71b0f5e (master) copy b1 file1 to common base
* 40d9565 initial
,因为--no-ff
与master
完全匹配,因为这个例子可能也是 小/小):
b1
合并都已完成,但是如果我们查看$ git checkout master
Switched to branch 'master'
$ git merge --no-ff --no-edit b1
Already up-to-date!
Merge made by the 'recursive' strategy.
$ git merge --no-edit b2
Merge made by the 'recursive' strategy.
file1 | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
我们的版本有错误:git认为它应该只使用file1
版本。
让我们放松一下,看看如果我们只是将b2
合并到b2
中会发生什么,现在他们有一个共同的基础:
b1
(我在这里剪切并粘贴了SHA-1,因为它仍然在窗口中;或者我可以使用$ git reset --hard 71b0f5e
HEAD is now at 71b0f5e copy b1 file1 to common base
,因为有两个合并要丢弃。)
master~2
唉,和以前一样(实际上这应该不足为奇),$ git checkout b1
Switched to branch 'b1'
$ git merge --no-edit b2
Merge made by the 'recursive' strategy.
file1 | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
只是选择file1
的更改,现在匹配b2
中的版本。所以,让我们抛出这个合并,事实上,回到我们完成所有变基之前的设置,因为这是错误的方法。
幸运的是,我们仍然在原始b2
输出中拥有所有初始提交ID。
git log --graph --oneline --decorate --all
(注意:使用$ git checkout master && git reset --hard 40d9565
Switched to branch 'master'
HEAD is now at 40d9565 initial
$ git checkout b1 && git reset --hard f603e15
Switched to branch 'b1'
HEAD is now at f603e15 add file1
$ git checkout b2 && git reset --hard 01ef1ca
Switched to branch 'b2'
HEAD is now at 01ef1ca add file2
,master
和b1
的reflog可以完成所有这些操作,但只要我们有原始的SHA-1,这更容易。)
b2
因为我们只想手动合并两个分支中的git merge-file
和file1
,所以使用&#34;空文件&#34;作为通用基础版本,我们真的只想做到这一点。首先,让我们将两个文件放入工作树中:
file2
现在我们告诉$ git show b1:file1 > file1
$ git show b2:file2 > file2
使用空文件git merge-file
作为公共基础:
/dev/null
在这种情况下,合并会在添加的项目之间发现冲突,因此我们必须手动编辑结果(在$ git merge-file file1 /dev/null file2
中),但现在它中包含通常的file1
样式标记,这应该使合并更容易。 (另外,记得在完成后删除<<<<<<<
。)
如果我们有一个更好的基本文件(比空file2
),/dev/null
可以做得更好。但无论如何,这可能是解决问题的方法。