如何将Git的分支名称添加到提交消息中?

130

我需要一些帮助,编写一个Bash脚本,可以自动将git分支名称作为哈希值添加到提交消息中。


5
似乎在这里来的任何人都会发现最佳答案在页面底部。 - Ben Taliadoros
顺便提一下,所有的 git branch | grep ... 获取当前分支的方法都是错误的。考虑使用 git symbolic-ref -q HEAD(如此答案所示)或 git rev-parse --abbrev-ref HEAD。如果你处于分离头状态并希望检测到该情况,请使用 symbolic-ref 命令。否则,rev-parse --abbrev-ref 方法可能是最好的选择。 - torek
@BenTaliadoros,除非您所说的“页面底部”是指最佳答案总是最近的一个,否则我认为这不是您想要表达的意思(为了后世纪)。 - Guildenstern
@BenTaliadoros 嗯,哪一个? - Ryan Walker
https://dev59.com/i2025IYBdhLWcg3wnncw#11524807 - Ben Taliadoros
14个回答

199

这是我作为示例的commit-msg脚本:

#!/bin/sh
#
# Automatically adds branch name and branch description to every commit message.
#
NAME=$(git branch | grep '*' | sed 's/* //') 
DESCRIPTION=$(git config branch."$NAME".description)

echo "$NAME"': '$(cat "$1") > "$1"
if [ -n "$DESCRIPTION" ] 
then
   echo "" >> "$1"
   echo $DESCRIPTION >> "$1"
fi 

创建以下提交消息:

[branch_name]: [original_message]

[branch_description]

我正在使用问题编号作为 branch_name,使用 git branch --edit-description [branch_name] 命令将问题描述放置到 branch_description 中。

有关分支描述的更多信息,请参见此问答

代码示例存储在以下Gist中。


13
这个脚本将多行提交信息压缩成单行。我用以下语句替换了你的回显语句: echo -n "$NAME"': '|cat - "$1" > /tmp/out && mv /tmp/out "$1" - Alex Spence
9
请将此文件放入 PROJECT/.git/hooks/ 文件夹中。 - Display Name
3
它运行良好。但对于 Mac,我还需要设置权限才能使其工作:>>> sudo chmod 755 .git/hooks/commit-msg - Manoj Shrestha
1
@ManojShrestha 是的,它必须是可执行的。 - David Mann
2
@AlexSpence 更简单的方法是使用 echo $NAME: "$(cat $1)" > $1。这个方法有效的原因是因为换行符被丢失的原因是 echo 将 $(cat "$1") 的每一行作为新的参数,并在它们之间加上空格进行回显。通过用双引号括起来 $(cat "$1"),echo 将 cat 输出视为单个参数。此外,我认为没有必要引用 $1,因为它的值是 .git/COMMIT_EDITMSG - PiersyP
显示剩余3条评论

65

使用 prepare-commit-msgcommit-msg githook

在您的 PROJECT/.git/hooks/ 目录中已经有一些示例。

作为安全措施,您需要手动在每个要使用该钩子的存储库上启用此钩子。不过,您可以提交脚本并将其复制到所有克隆存储库的 .git/hooks/ 目录中。


5
我不需要这样做,因为你已经有一个完全符合你要求的示例了,就像我之前说的那样,在.git/hooks/prepare-commit-msg.sample中。=)在遵循注释中的说明后,你只需要复制粘贴来自https://dev59.com/5HI-5IYBdhLWcg3w9thw的任何解决方案即可。 - ninjagecko
5
对于我来说,.git/hooks/prepare-commit-msg.sample包含三个例子。其中一个是将冲突部分注释掉,将git diff --name-status -r的输出添加到其中,以及添加Signed-off-by行...没有将分支名称添加到提交消息中。所以我被迫编写自己的钩子。 - shytikov
1
这句话“你将需要在每个想要使用它的仓库上手动启用这样的钩子”,是指您必须给该文件执行权限吗?如果是这样,我可以编辑答案并包含这一点(或者您能否请编辑一下)? - Dan Rosenstark
@DanRosenstark:我认为 .git/hooks 文件夹默认情况下不被跟踪,可能需要手动将文件复制到 .git/hooks 文件夹中(或者编写脚本来完成),或者使用其他自定义方法。关于我的意思,请参见 https://dev59.com/AG855IYBdhLWcg3wPBz6 。另外回答你的问题:.git/hooks 目录中的示例文件似乎没有建议添加 +exec 权限(尽管我看到一些网站说可能需要 exec 权限;因此我不知道你的问题的确切答案)。 - ninjagecko
@ninjagecko 好的,我们现在将其保留不变,但对于我在osx上,更改钩子目录中特定文件的权限为755对我有用。然而,最终我选择了一个更旧的解决方案,根本不使用钩子:https://dev59.com/Z2855IYBdhLWcg3w6IvV#4316350 ...这只涵盖了当前问题的一部分,但无论如何...谢谢! - Dan Rosenstark

43
一个更简单的脚本,可以在你编辑提交信息之前将分支名称添加到提交信息中。因此,如果您想要更改或删除它,可以这样做。
创建这个文件.git/hooks/prepare-commit-msg:
#!/bin/bash

