假设我有一个像这样的回购
$ git log --oneline --graph
* 3e0a28f Merge branch 'other_branch'
|\
| * d4fd67a Add something else
| * d0f16bf Merge branch 'master' into other_branch
| |\
| |/
|/|
* | 3684fe5 Make a change in master
| * 45b3ecb Make a change
|/
* b2a9034 Added some text, reword me
* 7b1ac57 Initial commit
d0f16bf
涉及修复一些合并冲突。 (3684fe5
和45b3ecb
修改同一行)。
我想改写b2a9034
$ git rebase -i b2a9034^
[detached HEAD a9bd978] Added some text, REWORDED
1 file changed, 2 insertions(+)
error: could not apply 3684fe5... Make a change in master
When you have resolved this problem, run "git rebase --continue".
If you prefer to skip this patch, run "git rebase --skip" instead.
To check out the original branch and stop rebasing, run "git rebase --abort".
Could not apply 3684fe570517de37e1ce7661e3821372e1eee967... Make a change in master
有没有办法在不修复合并冲突的情况下重新编写b2a9034
?
答案 0 :(得分:3)
当你说" reword"我假设你的意思是"更改提交消息,但不是提交的文件" (git rebase -i
含义)。
Git的"Swiss-Army Chainsaw"命令git filter-branch
,有一个选项。 (当然,filter-branch
很难使用,因此"Swiss-Army chainsaw" appellation。)
具体来说,filter-branch有--msg-filter
,允许在更改消息时复制提交:
这是用于重写提交消息的过滤器。争论 在shell中使用原始提交消息进行评估 标准输入;其标准输出用作新提交 消息。
这意味着您可以保留现有消息以用于不是重写目标的提交,并使用合适的--msg-filter
编辑或替换作为目标的提交,例如:
git filter-branch ... \
--msg-filter 'if [ $GIT_COMMIT == b2a9034... ]; then \
cat $HOME/new_msg; else cat; fi' \
...
此过滤器显然按原样(cat
)复制邮件,除非它是一个目标提交,在这种情况下它忽略了stdin消息并打印了预先准备的内容$HOME/new_msg
个文件。您当然可以编写任何有效的shell脚本,只需确保保留其他提交的确切原始消息。
您还需要在此填写完整的提交ID(或使用前缀匹配,但它可能更明智地填写完整的ID)。要从部分ID中获取完整ID,最简单的方法是使用git rev-parse
:
$ git rev-parse b2a9034
b2a9034... <-- full 40 char SHA-1 comes out
$
您还需要填写...
git filter-branch
部分的其余部分,这是非常重要的。
由于filter-branch很慢,您可能希望将其限制为少量提交。您可以使用git rev-list
参数执行此操作:filter-branch将它们传递给git rev-list
,并仅复制这样列出的提交。因此,您可以先测试全部:
$ git rev-list ^b2a9034^ branch1 branch2
这里两个branch
es是你想要重写的分支提示的分支名称(可能其中一个是master
,基于你上面的文字)。第一个参数^b2a9034^
应该导致git rev-list
省略b2a9034
的父提交和所有早期提交。 (第一个^
字符是git rev-list
的&#34; not&#34;运算符,第二个是gitrevisions
的父跟踪运算符。这可能有点令人困惑,所以替代拼写是^b2a9034~1
,它具有完全相同的含义,但不会以两种不同的方式使用^
。我不确定它到底有多混乱,虽然。)
(如果您的存储库提交很少,则此限制并不那么重要。)
最后,请注意,正如filter-branch文档所说:
该命令只会重写中提到的正引用 命令行(例如,如果您通过
a..b
,则只会重写b
。 ...
这个短语对我来说并不明显,直到我正确理解git的内部结构,并从那里开始,filter-branch如何做它的作用。在内部,git只能添加内容,因此git filter-branch
只需复制现有提交到新的提交。如果新提交与原始提交完全相同,因此副本与原始提交完全相同,则会获得副本的原始SHA-1,这意味着不会添加任何内容,也不会发生任何更改。但是,如果您更改了任何内容,则会获得一个新的,不同的SHA-1 ID。
这意味着当过滤器分支沿着复制提交运行时,它会复制&#34;在被修改之前的任何提交并再次获得原始SHA-1。然后它会击中您想要更改的第一个(也许只有),并获得一个新的SHA-1。原始提交仍保留在存储库中,但现在有一个带有新SHA-1的新副本。
一旦发生这种情况,所有后续提交过滤器分支副本至少进行了一次更改,即使您的过滤器都没有更改它们。特别是,它们具有(至少一个)其父ID,新ID。第一个新的子提交具有新的父ID,因此它也获得了新的ID;然后它的子承诺必须拿起那个新的ID;等等。
最终结果是提交的新副本为您提供了一个新的commit-ID链,以您过滤的(在这种情况下为两个)分支的新提交ID结尾。然后Git必须将这些ID保存为新的分支提示。当文档说&#34;只有b
将被重写&#34;时,这意味着filter-branch
将更新refs/heads/b
- 包含分支{{1}的ID的文件1}} - 获得复制的branch-tip的最终SHA-1。
因此,通过列出,例如b
,您提供了两个&#34;正面参考&#34;,即^b2a9034^ master develop
和refs/heads/master
,这些是两个filter-branch将更新。 refs/heads/develop
是&#34;否定参考&#34; (&#34;排除^b2a9034^
及更早版本),因此filter-branch在将其传递给b2a9034^
之后不做任何事情。
答案 1 :(得分:0)
您可以使用git rerere
功能。
您必须使用git config --global rerere.enabled 1
启用它,之后,您解决的每个冲突都会被存储以供以后使用,并且在相同的上下文中重新应用解析。但是,如果您现在尝试它将不会应用它,因为它尚未记录您的冲突合并。设置rerere&amp; amp;它将从那里开始。
您可以使用git rerere diff
检查存储的分辨率。
有关git rerere
的更多信息,请查看此tutorial。
正如J. C. Hamano在他的文章中提及的那样#34; Fun with rerere&#34;
- Rerere记得您选择如何解决冲突地区;
- Rerere还记得你是如何在冲突地区之外进行调整以适应语义变化的;
- Rerere可以重复使用以前的分辨率,即使您合并了两个分支,其内容与您之前解析的分支不同。
即使长期使用rerere的人也常常没有注意到最后一点。
因此,如果您在内容过于宽泛的情况下激活rerere
,则由于最后一点,您最终可能会出现令人惊讶或令人困惑的合并解决方案。
如果您错误地进行了合并,则将其丢弃,然后执行&#34;相同的&#34;再次合并,它将再次不正确。但是,您可以忘记记录的分辨率。来自the documentation:
git rerere forget <pathspec>
这会重新解决rerere为
<pathspec>
中当前冲突记录的冲突解决方案。
小心在特定路径上使用它;你不想在任何地方吹走所有录制的分辨率。 (forget
没有参数deprecated可以帮助您避免这样做,除非您输入git rerere forget .
来明确请求它。)
但是,如果你不想这样做,你很容易就会把错误的合并放到你的历史中。