问题 在没有交互式rebase的情况下,将两个Git提交到历史中间


我正在将旧的SVN存储库转换为Git,其中包括尝试在所有正确的位置获取所有分支/标记。这部分进展顺利,但有时候我想在我的脚本中添加历史记录中的提交,后来我想用下一次提交压缩。问题是我不是一个接一个地抓取提交,而是作为一个大组,因此当我将它们从SVN存储库中拉出时,我无法压缩它们。最后,我的存储库看起来像这样:

* (branch_2, HEAD) commit 5
* commit 4
* commit 3
* SQUASH ME!
* (branch_1) commit 2
* commit 1

我希望能够挤压 commit 3 同 SQUASH ME!使用交互式rebase显然很容易,但在脚本中更具挑战性。我似乎遇到的主要问题是虽然很容易结账 branch_1 或者在它之前的任何提交,很难以编程方式请求提交  它和我很难预测有多少提交 branch_2 我要走了。我真的希望能够做到这样的事情:

git checkout branch_1+2

有什么指针吗?


3502
2017-10-01 13:50


起源

看一下rebase命令的“执行”选项,用于执行诸如压缩之类的操作,而不必进行交互操作(非常适合脚本): blog.pivotal.io/labs/labs/git-rebase-onto - Jonathan.Brink
@ Jonathan.Brink这不是我想要的。该帖强调能够删除提交。我想压制(或修复) SQUASH ME! 提交,因为我确实想要更改。我只想让它们结合起来 commit 3。 - Wesley Bland
在交互式rebase期间让脚本充当编辑器可能更简单。 - coredump
@WesleyBland我的意思是,通过将GIT_EDITOR设置为您选择的程序,让脚本成为与git交互的人。您的程序打开文件,删除/修改一些行并退出。将整个内容包装在另一个脚本中: GIT_EDITOR=./scripts/editor-bot git rebase --interactive $0 或类似的东西,以满足您的需求。 - coredump
我刚刚读到这篇文章,并意识到我的答案完全相同,我甚至为你编写了剧本。 :-) - ams


答案:


你所说的不是一个 壁球,但是 修理,因为壁球会以交互方式询问你提交msg,而 fixup使用来自HEAD提交的提交消息

这是一个没有干预的脚本。

剧本 : /usr/bin/git-fixup

#/bin/bash
# This command fixesup one commit onto his parent
set -e

# We need a commit from the first argument of that command
commit=${1:?No commit given as first argument}
startingbranch=$(git rev-parse --abbrev-ref HEAD)

# Checkout the parent of the asked commit
git checkout "$commit"
git checkout HEAD~

# Merge the commit into it's parent, keeping the commit message of the parent
git merge --squash "$commit"
git add .
git add --update
git commit --amend --no-edit

# Store the current commit
newcommit=$(git rev-parse HEAD)

# Rebase the starting branch onto the new commit
git checkout "$startingbranch"
git rebase "$newcommit"

使用它

git fixup <commit-id>

例如,如果您的历史记录是:

ce0e2fd (master, HEAD) commit 4
72ab3c4 commit 3
8150939 commit 2
301c1e1 commit 1

你可以做 git fixup 72ab3c4 它将“commit 3”和“commit 2”合并为一个提交,并带有消息“commit 2”,并将您放回主分支。


8
2017-10-01 15:28



我建议添加 set -e 为了在工作目录变脏时中止。首先 git checkout 会抱怨,整个脚本将退出。 - coredump
谢谢你的建议,我已经说过了 - edi9999


git rebase --help

--autosquash

当提交日志消息以“squash!...”(或“fixup!...”)开头时,并且提交的标题以相同的提交开头   ...,自动修改rebase -i的待办事项列表   因此,标记为压缩的提交在修改提交之后立即生效,并更改已移动提交的操作   从挑选到壁球(或修复)。

此选项仅在使用--interactive选项时有效。

如果输入的形式正确,那看起来它会做你想要的一半。

另一半是阻止它启动交互式编辑器。幸运的是,编辑器是可配置的,所以我们可以将其设置为无害的。

尝试这个:

env EDITOR=true git rebase -i --autosquash <from-here>

将编辑器设置为 true (一个简单地退出成功的工具)就足以说服git继续使用默认的rebase设置,自动壁球应该设置为有用的东西。


或者,如果 --autosquash 没有你想要的,你可以设置 EDITOR 到你喜欢的任何脚本:

env EDITOR=mysquasher.sh git rebase -i <from-here>

该脚本可以执行您需要的任何操作,但在您的情况下,它只需要找到包含的每一行 "SQUASHME!",并改变 "pick" 在以下行阅读 "fixup"。使用awk可能最容易实现这一点:

#!/bin/bash -e

awk -- 'BEGIN {dosquash=0}
        dosquash==1 {gsub(/^pick/, "fixup"); dosquash=0}
        /SQUASHME/ {dosquash=1}
        {print}' "$1" > /tmp/tmp.$$

mv /tmp/tmp.$$ "$1"

5
2017-10-01 15:49



env EDITOR = true仍然为我拉起编辑器。 - Andrew
你有 VISUAL 设置,或任何其他可疑的环境变量? - ams
不是我能说出来的。 - Andrew
尝试设置 GIT_EDITOR 代替。 Git至少回应 EDITOR, VISUAL,和 GIT_EDITOR (对我来说),但我认为后者首先被检查。 - ams
你有 env 和 git 所有命令都在同一条线上,对吧? - ams