我目前正在对Java应用程序进行大规模重构,将服务类拆分为服务接口和服务实现,例如
package de.abc.def;
public class AuthenticationService {
public void doStuff() {
System.out.println("doing stuff");
}
}
变为
package de.abc.def;
public interface AuthenticationService {
void doStuff();
}
package de.abc.impl;
public class AuthenticationServiceImpl implements de.abc.def.AuthenticationService {
@Override public void doStuff() {
System.out.println("doing stuff");
}
}
所有这些都在master中发生在几个不同的操作中(首先将AuthenticationService
移动到新包并重命名,提交更改,然后使用重构来引入接口,从而在旧位置重新创建AuthenticationService.java
,并再次承诺)
当我现在尝试将对另一个分支中的服务类的更改合并到master中时,git会产生很多合并冲突 - 基本上我可以手动解决对服务类的每个更改。我认为/希望git会检测重命名操作并将对服务类所做的更改合并到服务实现中(它们与旧服务类几乎完全相同),但似乎git很高兴使用现有文件和试图合并它 - 完全失败。
有没有办法强制git首先考虑重命名?或者我是否必须重命名或移动所有服务接口,以便git找不到候选者(这是我想避免的......)?
由于
答案 0 :(得分:2)
基于端点的Git合并(与合并库相比)。让我们绘制提交图:
... - B - R1 - R2 <-- HEAD=master
\
S1 - S2 <-- service
此处B
是合并基础(两个分支发散的点),提交R1
重命名文件,提交R2
重新引入旧名称。
同时,提交S1
和S2
是service
(服务类)分支上的普通非重命名提交。
当你在master
并且git merge service
时,git会在下面有效地执行此操作:
git diff -M B R2 > our_changes
git diff -M B S2 > their_changes
# now combine the two sets of changes and make a new commit
-M
尝试检测重命名,但由于提交R2
重新引入旧文件名而被愚弄。
有两种明显的(?)方法可以自动进行合并:
service
中创建相同的名称,并在那里进行提交S3
,以便名称匹配(然后希望git不会意外地检测到比较中的{ {1}} B
并弄得一团糟,或者; 我怀疑(没有尝试)方法2会更好用,而且你已经完成了提交S3
和R1
的工作,所以你也可以使用它。
要执行此操作,请检查提交R2
(您可以将此作为&#34;分离的HEAD&#34;提交,或为其创建分支 - 让我们调用它{{1所以你做R1
),然后temp
得到一个新的合并提交。这次,当git执行从git checkout -b temp master^
到git merge service
的差异时,它应检测重命名,并将B
分支中的更改应用于重命名的文件(这些当然是< em> old ,无意义的文件,但这几乎是你必须要做的事情,以便应用更改)。现在图表看起来像这样:
R1
您现在可以通过执行service
将 R2 <-- master
/
... - B - R1 ---- M1 <-- HEAD=temp
\ /
S1 - S2 <-- service
合并到temp
。此合并的合并基础为master
:git将查看git checkout master; git merge temp
和R1
之间的更改,以及R1
和M1
之间的更改,以及尝试将R1
更改置于R2
之上,进行新的合并提交R1 to M1
:
R2
自M2
&#34;捕获&#34;重命名(好吧,我们希望), R2 - M2 <-- HEAD=master
/ /
... - B - R1 ---- M1 <-- temp
\ /
S1 - S2 <-- service
和M1
中的更改现在可能会出现在正确的(旧的,重构前)文件中。
上面假设了一定数量的提交,但总体思路是:通过选择更早的&#34;端点&#34;来帮助git。对于合并,重命名检测将执行您想要的操作。为这些合并结果创建一个临时分支。然后使用合并结果作为第二次合并的输入。 (如果需要,请重复两次以上的合并。)记得在完成后删除临时分支名称。