| Tested on | RHEL 10.2 (Coughlan) |
|---|---|
| Package | git 2.52.0 |
| Applies to | Any host with Git installed |
| Privilege | Normal user |
| Scope | Change the message on the most recent commit with git commit --amend --only, an older commit, or several commits in one pass, including commits already pushed to a remote. Covers why the commit hash changes, safe force pushing, and undoing a rewrite. Does not cover adding forgotten files or changing the author on a commit. |
| Related guides | git commit --amend examples Git rebase tutorial Git reflog tutorial Git push force examples Git commit message guide |
Almost everyone types a commit message they regret. You misspell a word, you write "fix bug" at midnight, or you paste something that was never meant to be public. Git lets you rewrite that message with git commit --amend --only, which changes only the message — the files saved in that commit stay the same.
Git does not edit the existing commit. It creates a new version of that commit with the new message. Because it is technically a new commit, its commit hash changes. That single fact explains every warning in this guide.
Which fix do you need?
The right command depends on two questions only: which commit has the bad message, and whether anyone else can already see it.
| Your situation | What to run | What to expect |
|---|---|---|
| Bad message on the newest commit, not pushed yet | git commit --amend --only |
Safe — only local history changes |
| Bad message on the newest commit, already pushed | git commit --amend --only, then git push --force-with-lease |
Requires force push |
| Bad message a few commits back, not pushed | git rebase -i and reword |
Safe if nobody else uses this history |
| Several bad messages at once | One git rebase -i with several reword lines |
Safe if nobody else uses this history |
| Bad message on a branch your team shares | Usually nothing — leave it | Avoid on shared branches |
If you are unsure whether the commit was already pushed, run git fetch first and then git status. If Git says your branch is ahead of its remote branch, those commits have not been pushed yet.
Change the last commit message
git commit --amend --only replaces only the latest commit message. The files stored in that commit remain unchanged. The commit hash will change, so if the old commit was already pushed, you will later need to update the remote history.
Before running it, look at what is actually on top of your branch, because amend only ever touches the newest commit:
git log --oneline -17f3677f updat login validatonTwo typos, and 7f3677f is the commit's short hash. Keep an eye on that hash, because it will not survive the fix.
The --only flag is important here because it prevents changes you previously staged with git add from accidentally being included in the amended commit. Plain --amend without --only can pull those staged changes into the commit you are rewriting.
Replace the message with:
git commit --amend --only -m "Update login validation to reject empty passwords"[main 2e67285] Update login validation to reject empty passwords
Date: Thu Aug 20 23:10:16 2026 +0530
1 file changed, 2 insertions(+)
create mode 100644 login.pyNotice two things in that output. The hash is now 2e67285 rather than 7f3677f, and the 1 file changed line matches the original commit exactly. Git rebuilt the commit with a new message, and the files saved in that commit stayed the same.
Confirm the branch reads the way you wanted:
git log --oneline2e67285 Update login validation to reject empty passwords
b4ad3a8 Fix bug
1dada68 Add config loader
36b9fca Add README with setup stepsThe typo is gone, and the three older commits kept their original hashes. Only the commit you amended was rebuilt.
Write a longer message in your editor
A one-line -m message is fine for a typo fix, but real commit messages often need a short subject and an explanatory paragraph. Leave off -m and Git opens your editor with the current message already filled in:
git commit --amend --onlySave and close the editor to apply the new message.
Adding a forgotten file or changing the commit author uses plain --amend with different flags, and those cases are covered in git commit --amend examples.
Why the commit hash changes
A Git commit ID is calculated from the commit's contents and metadata, including its message and parent. Changing the message therefore creates a different commit ID.
For the latest local commit, this is normally harmless. For an older commit, the commits after it also receive new hashes. If those commits were already pushed, you will need to force-push the rewritten history.
If the old commit was already pushed, the remote branch still has the old hash while your local branch has the new one. A normal push is rejected because those two histories no longer line up.
Behind the scenes, Git also stores a snapshot of your files inside each commit. With --only, that snapshot stays the same even though the commit ID changes. The old commit may still appear in the reflog for a while, which is why recovery is possible after a mistake.
Change a commit message you already pushed
git commit --amend --only changes only the message on the latest commit. The files in that commit stay the same, but the commit hash changes. After that rewrite, a normal push no longer works because the remote still has the old commit ID.
Amend locally exactly as in the previous section:
git commit --amend --only -m "Reject empty passwords in login validation"After you rewrite a commit that already exists on the remote, a normal push is rejected as non-fast-forward. Do not use git pull simply to fix that rejection; the intention here is to replace the remote commit with the rewritten history, not reconcile the two histories.
Only do this if the branch is yours to rewrite. If other people are working from the same branch, changing its existing history can make their local history conflict with the remote branch.
Push the replacement history with --force-with-lease rather than a plain --force:
git push --force-with-leaseTo gitlab.devlab.io:stackforge/deploy-kit.git
+ d5ff8e6...f5cd7e3 feature/login-validation -> feature/login-validation (forced update)The difference between the two force options matters:
- A plain
--forcecan move the remote branch away from a teammate's commit and remove it from the branch's visible history. --force-with-leaseadds a safety check. If the remote branch changed since the version you last saw, Git normally refuses to overwrite it.
There is no situation where --force is safer, so make --force-with-lease your habit. Git push force examples goes deeper into both.
Change an older commit message
git commit --amend --only only works on the newest commit. To rename an older commit, use interactive rebase. Git shows a list of recent commits, and you mark the one whose message you want to change with reword.
Look at what you are working with first:
git log --oneline2e67285 Update login validation to reject empty passwords
b4ad3a8 Fix bug
1dada68 Add config loader
36b9fca Add README with setup steps"Fix bug" is the problem, and it sits two commits back. git rebase -i HEAD~3 opens the last three commits for editing, which includes b4ad3a8.
Running that command changes nothing immediately. Git first opens a to-do list in your editor with one line per commit and a reference of every available action underneath:
pick 1dada68 Add config loader
pick b4ad3a8 Fix bug
pick 2e67285 Update login validation to reject empty passwords
# Rebase 36b9fca..2e67285 onto 36b9fca (3 commands)
#
# Commands:
# p, pick <commit> = use commit
# r, reword <commit> = use commit, but edit the commit message
# e, edit <commit> = use commit, but stop for amending
# s, squash <commit> = use commit, but meld into previous commitThe oldest commit is at the top, which is the reverse of git log and catches people out constantly. Every line starts as pick, meaning Git will keep that commit as it is unless you change the action.
To fix a message, change pick to reword on that one line and leave the others alone:
pick 1dada68 Add config loader
reword b4ad3a8 Fix bug
pick 2e67285 Update login validation to reject empty passwords- Change
picktorewordon the commit line you want to rename. - Do not delete commit lines you did not mean to remove.
- Save and close the to-do list, then enter the new message when Git prompts you.
- If something goes wrong, run
git rebase --abortto return to where you started.
Mark the Fix bug line as reword, save the to-do list, then type the replacement message in the second editor Git opens for you:
git rebase -i HEAD~3Rebasing (2/3)[detached HEAD 97a8772] Fix timeout value not being read from config
Date: Thu Aug 20 23:10:16 2026 +0530
1 file changed, 1 insertion(+)
Rebasing (3/3)Successfully rebased and updated refs/heads/main.The counter shows Git working through all three commits even though you only edited one. Check the result:
git log --onelined5ff8e6 Update login validation to reject empty passwords
97a8772 Fix timeout value not being read from config
1dada68 Add config loader
36b9fca Add README with setup steps"Fix bug" now reads properly. Look carefully at the commit above it, though: it was 2e67285 before the rebase and it is d5ff8e6 now, even though you never touched its message.
The commit after "Fix bug" also gets a new hash because its parent commit changed. Git therefore has to recreate every later commit up to the current branch tip. Your files may still be identical, but Git now sees those as different commits. This is why changing an older commit that was already pushed requires rewriting more remote history.
If the rebase stops on a conflict or you simply change your mind, git rebase --abort returns the branch to exactly where it started. More rebase mechanics live in the Git rebase tutorial.
Change several commit messages at once
You do not need one rebase per message. Mark as many lines as you like in the same to-do list.
Here are three commits, two of which say nothing useful:
git log --oneline -30515c31 Add cache warmup task
ce2892d stuff
a2c2886 wipOne interactive rebase covers all three. In its to-do list, set reword on both weak lines and leave the good one as pick:
reword a2c2886 wip
reword ce2892d stuff
pick 0515c31 Add cache warmup taskGit walks the list from top to bottom, opening an editor once per reworded commit — so expect two editor sessions here, not one. Each opens with that commit's old message, and the rebase moves on as you save each one:
git rebase -i HEAD~3Rebasing (1/3)[detached HEAD c2c8579] Add request retry helper
Date: Thu Aug 20 23:11:53 2026 +0530
1 file changed, 1 insertion(+)
create mode 100644 a.txt
Rebasing (2/3)[detached HEAD 8f9bb2a] Add response cache layer
Date: Thu Aug 20 23:11:53 2026 +0530
1 file changed, 1 insertion(+)
create mode 100644 b.txt
Rebasing (3/3)Successfully rebased and updated refs/heads/multi-demo.Both replacements are reported, one per reworded commit. The history is now readable:
git log --oneline -4569e5d9 Add cache warmup task
8f9bb2a Add response cache layer
c2c8579 Add request retry helper
d5ff8e6 Update login validation to reject empty passwordsThis is the usual tidy-up before opening a pull request: one rebase, every vague message replaced, reviewers get a history they can follow.
Undo a message change
Because Git does not delete the old commit immediately, a rewrite you regret is recoverable. Say you amend and immediately realise the new message is worse:
git log --oneline -15e9a37e OOPS wrong message entirelyThe commit you want back is not in git log any more, because your branch no longer points at it. It is still listed in the reflog, which records recent positions your branch has been in:
git reflog -35e9a37e HEAD@{0}: commit (amend): OOPS wrong message entirely
569e5d9 HEAD@{1}: rebase (finish): returning to refs/heads/multi-demo
8f9bb2a HEAD@{2}: rebase (pick): Add response cache layerRead this as a history of where your branch has pointed. Find the entry from immediately before the bad amend — here HEAD@{1} at 569e5d9 with the message you wanted — not a fixed offset like HEAD@{1} in every situation.
This moves the branch back to the selected commit without discarding your current working-directory or staged changes:
git reset --soft 'HEAD@{1}'You can also reset directly to the hash if that is clearer:
git reset --soft 569e5d9HEAD is now at 569e5d9 Add cache warmup taskThe branch now points back to the original commit. The incorrect amended commit is no longer part of the branch history, although Git may keep it temporarily and it can still appear in the reflog. Reflog expiration is configurable; by default Git expires unreachable reflog entries after 30 days and other entries after 90 days. Git reflog tutorial covers recovering from harder situations, and Git reset examples explains the difference between --soft, --mixed, and --hard.
When you should leave the message alone
Every technique above rewrites history, which is harmless on your own branch and disruptive on a shared one. Rewriting a branch your teammates have already pulled means their copy no longer matches the server, and each of them has to sort it out by hand.
Leave the message as it is when:
- the commit is on a long-lived branch such as
main,master, ordevelop - the branch is protected by your host and rejects force pushes anyway
- someone else has already built work on top of those commits
- the message is merely imperfect rather than wrong or leaking something
Rewrite it when the branch is yours alone, when it is a feature branch nobody has pulled, or when the message contains something that genuinely must not stay.
One clarification, because it is a common misunderstanding: git revert does not fix a message. It adds a new commit that undoes the code changes, and the original commit keeps its bad message in the history. Reverting a commit because you disliked its wording removes working code and fixes nothing.
If a message on a shared branch is truly wrong, the low-drama option is to explain the correction in the pull request or in a later commit message, and to coordinate with your team before rewriting anything.
Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
! [rejected] ... (non-fast-forward) on push |
You rewrote a commit the remote already has | Push the replacement with git push --force-with-lease; do not use git pull simply to clear the rejection |
--force-with-lease itself is rejected |
Someone pushed to the branch since your last fetch | Fetch and inspect the new remote commits. Integrate or coordinate those changes before deciding whether the remote history should still be replaced |
Editor opens with # comment lines and nothing else useful |
Normal to-do list; comments explain the actions | Edit only the pick words above the comment block |
| Rebase stopped and the shell prompt says rebasing | Git paused for a reword or a conflict | Finish the message and git rebase --continue, or git rebase --abort |
| Commits after the one you edited have new hashes | Expected — a new parent changes every later commit | Nothing to fix; force push the branch if it was already pushed |
| Amended the wrong commit | Amend hit the newest commit, not the one you meant | Run git reflog, identify the entry before the rewrite, then git reset --soft to that entry or hash |
| Wanted to edit an older commit but amend was used | --amend only ever touches the newest commit |
Reset back using the correct reflog entry, then use git rebase -i |
git log no longer shows a commit you need |
Nothing references it after the rewrite | Find it in git reflog and reset or cherry-pick it back |
References
Summary
Use git commit --amend --only for the newest commit, git rebase -i with reword for older or multiple messages, and git push --force-with-lease when the rewritten commit was already on the remote. If a rewrite goes wrong, recover through git reflog by resetting to the entry from before the change.

