我对git有些新意,我已经使用它好几个月了,我很乐于完成大部分的基本任务.所以......我认为是时候承担一些更复杂的任务了.在我的工作中,我们有一些人在使用旧代码来更新它,这涉及实际的代码工作并更新目录结构以使其更加模块化.我的问题是这两件事可以在并行分支中完成,然后合并或重新组合.我的直觉说不,因为dir重组是重命名,git通过添加新文件并删除旧文件来重命名(至少这是我理解它的方式).但我想确定一下.
这是场景:父分支看起来像:
??? a.txt
??? b.txt
??? c.txt
Run Code Online (Sandbox Code Playgroud)
然后我们分支两个说,branchA和branchB.在branchB中我们修改结构:
??? lib
? ??? a.txt
? ??? b.txt
??? test
??? c.txt
Run Code Online (Sandbox Code Playgroud)
然后在branchA中我们更新a,b和c.
有没有办法将branchA中完成的更改与branchB中的新结构合并?想到rebase,但是,我不认为lib/a.txt实际上是在git mv之后连接到a.txt ...
詹姆森
tor*_*rek 26
首先,简短说明:您可以随时尝试合并,然后将其退出,看看它的作用:
$ git checkout master
Switched to branch 'master'
$ git status
Run Code Online (Sandbox Code Playgroud)
(当变化不好时,确保它从失败的合并中恢复干净)
$ git merge feature
Run Code Online (Sandbox Code Playgroud)
如果合并失败:
$ git merge --abort
Run Code Online (Sandbox Code Playgroud)
如果自动合并成功,但您还不想保留它:
$ git reset --hard HEAD^
Run Code Online (Sandbox Code Playgroud)
(请记住,这HEAD^是当前提交的第一个父级,并且合并的第一个父级是"合并之前的内容".因此,如果合并有效,HEAD^则是合并之前的提交.)
这是一个简单的方法,用于找出重命名git merge将自动检测的内容.
确保diff.renamelimit1是0并且diff.renames是true:
$ git config --get diff.renamelimit
0
$ git config --get diff.renames
true
Run Code Online (Sandbox Code Playgroud)
如果这些尚未设置,请设置它们.(这会影响以下diff步骤.)
选择要合并的分支,以及要合并的分支.也就是说,你将git checkout master; git merge feature很快做一些事情; 我们需要知道这两个名字.找到它们之间的合并基础:
$ into=master from=feature
$ base=$(git merge-base $into $from); echo $base
Run Code Online (Sandbox Code Playgroud)
你应该看到一些40个字符的SHA-1,就像ae47361...这里一样.(随意打出来master,并feature取代$into和$from无处不在这里.我现在用的变量,所以,这是一个"处方",而不是一个"榜样".)
比较两者的合并基数,$into并$from查看哪些文件被检测为"重命名":
$ git diff --name-status $base $into
R100 fileB fileB.renamed
$ git diff --name-status $base $from
R100 fileC fileD
Run Code Online (Sandbox Code Playgroud)(您可能希望运行这些差异并将输出保存到两个文件中,然后仔细阅读文件.旁注:您可以使用特殊语法获得第三个差异的效果master...feature:这里的三个点表示"找到合并基础".)
两个输出部分有文件的列表Added,Deleted,Modified,Renamed,等等(例子中只有两个重命名,用100%匹配).
既然$into是master,第一个列表是git认为已经发生的事情master.(当你合并时,这些是git"想要保留"的变化feature.)
同时,$from是feature,所以第二个列表是git认为发生的事情feature.(master当你进行合并时,这些是git想要"现在添加到" 的更改.)
此时,你必须做一堆工作:
R,git将检测为重命名.R两个分支中的两个列表相同,那么您可能都很好(但无论如何都要阅读).如果R第一个列表中的s不在第二个...井中,请参见下文.git checkout master; git merge feature(或git checkout $into; git merge $from)git时,将执行第二个列表中显示的重命名,以便"添加这些更改" master.D和A你输入想要有显示为R条目:这些发生时,在其中一个分支,你不仅重命名的文件,而且还改变了内容,以至于混帐不再检测到重命名.如果第二个列表没有显示您想要查看的所有内容,那么您将需要帮助git out.请参阅下面的更长描述.
如果第一个列表具有不在第二个列表中的重命名,则这可能完全无害,或者可能导致"不必要的"合并冲突并错过真正合并的机会.Git将假设您打算保留此重命名,并且还要查看merge-from分支中发生的事情($from或者feature在本例中).如果原始文件在那里被修改,git将尝试将更改从那里带入重命名的文件.这可能就是你想要的.如果原始文件没有在那里被修改,git没有任何东西可以引入,并且只保留文件.这也可能是你想要的."坏"情况再次是未检测到的重命名:git认为原始文件已在分支中删除feature,并且创建了具有其他名称的新文件.
在这个"坏"的情况下,git会给你一个合并冲突.例如,它可能会说:
CONFLICT (rename/delete): newname deleted in feature and renamed in HEAD.
Version HEAD of newname left in tree.
Automatic merge failed; fix conflicts and then commit the result.
Run Code Online (Sandbox Code Playgroud)
这里的问题不是git以其新名称保留了该文件master(我们probalby 想要的); 这就是git可能错过了合并分支中所做更改的机会feature.
更糟糕 - 这可能是一个错误 - 如果新名称出现在merge-from分支中feature,但git认为它是一个新文件,git只留下工作树中文件的合并版本.发出的消息是相同的.在这里,我进行了一些更改master以重命名fileB为fileE,并且feature确保git不会将更改检测为重命名:
$ git diff --name-status $base master
R100 fileB fileE
$ git diff --name-status $base feature
D fileB
R100 fileC fileD
A fileE
$ git checkout master; git merge feature
CONFLICT (rename/delete): fileE deleted in feature and renamed in HEAD.
Version HEAD of fileE left in tree.
Automatic merge failed; fix conflicts and then commit the result.
Run Code Online (Sandbox Code Playgroud)
请注意可能具有误导性的消息fileE deleted in feature.Git正在打印新名称(名称的master版本); 这就是它认为你"想要"看到的名字.但它是fileB被"删除"的文件feature,取而代之的是全新的fileE.
(git-imerge,如下所述,可能能够处理这种特殊情况.)
1还可以单独设置merge.renameLimit(limit在源中拼写小写,但这些配置变量不区分大小写).将这些设置为0会告诉git使用"合适的默认值",随着CPU速度的提高,这种情况已经发生了多年的变化.如果未设置单独的合并重命名限制,git将使用diff rename限制,如果未设置或为0,则再次使用合适的默认值.如果设置不同,则merge和diff将在不同情况下检测重命名.
您现在还可以在递归合并中设置"重命名阈值" -Xrename-threshold=,例如-Xrename-threshold=50%.这里的用法与git diff's -M选项相同.该选项首先出现在git 1.7.4中.
假设你在分支机构master,你做git merge 12345467或git merge otherbranch.这是git的作用:
找到合并基础:git merge-base master 1234567或git merge-base master otherbranch.
这会产生一个commit-ID.我们称这个ID B为"Base".Git现在有三个特定的提交ID:B合并基础; 当前分支的提示的提交ID master; 和你给它的提交ID,1234567或分支的提示otherbranch.为了完整性,让我们根据提交图来绘制这些; 让我们说它看起来像这样:
A - B - C - D - E <-- master
\
F - G - H - I <-- otherbranch
Run Code Online (Sandbox Code Playgroud)
如果一切顺利,git将生成一个包含E和I作为其两个父项的合并提交,但我们希望将重点放在生成的工作树而不是提交图上.
鉴于这三个提交(B E和I),git计算两个差异,一个la git diff:
git diff B E
git diff B I
Run Code Online (Sandbox Code Playgroud)
第一个是在上面进行的更改集,branch第二个是otherbranch在这种情况下所做的更改集.
如果git diff手动运行,可以设置重命名检测的"相似性阈值" -M(参见上面的合并期间设置).Git的默认合并将自动重命名检测设置为50%,这是您没有-M选项并diff.renames设置为true.
如果文件"足够相似"(并且"完全相同"总是足够的话),git将检测重命名:
$ git diff B otherbranch # I tagged the merge-base `B`
diff --git a/fileB b/fileB.txt
similarity index 71%
rename from fileB
rename to fileB.txt
index cfe0655..478b6c5 100644
--- a/fileB
+++ b/fileB.txt
@@ -1,3 +1,4 @@
file B contains
several lines of
stuff.
+changeandrename
Run Code Online (Sandbox Code Playgroud)
(在这种情况下,我只是重命名fileB为fileB.txt但是检测也可以跨目录工作.)让我们注意这可以通过git diff --name-status输出方便地表示:
$ git diff --name-status B otherbranch
R071 fileB fileB.txt
Run Code Online (Sandbox Code Playgroud)
(我还应该注意到我已经diff.renames设置了true并diff.renamelimit = 0在我的全局git配置中.)
B到I(上otherbranch)从进入改变B到E(上branch).如果 git能够检测到lib/a.txt重命名的a.txt,它将连接它们.(并且您可以通过执行a来预览它git diff.)在这种情况下,自动合并结果可能是您想要的,或者足够接近.
如果没有,它不会.
当自动重命名检测失败时,有一种方法可以逐步分解提交(或者可能已经足够分解).例如,假设序列中F G H I提交,一个步骤(也许G)简单地重命名a.txt来lib/a.txt,等步骤(F,H和/或I)作出许多其他变化a.txt(以任何名义)以欺骗的git到没有意识到该文件是重命名.你在这里可以做的是增加合并的数量,以便git可以"看到"重命名.让我们假设为简单起见,这F不会改变a.txt和G重命名它,这样从DIFF B到G显示重命名.我们能做的是首次合并提交G:
git checkout master; git merge otherbranch~2
Run Code Online (Sandbox Code Playgroud)
一旦合并完成和Git已经从更名a.txt到lib/a.txt树为新的合并提交的分支branch,我们做了第二次合并的提交,使H和I:
git merge otherbranch
Run Code Online (Sandbox Code Playgroud)
这两步合并导致git"做正确的事".
在最极端的情况下,增量的提交提交合并序列(手动操作非常痛苦)将获取可以拾取的所有内容.幸运的是,有人已经为您编写了这个"增量合并"计划:git-imerge.我没有试过这个,但这是针对疑难病例的明显答案.
| 归档时间: |
|
| 查看次数: |
7993 次 |
| 最近记录: |