It's technically impossible to change any commit. But that's not really a problem, because it is technically simple and easy to replace a commit, or even a whole chain of commits, with new and improved replacements. The issue here is not replacing the commits, but rather, because Git is distributed, updating every copy of the repository, including all the clones everyone else made.
The question you must answer first, before you do anything else, is: Who else has this commit? To have a commit that you made, you have to have given it to someone else. Once one other person has it, they might tell their friends, who tell their friends, and in a few milliseconds, that one commit you made is spread through ten thousand clones.
Normally, you'd give a new commit to, say, GitHub, using git push
. If you already did a git push
, then at least the Git repo on GitHub has your commit. Anyone else who can access your repository there, might also have copied it from there. Are they all going to switch from your previous commit to your new and improved commit? If so, feel free to replace your bad commit with your new and improved one. Or, if nobody else should have it, or you don't care if they do, again, you can proceed.
There are therefore two steps: replace the commit locally, then convince other Gits to do the same
To replace a commit locally, you can:
- use
git commit --amend
(easiest for the last one)
- use
git reset
, as outlined in another answer just posted
- use
git rebase -i
, as suggested in comments and a linked almost-duplicate
- do anything else you like: this repository is your clone, you can do whatever you want! But the three methods above are probably the easiest.
Once you have done the local replacement, you must convince the other Git—the one over on GitHub or wherever it is you git push
to—that they should throw out your old commits in favor of your new and improved replacement commits. To to that, you generally need to use git push --force
or git push --force-with-lease
.
Both operations tell the other Git: Yes, I know this action will throw out some old commit(s). Do it anyway! They turn what is otherwise a polite request, please, if it's OK, set your branch name to a forceful command: Set your branch name! As long as you have permission to make such demands, the other Git will obey your Git's force-push.
The difference between a plain git push --force
and a git push --force-with-lease
is that with --force-with-lease
, your Git sends a more complicated command, of the form: I think your branch _____ points to commit _____. If I'm right, set it to _____ instead. Either way, let me know if I was right. (Your Git fills in these blanks with the branch name you want their Git to change—i.e., the name you use in your git push
—and the two commit hash IDs.)
That means the --force-with-lease
option lets you update a shared GitHub repo while checking that what you're doing is just replacing your mistake. If your GitHub repository isn't shared, there's no need for the fancier option.
Using git commit --amend
is easy
After you make a commit, if you realize there is something minor wrong with it, you just:
- fix any files needed and
git add
them if needed
- then run
git commit --amend
The --amend
option tells your Git: shove the last commit out of the way, making this new commit replace it rather than adding on to it. You get the opportunity to edit the commit message again—you can use --no-edit
to suppress this, if the commit message is good—and when you're done, the old commit is nowhere to be seen. It's still there in your repository, you just can't see it any more. The old commit will stick around for a while—at least 30 days by default—and you can get it back if needed, but mostly, it's been replaced by the new one; the new one is what you will see.
Having shoved the old commit out of the way and replaced it with a new and improved one, you may now need to use git push --force
or git push --force-with-lease
to convince another Git repository to also shove the old commit out of the way in favor of a new one. Remember that this other Git repository might, if it's shared, have several contributors' new commits built up atop your commit by now: to shove yours aside, you'll have to shove all of theirs aside as well!