branchPath=$(git symbolic-ref -q HEAD) #Somthing like refs/heads/myBranchName
branchName=${branchPath##*/}      #Get text behind the last / of the branch path

firstLine=$(head -n1 $1)

if [ -z "$firstLine"  ] ;then #Check that this is not an amend by checking that the first line is empty
    sed -i "1s/^/$branchName: \n/" $1 #Insert branch name at the start of the commit message file
fi

6
当我使用这个命令时,会显示以下错误信息:sed: 1: ".git/COMMIT_EDITMSG": invalid command code . - Adam Parkin
2
像修正和修复案例的检查一样。 - pogopaule
5
如果您收到以上错误消息,需要为OSX提供文件扩展名才能运行。翻译后的内容:sed -i '.bak' "1s/^/$branchName : \n/" $1 - canintex
3
为避免sed命令出现错误,你可以使用符号@作为分隔符代替正斜杠/。因为正斜杠在分支名称或提交信息中可能会出现,导致sed命令无法正常工作。 - Ory Band
1
@Pylinux,我现在明白你在说什么了。你正在使用“/”从分支名称中删除前缀部分,从而避免了我遇到的情况。在我的版本中,我跳过了替换,直到现在才注意到这一点。谢谢! - Ory Band
显示剩余10条评论

31

您可以通过准备提交消息和预提交钩子的组合来完成。

.git/hooks/prepare-commit-msg

#!/bin/sh

BRANCH=`git branch | grep '^\*' | cut -b3-`
FILE=`cat "$1"`
echo "$BRANCH $FILE" > "$1"

.git/hooks/pre-commit

#!/bin/bash

find vendor -name ".git*" -type d | while read i
do
        if [ -d "$i" ]; then
                DIR=`dirname $i`
                rm -fR $i
                git rm -r --cached $DIR > /dev/null 2>&1
                git add $DIR > /dev/null 2>&1
        fi
done

设置权限

sudo chmod 755 .git/hooks/prepare-commit-msg
sudo chmod 755 .git/hooks/pre-commit

1
请注意,如果您使用--amend等选项,则此操作可能会删除原始提交消息。您应该使用sed而不是echo。以下是一行命令:sed -i "1s@^@$(git branch | grep '^\*' | cut -b3-) @" $1 - Ory Band

16

在 prepare-commit-msg 文件中添加下面的代码。

#!/bin/sh
#
# Automatically add branch name and branch description to every commit message except merge commit.
#

COMMIT_EDITMSG=$1

addBranchName() {
  NAME=$(git branch | grep '*' | sed 's/* //') 
  DESCRIPTION=$(git config branch."$NAME".description)
  echo "[$NAME]: $(cat $COMMIT_EDITMSG)" > $COMMIT_EDITMSG
  if [ -n "$DESCRIPTION" ] 
  then
     echo "" >> $COMMIT_EDITMSG
     echo $DESCRIPTION >> $COMMIT_EDITMSG
  fi 
}

MERGE=$(cat $COMMIT_EDITMSG|grep -i 'merge'|wc -l)

if [ $MERGE -eq 0 ] ; then
  addBranchName
fi

除了合并提交,它将在提交消息中添加分支名称。合并提交默认情况下具有分支信息,因此额外的分支名称是不必要的,并且会使消息变得不美观。


2
那么,当它在消息中找到合并单词时,这将不会修改提交消息吗? - thoroc
3
@thoroc 这在技术上是正确的,但在正常使用中这不是什么大问题。被解析的提交消息是在你编辑它们之前的“默认”消息。因此,只要你的提交模板中没有包含“merge”一词,我认为你应该没问题(只要其他“默认”消息也没有除了默认合并提交消息以外的内容)。我最初误解了这一点,现在我相信我正确理解了。 - Novice C

6
受 Tim 回答的启发,其建立在顶级答案之上,原来 prepare-commit-msg 钩子作为参数,指定正在发生的提交类型。如默认的 prepare-commit-msg 所示,如果 $2 为 'merge',则它是合并提交。因此,case 开关可以修改以包括 Tim 的 addBranchName() 函数。
我包含了自己喜欢的添加分支名称的方式,以及默认的 prepare-commit-msg.sample 钩子中未注释部分。

prepare-commit-msg

#!/bin/sh

addMyBranchName() {
  # Get name of current branch
  NAME=$(git branch | grep '*' | sed 's/* //')

  # First blank line is title, second is break for body, third is start of body
  BODY=`cut -d \| -f 6 $1 | grep -v -E .\+ -n | cut -d ':' -f1 | sed '3q;d'`

  # Put in string "(branch_name/): " at start of commit message body.
  # For templates with commit bodies
  if test ! -z $BODY; then
    awk 'NR=='$BODY'{$0="\('$NAME'/\): "}1;' $1 > tmp_msg && mv tmp_msg "$1"
  else
    echo "title\n\n($NAME/):\n`cat $1`\n" > "$1"
  fi
}

# You might need to consider squashes
case "$2,$3" in
  # Commits that already have a message
  commit,?*)
  ;;

  # Messages are one line messages you decide how to handle
  message,)
  ;;

  # Merge commits
  merge,)
    # Comments out the "Conflicts:" part of a merge commit.
    perl -i.bak -ne 's/^/# /, s/^# #/#/ if /^Conflicts/ .. /#/; print' "$1"
  ;;

  # Non-merges with no prior messages
  *)
    addMyBranchName $1
  ;;
