将远程仓库作为子目录导入,并保留完整的提交历史记录

4

我希望将远程仓库作为子目录与完整历史记录合并和解耦。有多种方法和问题需要解决。


我的第一次尝试是使用 subtree ,但它似乎不能重写文件的历史记录,所以我无法查看合并仓库的历史记录。

接下来的尝试是手动合并,就像Seth Robertson在他的答案中展示的那样:

And the trick to making this work: force Git to recognize the rename by creating a subdirectory and moving the contents into it.

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

Return to repository "A" and fetch and merge the contents of "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"

To show that they're now merged:

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

To prove the full history is preserved in a connected chain:

git log --follow B

目前为止工作正常,但似乎大多数工具都不使用 follow 选项来执行 git log 命令。

因此,我需要另一种方法来完成此操作。有人可以告诉我一种不需要重命名且可以保留历史记录的方法吗?

提前感谢。


从同一个 LinkedIn 回答中查看 filter-branch 选项。 - Andrew C
@AndrewC 嗯... 链接的答案中并没有关于 filter-branch 选项的内容。我无法理解你的确切意思。 - CSchulz
你看过 http://git-scm.com/book/zh/v2/Git-工具-子树合并 吗? - Anshul Goyal
是的,我看到了它,但我不知道它如何在我的情况下帮助我。 - CSchulz
问题本身带有筛选分支。如果您不希望文件被“移动”,则需要使它们在历史的每个时刻都出现在新位置上,可以使用filter-branch来实现。 - Andrew C
嗯,我早就尝试过使用filter-branch了,但结果很糟糕。如果您有一些如何开始使用filter-branch的提示,请毫不犹豫地回答这个问题,我会好好研究的。 :) - CSchulz
3个回答

4

最终的解决方案是将仓库B作为A仓库的子目录导入。我偶然发现了一个小工具,其中包含如何使用git filter-branch的说明。

git filter-branch --index-filter \
  'git ls-files -s | sed "s-\t\"*-&newsubdir/-" | GIT_INDEX_FILE=$GIT_INDEX_FILE.new git update-index --index-info && mv "$GIT_INDEX_FILE.new" "$GIT_INDEX_FILE"' \
  --tag-name-filter cat \
  -- --all

完整的方法是在仓库 B 上使用上述发布的 git filter-branch,然后在仓库 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"

4

这个问题中的两个解决方案都是子树合并。实际上,为了简化在目标仓库中检查历史记录,您需要重写源仓库中的历史记录并进行合并。但是,filter-branch是不被推荐的(discouraged)

假设您想将仓库a合并到b中(我假设它们位于相邻位置):

cd a
git filter-repo --to-subdirectory-filter a
cd ..
cd b
git remote add a ../a
git fetch a
git merge --allow-unrelated-histories a/master
git remote remove a

要实现此操作,您需要安装 git-filter-repo 工具。

以下是一个将两个大型仓库合并,并将其中一个放入子目录的示例:https://gist.github.com/x-yuri/9890ab1079cf4357d6f269d073fd9731

更多相关信息可在此处找到。


0

正如在stackoverflow.com#6442034的评论中所讨论的那样,@CSchulz的stackoverflow.com#26001954/577001解决方案(感谢!)可能在新平台上失败。此外,一些符号可以更好地解释(例如“B”,“b”,“master”)。因此,我正在尝试使它们自我解释。

export SUBDIR_TOBE_IMPORTED=subdir_tobe_imported
export BRANCH_ON_GITREPO_TOBE_IMPORTED=branch_on_gitrepo_tobe_imported
export GITREPO_DEST=repo_dest
export GITREPO_DEST_PATH=<% Absolute path to $GITREPO_SOURCE %>
export GITREPO_SOURCE=name_gitrepo_tobe_imported
export GITREPO_DEST_PATH=<% Absolute path to $GITREPO_SOURCE %>

cd $GITREPO_DEST_PATH
git filter-branch --index-filter \
  'git ls-files -s | sed "s-\t\"*-&$SUBDIR_TOBE_IMPORTED/-" | GIT_INDEX_FILE=$GIT_INDEX_FILE.new git update-index --index-info && mv "$GIT_INDEX_FILE.new" "$GIT_INDEX_FILE"' \
  --tag-name-filter cat \
  -- --all

cd $GITREPO_DEST_PATH
mkdir $SUBDIR_TOBE_IMPORTED && git add $SUBDIR_TOBE_IMPORTED && git commit -m "Commiting a subdir to be imported. Without this, 'git merge' later may fail"
git remote add -f $GITREPO_SOURCE ../$GITREPO_SOURCE
git merge -s ours --allow-unrelated-histories --no-commit $GITREPO_SOURCE/$BRANCH_ON_GITREPO_TOBE_IMPORTED
git read-tree --prefix=$SUBDIR_TOBE_IMPORTED -u $GITREPO_SOURCE/$BRANCH_ON_GITREPO_TOBE_IMPORTED
git commit -m "Merge subdir from source repo into dest repo."

echo "Optionally I thought of squashing the 2 commits, as 1st commit made above is absolutely dummy, but I found the 2nd commit includes the entire imported history. Since squashing them would almost invalidate all the effort up until this point, you don't want to squash."

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接