关于处理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
的情况下进行合并。
答案 0 :(得分:2)
这确实不是一个很好的答案,因为它更多地与您使用的脚本有关,或者也许我应该说,您没有使用的脚本,如your comment所说,您使用了一个基于{em> the script to which you linked的,但是我将展示在下面一些原始存储库的假设脚本转换中得到的比较复杂的图。请注意,此特定脚本使所有转换都具有一个合并基础的提交,实质上是提交B
,而提交B
本身是 empty 。
您的问题说:
现在,我想将分支
source
合并到分支target
中,请注意,我确保在最后一次转换之前,两者是同步的。
正如您将在下面看到的那样,所有新分支都以它们来自的项目命名—没有明确的方法将source
和target
映射到例如P或Q。但是如果要运行:
git checkout P/master
git merge Q/master
在下面说明的过程之后,此git merge
的合并基础将为空提交-B
,并且合并将顺利进行:Git会查看我作为{{1}绘制的提交}和D
,分别追溯其祖先,找到提交H
作为合并基础,并运行两个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
中的所有文件都命名为{{1} },P/master
中的所有内容都命名为P/*
。不会发生名称冲突,合并将自行完成。
很明显,那不是您在做什么。但是您正在做的事情,以及哪些提交是合并基础,仍然很神秘。看来您正在挑选两个提示提交,因此两个提示提交的合并基础是确实包含文件的提交,和这些文件尚未重命名从基础到技巧。
您链接的脚本的重点是进行设置,以便每个不相关项目的合并基础是 empty 提交。可能在该脚本之后(或实际上是在该脚本之后)要做的事情是对所有最终提交进行一次大型章鱼合并(注意:这未经测试,很明显):
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
名称进入。现在,原始存储库几乎都没有用,尽管如果其中有新的提交,则可以从中获取它们,添加重命名提交,然后从重命名提交进行合并(如果导入脚本不删除添加的遥控器。
我从未遇到过这个特殊问题,但是脚本中的方法似乎是一个不合理的起点。我可能会做一些不同的事情,不要为空的合并基础而烦恼,而不会使用B
和project/*
来创建和创建章鱼最终合并。主键是在下面的草图中的每个传入项目分支(git read-tree
,git commit-tree
等)的末尾添加重命名提交。
该脚本似乎可以这样工作。它具有输入项目P,Q,R(最后一个组成部分视为项目名称的URL)。
进行两次初始提交:
P/*
提交A有一个文件,提交B没有文件(为什么不是 将空树提交为B?但没关系)。
循环,用于所有三个项目。在这里,我扩展了循环以查看 发生了什么事。
(循环迭代1)Q/*
和A--B <-- master
(带有git remote add P <url>
!?)。在这里,我们假设P具有master和dev。
git fetch P
使用git ls-remote --heads查找P中的提交名称,即
我们在--tags
中具有相同的名称集。 (假设
远程hsa在获取期间未更改-不明智,但可能还可以。)
遍历这些名称。结果再次扩大以示说明...
运行A--B <-- master
P1-P2-...-Pm <-- origin/P/master
\
Pd <-- origin/P/dev
。效果:
refs/remotes/P/*
运行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 commit -m ...`。
效果:
git clean -d --force
也许用一些松散的代码来重命名文件(160-180行): 如果项目P具有一个名为P的顶级目录,则不执行任何操作,否则 创建名为P的目录(不检查是否失败),然后 然后生效:
git merge --allow-unrelated-histories --no-commit remotes/P/master"
(does merge, but does not commit yet) and then run
给予:
A--B <-- master
\
\-------C <-- P/master
/
P1-P2-...-Pm <-- origin/P/master
\
Pd <-- origin/P/dev
请注意,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
操作之一将失败。但是,除了
对于子目录P和-k
本身,位于顶层的所有文件
工作树的索引应该在索引中,git mv
应该在索引中
除非其中之一被命名为.git
(在这种情况下为yikes!),否则成功。
我在这里假设我们做了mv,否则提交git mv
不存在。
为P
重复循环(请参阅步骤5)。运行D
:
dev
又一次无效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
如步骤7和8所示。(如果git reset
在第10步真的很糟糕吗?)是否进行了一个时髦的两步合并?
第9步,给出:
git clean
其中B下方的线与E上方的线连接。 图在这一点上已经失控了。
重命名并按照步骤10进行提交。我在这里假设
项目既不在git mv
的子目录中,也
和A--B <-- master
|\
| \-------C--D <-- P/master
/
P1-P2-...-Pm <-- origin/P/master
\
\ Pd <-- origin/P/dev
\ \
\---E <-- P/dev
。
master
在第190-207行确实很难重命名标签。这个 应该在获取时使用聪明的refspec完成。 撰写此内容的人可能不知道带注释的vs 轻量级标签。我不清楚这是否有效 正确,我没有仔细看。假设没有标签 现在。
删除远程P。这也会删除dev
名称,
但是当然,提交被保留,因为它们被保留
新的A--B <-- master
|\
| \-------C--D <-- P/master
/
P1-P2-...-Pm <-- origin/P/master
\
\ Pd <-- origin/P/dev
\ \
\---E--F <-- P/dev
分支:
origin/P/*
使用远程Q重复外循环(步骤3)。我们将添加Q和
获取(再次使用P/*
,而不是步骤5中所述的好计划
14,但让我们假设没有标签)。所以现在我们得到另一个
A--B <-- master
|\
| \-------C--D <-- P/master
/
P1-P2-...-Pm
\
\ Pd
\ \
\---E--F <-- P/dev
名称的不相交子图。为简单起见
让我们假设这次只有--tags
存在:
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 --hard
一起使用时髦的两步合并
像这样创建新的提交git clean
:
--allow-unrelated-histories
:将G中的所有文件重命名为Q /和 承诺。再次让我们假设发生了 :
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--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
名称。 (无需绘制。)
为存储库R重复外部循环。假定它仅具有
它自己的Q
,我们将得到一个像这样的纠结图:
origin/Q/*
(分析结束)