将其他repo合并到subdir后修复损坏的git历史记录

时间:2013-10-15 15:48:10

标签: git merge git-filter-branch git-subtree

我有以下git存储库

  • repoA
  • repoB
  • repoC

我加入了

  • repoAll每个仓库被移动到一个子目录

所以这看起来像

  • repoAll
    • DIRA
    • DIRB
    • DIRC

我已按照http://jasonkarns.com/blog/merge-two-git-repositories-into-one/上的说明进行操作。这基本上意味着

git remote add -f repoA /path/to/repoA
git merge -s ours --no-commit repoA/master
git read-tree --prefix=dirA/ -u repoA/master
git ci -m "merging repoA into dirA"
...

所以现在,自

以来不再连接文件的历史记录
git log --follow dirA/pom.xml

什么都没有。

然而,

git log --follow pom.xml

确实显示该文件的正确(旧)历史记录。这不是很好,因为没有像eclipse或其他git客户端这样的工具能够显示完整的历史记录。

更糟糕的是,组合仓库已经有了新的提交,所以再次进行合并并不是一个真正的选择(我现在知道我应该在repoA/*之前将repoA/dirA移到{{1}}合并)。

我考虑过在repoAll上初始合并之前插入一个提交的提交,但这需要我修改所有更改(现在是100+)并解决更改。

问题/解决方案Git log shows very little after doing a read-tree mergeHow can I rewrite history so that all files, except the ones I already moved, are in a subdirectory?似乎只适用于整个存储库,而不适用于特定的子目录(或者至少如果你已经在repoAll上提交了新提交的话)。

我认为应该有一些方法来重写特定子目录的历史(例如dirA),但我似乎无法弄清楚如何。

2 个答案:

答案 0 :(得分:2)

我最后用一个稍微费力的解决方案来修复问题,但它可能更简单

  1. 我记录了开发人员在repoAll上进行的第一次提交的SHA1(这是加入存储库后的第一次真正提交)。理想情况下,您创建一个分支,以便能够再次找到它(git branch changes_start_here <SHA1>
  2. 我从一个空的存储库再次开始,并重新克隆了各个存储库(repoA,...)
  3. 我去了repoA并添加了一个提交,我把repoA的所有内容都移到了dirA(仍然在repoA上)

    cd repoA
    mkdir dirA
    git mv src pom.xml other* dirA  (i.e. all contents except for dirA will be moved to dirA)
    git commit -m "moved repoA to dirA"
    

    为每个回购重复

  4. 关于新的(空组合存储库)repoAllNew我现在将所有本地存储库副本添加为远程

    cd repoAllNew
    git remote add -f origin-repoA ../repoA
    git pull origin-repoA master
    

    重复每个回购

  5. 通过执行类似

    的操作确保历史记录正常
    git blame dirA/src/main/java/HelloWorld.java
    

    (显然这必须是具有更长历史的现有文件)。检查一下  blame包含每个源代码行的有意义的消息。

  6. 重新导入合并repos后开发人员所做的所有更改。 这可以通过将旧的repoA添加为远程:

    来完成
    git remote add -f origin-repoAllOld ../repoAll
    

    现在我们需要合并将repos加入清理后的存储库后所做的所有新更改。

    git branch start <SHA1 of origin-repoAllOld/changes_start_here>
    git branch end <SHA1 of origin-repoAllOld/master>
    git rebase --onto master start end
    
  7. 现在你应该拥有与repoA相同但具有正确历史的状态。

  8. 管理摘要

    我们必须在开始repo迁移之前插入一个更改,将每个存储库的内容移动到相应的子目录中。这样,历史仍然是正确的,像责备等等的工作就好了。 除非你想开始弄乱git read-tree --prefix ...(在你不想要的99%的情况下),否则应该避免使用恕我直言git filter-branch

答案 1 :(得分:2)

基于r3m0t重写历史的想法,以下几行为我做了整个技巧,将另一个git存储库作为新分支合并到我现有的存储库中进入子目录:

(在git-sh工作我可以省略命令的主要'git')

co -b my-new-branch 
remote add -f origin-my-old-standalone-project ../my-old-standalone-project/
pull origin-my-old-standalone-project master
mkdir my-new-subdir
ci -am "merge 'old' standalone project as new branch 'my-new-branch'"
git filter-branch --index-filter \
        'git ls-files -s | sed "s%\t\"*%&my-new-subdir/%" |
                GIT_INDEX_FILE=$GIT_INDEX_FILE.new \
                        git update-index --index-info &&
         mv "$GIT_INDEX_FILE.new" "$GIT_INDEX_FILE"' HEAD

之后我同时拥有:新子目录中的单个文件的历史记录,就像它们一直在那里一样,以及主目录中的正常历史记录,就好像子目录中的新文件一直是那里。 (正如您所看到的,没有必要使用读取树或任何其他日常使用的命令,'filter-branch'完成整个技巧。)IDE能够(分别应该成功测试PyCharm)正常工作结果

之后,您应该能够正常合并您的分支,将所有项目合并为一个。

tl; dr: --follow按预期正常工作,执行上述6个命令后将旧git项目合并到其他git项目的新分支和子目录