Edit the root commit in Git?
Asked Answered
P

5

410

There's ways to change the message from later commits:

git commit --amend                    # for the most recent commit
git rebase --interactive master~2     # but requires *parent*

How can you change the commit message of the very first commit (which has no parent)?

Piave answered 22/1, 2010 at 18:21 Comment(3)
See also stackoverflow.com/questions/11987914/…Abc
In particular: the use of GIT_COMMIT environment variable in the script of git filter-branch --msg-filterAbc
see also https://mcmap.net/q/12045/-change-first-commit-of-project-with-git-duplicateColmar
E
322

Assuming that you have a clean working tree, you can do the following.

# checkout the root commit
git checkout <sha1-of-root>

# amend the commit
git commit --amend

# rebase all the other commits in master onto the amended root
git rebase --onto HEAD HEAD master
Ec answered 22/1, 2010 at 18:53 Comment(21)
I believe this should be git rebase --onto HEAD <sha1-of-root> master.Shaylyn
@Andrew: In this example HEAD is the amended root commit.Ec
Right, but you want the original root commit for the <upstream> of git rebase. git rebase applies commits in <branch> (master) that are not in <upstream>; HEAD is not in master, so your version tries to apply all of master.Shaylyn
Yes, make sure it's git rebase --onto HEAD <sha1-of-root> master, where <sha1-of-root> is the same used in git checkout <sha1-of-root>. Otherwise, you'll have 2 first commit's.Interment
if the first commit was made with --allow-empty is needed to use git commit --ammend --allow-empty. Rest is the sameClamworm
@VonC you want to see another crazy usage of --onto, along with --root? Check out my answer to Git: how to add commits before first (root) commit?!Dried
@Cupcake: Did you test the old version of the command? It should work fine. The amend is changing the commit message only so the old and new root commits introduce exactly the same changes so the old root commit is skipped automatically. The second HEAD ensures that all commits are considered and that we can use the two parameter version of rebase to move back onto master. Please note that this answer predates the existence of the --root option to rebase.Ec
@VonC (and all) I don't see why you need --onto ,I've omitted and it works just fine (well seemed to), I thought --onto was only necessary if it didn't match the <upstream> commit (which it does in this case)?Purview
@Purview if you omit it, it should be a no-op (ie rebase but don't do much). You can also use the git rebase -i --root mentioned below (no --onto there).Nork
@Nork but it did work I changed the base commit message and rebased all the above commits.... are you referring to an older git? It was git 1.7.1 . The rebase did happen. So can we say if it works, fine if it no-ops then use --onto ?Purview
@Nork the man says If the --onto option is not specified, the starting point is <upstream> that means git rebase HEAD master is the same as git rebase --onto HEAD HEAD master and it works for me!Purview
@Purview that makes sense. Let's wait Charles's opinion on that.Nork
Reading the docs, it does sound like that should be the same but I haven't tested it. I'd prefer to test before changing the answer.Ec
I've tried it from 1.7.1 up and it works. On the good side --onto makes more sense to me now after researching it after this answer.Purview
Be interested to hear your results :)Purview
@CharlesBailey excellent answer btw, most long term support servers are not on 1.7.12 yet!Purview
I got a corrected commit message, but now I get the old and new commits when I do git log. Also when I pushed it I had to do a pull and merge and then push. I wanted ONLY to change my first commit message, all the rest should have stayed EXACTLY the same.Willodeanwilloughby
@powder366: well, if you merged the old and the new histories then you will get both as the ancestors of your updated master branch. You would have had to use a forced or non-fastfoward push (if allowed by your server) to replace the history.Ec
ecdpalma's answer below is much easier and simpler and has more votes, scroll down people!Lampblack
Worked like a charm for meDiscredit
You want to rebase onto HEAD (the new root commit), from HEAD@{1} (the previous position of HEAD) to master. Use the command git rebase --onto HEAD HEAD@{1} master It is then easy to rebase branches onto others, e.g. if branch2 is after branch1 and branch1 after master, you can do git rebase --onto upstream/master master branch1 followed by git rebase --onto branch1 branch1@{1} branch2Aforementioned
C
696

As of Git version 1.7.12, you may now use

git rebase -i --root

Documentation

Collaboration answered 31/1, 2013 at 16:24 Comment(6)
is it possible to rebase the root of all branches using this command? Seems like this will detach the current branch onto the new root and all the other branches will stay on the old rootBeadruby
@Beadruby you will have to rebase branches onto new root then. as usual.Tasse
@Atcold it doesn't work if there is no upstream rootDislocate
Warning: I wrongly assumed this would do the root of my checked out branch, but it takes a while to load all the commits, then rebase them all.Trisoctahedron
FWIW: I would also like to see this as the accepted answer, especially as it matches my all-time favourite git command for cleaning up the history of new projects in the early stages of development, namely: git rebase --interactive --autosquash --autostash --rootNeale
@Trisoctahedron what does your comment mean? I can't see the link between the first part and the second - what does taking a while have to do with it?Diactinic
E
322

Assuming that you have a clean working tree, you can do the following.

# checkout the root commit
git checkout <sha1-of-root>

# amend the commit
git commit --amend