esac

6
如果您想在提交消息中添加JIRA工单,请使用以下脚本。 提交消息应该类似于PROJECT-2313:添加了很棒的功能,这需要您的分支名称以jira工单开头。 这是这些解决方案的组合: 它适用于OS X,并且具有sed -i '.bak',也可以从SourceTree使用。 https://gist.github.com/georgescumihai/c368e199a9455807b9fbd66f44160095
#!/bin/sh
#
# A hook script to prepare the commit log message.
# If the branch name it's a jira Ticket.
# It adds the branch name to the commit message, if it is not already part of it.

branchPath=$(git symbolic-ref -q HEAD) #Somthing like refs/heads/myBranchName
branchName=${branchPath##*/}      #Get text behind the last / of the branch path

regex="(PROJECTNAME-[0-9]*)"

if [[ $branchName =~ $regex ]]
then
    # Get the captured portion of the branch name.
    jiraTicketName="${BASH_REMATCH[1]}"

    originalMessage=`cat $1`

    # If the message already begins with PROJECTNAME-#, do not edit the commit message.
    if [[ $originalMessage == $jiraTicketName* ]]
        then
        exit
    fi

    sed -i '.bak' "1s/^/$jiraTicketName: /" $1 #Insert branch name at the start of the commit message file
fi

这个客户端文件的功能很好:prepare-commit-msg自动填充提交前缀。但是如果我想在服务器端钩子上执行相同的操作,即Bitbucket服务器(在我的情况下),并且我正在尝试在Bitbucket服务器路径的pre-receive hook上添加此逻辑:BITBUCKET_HOME/shared/data/repositories/<repository-id>/hooks/21_pre_receive,它不起作用,因为“git symbolic-ref -q HEAD”会给出'master',尽管我是从客户端的feature/abc分支提交的。这里还有另一种方法吗? - santhosh

5
如果您想要将其应用于所有项目:
创建一个名为 git-msg 的文件,其内容为 shytikov的答案,并将其放置在某个文件夹中。
mkdir -p ~/.git_hooks
# make it executable
chmod a+x ~/.git_hooks/commit-msg

现在启用钩子:
git config --global init.templatedir '~/.git_hooks'

在每个你想使用它的项目中再次运行 git init

2
我发现要使用这个功能,我必须将“commit-msg”放入“hooks”目录中,该目录位于配置为“init.templatedir”的目录内部,以便在整个templatedir被复制到“git init”时,“commit-msg”最终出现在项目的“.git/hooks”目录中。 - Dan Zaner

5
我修改了这篇答案(https://dev59.com/i2025IYBdhLWcg3wnncw#17270862),使其也适用于分支名称中包含斜线的情况,使用 @ 替代 / 作为 sed 分隔符。现在使用 git branch --show-current 更简单地获取分支名称。我还将分支名称移动到提交消息的底部,因为实际标题是您首先看到的。

文件仍应称为.git/hooks/prepare-commit-msg

#!/bin/bash

branchName=$(git branch --show-current)
firstLine=$(head -n1 $1)

if [ -z "$firstLine"  ] ;then #Check that this is not an amend by checking that the first line is empty
    sed -i "1s@^@\n\n$branchName@" $1 #Insert branch name at the end of the commit message file
fi

附注:对于Mac用户,请记得添加一个空字符串作为备份文件扩展名,即 sed -i "" "1s@^@\n\n$branchName@" $1 - PJCHENder

3
我为自己的需求进行了适应:
#!/bin/bash

# hook arguments
COMMIT_MSG_FILE=$1
COMMIT_SOURCE=$2
SHA1=$3

BRANCH_NAME=$(git branch --show-current)

# check branch name isn’t empty (typical e.g. during rebase)
if [ -n "$BRANCH_NAME" ]
then
  # check that this is a regular commit
  if [ "$COMMIT_SOURCE" = "message" ] || [ -z "$COMMIT_SOURCE" ]
  then
      sed -r -i "1!b;/^(fixup|squash)/! s@^@$BRANCH_NAME @" $COMMIT_MSG_FILE # insert branch name at the start of the commit message file
  fi
fi

将以下代码复制到名为prepare-commit-msg的文件中,放置于你的仓库的.git/hooks文件夹下。
这会在使用git commitgit commit -m …命令时将分支名称添加进去,而在合并(merge)、变基(rebase)等操作时不进行任何处理。

1
这个对我来说(根据我的需求,稍作修改)起到了作用,但我不知道该放在哪里,后来发现它应该放在.git/hooks/prepare-commit-msg中,你可以更新你的答案 ;) - gluttony

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接