2011-06-21 52 views
64

我想将我的工作git存储库中的远程git存储库合并为其子目录。我希望生成的存储库包含两个存储库的合并历史记录,并且合并到的存储库的每个文件都保留其在远程存储库中的历史记录。我尝试使用How to use the subtree merge strategy中提到的子树策略,但是在遵循该过程之后,尽管生成的存储库确实包含两个存储库的合并历史记录,但来自远程存储库的单个文件没有保留其历史记录(`git log'on他们中的任何一个都只显示一条消息“合并分支...”)。在子目录中合并git存储库

另外我不想使用子模块,因为我不希望两个组合的git存储库再分开。

是否有可能将另一个远程git存储库合并为一个子目录,并将远程存储库中的单个文件保留其历史记录?

非常感谢您的帮助。

编辑: 我目前正在尝试使用git filter-branch重写合并版本库历史记录的解决方案。它似乎工作,但我需要再测试一下。我会回来报告我的发现。

编辑2: 希望我能让自己更清楚我给出了我在git的子树策略中使用的确切命令,这会导致远程存储库文件的历史记录明显丢失。 让A是我目前工作的git repo,B是git repo,我想将它作为子目录并入到A中。它做了以下内容:

git remote add -f B <url-of-B> 
git merge -s ours --no-commit B/master 
git read-tree --prefix=subdir/Iwant/to/put/B/in/ -u B/master 
git commit -m "Merge B as subdirectory in subdir/Iwant/to/put/B/in." 

这些命令后,并进入目录子目录/ Iwant /到/把/ B /中,我看到他们中的任何一个B的所有文件,但git log却与此提交信息“合并B作为subdir/Iwant/to/put/B/in中的子目录”。它们在B中的文件历史记录丢失。

似乎什么工作(因为我在混帐初学者我可能是错的)如下:

git remote add -f B <url-of-B> 
git checkout -b B_branch B/master # make a local branch following B's master 
git filter-branch --index-filter \ 
    'git ls-files -s | sed "s-\t\"*-&subdir/Iwant/to/put/B/in/-" | 
     GIT_INDEX_FILE=$GIT_INDEX_FILE.new \ 
       git update-index --index-info && 
     mv "$GIT_INDEX_FILE.new" "$GIT_INDEX_FILE"' HEAD 
git checkout master 
git merge B_branch 

上面过滤器分支的命令是从git help filter-branch拍摄,我在其中只改变了subdir路径。

+0

'gitk'对历史有何说法?我过去成功地使用过git subtree merge。也许你可以揭示你的确切命令?我不确定git-filter-branch是否正确。我可能会建议尝试git-fast-export和git-fast-import来合成一个新的历史记录。 –

+0

在完成子树过程后,'gitk'显示两个在他们的提示上合并的回购和他们的初始提交无关。 (如果我发布gitk的历史视图截图,它会有帮助吗?我可以吗?)不幸的是,如果我在终端'git log '中,远程仓库的单个文件没有保留它们的历史记录。我研究'git-fast-export'和'git-fast-import';我对git很陌生。我将编辑我的问题,以确切地显示我使用git子树的命令。非常感谢您的回复。 – christosc

+0

@christosc:你的第二种方法非常简单,非常感谢!我只需要改变subdir/Iwant/to/put/B/in /并使其成为一个线索(因为Windows上的msysgit似乎不支持带\的命令行回车): git filter-branch --index-过滤'git ls-files -s | sed“s- \ t \”* - &subdir/Iwant/to/put/B/in/- “| GIT_INDEX_FILE = $ GIT_INDEX_FILE.new git update-index --index-info && mv”$ GIT_INDEX_FILE.new“”$ GIT_INDEX_FILE“'HEAD – gaborous

回答

29

在得到了有关发生的更全面的解释后,我想我明白了,无论如何,最后我有一个解决方法。具体来说,我相信正在发生的事情是重命名检测被子树与--prefix合并所迷惑。这里是我的测试用例:

mkdir -p z/a z/b 
cd z/a 
git init 
echo A>A 
git add A 
git commit -m A 
echo AA>>A 
git commit -a -m AA 
cd ../b 
git init 
echo B>B 
git add B 
git commit -m B 
echo BB>>B 
git commit -a -m BB 
git commit -a -m BB 
cd ../a 
git remote add -f B ../b 
git merge -s ours --no-commit B/master 
git read-tree --prefix=bdir -u B/master 
git commit -m "subtree merge B into bdir" 
cd bdir 
echo BBB>>B 
git commit -a -m BBB 

我们制作git目录a和b,每个提交一个提交。我们做一个子树合并,然后我们在新的子树中做最后的提交 。

运行gitk(在z/a中)表明历史确实出现,我们可以看到它。运行git log显示历史确实出现。然而,看一个特定的文件有一个问题:git log bdir/B

嗯,有一个技巧,我们可以玩。我们可以使用--follow来查看特定文件的预重命名历史记录。 git log --follow -- B。这很好,但并不好,因为它无法将预合并的历史与合并后的历史链接起来。

我试着玩-M和-C,但我无法让它跟随一个特定的文件。

所以,我觉得,解决方案是告诉git有关将作为子树合并的一部分进行的重命名。不幸的是,git-read-tree对于子树合并非常挑剔,所以我们必须通过一个临时目录,但是在我们提交之前可能会消失。之后,我们可以看到完整的历史。

首先,创建一个 “A” 信息库,并提出一些提交:

mkdir -p z/a z/b 
cd z/a 
git init 
echo A>A 
git add A 
git commit -m A 
echo AA>>A 
git commit -a -m AA 

其次,创建一个 “B” 信息库,并提出一些提交:

cd ../b 
git init 
echo B>B 
git add B 
git commit -m B 
echo BB>>B 
git commit -a -m BB 

而诀窍使这项工作:强制Git通过创建一个子目录并将内容移入它来识别重命名。

mkdir bdir 
git mv B bdir 
git commit -a -m bdir-rename 

返回到库 “A” 和取回并合并 “B” 的内容:

cd ../a 
git remote add -f B ../b 
git merge -s ours --no-commit B/master 
git read-tree --prefix= -u B/master 
git commit -m "subtree merge B into bdir" 

表明他们现在已经合并:

cd bdir 
echo BBB>>B 
git commit -a -m BBB 

为了证明完整的历史保存在连接的链中:

git log --follow B 

我们在做这件事之后得到了历史,但问题是,如果您实际上保留旧的“b”回购,并且偶尔从中合并(假设它实际上是第三方单独维护的回购),那么自从第三方党不会做重命名。您必须尝试将新的更改合并到您的b版本中,并担心这种情况不会顺利进行。但是如果b消失了,你就赢了。

+0

确实有效@Seth!而且我不必像过滤分支一样诉诸历史重写,这会造成有点欺骗性的历史(例如,在查看'git log --stat'时)。我还没有注意到git log文档中的'--follow'开关;似乎很方便与重命名。非常感谢您提供如此详细和翔实的答复! – christosc

+2

如果示例代码被分解为可读行而不是单个分号分隔的单行内容,则此响应将更有帮助。 ;) – jwadsack

+0

我想将“b”合并为“a”并保留其完整的历史记录。我怎么能这样做? – Emerald214

2

您是否尝试将额外的存储库添加为git子模块?它不会将历史与包含的存储库合并,事实上,它将是一个独立的存储库。

我提到它,因为你没有。

+1

感谢您的答案Abizern。其实我想要两个存储库历史被合并为一个;我不希望它们再分开,这就是为什么我没有提到子模块。 – christosc

4

如果你真的想把东西缝在一起,请查找嫁接。你也应该使用git rebase --preserve-merges --onto。还有一个选项可以保留提交者信息的作者日期。

+0

@adymitruk谢谢,你的回复。我真的是新的git,所以我会研究我试过'git filter-branch',它似乎可以工作,但也许你的工作更好,我会尝试一下。 – christosc

+0

@adymitruk我可以使用rebase与两个不相关的存储库作为分支?我的意思是我想合并的两个存储库没有共同的初始提交... – christosc

+1

是的。 “--onto”是你想要的 –

2

我发现以下解决方案对我可行。首先我进入项目B,创建一个新分支,其中所有文件都将被移动到新的子目录中。然后我将这个新分支推到原点。接下来,我去项目A,添加和获取B的远程,然后我签的移动分支,我重新回到主人和合并:

# in local copy of project B 
git checkout -b prepare_move 
mkdir subdir 
git mv <files_to_move> subdir/ 
git commit -m 'move files to subdir' 
git push origin prepare_move 

# in local copy of project A 
git remote add -f B_origin <remote-url> 
git checkout -b from_B B_origin/prepare_move 
git checkout master 
git merge from_B 

如果我去子目录subdir,我可以使用git log --follow和还有历史。

我不是git专家,所以我不能评论这是一个特别好的解决方案,还是它有警告,但到目前为止似乎一切正常。

+0

人们似乎在这里强调这种方法:http://stackoverflow.com/questions/1683531/how -to-import-existing-git-repository-into-another – nacross

35

git-subtree是一个用于将多个存储库合并为一个,同时保留历史记录(和/或分割子树的历史记录,虽然这似乎与此问题无关)的使用案例的脚本。它作为git树since release 1.7.11的一部分进行分发。

要在修订<rev>合并的仓库<repo>作为子目录<prefix>,使用git subtree add如下:

git subtree add -P <prefix> <repo> <rev> 

git的子树实现了对用户更友好的方式subtree merge strategy

+0

不错!这正是我需要的一条线。谢谢,未来! – iameli

+0

这是将另一个存储库合并到我的存储库的完美解决方案。 – eitch

相关问题