当交互式Git rebase完成时,我如何运行命令



我有一个封装git rebase --interactive的shell脚本,我希望它在rebase完成后做一些事情。(如果回购的初始和最终内容之间存在差异,我希望它能通知用户。(然而,不可能只写

git rebase --interactive some_commit
# ...check for differences here...

因为如果用户选择编辑任何提交,或者如果有任何冲突需要解决,那么git将在此时退出,并运行以下行,即使在概念上还没有完成rebase。

我想过安装一个临时的"post-rebase"挂钩,但没有这样的挂钩。这个问题的答案表明,重写后挂钩在某些情况下会起作用。然而,即使用户运行了git rebase --abortgit rebase --quit,我也希望清理钩子,并且我认为在这些情况下重写后的钩子不会启动。(我不想安装永久挂钩来支持这个脚本,因为我希望这个脚本可以在任何回购上运行,而无需事先进行任何设置。此外,我的一些回购已经使用结账后挂钩来做不相关的事情。(

有没有一种方法可以让Git在rebase完成时运行某个脚本?

解决方案是有两个独立的脚本:一个启动git rebase --interactive("包装脚本"(,另一个将在rebase结束时运行("post-rebase脚本"(。您可以在rebase todo列表中插入一行exec,以确保后者在rebase结束时自动运行。此外,您可以设置GIT_SEQUENCE_EDITOR环境变量,以自动添加exec行本身。

后重新基本脚本

取应该在重新基准结束时运行的逻辑,并将其放入一个单独的脚本中。(请注意,如果rebase被中止或退出,则此脚本将不会运行。(如果脚本在rebase期间需要环境中可用的上下文之外的任何上下文,则应接受此信息作为命令行参数。

例如,我的shell脚本名为check_git_diff,它看起来像

#!/usr/bin/env zsh
if [[ $# -ne 1 ]]; then
print >&2 "usage: check_git_diff ORIGINAL_HEAD"
exit 1
fi
readonly original_head=$1
if ! git diff --quiet $original_head HEAD; then
kind='non-whitespace'
if git diff --quiet --ignore-all-space $original_head HEAD; then
kind='whitespace'
fi
print -P >&2 "%F{red}Warning:%f There are $kind differences from the original HEAD. See"
print -P >&2 "n  %F{blue}git diff $original_head HEAD%fn"
fi

自动调用此脚本

通过在todo列表的末尾添加一行exec,您可以要求Git本身在rebase的末尾运行此脚本。在所有picksquashfixup等行之后,添加一个用于重新基础后脚本:

pick 8bbfd15 update build instructions
pick caf5bfd frobnicate some additional blobs
exec check_git_diff caf5bfd3d4b6099aeed13604936976e610a08e18
# Rebase 9f594d2..caf5bfd onto 8bbfd15 (2 commands)
#
# ...

正如您所看到的,如果需要的话,您可以将参数包含到后重基脚本中

如果运行git rebase --abortgit rebase --quit,Git将停止运行这些命令,这就是为什么我在上面说过,check_git_diff只会在成功重新基础结束时运行。

自动调用此脚本

由于首先启动git rebase --interactive的是您的脚本,因此您有机会自动化前一步。在包装脚本中,在调用git rebase --interactive之前设置GIT_SEQUENCE_EDITOR环境变量。例如,

original_head=$(git rev-parse HEAD)
export GIT_SEQUENCE_EDITOR="f() {
sed -i -e '/^$/a exec check_git_diff $original_headn' $1 && exec vim $1
}; f"

如果设置了GIT_SEQUENCE_EDITOR环境变量,GIT将使用其值作为用于编辑待办事项列表的编辑器的名称。通常,您会将其设置为类似vimemacs的值,但在这里,我实际上定义了一个shell函数,然后将该函数用作编辑器。(Git使用Bash来运行Git_SEQUENCE_EDITOR的内容。(shell函数做两件事:它调用sed来修改todo列表,使其包含exec行,然后它运行Vim,以便您可以像往常一样编辑todo列表。当然,您可以将vim替换为首选编辑器的名称。

净效果是,您可以在交互重新基准开始前运行某些特定的逻辑,然后在成功结束后运行某些其他逻辑。

感谢@torek让我注意到GIT_SEQUENCE_EDITOR变量。这使得解决方案简单多了

我不确定我喜欢这个,但也许这个想法会激励人们写一些好的东西。。。

git rebase -i的包装外壳脚本可以做一些类似的事情

git_rebase_in_progress() {
[[ -d "$(git rev-parse --git-path rebase-merge)" || -d "$(git rev-parse --git-path rebase-apply)" ]]
}
if git rebase --interactive ...
then
while git_rebase_in_progress
do
echo "Rebase is in progress, exit 1 to abort the rebase, or run the normal interactive rebase commands like 'git rebase --skip' or 'git rebase --continue"
# TODO: Set the shell prompt to indicate the following is not a normal shell
if ${SHELL}
then
echo "Successful rebase step by user, continuing the rebase"
continue
else
echo "User has aborted the rebase."
break
fi
done
echo "Rebase has finished, running special commands"
# special commands go here
else
echo "The rebase has failed."
fi

最新更新