ChatGPT解决这个技术问题 Extra ChatGPT

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

l
laszlok

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.


It made git log --graph stopped drawing branch graph.
If you just want to change the default merge commit from "merged branch ... into master", try using git merge --no-edit -m "your-custom-commit-message"
-1: This does not answer the question. No matter what queries google directs here, this should not be the top answer. It possibly shouldn't even be an answer to this question at all.
@Jasper Technically correct, but it's hardly my fault as the answerer that the title doesn't accurately capture the question in the body text. It's also hardly my fault that apparently people find this helpful enough for it to rise to the top. Having used SO for a while now (and having sufficient reputation to do so, in no small part due to this particular answer), nowadays I would probably edit the question instead, both to clarify the title and to add a link to a related Q&A.
In my opinion, it's the answerer's fault for not attempting to answer the question, but instead answering a question that was never asked. (It wouldn't have been so bad if this question hadn't been self-answered, but that doesn't change anything.) Perhaps a comment with a link to the question that is similar but different would be a better idea.
R
Roman

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).


C
Community

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)


OK, great. But frankly - this is far more difficult than it should be.
a much better answer was posted! check out the accepted one
G
GaetaWoo

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.


This is awesome!! It fits perfectly with the question and is can be easily customized. Thanks!
Indeed, I've already customized it a bit more. I made it just for a particular project but I might use it for all merges.
p
przbadu

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"

Why would you do an --amend on a commit that hasn't happened yet, in the last line of your answer? Shouldn't it just be git commit -m "your commit message"?
My bad, thanks for pointing it out. It should be git commit -m "your commit message" since no commits are done for merge.
"since no commits are done for merge" when you specify the --no-commit flag, as you have done :-)
here, git merge b --no-commit will merge branch b to branch a, with all branch b commits(which is obvious) and with branch b's commit messages. It will not add extra commit messages like: "merged branch b with branch a"
If your merge is not fast-forward, you will get message like "Automatic merge went well; stopped before committing as requested". Now, you don't need to run git add ., But, directly you can commit the merge using git commit -m "your commit message"
C
Cascabel

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.


well I already have that option set globally, hooks won't do the trick as they do not activate during the merge (unless you stop it with --no-commit)
"hooks won't do the trick as they do not activate during the merge (unless you stop it with --no-commit)"; that a good question in itself: why no hook is being activating during the commit part of a merge?
@VonC: I think that's deliberate, actually - from a quick skim of the source, merge calls commit_tree directly.
The -m option is useful with --log. Also, for noobs like myself, don't use the --oneline option when examining the resulting log of the branch you merged into (using --log), otherwise you'll see just the merge commit message and not the individual commit messages of the merged branch.
V
VonC

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


Works great up to a point, I have to merge with --no-commit and only then commit, otherwise the prepare-commit-msg hook doesn't seem to run (merge_msg is standard) .. I would like for the merge full details to be added automatically, as these are the commits that i submit to CVS
@shil88: did you try and see if the commit-msg hook was activated during a git merge?
@shil88: just realized I have already answered your first question (stackoverflow.com/questions/2817897/…): did you finally manage to implement a hook for keeping git and cvs repo in synch?
@VonC sorry did not had enough reputation to comment on answers, but I implemented a CVSNT postcommand hook that after a small delay forces the 'main' Git repo to perform a cvsimport... still tweaking it though, I will post my solution when it is fully usable (committing automatically is a really bad idea and for now I prefer committing from my local repo)
@VonC commit-msg hook does not activate
C
Christian Severin

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.


Am I mistaken, or does this act like a fast forward where you lose the branch context? I just tried it and maybe I used it wrong. The command you give itself doesn't seem to commit the merge, but then i ran git merge and it just did a fast forward. I'm curious about what's going on here, or how to use it as you describe.
V
VonC

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 " 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 " 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 " 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.


n
nilsM

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...