John
John

Reputation: 30195

How can I rebase a commit made by another author without adding myself as the committer?

Normally, when you rebase another author's commit with git, git adds a Commit: header with your name and email address. I have a situation where I don't want this to happen. I want the rebased commit to end up with the same SHA1 as it would have if the original author had done the equivalent rebase him/herself. Is this possible?

Upvotes: 38

Views: 15722

Answers (3)

ADTC
ADTC

Reputation: 10086

You can preserve the original committer information when rebasing by using Interactive Rebase with instructionFormat.

In your config file .gitconfig, set this:

[rebase]
    instructionFormat = %s%nexec GIT_COMMITTER_DATE=\"%cI\" GIT_COMMITTER_NAME=\"%cN\" GIT_COMMITTER_EMAIL=\"%cE\" git commit --amend --no-edit%n
[alias]
    rb = rebase --interactive

Explanation:

  • rebase.instructionFormat - a configuration for interactive rebase to give a custom instruction in the to-do file.
  • %s - the subject of the commit message (the default value of instructionFormat).
  • %n - a line break; can double-up (%n%n) to add a blank line for readability.
  • exec - instructs git rebase to execute the rest of the line as a shell command.
    • It can be shortened to x (useful if you're using abbreviateCommands = true).
  • GIT_COMMITTER_DATE=\"%cI\" - get the commit date in strict ISO 8601 format.
  • GIT_COMMITTER_NAME=\"%cN\" - get the name of the committer.
  • GIT_COMMITTER_EMAIL=\"%cE\" - get the email address of the committer.
    • Above two respect .mailmap file if any found. To disregard the file, use %cn and %ce instead.
  • git commit --amend --no-edit - amend the commit with the above three environment variables, without opening an editor for the commit message.
  • %n - a line break (to add a blank line for readability in the to-do file; can repeat for more blank lines).
  • alias.rb - for convenience, an alias is defined to quickly call interactive rebase with git rb [new base]

To use it, be sure you're checked out on the branch you wish to rebase, then run the command git rb [new base] where [new base] is the branch or commit-ish on top of which you want to rebase the checked-out branch. (Example: git rb main)

An editor will open with the interactive rebase to-do. Simply close the editor. If you know what you're doing and want to edit it, note the following:

  • You can see the output of the above config in the file as an exec line below each commit.
  • If you move a commit (changing the order of commits), be sure to move the corresponding exec line as well.
  • If you squash, fixup or drop commits, be sure to delete the corresponding exec lines.

Note 1: This is only possible via Interactive Rebase, since the regular rebase doesn't support instructionFormat. You have to bear with the editor opening for no good reason, and just close it (if you have no plans to edit it).

Note 2: Even if you preserve all of the committer information, you may still not get the same SHA1 because the SHA1 is dependent on the parent commit. When the parent changes, the SHA1 changes too. However, if the parent didn't change, you will always get the same SHA1. So this is a deterministic operation.

Note 3: For Windows, you need to use Git Bash or WSL2. This won't work in Command Prompt and PowerShell since DOS doesn't support inline environment variables. (I'm not sure if multiple exec lines with SET commands can work. YMMV but it's so much easier to just use Git Bash or WSL2 than trying to make it work in DOS.)

Upvotes: 8

bdonlan
bdonlan

Reputation: 231113

All git commits have a committer field internally; you can see this by typing git cat-file commit HEAD immediately after committing something. As such you cannot erase it; you can only make it equal to the author field.

That said, you might be seeing git porcelain showing the commit field because the datestamp has changed. It's not possible to predict what someone else would get for the commit datestamp if they were rebasing, obviously, but you can alter it to be equal to the original commit timestamp, at least.

git filter-branch --commit-filter 'export GIT_COMMITTER_NAME="$GIT_AUTHOR_NAME"; export GIT_COMMITTER_EMAIL="$GIT_AUTHOR_EMAIL"; export GIT_COMMITTER_DATE="$GIT_AUTHOR_DATE"; git commit-tree "$@"' -- basecommit..HEAD

This will alter commits after basecommit, in the history of HEAD (including HEAD, not including basecommit), making their committer field identical to the author field in all respects. If the original author agrees to do the same thing, then you can get a consistent SHA1.

Upvotes: 34

Paŭlo Ebermann
Paŭlo Ebermann

Reputation: 74750

Try setting the environment variable GIT_COMMITTER_NAME and GIT_COMMITTER_EMAIL when rebasing (maybe also GIT_COMMITTER_DATE, too). (This will effect all commits created now, though.)

Upvotes: 9

Related Questions