当交互式 Git 变基完成时,我如何 运行 命令?
How do I run a command when an interactive Git rebase is finished?
我有一个包装 git rebase --interactive
的 shell 脚本,我希望它在 rebase 完成后做一些事情。 (如果 repo 的初始内容和最终内容之间存在差异,我希望它通知用户。)但是,只写
是不可能的
git rebase --interactive some_commit
# ...check for differences here...
因为如果用户选择编辑任何提交,或者如果有任何冲突需要解决,那么 git
将在此时退出并且以下行将是 运行,尽管从概念上讲 rebase 还没有完成。
我想安装一个临时的“post-rebase”钩子,但是没有这样的钩子。 this question 的答案表明 post-rewrite 挂钩在某些情况下会起作用。但是,即使用户 运行 git rebase --abort
或 git rebase --quit
,我也想清理挂钩,并且我假设 post-rewrite 挂钩在这些情况下不会触发。 (我不想安装永久挂钩来支持这个脚本,因为我希望脚本可以在任何 repo 上工作而无需事先进行任何设置。此外,我的一些 repo 已经使用 post-checkout hooks 来做不相关的事情。)
有没有办法在 rebase 完成时让 Git 到 运行 某个脚本?
解决方案是使用两个单独的脚本:一个启动 git rebase --interactive
的脚本(“包装脚本”)和一个 运行 在变基结束时的脚本(“ post-变基脚本”)。您可以在 rebase 待办事项列表中插入 exec
行,以确保后者在 rebase 结束时自动为 运行。此外,您可以设置 GIT_SEQUENCE_EDITOR 环境变量以自动添加 exec
行本身。
post-rebase 脚本
将变基结束时应 运行 的逻辑放入单独的脚本中。 (请注意,如果 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=
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%f\n"
fi
自动调用此脚本
您可以通过在待办事项列表末尾添加 exec
行,要求 Git 本身在 rebase 末尾 运行 此脚本。在所有 pick
、squash
、fixup
等行之后,为 post-rebase 脚本添加一个:
pick 8bbfd15 update build instructions
pick caf5bfd frobnicate some additional blobs
exec check_git_diff caf5bfd3d4b6099aeed13604936976e610a08e18
# Rebase 9f594d2..caf5bfd onto 8bbfd15 (2 commands)
#
# ...
如您所见,如果需要,您可以在 post-rebase 脚本中包含参数。
如果你 运行 git rebase --abort
或 git rebase --quit
,Git 将停止 运行ning 这些命令,这就是为什么我在上面说 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_head\n' $1 && exec vim $1
}; f"
如果设置了 GIT_SEQUENCE_EDITOR 环境变量,Git 将使用它的值作为编辑器的名称来编辑待办事项列表。通常你会将其设置为 vim
或 emacs
之类的东西,但在这里我实际上定义了一个 shell 函数,然后将该函数用作编辑器。 (Git 使用 Bash 到 运行 GIT_SEQUENCE_EDITOR 的内容。) shell 函数做了两件事:它调用 sed
修改待办事项列表以包含 exec
行,然后是 运行s Vim 以便您可以像往常一样编辑待办事项列表。当然,您可以将 vim
替换为您喜欢的编辑器的名称。
最终效果是您可以在交互式变基开始之前运行一些特定的逻辑,然后在它成功结束后运行一些特定的其他逻辑。
感谢@torek 让我注意到 GIT_SEQUENCE_EDITOR 变量。这让解决方案变得简单多了!
我不确定我喜欢这个,但也许这个想法会激发某人写一些好东西...
git rebase -i
的包装器 shell 脚本可以按照
的方式做一些事情
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
我有一个包装 git rebase --interactive
的 shell 脚本,我希望它在 rebase 完成后做一些事情。 (如果 repo 的初始内容和最终内容之间存在差异,我希望它通知用户。)但是,只写
git rebase --interactive some_commit
# ...check for differences here...
因为如果用户选择编辑任何提交,或者如果有任何冲突需要解决,那么 git
将在此时退出并且以下行将是 运行,尽管从概念上讲 rebase 还没有完成。
我想安装一个临时的“post-rebase”钩子,但是没有这样的钩子。 this question 的答案表明 post-rewrite 挂钩在某些情况下会起作用。但是,即使用户 运行 git rebase --abort
或 git rebase --quit
,我也想清理挂钩,并且我假设 post-rewrite 挂钩在这些情况下不会触发。 (我不想安装永久挂钩来支持这个脚本,因为我希望脚本可以在任何 repo 上工作而无需事先进行任何设置。此外,我的一些 repo 已经使用 post-checkout hooks 来做不相关的事情。)
有没有办法在 rebase 完成时让 Git 到 运行 某个脚本?
解决方案是使用两个单独的脚本:一个启动 git rebase --interactive
的脚本(“包装脚本”)和一个 运行 在变基结束时的脚本(“ post-变基脚本”)。您可以在 rebase 待办事项列表中插入 exec
行,以确保后者在 rebase 结束时自动为 运行。此外,您可以设置 GIT_SEQUENCE_EDITOR 环境变量以自动添加 exec
行本身。
post-rebase 脚本
将变基结束时应 运行 的逻辑放入单独的脚本中。 (请注意,如果 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=
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%f\n"
fi
自动调用此脚本
您可以通过在待办事项列表末尾添加 exec
行,要求 Git 本身在 rebase 末尾 运行 此脚本。在所有 pick
、squash
、fixup
等行之后,为 post-rebase 脚本添加一个:
pick 8bbfd15 update build instructions
pick caf5bfd frobnicate some additional blobs
exec check_git_diff caf5bfd3d4b6099aeed13604936976e610a08e18
# Rebase 9f594d2..caf5bfd onto 8bbfd15 (2 commands)
#
# ...
如您所见,如果需要,您可以在 post-rebase 脚本中包含参数。
如果你 运行 git rebase --abort
或 git rebase --quit
,Git 将停止 运行ning 这些命令,这就是为什么我在上面说 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_head\n' $1 && exec vim $1
}; f"
如果设置了 GIT_SEQUENCE_EDITOR 环境变量,Git 将使用它的值作为编辑器的名称来编辑待办事项列表。通常你会将其设置为 vim
或 emacs
之类的东西,但在这里我实际上定义了一个 shell 函数,然后将该函数用作编辑器。 (Git 使用 Bash 到 运行 GIT_SEQUENCE_EDITOR 的内容。) shell 函数做了两件事:它调用 sed
修改待办事项列表以包含 exec
行,然后是 运行s Vim 以便您可以像往常一样编辑待办事项列表。当然,您可以将 vim
替换为您喜欢的编辑器的名称。
最终效果是您可以在交互式变基开始之前运行一些特定的逻辑,然后在它成功结束后运行一些特定的其他逻辑。
感谢@torek 让我注意到 GIT_SEQUENCE_EDITOR 变量。这让解决方案变得简单多了!
我不确定我喜欢这个,但也许这个想法会激发某人写一些好东西...
git rebase -i
的包装器 shell 脚本可以按照
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