In my most recent commit to my repository that has already been pushed, I noticed I misspelled a single word in a file and now would like to change it. However, I do not want to create a whole new commit. Is there a way to amend my latest commit and push that into my repository without having to reset, remove, and push?
In the past I did:
git reset HEAD~1 --soft
git push -f
# correct the spelling
git add .
git commit -m "bug10 fix"
Is there a way to correct the spelling of the file, amend my latest commit and push without having to reset, force remove, then re-commit?
I tried:
# fixed the file
git add .
git commit --amend --no-edit
git push
! [rejected] Nov21 -> Nov21 (non-fast-forward)
It's impossible to change any commit. That includes before it's pushed. The reason this is important to know—the reason you need to know that
git commit --amendis a lie—is that whatgit commit --amenddoes locally, can be done here when pushing a commit to another Git repository.The (tiny) lie with
git commit --amendis that it didn't change a commit at all. It just seemed to do that. What it really did was make a new and improved replacement commit.You can do this any time you like. You just need to know that each commit is numbered—every commit has a unique number, which is its hash ID—and that Git finds commits in a sort of backwards (perhaps even backassward) fashion, by having a branch name or other name locate the most recent commit for you, and then working backwards from there.
Imagine a simple chain of commits, ending with your most recent commit whose hash is some big ugly random-looking hash ID that we'll just call
H. Here's how Git sees—and finds—these commits:Your branch name "points to" (contains the hash ID of) commit
H. CommitHitself contains the hash ID of earlier commitG. (CommitHalso contains a full snapshot of every file, though we won't look at this at all here.)Commit
G, being a commit, is numbered by some big ugly random-looking hash ID, and has a snapshot and metadata and hence points backwards to still-earlier commitF. That's a commit so it's numbered and has the same kind of stuff and points backwards yet again, and so on.Now, if you have a tiny typo in commit
Hand rungit commit --amend, what Git does is to make a new commit. The new commit is exactly likeHexcept in two ways:I; andCommit
His still there in your repository. It's just no longer used.Hstill points back toG, but so does new commitI:Git updates your branch name to point to new commit
I. This effectively "kicksHoff the branch", because Git finds commits by starting at the end and working backwards.Now, if you ran
git push, what you did was send commitHto another Git repository, and then have them update one of their branch names—probably the same name you're using in your repository—to point to the now-shared commit:If you make a new commit
Ithat's a new-and-improved replacement forHand just send it to them with a regulargit push, they'll take the commit and put it into their repository temporarily:and then you have your Git ask their Git to set their branch name to point to
Itoo.The problem comes in here, because when you do that, they check: If I update my branch name to point to
I, do I lose any commits off the end? And of course they do: they lose commitHoff the end of their branch. That's what you want them to do—that's what your Git did with yourgit commit --amend, after all—but they don't know that. All they know at this point is that they do haveHand you're asking them to drop it now.You can use:
or:
to send them a command, instead of a polite request, that tells them that they definitely should make their name point to
I(regular--force), or that you think their name points toHnow and if so they should make it point toIinstead (--force-with-lease). If they will take such commands from you—most hosting sites, like GitHub and Bitbucket and GitLab have fancy configurations they add atop Git to control these things—then this lets you replaceHwithIafter all.Why you might use
--force-with-leaseNote that if the hosting site is taking
git pushrequests from other people, they might by now have:and hence your command that they set their branch to point to
Iwill drop not just your commitH, but also these other two additional commitsJ-K, off the end of their branch.Using
git push --force-with-leasesends them your new commitIbut also sends them the hash IDHthat you believe they have as their final commit on their branch. If your belief is wrong, your conditional command—that they must change their name to point toIinstead—gets a rejection that says your Git is now out of date, and you need to rungit fetchfirst.If this can't happen, you don't need
--force-with-lease. Very old Git versions don't have--force-with-leaseeither, but all modern ones do and it's usually wisest to keep the safety check in place even if it's not necessary, since "habits" may become "bad habits" over time.