tags:

views:

61

answers:

3

I don't really understand what happens if I check out an old commit, do some modifications, and commit using git commit --amend.

Will that change automatically propagate to future commits? How does it work?

+4  A: 

In git, commits are just objects. When you git commit --amend, you're just creating a new commit with the same parent. Initially that looks like this:

                  {HEAD}
                  {master}
---[A]---[B]---[C]

Now you amend C, creating a new commit D:

                  {HEAD}
                  {master}
---[A]---[B]---[D]
            \
             \
              [C]

The old C is still there for the moment. However, it is no longer referenced by any branch, so the next time a garbage collection occurs, it's going to be swept away.

John Feminella
And the garbage collection is done by `git gc`, which is run periodically when git decides things need cleaning up (though it can be run manually). Loose objects by default are only pruned if they're at least two weeks old. The upshot is that as long as you don't take a month to realize it, you can get the old `C` back if you decide `D` isn't what you wanted.
Jefromi
@Jefromi: +1, thanks for that helpful addition. "Garbage collection" usually implies a very short time frame for most folks, so it's worth noting that you can almost always say "oops, I didn't mean to do that" in Git.
John Feminella
Ah, I see. Actually, git displayed the message: `detached commit ...`, which means that my changes will disappear at the next garbage collection. That's really dangerous...
Olivier
it doesn't get gc'ed until it expires from the reflog (I think the default expiry date is a month)
hasen j
@Olivier: When did it display that? When you did something to do with C? Did you commit with a detached HEAD?
Jefromi
@hasen j: git gc prunes objects which are not reachable from any ref. I'm fairly certain this really does mean ref, not entry in reflog, so an amended commit immediately creates a loose object. (But if you have something to back up your assertion, I'm of course interested.) Also, reflog entries are indeed expired after 30 days by default - but only if they're unreachable from the current position of the ref. Otherwise they last 90 by default. (It really doesn't take long to read the man page and find the right details.)
Jefromi
@hasen j it displayed the message right after I had commited with --amend; I was indeed not on any branch
Olivier
@Jefromi read the man page for git-gc, specially the notes section: git gc tries very hard to be safe about the garbage it collects. In particular, it will keep not only objects referenced by your current set of branches and tags, but also objects referenced by the index, remote tracking branches, refs saved by git filter-branch in refs/original/, or reflogs (which may reference commits in branches that were later amended or rewound).
hasen j
@hasen j: Ah, thanks. That's a little different from the definition of loose object given elsewhere, good to know.
Jefromi
+1  A: 

A new commit is created with the same parent as the old commit, and your current branch now refers to the new commit. The old commit is still in the object database and can be found with git reflog.

Christoffer Hammarström
+1  A: 

To complement John's answer, if you ammend an old commit, nothing happens its children.

       old commit
            v
o--o--o--o--o--o--o--o--o < original branch tip
          \
           o  < ammended old commit & new branch tip

Maybe what you want to do can be accomplished with a interactive rebase that squashes commits.

hasen j