如何使用一行压缩 git 中的两个非连续提交?

How do I squash two non-consecutive commits in git, using one line?

我知道我可以使用 rebase -i 来做到这一点,但我真的在寻找一种在一行中完成它的方法,例如,使用脚本。 例如我看了看How do I run git rebase --interactive in non-interactive manner?,但是我的bashfu太弱了,无法正常使用。

明确地说,这是我现在拥有的:

A1B2C3 一些承诺
AAAAAA 其他一些提交
A3B2C1 更多承诺
BBBBBB 另一个提交

我想在一行(或多行)git 行中以非交互方式压缩 AAAAAA 和 BBBBBB。

谢谢

您可以通过一系列精选和修改后的提交来模拟变基交互:

git checkout AAAAAA
git cherry-pick BBBBBB -n && git commit --amend -m "AAAAA squashed with BBBBB"
git cherry-pick A3B2C1

我最终在链接问题的帮助下使用了 bash 脚本。 为了完整起见,我附上了它。 注:我的bash是。 但有一点很重要:由于脚本不能接受参数,我选择了自修改代码。 如果有更好的解决方案(不使用 globals/environment 变量),我很想知道。 另外,我不完全确定如何交换 bash 中的两行(google 不是很好的帮助),所以我最终使用了 for 循环。

#!/bin/bash
commit1=???
commit2=???
if [ "$#" -eq 2 ]; then #2 arguments, first phase of script
    #modify lines 2 and 3 with arguments
    currentFile=${BASH_SOURCE[0]}
    cat $currentFile | sed "2s/.*/commit1=/" | sed "3s/.*/commit2=/" >| $currentFile
    echo "$currentFile"
    exit
fi

# second phase of script
line1=$(grep -nr "pick $commit1"  | cut -f1 -d:)
if [ -z $line1 ]; then
    echo "Could not find a match for $commit1"
    exit
fi
line2=$(grep -nr "pick $commit2"  | cut -f1 -d:)
if [ -z $line2 ]; then
    echo "Could not find a match for $commit2"
    exit
fi
oldLine=$(($line1<$line2?$line1:$line2))
newLine=$(($line1>$line2?$line1:$line2))
cat 
#move newLine to be below oldLine, by iterating over all lines in reverse and swapping one by one
for currentLineIndex in `seq $newLine -1 $(expr $oldLine + 2)`; do
    currentLine=$(sed -n "${currentLineIndex}p" "")
    nextLineIndex=$(expr $currentLineIndex - 1)
    nextLine=$(sed -n "${nextLineIndex}p" "")
    cat  | sed $currentLineIndex"s/.*/$nextLine/" | sed $nextLineIndex"s/.*/$currentLine/" >| 
done

#change the pick to squash
cat  | sed $(expr $oldLine + 1)"s/pick/squash/" >| 

使用示例:$ GIT_SEQUENCE_EDITOR=$(./rebase.sh d199 a548) git rebase -i 95e2a7c

一个更简单的选择,如果你只想修复暂存区的现有提交:(感谢@Andrew C 将我指向 --fixup

function git_fix {
    git commit --fixup=
    GIT_SEQUENCE_EDITOR=: git rebase -i --autosquash "^" #empty script
}

作为 git 别名:

fix="!f() {\
    git commit --fixup=;\
    GIT_SEQUENCE_EDITOR=: git rebase -i --autosquash "^";\
}; f"