git - 重命名所有文件后git merge

标签 git merge git-mv

关于处理merge重命名还有其他答案,但是我的情况非常复杂,以至于我认为这值得一个单独的问题。

我们有一个git项目,最初由20多个存储库组成。我们使用包装器脚本来处理许多标准git操作。因为我们现在要迁移到GitHub,所以我们不能以这种方式处理项目。

因此,我们基本上使用saintgimp上描述的方法将所有存储库移动到单个存储库中。当然,这意味着所有文件现在都已重命名,但是SHA在历史上是相同的。

好的,所以现在我想将分支source合并到分支target中,请注意,我确保在最后一次转换之前两者已同步。我的第一次尝试使用git merge <source>导致了成千上万的冲突,关于在一侧或另一侧更改/删除文件的投诉等。

然后我在Advanced Merging page上找到了一个宝石:


如果您想做这样的事情,但连Git都没有,请尝试
从另一侧合并更改,这是更严厉的
选项,这是“我们的”合并策略。这不同于
“我们的”递归合并选项。


啊,听起来像我需要的。好的,我执行了以下操作:

$ git merge -s ours SHA


其中,SHA是统一的最后一次提交。换句话说,我希望所有历史记录(包括SHA在内)都已被合并为target。我希望这将是一次合并,并将解决所有将来的合并。

现在,当我尝试合并来自source的第一个新提交时,效果是正确的,但是我继续收到以下警告:

[user@host src] git merge --no-commit next_unmerged_commit
Auto-merging /path/to/file/changed/foo.c
warning: inexact rename detection was skipped due to too many files.
warning: you may want to set your merge.renamelimit variable to at least 5384 and retry the command.
Automatic merge went well; stopped before committing as requested


而且,实际上,如果将renamelimit设置为10000,则执行下一个合并(称为B)时不会发出警告,但是会降低性能。再一次,一次性费用是可以接受的,如果我的后续合并再次恢复正常,我将支付该费用。

我使用默认C的下一个合并renamelimit再次发出警告。

所以,最后,我的问题是:我如何才能使git相信target分支与source保持同步,以便它在统一之前不再试图恢复历史?由于性能下降,我希望能够在不增加renamelimit的情况下进行合并。

最佳答案

这确实不是一个很好的答案,因为它更多地与您使用的脚本有关,或者也许我应该说,您未使用的脚本,如your comment说您使用的是基于the script to which you linked的脚本,但我图11显示了我在下面的一些原始存储库的假设脚本转换中得到的缠结图。请注意,此特定脚本使所有转换都具有一个合并基本提交,实质上是commit B,而commit B本身为空。

您的问题是:


现在,我想将分支source合并到分支target中,请注意,我确保在最后一次转换之前两者已同步。


正如您将在下面看到的那样,所有新分支都以它们来自的项目命名—没有明确的方法将sourcetarget映射到例如P或Q上。但是,如果要运行:

git checkout P/master
git merge Q/master


经过以下所示的过程后,此git merge的合并基础将为empty-commit- B,并且合并将顺利进行:Git会分别查看我作为DH绘制的提交,并跟踪它们祖先,找到提交B作为其合并基础,然后运行两个git diff

git diff <hash-of-B> <hash-of-D>   # what we did on P/master
git diff <hash-of-B> <hash-of-H>   # what they did on H/master


