how can I customize git's merge commit message?

Every time I do a merge I need for a merge commit to be generated and I would like it to have more than just the summary of all the commits.

My question is how can I format git-fmt-merge-msg or what determines this automated message (I can do this manually after a commit by amending it and using git-log --pretty=format:'...')

For example I would like to format it as such:

 Merge branch 'test'
* test:
[BZ: #123] fifth commit subject
[BZ: #123] fourth commit subject
[BZ: #123] third commit subject
[BZ: #123] second commit subject
[BZ: #123] first commit subject


__________________________________________
Merge details:
[BZ: #123] fifth commit subject
at 2010-06-30 11:29:00 +0100
- fifth commit body


[BZ: #123] fourth commit subject
at 2010-06-30 11:22:17 +0100
- fourth commit body


[BZ: #123] third commit subject
at 2010-06-30 11:21:43 +0100
- third commit body


[BZ: #123] second commit subject
at 2010-06-30 11:21:30 +0100
- second commit body


[BZ: #123] first commit subject
at 2010-06-30 11:29:57 +0100
- first commit body
86433 次浏览

You could try defining a prepare-commit-msg hook (the sample one does generate some custom "default commit messages")

There's also a --log option for git-merge now, which does half of what you want - it places the shortlog (commit summaries) in the merge message. A full solution will have to use a hook like in VonC's answer, though.

I've found there are two ways for solving this problem

note: don't use both at the same time, as if the commit fails to merge it will add the log again to the bottom.

personal note: I'm using the first solution as it relies entirely on git's hooks and config properties, instead of an external script.
For a real solution one would have to extend a git command named 'fmt-merge-msg' that generates the oneline descriptions when passing the --log option (if you really need this solution you'll have to create your own patch (for git) and compile it from source).

1. using prepare-commit-message as VonC suggested
this solution has the problem that you need to interrupt the commit and then commit manually

setting an alias that will build the desired commit message:

[alias]
lm = log --pretty=format:'%s%n   by %C(yellow)%an%Creset (%ad)%n %n%b' --date=local

creating the prepare-commit-msg hook by creating an executable prepare-commit-msg in $GIT_DIR/hooks/ (example script below)

#!/bin/sh
#...


case "$2,$3" in
merge,)
echo "Merge details:" >> $1
echo "" >> $1
git lm ORIG_HEAD..MERGE_HEAD >> "$1" ;;
*) ;;
esac

one should define an alias commit msg such as

[alias]
m = merge --no-ff --no-commit

2. using a custom command that will generate the merge automatically
(using the lm alias created in 1.)

#!/bin/sh


echo ""
echo "merge with commit details -- HEAD..$1"
git merge --no-ff --no-log -m "`git lm HEAD..$1`" --no-commit $1

and then execute a rather rigid command:

./cmd-name <branch to merge>

if you still wish to have the oneline description of the commits you'll need to add new commands or whatever to the -m argument (if you use --log then it will be generated on the bottom)

I'm aware this isn't answering the original question, but for the benefit of git noobs like myself who reach this page because it's currently the first Google result for "git change merge commit message", I'll mention that it is possible to:

git commit --amend -m"New commit message"

to change the commit message of a merge commit without losing the link to any of the parents of the merge commit.

Looks like as of version Git 1.7.8 you can do git merge --edit ... to specify the commit message.

And as of 1.7.10, dropping into edit mode will be the default behavior

From this release on, the "git merge" command in an interactive session will start an editor when it automatically resolves the merge for the user to explain the resulting commit, just like the "git commit" command does when it wasn't given a commit message.

(though I'm not seeing it on in msysgit on windows).

Once you merge your <branch A> to <branch B>, git will automatically commit a message saying "merge branch <branch A> into <branch B>.

If you want to customize git's merge commit message you can try:

$ git commit --amend -m "Your merge message"

This command will update your git's merge commit message to your commit message.

you can also try :

$ git merge <branch A> --no-commit

it will merge your <branch B> with <branch A>, with list of <Branch B>'s commit and commit messages

If it fails to do fast-forward, then you will get something like this:

Automatic merge went well; stopped before committing as requested


# check this with git status
$ git status

It will show you, your commits are already added to stage but not yet commited so, you can commit them without running git add:

$ git commit -m "your merge commit message"

If you want to change <branch B>'s last commit message then again you can try:

$ git commit --amend -m "your new commit message"

But, generally, we don't update other commit messages, unless they are incorrect.

Suppose, you get conflict after git merge, then simply resolve your conflict and do:

$ git add .
$ git commit -m "your commit message"

A very simple bash function that set's a default message and adds your argument. It opens your editor with the --edit switch if you want make changes.

edit ~/.bashrc or bash_aliases. (Don't forget to source ~/.bashrc) to apply changes in your bashrc

function mergedevelop()
{
git merge --no-ff --edit -m "master <-- develop: $1" develop;
}

use:

mergedevelop "PR #143..." to have message:

master <-- develop: PR #143...

Coming late to the party, but nowadays we can just use
git merge --squash <branch>
to merge another branch into a single commit on my current branch and prepopulating our commit message with all the merged commit messages -- and the detailed messages, too, not just the one-liners.
I looked for ages for this command.

I wanted to do something just like this. I didn't find any reasonable way to get git fmt-merge-msg to work. I think it doesn't work the way I was hoping (passing in a completely custom text to use for the message). So instead I figured out another way using the -no-commit and commit -F commands. The output is of course customizable but it reflects almost exactly what you said you wanted the output to be.

Sample Commit Message Output:

Merge branch fix4 into master


::SUMMARY::
Branch fix4 commits:
Add fix4b-4
Add fix4b-3
Add fix4b-2
Add fix4b-1


Branch master commits:
fix4b-5 on master


* * * * * * * * * * * * * * * * * * * * * * * * *
::DETAILS::
commit < 98ffa579e14610b3566e1a3f86556a04dc95a82b
Author: -----
Date:   Fri Aug 17 17:23:26 2018 -0400


fix4b-5 on master


commit > 7e386dddee16a7c2588954d25dd6793cdaa1b562
Author: -----
Date:   Fri Aug 17 15:18:17 2018 -0400


Add fix4b-4


use log output as commit message


commit 2e630b1998312ec1093d73f9fe77b942407f45e8
Author: -----
Date:   Fri Aug 17 15:15:28 2018 -0400


Add fix4b-3


commit > 2e630b1998312ec1093d73f9fe77b942407f45e8
Author: -----
Date:   Fri Aug 17 15:15:28 2018 -0400


Add fix4b-3


commit > c9bb199be49c17ca739d019d749263314f05fc46
Author: -----
Date:   Fri Aug 17 15:15:27 2018 -0400


Add fix4b-2


commit > 5b622a935c9d078c7d0ef9e195bccf1f98cce5e4
Author: -----
Date:   Fri Aug 17 15:15:27 2018 -0400


Add fix4b-1

And the usage would be:

$ git mergelogmsg branch-name

I'll copy the alias here:

[alias]
mergelogmsg = "!f() { var=$(git symbolic-ref --short HEAD) && printf 'Merge branch %s into %s\n\n::SUMMARY::\nBranch %s commits:\n' $1 $var $1 > temp_merge_msg && git log --format=format:'%s' $var..$1 >> temp_merge_msg && printf '\n\nBranch %s commits:\n' $var >> temp_merge_msg && git log --format=format:'%s' $1..$var >> temp_merge_msg && printf '\n\n* * * * * * * * * * * * * * * * * * * * * * * * *\n::DETAILS::\n' >> temp_merge_msg && git log --left-right $var...$1 >> temp_merge_msg && git merge --no-ff --no-commit $1 && git commit -eF temp_merge_msg; rm -f temp_merge_msg;}; f"

If you want to copy and paste it to customize it, use the above. The below version has line breaks which you do not want but I'll use to explain what I'm doing:

[alias]
1   mergelogmsg = "!f() { var=$(git symbolic-ref --short HEAD) &&
2        printf 'Merge branch %s into %s\n\n::SUMMARY::\nBranch %s commits:\n' $1 $var $1 > temp_merge_msg &&
3        git log --format=format:'%s' $var..$1 >> temp_merge_msg &&
4        printf '\n\nBranch %s commits:\n' $var >> temp_merge_msg &&
5        git log --format=format:'%s' $1..$var >> temp_merge_msg &&
6        printf '\n\n* * * * * * * * * * * * * * * * * * * * * * * * *\n::DETAILS::\n' >> temp_merge_msg &&
7        git log --left-right $var...$1 >> temp_merge_msg &&
8        git merge --no-ff --no-commit $1 &&
9        git commit -eF temp_merge_msg; rm -f temp_merge_msg;}; f"

Alright...

Line 1 starts the custom function as a bash shell script so git knows it's not a git command. It sets the current branch (master if you are merging a different branch into master) to a variable so we can use it later.
Line 2 prints the first line using the current branch and the branch name you've given the original command (just as you would in a normal merge command). It writes this to a temp file.
Line 3 gets the log of the commits in the incoming branch that are not in the current branch, and writes out only the subjects of those commits to the temp file.
Line 4 prints the next line to temp.
Line 5 gets the log of the commits in the current branch that are not in the incoming branch, and writes out only the subjects of those commits to the temp file.
Line 6 prints a little horizontal separator between the summary and detail portions.
Line 7 gets the log of all the commits in the current branch and incoming branch back to the time just after they branched off from each other, or last shared an ancestor. The left-right gives an arrow that shows which branch the commit comes from. < means current branch and > means incoming branch.
Line 8 executes a merge command with the incoming branch with no fast-forward (so you get a commit) and with no commit (so you have to write one yourself... ah but you don't!)
Line 9 executes the commit command with the -e and -F parameters to allow for editing and to tell the commit to populate the message with the text in the specified file. Once you've finished the commit message as desired, it commits the merge and deletes the temp file.

Tada! The two ; at the end of that long command make it so the printf functions do not write out to the console, only to the file.

In addition of git merge --squash mentioned in Christian Severin's answer, you now have the config merge.suppressDest as a new way to customize a (small part of the) commit message.

With Git 2.29 (Q4 2020), "git merge"(man) learned to selectively omit " into <branch>" at the end of the title of default merge message with merge.suppressDest configuration.

See commit 6e6029a (29 Jul 2020), and commit 2153192 (30 Jul 2020) by Junio C Hamano (gitster).
(Merged by Junio C Hamano -- gitster -- in commit 341a196, 01 Aug 2020)

fmt-merge-msg: allow merge destination to be omitted again

Helped-by: Linus Torvalds
Helped-by: Jeff King

In Git 2.28, we stopped special casing 'master' when producing the default merge message by just removing the code to squelch "into 'master'" at the end of the message.

Introduce multi-valued merge.suppressDest configuration variable that gives a set of globs to match against the name of the branch into which the merge is being made, to let users specify for which branch fmt-merge-msg's output should be shortened.
When it is not set, 'master' is used as the sole value of the variable by default.

The above move mostly reverts the pre-2.28 default in repositories that have no relevant configuration.

git config now includes in its man page:

merge.suppressDest

By adding a glob that matches the names of integration branches to this multi-valued configuration variable, the default merge message computed for merges into these integration branches will omit " into <branch name>" from its title.

An element with an empty value can be used to clear the list of globs accumulated from previous configuration entries. When there is no merge.suppressDest variable defined, the default value of master is used for backward compatibility.

And:

Revert "fmt-merge-msg: stop treating master specially"

This reverts commit 489947cee5095b168cbac111ff7bd1eadbbd90dd, which stopped treating merges into the 'master' branch as special when preparing the default merge message.
As the goal was not to have any single branch designated as special, it solved it by leaving the "into <branchname>" at the end of the title of the default merge message for any and all branches.
An obvious and easy alternative to treat everybody equally could have been to remove it for every branch, but that involves loss of information.

We'll introduce a new mechanism to let end-users specify merges into which branches would omit the "into <branchname>" from the title of the default merge message, and make the mechanism, when unconfigured, treat the traditional 'master' special again, so all the changes to the tests we made earlier will become unnecessary, as these tests will be run without configuring the said new mechanism.