我将如何在没有任何冲突的情况下尽早将提交移至分支上(无需太多手动工作,例如 rebase -i)?
例如
A-B-C-D-X
应该成为
A-B-X-C-D
如果将 X 与 C 和 D 交换没有冲突,但将 X 与 B 交换则会导致冲突。
谢谢。
这是我经过 15 分钟的黑客攻击后得到的演示。这不是所提出问题的完整解决方案,但它应该减少所涉及的工作。
目标是使用
git bisect
找到未来提交的最早无冲突合并点。该解决方案利用 git bisect
固有的二分搜索功能来减少步骤。
不幸的是,这并不能阻止以后的提交发生冲突,因此需要交互式变基来审查结果(但这就是重点,无论如何)。
一个缺点/警告是,当你在测试补丁时指示 git 该步骤是失败还是成功时,你必须颠倒头脑中
good
和 bad
的含义。
如果以下任何步骤不清楚,请告诉我,我会尽力详细说明。
首先在一系列提交中创建以下文件。每次提交都应添加一系列四个相同的行(a、然后 b、然后 c、然后 d)。
a
a
a
a
b
b
b
b
c
c
c
c
d
d
d
d
此时,
git log
应该输出类似:
commit 6f2b809863632a86cc0523df3a4bcca22cf5ab17
Author: Todd Sundsted <...>
Date: Tue Dec 20 22:45:44 2011 -0500
Added d.
commit 91ba7e6f19db74adb6ce79e7b85ea965788f6b88
Author: Todd Sundsted <...>
Date: Tue Dec 20 22:44:26 2011 -0500
Added c.
commit f83beee55d6e060536584852ebb55c5ac3b850b2
Author: Todd Sundsted <...>
Date: Tue Dec 20 22:44:00 2011 -0500
Added b.
commit d6d924b0a30a9720f6e01dcc79dc49097832a587
Author: Todd Sundsted <...>
Date: Tue Dec 20 22:43:38 2011 -0500
Added a.
commit 74d41121470108642b1a5df087bc837fdf77d31c
Author: Todd Sundsted <...>
Date: Tue Dec 20 22:43:11 2011 -0500
Initial commit.
现在编辑文件,使其包含以下内容,然后提交:
a
a
a
a
b
x
x
b
c
x
x
c
d
d
d
d
日志现在应该包含另一次提交:
commit 09f247902a9939cb228b580d39ed2622c3211ca6
Author: Todd Sundsted <...>
Date: Tue Dec 20 22:46:36 2011 -0500
Replaced a few lines with x.
现在为
X
提交生成补丁。
git diff -p master~ > x.patch
启动
bisect
——记得在补丁失败时使用git bisect good
,在补丁成功时使用git bisect bad
:
$ git bisect start
$ git bisect good 74d41121470108642b1a5df087bc837fdf77d31c
$ git bisect bad master
Bisecting: 2 revisions left to test after this (roughly 1 step)
[f83beee55d6e060536584852ebb55c5ac3b850b2] Added b.
$ patch --dry-run -p1 < x.patch
patching file file.txt
Hunk #1 FAILED at 3.
1 out of 1 hunk FAILED -- saving rejects to file file.txt.rej
$ git bisect good
Bisecting: 0 revisions left to test after this (roughly 1 step)
[6f2b809863632a86cc0523df3a4bcca22cf5ab17] Added d.
$ patch --dry-run -p1 < x.patch
patching file file.txt
$ git bisect bad
Bisecting: 0 revisions left to test after this (roughly 0 steps)
[91ba7e6f19db74adb6ce79e7b85ea965788f6b88] Added c.
$ patch --dry-run -p1 < x.patch
patching file file.txt
Hunk #1 succeeded at 3 with fuzz 2.
$ git bisect bad
91ba7e6f19db74adb6ce79e7b85ea965788f6b88 is the first bad commit
commit 91ba7e6f19db74adb6ce79e7b85ea965788f6b88
Author: Todd Sundsted <...>
Date: Tue Dec 20 22:44:26 2011 -0500
Added c.
$ git bisect reset
正如预期的那样,提交
X
中的编辑可以在提交 C
后立即移动。交互式变基证实了这一点:
91e92489 * Added d.
6c082b1f * Replaced a few lines with x.
a60ae2a9 * Added c.
4d5e78f2 * Added b.
7d2ff759 * Added a.
74d41121 * Initial commit.
嗯,这几乎有效,但需要一些清理。
使用一段时间后,我遇到了另一个问题,我已将其发布在here。
#!/bin/sh -e # todo: 与 git 集成 GIT_DIR=./.git/ 提交ID=$1 如果[“$1”=“”];然后 echo 用法: $0 commitid 1号出口 菲 tmpdir="$GIT_DIR/气泡工作" /bin/rm -rf "$tmpdir" mkdir“$tmpdir” # 签出带有分离头的提交 git checkout -q $commitid^0 ||死亡“无法分离头部” 而[1=1];做 # todo 管道输出以避免临时文件 # 查看 git-rebase.sh patchfile=`git format-patch -k --full-index --src-prefix=a/ --dst-prefix=b/ --no-renames -o "$tmpdir" HEAD~1` 回显补丁= $补丁文件 git checkout -q HEAD~2 git am --rebasing "$patchfile" ||死了“git 失败了” /bin/rm -f "$补丁文件" 回声循环 完毕 /bin/rm -rf "$tmpdir"
我们确实需要逐个提交,以便找到相关提交和所有后来的提交适用的最早点。有效地做到这一点的一种方法是向后迭代提交列表。
下面的(非常粗略的)脚本找到了这一点并打印了有关它的有用信息,这有助于决定您是否真的想将提交移到那么远。要实际移动提交,请使用
git rebase -i
。虽然这可以自动化,但对过程的一些控制在这里似乎实际上很有用。该脚本需要在命令行进行提交,如果未给出,则使用最后一次提交。
冒泡提交是相关的,可以通过向前迭代来实现。
#!/bin/sh
set -ex
top_level=$(git rev-parse --show-toplevel)
# TODO: Does this always exist?
git_dir=${top_level}/.git
tmpdir="git_dir/bubble-work"
/bin/rm -rf "$tmpdir"
git clone "$top_level" "$tmpdir"
start_commit=$(git rev-parse HEAD)
if [ -n "$1" ]; then
my_commit=$1
else
my_commit=$start_commit
fi
git -C $tmpdir reset --hard ${my_commit}^
# FIXME: ask Git for list of commit instead of using loop
for step in $(seq 1 100); do
current_commit=$(git rev-parse ${my_commit}~$step)
git -C $tmpdir revert --no-edit $current_commit
if ! git -C $tmpdir cherry-pick --no-commit $my_commit; then
# Show info that helps decide if this is really the right place
# to move that commit to, and to navigate in the subsequent `git rebase -i`
git --no-pager show $current_commit
git --no-pager show $my_commit
git log --oneline -n 1 $current_commit
git log --oneline -n 1 $my_commit
exit 1
fi
git -C $tmpdir reset --hard HEAD
done
echo "End reached, increase loop range"
git reset --hard $my_commit