这些git diff的输出将表明每个文件都是从头开始创建的,并且它们的名称都不同:P/master中的所有内容都命名为P/*,而H/master中的所有内容都命名为Q/*。不会发生名称冲突,合并将自行完成。

显然,那不是您在做什么。但是您正在做什么,以及哪个提交是合并基础,仍然是个谜。看起来您正在挑选两个提示提交,因此两个提示提交的合并基础是确实包含文件的提交,并且这些文件尚未从基础重命名为提示。

链接的脚本的重点是进行设置,以使每个不相关项目的合并基础为空提交。可能在该脚本之后(或者实际上是在该脚本之后)要做的事情是对所有最终提交进行一次大规模的章鱼合并(注意:这未经测试,很明显):

git checkout P/master                 # just to be somewhere that's not master
git branch -d master                  # discard existing master branch name
git checkout --orphan master          # arrange to create new master
git merge P/master Q/master R/master  # make big octopus merge to marry all projects


此章鱼合并的合并基础将再次是commit B,结果将是一次合并,使所有项目以其新的project/*名称进入。现在,原始存储库几乎都没有用,但是如果其中有新的提交,则可以从它们中获取信息,添加重命名提交,然后从重命名提交进行合并(如果导入脚本没有删除添加的内容,这样做会更容易遥控器)。

关于链接脚本工作方式的观察

我从未遇到过这个特殊问题,但是脚本中的方法似乎是一个不合理的起点。我可能会做一些不同的事情,不要为空的合并库而烦恼,而不必使用git read-treegit commit-tree来构建和创建章鱼末端合并。主键是在下图中的每个传入项目分支(P/*Q/*等)的末尾添加重命名提交。

该脚本似乎以这种方式工作。它具有项目P,Q,R(最后一个组成部分视为项目名称的URL)作为输入。


进行空的回购。
进行两次初始提交:

A--B   <-- master



提交A有一个文件,提交B没有文件(为什么不只是
将空树提交为B?但是不要紧)。


循环,用于所有三个项目。在这里,我扩展了循环以查看
发生了什么。
(循环迭代1)git remote add P <url>git fetch P(带有--tags !?)。我们在这里假设P具有master和dev。

A--B   <-- master

P1-P2-...-Pm   <-- origin/P/master
       \
        Pd   <-- origin/P/dev

使用git ls-remote --heads查找P中提交的名称,即
我们在refs/remotes/P/*中具有相同的名称集。 (假设
提取期间未更改远程hsa-不明智,但可能还可以。)

循环这些名称。结果再次扩大以示说明...
运行git checkout -b P/master master。影响:

A--B   <-- master, P/master

P1-P2-...-Pm   <-- origin/P/master
       \
        Pd   <-- origin/P/dev

没有明显的原因运行git reset --hard:无效果。也许
这可能对以后的步骤有所影响。
没有明显的原因运行git clean -d --force:无效果。
运行git merge --allow-unrelated-histories --no-commit remotes/P/master" (does merge, but does not commit yet) and then run git commit -m ...`。
影响:

A--B   <-- master
    \
     \-------C   <-- P/master
            /
P1-P2-...-Pm   <-- origin/P/master
       \
        Pd   <-- origin/P/dev

也许用一些松散的代码来重命名文件(160-180行):
如果项目P具有一个名为P的顶级目录,则不执行任何操作,否则
创建名为P的目录(不检查是否失败),然后
然后生效:

git mv all-but-P P/
git commit -m "[Project] Move ${sub_project} files into sub directory"


给予:

A--B   <-- master
    \
     \-------C--D   <-- P/master
            /
P1-P2-...-Pm   <-- origin/P/master
       \
        Pd   <-- origin/P/dev


请注意,为git mv指定了-k,因此,如果
git mv操作之一将失败。但是,除了
对于子目录P和.git本身,位于顶层的所有文件
工作树的索引应该在索引中,而git mv应该
除非其中之一被命名为P,否则成功。

我在这里假设我们做了MV,否则提交D不存在。
dev重复循环(请参阅步骤5)。运行git checkout -b P/dev master

A--B   <-- master, P/dev
    \
     \-------C--D   <-- P/master
            /
P1-P2-...-Pm   <-- origin/P/master
       \
        Pd   <-- origin/P/dev

可能无效的git resetgit clean再次
如步骤7和8所示。(如果git mv
在第10步真的很糟糕吗?)是否进行了一个时髦的两步合并,如
第9步,给出:

A--B   <-- master
   |\
   | \-------C--D   <-- P/master
            /
P1-P2-...-Pm   <-- origin/P/master
       \
     \  Pd   <-- origin/P/dev
      \   \
       \---E   <-- P/dev


从B向下的线与从E向上的线连接。
图在这一点上已经失控了。
重命名并按照步骤10进行提交。我在这里假设
项目既不在master的子目录中,也没有
已经假定,并且dev

A--B   <-- master
   |\
   | \-------C--D   <-- P/master
            /
P1-P2-...-Pm   <-- origin/P/master
       \
     \  Pd   <-- origin/P/dev
      \   \
       \---E--F   <-- P/dev

在第190-207行,确实很难重命名标签。这个
应该在获取时使用聪明的refspec完成。
撰写此内容的人可能不知道带注释的vs
轻量级标签。我不清楚这是否有效
正确,我没有仔细看。假设没有标签
目前。
删除远程P。这也会删除origin/P/*名称,
但是当然,提交被保留,因为它们被保留
新的P/*分支:

A--B   <-- master
   |\
   | \-------C--D   <-- P/master
            /
P1-P2-...-Pm
       \
     \  Pd
      \   \
       \---E--F   <-- P/dev

对远程Q重复外部循环(第3步)。我们将添加Q和
获取(再次使用--tags,不是步骤5中指出的好计划
14,但让我们假设没有标签)。所以现在我们得到另一个
origin/Q/*名称的不相交子图。为简单起见
让我们假设这次只有origin/Q/master存在:

A--B   <-- master
   |\
   | \-------C--D   <-- P/master
            /
P1-P2-...-Pm
       \
     \  Pd
      \   \
       \---E--F   <-- P/dev

Q1-Q2-...-Qm   <-- origin/Q/master

运行git checkout -b Q/master master

A--B   <-- master, Q/master
   |\
   | \-------C--D   <-- P/master
            /
P1-P2-...-Pm
       \
     \  Pd
      \   \
       \---E--F   <-- P/dev

Q1-Q2-...-Qm   <-- origin/Q/master

运行(可能无效并且仍然神秘)
git reset --hardgit clean步骤。
通过--allow-unrelated-histories使用时髦的两步合并
创建新的提交G,如下所示:

     ---------------G   <-- Q/master
    /               |
A--B   <-- master   | (down to Qm)
   |\
   | \-------C--D   <-- P/master
            /
P1-P2-...-Pm
       \
     \  Pd
      \   \
       \---E--F   <-- P/dev

             / (up to G)
            /
Q1-Q2-...-Qm   <-- origin/Q/master

同样,可选:将G中的所有文件重命名为Q /和
承诺。再次让我们假设这确实发生了:

     ---------------G--H   <-- Q/master
    /               |
A--B   <-- master   | (down to Qm)
   |\
   | \-------C--D   <-- P/master
            /
P1-P2-...-Pm
       \
     \  Pd
      \   \
       \---E--F   <-- P/dev

             / (up to G)
            /
Q1-Q2-...-Qm   <-- origin/Q/master

难以重命名标签;我们将忽略这一点。
删除远程Qorigin/Q/*名称。 (无需绘制。)
对存储库R重复外部循环。假设它只有一个
它自己的master,我们将得到如下图所示的纠结图:

     --------------------I--J   <-- R/master
    /                    | (down to Rm)
   /
   | ---------------G--H   <-- Q/master
   |/               |
A--B   <-- master   | (down to Qm)
   |\
   | \-------C--D   <-- P/master
            /
P1-P2-...-Pm
       \
     \  Pd
      \   \
       \---E--F   <-- P/dev

             / (up to G)
            /
Q1-Q2-...-Qm
                / (up to I)
               /
R1-R2-...----Rm



(分析结束)

关于git - 重命名所有文件后git merge,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55285121/

相关文章:

git - 如何从我的 Git 存储库中删除未引用的 blob

javascript - 选择 rxjs 中重复的第一项

c# - 将 2x DOCX 文件合并为 1 个?

git - 将 git 存储库移动到同一路径中的子目录,同时保留其所有历史记录

git - 无法提交文件,意外地用 mv 重命名

GitLab git 用户密码

带有 ssh 问题的 git clone

c# - 最佳实践 : successfully merge two software?

git 复制文件,而不是 `git mv`

android - 与 cyanogenmod repo 同步时出错