# rebase all the other commits in master onto the amended root
git rebase --onto HEAD HEAD master
Ec answered 22/1, 2010 at 18:53 Comment(21)
I believe this should be git rebase --onto HEAD <sha1-of-root> master.Shaylyn
@Andrew: In this example HEAD is the amended root commit.Ec
Right, but you want the original root commit for the <upstream> of git rebase. git rebase applies commits in <branch> (master) that are not in <upstream>; HEAD is not in master, so your version tries to apply all of master.Shaylyn
Yes, make sure it's git rebase --onto HEAD <sha1-of-root> master, where <sha1-of-root> is the same used in git checkout <sha1-of-root>. Otherwise, you'll have 2 first commit's.Interment
if the first commit was made with --allow-empty is needed to use git commit --ammend --allow-empty. Rest is the sameClamworm
@VonC you want to see another crazy usage of --onto, along with --root? Check out my answer to Git: how to add commits before first (root) commit?!Dried
@Cupcake: Did you test the old version of the command? It should work fine. The amend is changing the commit message only so the old and new root commits introduce exactly the same changes so the old root commit is skipped automatically. The second HEAD ensures that all commits are considered and that we can use the two parameter version of rebase to move back onto master. Please note that this answer predates the existence of the --root option to rebase.Ec
@VonC (and all) I don't see why you need --onto ,I've omitted and it works just fine (well seemed to), I thought --onto was only necessary if it didn't match the <upstream> commit (which it does in this case)?Purview
@Purview if you omit it, it should be a no-op (ie rebase but don't do much). You can also use the git rebase -i --root mentioned below (no --onto there).Nork
@Nork but it did work I changed the base commit message and rebased all the above commits.... are you referring to an older git? It was git 1.7.1 . The rebase did happen. So can we say if it works, fine if it no-ops then use --onto ?Purview
@Nork the man says If the --onto option is not specified, the starting point is <upstream> that means git rebase HEAD master is the same as git rebase --onto HEAD HEAD master and it works for me!Purview
@Purview that makes sense. Let's wait Charles's opinion on that.Nork
Reading the docs, it does sound like that should be the same but I haven't tested it. I'd prefer to test before changing the answer.Ec
I've tried it from 1.7.1 up and it works. On the good side --onto makes more sense to me now after researching it after this answer.Purview
Be interested to hear your results :)Purview
@CharlesBailey excellent answer btw, most long term support servers are not on 1.7.12 yet!Purview
I got a corrected commit message, but now I get the old and new commits when I do git log. Also when I pushed it I had to do a pull and merge and then push. I wanted ONLY to change my first commit message, all the rest should have stayed EXACTLY the same.Willodeanwilloughby
@powder366: well, if you merged the old and the new histories then you will get both as the ancestors of your updated master branch. You would have had to use a forced or non-fastfoward push (if allowed by your server) to replace the history.Ec
ecdpalma's answer below is much easier and simpler and has more votes, scroll down people!Lampblack
Worked like a charm for meDiscredit
You want to rebase onto HEAD (the new root commit), from HEAD@{1} (the previous position of HEAD) to master. Use the command git rebase --onto HEAD HEAD@{1} master It is then easy to rebase branches onto others, e.g. if branch2 is after branch1 and branch1 after master, you can do git rebase --onto upstream/master master branch1 followed by git rebase --onto branch1 branch1@{1} branch2Aforementioned
D
78

To expand on ecdpalma's answer, you can now use the --root option to tell rebase that you want to rewrite the root/first commit:

git rebase --interactive --root

Then the root commit will show up in the rebase TODO list, and you can select to edit or reword it:

reword <root commit sha> <original message>
pick <other commit sha> <message>
...

This is the explanation of --root from the Git rebase docs (emphasis mine):

Rebase all commits reachable from <branch>, instead of limiting them with an <upstream>. This allows you to rebase the root commit(s) on a branch.

Dried answered 14/7, 2013 at 19:47 Comment(1)
only changes the commit message of the root inside 1 branch, how to do it on all 25 branchesTurnbuckle
V
17

Just to provide an alternative to the higher rated answers:

If you are creating a repo, and know upfront that you'll be rebasing on top of its "first" real commit in the future, you can avoid this problem altogether by making an explicit empty commit at the beginning:

git commit --allow-empty -m "Initial commit"

and only then start doing "real" commits. Then you can easily rebase on top of that commit the standard way, for example git rebase -i HEAD^

Vitiated answered 6/3, 2014 at 18:37 Comment(3)
Doesn't this mean that, in order for this to work, you need to have the foresight (or be psychic) to make an empty commit right at the very beginning of your project? This seems to be extremely situational, to me, and generally not practical. What do you think? What happens if I've already made 100 commits, and I suddenly need to edit the root commit. Will this still work, in that case, if I didn't make that empty commit at the start?Dried
Editing the message of the root commit is probably not something you would do after having 100s of them. I sometimes happen to just want to have a git repo, doing some trashy commits, knowing that once I reach some usable state, I'd squash them into one for instance, and reword the message. Anyway, now I changed my mind and I think the absolutely most useful thing for the first commit would be putting .gitattributes file instead of doing an empty commit.Vitiated
Reply to the first commenter: You don’t have to have the foresight to make an empty commit every time. You could (1) create a repository with this empty commit (2) place it in some “git repository templates” directory (3) copy it over whenever you need a new repository.Semiskilled
H
4

You could use git filter-branch:

cd test
git init

touch initial
git add -A
git commit -m "Initial commit"

touch a
git add -A
git commit -m "a"

touch b
git add -A
git commit -m "b"

git log

-->
8e6b49e... b
945e92a... a
72fc158... Initial commit

git filter-branch --msg-filter \
"sed \"s|^Initial commit|New initial commit|g\"" -- --all

git log
-->
c5988ea... b
e0331fd... a
51995f1... New initial commit
Hydrotherapeutics answered 22/1, 2010 at 18:46 Comment(1)
I'm using filter-branch change the author / committer, and the -- --all option indeed is the key in this case to be able to also handle the root commit.Ironhanded

© 2022 - 2024 — McMap. All rights reserved.