git - 使用分支名称的一部分扩展提交挂钩

git - extend commit hook with part of branch name

我想使用 prepare-commit-msg 挂钩。我正在使用功能和错误修复分支 (feature/ISSUEKEY-23123-some-feature),我想将 ISSUEKEY-23123 添加到提交消息中:

#!/bin/bash
BRANCH_NAME=$(git branch | grep '*' | sed 's/* //')
STR=`echo $BRANCH_NAME | grep -E 'ISSUEKEY-[0-9]*' -o`
if [[ $BRANCH_NAME == *"feature"* ]] || [[ $BRANCH_NAME == *"bugfix"* ]]
then
    echo $STR > 
fi

这有效,但它丢弃了 vi 显示给我的提交的标准消息,例如:

# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
# Explicit paths specified without -i or -o; assuming --only paths...
# On branch feature/ISSUEKEY-1716-model-implement
# Your branch is based on 'origin/feature/ISSUEKEY-1716-model-implement', but the upstream is gone.
#   (use "git branch --unset-upstream" to fixup)
#
# Changes to be committed:
#       new file:   asd
#
# Untracked files:
#       FSTD-1716
#       TMP
#

有没有办法将 STR 添加到输出中,或者调用 git 命令打印上述标准提交消息?

打开的提交信息应该是:

ISSUEKEY-1716 
# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
# Explicit paths specified without -i or -o; assuming --only paths...
# On branch feature/ISSUEKEY-1716-model-implement
# Your branch is based on 'origin/feature/ISSUEKEY-1716-model-implement', but the upstream is gone.
#   (use "git branch --unset-upstream" to fixup)
#
# Changes to be committed:
#       new file:   asd
#
# Untracked files:
#       FSTD-1716
#       TMP
#

我修好了:

#!/bin/bash

BRANCH_NAME=$(git branch | grep '*' | sed 's/* //')
COMMIT_MSG=`echo $BRANCH_NAME | grep -E 'ISSUEKEY-[0-9]*' -o`
if [[ $BRANCH_NAME == *"feature/"* ]] || [[ $BRANCH_NAME == *"bugfix/"* ]]
then
    PRE_COMMIT_MSG=$(cat )
    if [ -z "$COMMIT_MSG" ] ; then
        exit 0
    else
        COMMIT_MSG="$COMMIT_MSG - $PRE_COMMIT_MSG"
        echo "$COMMIT_MSG" > 
    fi
fi

引用自the githooks documentation

This hook is invoked by git commit right after preparing the default log message, and before the editor is started. ... The purpose of the hook is to edit the message file in place, and ...

(我的粗体字)。这意味着你必须按照它说的去做:就地编辑文件。不要只是覆盖它!因此,而不是:

echo $STR > 

你可能会这样做:

ed - "" << end
0a
$STR
.
w
q
end

在 "here document" 脚本上运行 ed 编辑器,该脚本包含在第 0 行之后添加 $STR 扩展的指令,然后编写并退出编辑器。 (您可以使用任何编辑器;sed 在这里也很受欢迎。)

顺便说一句,不要这样做:

BRANCH_NAME=$(git branch | grep '*' | sed 's/* //')

因为它过度依赖于各种 "porcelain" 命令输出样式。相反,使用 git symbolic-ref(通常是脚本的首选方法)或 git rev-parse。这两者的区别有点哲理:

  • git symbolic-ref <options> HEAD 获取您当前所在分支的名称,如果您处于特殊的匿名 "detached HEAD" 情况下,则失败。 (您在这里想要的 <options> 只是 --short 以省略 refs/heads/ 前缀。)

  • git rev-parse <options> HEAD 基本上可以保证产生某种成功的名称,因为 HEAD 始终 是一个有效的名称1 因此将对应于 something。它的目的是将 HEAD 变成一个修订散列 ID,但是使用 --symbolic 选项它会留下一个名称 "as symbolic as possible" 而使用 --abbrev-ref 它也会遗漏 refs/heads/ 当它这样做时。

主要区别在于当 HEAD 为 "detached" 时 rev-parse 方法不会失败,而 symbolic-ref 方法会失败。失败可以让您区分这种情况,这有时很重要。出于您的目的,它并不是那么重要,并且在使用 symbolic-ref 时,您需要为 "no name for current branch" 分离的 HEAD 案例提供回退。

因此,您想要:

BRANCH_NAME=$(git rev-parse --symbolic --abbrev-ref HEAD)

或:

BRANCH_NAME=$(git symbolic-ref --short HEAD || echo HEAD)

(奇怪的是,这些命令的字节数完全相同)。


1这里有一个极端情况。虽然 HEAD 始终有效——如果它无效,则您没有 Git 存储库(Git 会给您一个 "fatal:" 消息)—如果您在 "unborn branch" 上,HEAD 是对不存在的分支名称的符号引用。在这种特殊情况下,git symbolic-ref 成功 git rev-parse HEAD 失败 ,这与更常见的情况相反一个独立的 HEAD。