Creates a brand new commit object with a new SHA hash
Old commit is orphaned, not deleted — recoverable via reflog for 90 days
Every commit after the amended one would lose its parent — so amend only works on HEAD
--no-edit — keep existing message (the daily workhorse flag)
-m 'new message' — replace message inline without opening editor
--author='Name ' — fix wrong Git identity on the commit
--force-with-lease — the amended commit to remote
Plain-English First
Imagine you sealed an envelope with a letter inside, stamped it, and dropped it in the mailbox. Then you realized you forgot to include a second page. git commit --amend is like reaching into the mailbox, pulling out the envelope, adding the missing page, resealing it, and putting it back — with a new seal (new commit hash) that replaces the old one. The old envelope is gone. Nobody who hasn't already picked up the old envelope will ever know it existed.
The critical detail: the new envelope has a different tracking number (SHA hash). If someone already has the old tracking number recorded somewhere — like a teammate who pulled your branch — their records won't match the new envelope. That's why amending pushed commits causes problems: you changed the tracking number after other people wrote it down.
git commit --amend creates a new commit replacing the last one. It fixes typos in messages, adds forgotten files, or corrects the commit author — all without leaving broken commits in the history.
The core trade-off: amend rewrites history. On a local branch, this is invisible and safe. On a shared branch, every teammate's local history diverges from the remote. One ONLY safe way to push an force-push on main in a 40-engineer monorepo caused 6 divergent histories and 2 hours of recovery work.
Common misconceptions: that amend modifies the existing commit (it creates a new one), that --force is fine for personal branches (--force-with-lease is always safer), and that ORIG_HEAD persists indefinitely (it's overwritten by the next dangerous Git operation).
How Amend Actually Works Under the Hood
Before using amend, understand what it does to your repository. git commit --amend does NOT modify the existing commit. It creates an entirely new commit object with a new SHA hash, containing your updated message and/or content. The old commit is then orphaned — no branch points to it anymore. Git's garbage collector eventually deletes orphaned commits (default: 90 days), but until then, the old commit still exists in the repository's object database.
This is why amending a pushed commit requires a force push: the remote branch still points to the old commit hash. Your local branch now points to a different commit hash. Normal git push is rejected because the remote's history has a commit your local history doesn't have. You need --force-with-lease to tell the remote 'replace your pointer with mine, but only if nobody else has pushed since I last fetched.'
The parent pointer also changes. The new commit's parent is whatever was before the old commit — not the old commit itself. This means every commit that came after the amended commit (if there were any) would lose its parent and become orphaned. That's why you can only amend the most recent commit directly — amending an older commit would orphan every commit after it.
To amend an older commit, you need interactive rebase, which replays commits and stops at the one you want to modify. That's covered in a later section.
io/thecodeforge/git/AmendInternals.shBASH
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
# io.thecodeforge — GitAmendInternals
# ─────────────────────────────────────────────────────────────
# SCENARIO: Inspecting what amend does to commit objects.
# ─────────────────────────────────────────────────────────────
# Step1: Make a commit and record its SHA
git add src/payments/RetryConfig.java
git commit -m "feat: add retry config"
BEFORE_AMEND=$(git rev-parse HEAD)
echo "Before amend: $BEFORE_AMEND"
# Example output: 9f2c4a1b3d5e7f8a9b0c1d2e3f4a5b6c7d8e9f0a
# Step2: Amend the commit message
git commit --amend -m "feat(payment): add PaymentRetryService with retry config"
AFTER_AMEND=$(git rev-parse HEAD)
echo "After amend: $AFTER_AMEND"
# Example output: 3a7b9c1d2e4f6a8b0c2d4e6f8a0b2c4d6e8f0a2c
# DifferentSHA — it's a completely new commit object.
# Step3: Prove the old commit still exists in the object database
echo "Old commit still exists:"
git cat-file -t $BEFORE_AMEND
# Output: commit — the old commit object is still in the repo.
git log --oneline $BEFORE_AMEND -1
# Output: 9f2c4a1 feat: add retry config
# The old commit with the old message is still there.
# Step4: Show that no branch points to the old commit
echo "Branches containing old commit:"
git branch --contains $BEFORE_AMEND
# Output: (empty) — no branch references the old commit anymore.
# It's orphaned. Git's gc will delete it after 90 days by default.
# Step5: Thenew commit has a different parent chain
echo "New commit's parent:"
git log --oneline -2
# Thenew commit's parent is the commit BEFORE the old commit.
# Not the old commit itself — the old commit is bypassed entirely.
# ─────────────────────────────────────────────────────────────
# RECOVERINGTHEOLDCOMMIT (if you amended by mistake)
# ─────────────────────────────────────────────────────────────
# ORIG_HEAD points to where HEAD was before the amend.
git show ORIG_HEAD --oneline --no-patch
# Output: 9f2c4a1 feat: add retry config
# This is your escape hatch.
# To undo the amend and go back to the old commit:
git reset --hard ORIG_HEAD
# WARNING: this discards the amended commit. The old commit is restored.
# If ORIG_HEAD is gone (you ran other Git commands since the amend):
git reflog
# Shows every HEAD movement. Find the line with the old commit.
# Then: git reset --hard <old-commit-hash>
Output
Before amend: 9f2c4a1b3d5e7f8a9b0c1d2e3f4a5b6c7d8e9f0a
After amend: 3a7b9c1d2e4f6a8b0c2d4e6f8a0b2c4d6e8f0a2c
Old commit still exists:
commit
9f2c4a1 feat: add retry config
Branches containing old commit:
(empty)
New commit's parent:
3a7b9c1 feat(payment): add PaymentRetryService with retry config
8d1e3f5 (HEAD~2) Add PaymentClient interface
Amend = New Commit, Not Edited Commit
New SHA means every reference to the old SHA is now a dangling pointer
Old commit is recoverable via reflog for 90 days — then garbage-collected permanently
CI webhooks, deployment trackers, and teammate branches may reference the old SHA
Understanding this prevents both 'why is my force push rejected' and 'where did my work go'
Production Insight
The 90-day reflog window is your safety net, but it's not a strategy. In high-velocity teams, 90 days of reflog entries can make git reflog output overwhelming. The practical window for recovery is much shorter — usually hours, not days. After a force-push, the old commit is immediately unrecoverable from the remote. Only local reflogs preserve it. If you amend and force-push, then realize the mistake a week later, the old commit exists only in your local reflog — not on the remote, not in teammates' reflogs (they never had the old commit locally unless they pulled it before your force-push).
Key Takeaway
Amend creates a new commit object with a new SHA. The old commit is orphaned, not deleted. This is why force-push is required after amending a pushed commit, and why CI systems may reference dangling SHAs. The reflog is your recovery mechanism — but it's local-only and time-limited.
Amend the Last Commit: Message, Content, and Author
There are three things you can change with amend: the commit message, the commit content (files), and the commit author. You can change one, two, or all three in a single amend command.
For message-only: git commit --amend -m 'new message'. No editor, one line, done.
For content changes: stage the additional files or changes first with git add, then amend. The staged changes get rolled into the existing commit. Use --no-edit to keep the existing message unchanged.
For author correction: git commit --amend --author='Name <email>' --no-edit. This is how you fix commits made with the wrong Git config — common when switching between work and personal machines, or when a CI bot makes commits under the wrong identity.
The most common production pattern: stage a forgotten file, amend with --no-edit. This is the 'oops, I forgot the test file' command that every developer runs multiple times a week.
io/thecodeforge/git/AmendPatterns.shBASH
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
# io.thecodeforge — GitAmendPatterns
# ─────────────────────────────────────────────────────────────
# PATTERN1: Fix commit message (no editor)
# ─────────────────────────────────────────────────────────────
git commit --amend -m "feat(payment): Add PaymentRetryService with exponential backoff"
# Done. New commit with new message. Old commit orphaned.
# ─────────────────────────────────────────────────────────────
# PATTERN2: Fix commit message (with editor)
# Opens your configured editor (vim, nano, VSCode) with the existing message.
# ─────────────────────────────────────────────────────────────
git commit --amend
# Edit the message, save, close. New commit created.
# ─────────────────────────────────────────────────────────────
# PATTERN3: Add forgotten file, keep existing message
# The most common 'oops' pattern.
# ─────────────────────────────────────────────────────────────
git add src/main/java/io/thecodeforge/payment/RetryConfig.java
git commit --amend --no-edit
# ─────────────────────────────────────────────────────────────
# PATTERN4: Add forgotten file AND change message
# ─────────────────────────────────────────────────────────────
git add src/main/java/io/thecodeforge/payment/RetryConfig.java
git commit --amend -m "feat(payment): Add PaymentRetryService with retry config and tests"
# ─────────────────────────────────────────────────────────────
# PATTERN5: Fix author name/email
# ─────────────────────────────────────────────────────────────
git commit --amend --author="John Doe <john.doe@thecodeforge.io>" --no-edit
# Author changed. Commit hash changes. Message and content unchanged.
# ─────────────────────────────────────────────────────────────
# PATTERN6: AddDCO sign-off to the last commit
# Required by some open-source projects (Linux kernel, CNCF).
# ─────────────────────────────────────────────────────────────
git commit --amend --signoff --no-edit
# Adds'Signed-off-by: Name <email>' to the commit message.
# The sign-off certifies you have the right to submit the code.
# ─────────────────────────────────────────────────────────────
# PATTERN7: Remove a file that shouldn't have been committed
# ─────────────────────────────────────────────────────────────
git rm --cached src/main/resources/application-secrets.yml
git commit --amend --no-edit
# The file is removed from the commit but NOT from your working directory.
# --cached means 'remove from index only, keep the file on disk.'
# ─────────────────────────────────────────────────────────────
# AFTERAMEND: Push to remote (your own branch only)
# ─────────────────────────────────────────────────────────────
git push origin feature/payment-retry --force-with-lease
# --force-with-lease is SAFER than --force. Explained in next section.
Output
# After git commit --amend --no-edit (added forgotten file):
[feature/payment-retry a1b2c3d] feat(payment): Add PaymentRetryService with exponential backoff
Date: Mon Mar 30 14:22:00 2026 +0530
3 files changed, 87 insertions(+)
# After git commit --amend --signoff --no-edit:
[feature/payment-retry e4f5g6h] feat(payment): Add PaymentRetryService with exponential backoff
Date: Mon Mar 30 14:22:00 2026 +0530
3 files changed, 87 insertions(+)
The Forgotten-File Pattern You'll Use Daily
Stage first, then amend — the order matters. Amend uses whatever is staged.
--no-edit preserves the message. Without it, your editor opens every time.
Verify with git diff HEAD~1 HEAD after amending to confirm the file is included.
This pattern is safe only on branches nobody else has pulled.
Production Insight
The forgotten-file pattern is so common that some teams create a shell alias for it: alias oops='git add -A && git commit --amend --no-edit'. The risk: git add -A stages ALL changes, not just the forgotten file. If you have unrelated working-directory changes, they get folded into the amend. Always use git add <specific-file> for amends, not git add -A. The author correction pattern (--author) is critical for CI/CD pipelines where bots commit under default identities. If your release process relies on commit authors for changelog generation, a misattributed bot commit can produce incorrect release notes.
Key Takeaway
Amend can change the message, content, and author of the last commit — independently or together. The daily workhorse is git add <file> && git commit --amend --no-edit. Always stage the specific file before amending — never use git add -A for amends to avoid accidentally including unrelated changes.
--force-with-lease vs --force: The Safety Net You Should Never Skip
After amending a pushed commit, you need to force push. The remote branch still points to the old SHA. Your local branch points to the new SHA. A normal git push is rejected. But there are two force push options, and one of them can destroy your teammates' work.
--force overwrites the remote branch with your local branch, no questions asked. If a teammate pushed commits since your last fetch, --force silently destroys those commits. They're not merged, they're not saved — they're erased from the remote branch.
--force-with-lease does the same overwrite but first checks that the remote branch matches your local tracking reference. If someone else pushed, --force-with-lease rejects the push with an error. It forces you to fetch and review the conflicting commits before proceeding.
There is never a legitimate reason to use --force over --force-with-lease on a branch where anyone else might have pushed. Never.
io/thecodeforge/git/ForceWithLease.shBASH
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
# io.thecodeforge — --force-with-lease vs --force
# ─────────────────────────────────────────────────────────────
# WRONG: --force overwrites without checking
git push origin feature/payment-retry --force
# If a teammate pushed commit X after your last fetch:
# Commit X is SILENTLYDESTROYED. No warning. Norecovery (except reflog).
# The remote branch now points to your amended commit.
# Your teammate's work is gone from the branch.
# ─────────────────────────────────────────────────────────────
# RIGHT: --force-with-lease checks before overwriting
git push origin feature/payment-retry --force-with-lease
# If the remote has commits your local tracking ref doesn't know about:
# error: failed to push some refs to '...'
# hint: Updates were rejected because the tip of your current branch is behind
# hint: its remote counterpart. Integrate the remote changes before pushing again.
# This means: someone else pushed. Fetch and review before force-pushing.
# ─────────────────────────────────────────────────────────────
# VERIFYSAFETY: Check what the remote has that you don't
# ─────────────────────────────────────────────────────────────
# Before force-pushing, always fetch and check
git fetch origin
git log origin/feature/payment-retry..HEAD --oneline
# Shows commits YOU have that the remote doesn't (your amend).
git log HEAD..origin/feature/payment-retry --oneline
# Shows commits the REMOTE has that you don't.
# Ifthis is empty, safe to force-push.
# Ifthis is NOT empty, someone else pushed. Don't force-push blindly.
# ─────────────────────────────────────────────────────────────
# MAKE --force-with-lease THEDEFAULT:
# ─────────────────────────────────────────────────────────────
# Option1: Alias
git config --global alias.pushf 'push --force-with-lease'
# Now: git pushf feature/payment-retry
# Option2: MakeALL force pushes use --force-with-lease
git config --global push.useForceIfIncludes true
# InGit2.30+, this makes --force behave like --force-with-lease
# by requiring the remote ref to match your tracking ref.
# ─────────────────────────────────────────────────────────────
# WHATIF --force-with-lease REJECTSYOURPUSH?
# ─────────────────────────────────────────────────────────────
# Step1: Fetch to see what the remote has
git fetch origin
# Step2: See what your teammate pushed
git log HEAD..origin/feature/payment-retry --oneline
# Output:
# b7c8d9e Add error handling forPaymentRetryService
# f1a2b3c Fixnull check in RetryConfig
# Step3: Rebase your amended commit on top of their work
git rebase origin/feature/payment-retry
# Your amended commit is replayed on top of their two commits.
# Step4: Now force-push is safe (your tracking ref is up to date)
git push origin feature/payment-retry --force-with-lease
# hint: Updates were rejected because the tip of your current branch is behind
There Is Never a Reason to Use --force Over --force-with-lease
--force silently destroys any commits pushed after your last fetch
--force-with-lease checks your local tracking ref against the remote before overwriting
If --force-with-lease rejects: fetch, review, rebase — then retry
Git 2.30+ has push.useForceIfIncludes to make --force behave like --force-with-lease globally
Production Insight
The --force-with-lease check compares the remote branch against your local origin/<branch> tracking reference — not against your local branch. This means the safety net only works if you've fetched recently. If you last fetched 3 hours ago and a teammate pushed 2 hours ago, --force-with-lease will reject. But if you last fetched 3 hours ago, a teammate pushed 2 hours ago, and you then fetched 1 hour ago (updating your tracking ref), --force-with-lease will think it's safe — even though the teammate's commits are now on the remote. The safety is only as good as your last fetch. Always git fetch before --force-with-lease.
Key Takeaway
--force-with-lease is the only acceptable force push in production. It checks that the remote matches your tracking reference before overwriting. --force skips this check and silently destroys teammates' work. Configure push.useForceIfIncludes=true (Git 2.30+) to make --force behave like --force-with-lease globally.
Amending Older Commits with Interactive Rebase
git commit --amend only works on the most recent commit (HEAD). What if the commit you need to fix is three commits back? That's where git rebase -i (interactive rebase) comes in.
Interactive rebase lets you replay a series of commits and stop at any point to modify them. You mark the commit you want to amend with the 'edit' keyword, Git pauses at that commit, you make your changes, amend the commit, then continue the rebase.
The syntax: git rebase -i HEAD~3 opens an editor showing the last 3 commits. Change 'pick' to 'edit' on the commit you want to modify. Save and close. Git replays commits until it reaches the marked one, then pauses. You stage your changes, run git commit --amend, then run git rebase --continue to replay the remaining commits.
Critical warning: interactive rebase rewrites every commit from the one you modify onward. All those commits get new SHAs. This is fine for local-only branches. For branches that teammates have pulled, this breaks their local history — the same problem as amending a pushed commit, but affecting multiple commits instead of one.
io/thecodeforge/git/InteractiveRebaseAmend.shBASH
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
# io.thecodeforge — AmendingOlderCommits with InteractiveRebase
# ─────────────────────────────────────────────────────────────
# SCENARIO: You have 5 commits on feature/payment-retry.
# Commit #3 (counting from HEAD) has a bug in PaymentService.java.
# You need to fix that file IN that commit, not in a new commit.
# ─────────────────────────────────────────────────────────────
# Step1: See your recent commits
git log --oneline -5
# a1b2c3d (HEAD -> feature/payment-retry) Add integration test
# e4f5g6h AddRetryConfig validation
# i7j8k9l AddPaymentService.retryPayment() method ← THISONEHASTHEBUG
# m0n1o2p AddRetryConfig data class
# q3r4s5t AddPaymentClientinterface
# Step2: Start interactive rebase for the last 5 commits
git rebase -i HEAD~5
# Editor opens with:
#
# pick q3r4s5t AddPaymentClientinterface
# pick m0n1o2p AddRetryConfig data class
# pick i7j8k9l AddPaymentService.retryPayment() method ← change 'pick' to 'edit'
# pick e4f5g6h AddRetryConfig validation
# pick a1b2c3d Add integration test
#
# Save and close.
# Step3: Git pauses at commit i7j8k9l
git status
# Output: interactive rebase in progress; onto abc1234
# Last command done: edit i7j8k9l AddPaymentService.retryPayment() method
# You are currently editing a commit.
# Step4: Fix the bug in PaymentService.java
echo '// fixed: null check added' >> src/main/java/io/thecodeforge/payment/PaymentService.java
# Step5: Stage the fix and amend the commit
git add src/main/java/io/thecodeforge/payment/PaymentService.java
git commit --amend --no-edit
# The fix is now part of commit i7j8k9l (with a newSHA).
# Step6: Continue the rebase — replay the remaining commits
git rebase --continue
# Git replays e4f5g6h and a1b2c3d on top of the amended commit.
# If there are conflicts during replay, resolve them and run git rebase --continue.
# Step7: Verify the history is clean
git log --oneline -5
# Shows all 5 commits with the fix baked into commit #3.
# AllSHAs are different from before (history was rewritten).
# ─────────────────────────────────────────────────────────────
# ABORT: If things go wrong during interactive rebase
# ─────────────────────────────────────────────────────────────
git rebase --abort
# Resets everything to the state before you started the rebase.
# Safe escape hatch. Use it without hesitation.
# ─────────────────────────────────────────────────────────────
# PUSHAFTERINTERACTIVEREBASE (your own branch only)
# ─────────────────────────────────────────────────────────────
git push origin feature/payment-retry --force-with-lease
# Required because multiple commits were rewritten.
Output
# git rebase -i HEAD~5 (editor)
# Successfully rebased and updated refs/heads/feature/payment-retry.
# git log --oneline -5 (after rebase)
# d9e0f1g (HEAD -> feature/payment-retry) Add integration test
# h2i3j4k Add RetryConfig validation
# l5m6n7o Add PaymentService.retryPayment() method ← fix is baked in
# m0n1o2p Add RetryConfig data class
# q3r4s5t Add PaymentClient interface
# All SHAs after the amended commit changed — history rewritten.
Interactive Rebase Rewrites All Commits After the Edited One
Commits after the edit point get new SHAs even if their content is unchanged
The parent chain changes — each subsequent commit's parent is now a different object
This amplifies the force-push blast radius: N commits rewritten instead of 1
Interactive rebase on shared branches is significantly more dangerous than single amend
Production Insight
Interactive rebase has a failure mode that single amend doesn't: merge conflicts during replay. When you amend commit #3 and Git replays commits #4 and #5 on top, those later commits may conflict with your amendment. If commit #4 modified the same line you just fixed in commit #3, you'll get a conflict during git rebase --continue. The more commits between your edit point and HEAD, the higher the conflict probability. For complex chains, consider creating a fixup commit instead — it achieves the same clean history without the rebase conflict risk.
Key Takeaway
Interactive rebase lets you amend older commits by marking them with 'edit'. But it rewrites every commit from the edit point onward — all get new SHAs. This amplifies the force-push blast radius. For complex chains with potential conflicts, prefer git commit --fixup with autosquash over interactive rebase.
When NOT to Amend: The Decision Tree
Amend is not always the right tool. Here's the decision tree that prevents 95% of amend-related incidents.
Amend is safe when: the commit has NOT been pushed to a shared branch, OR the commit is on a branch only you work on, OR you're about to push for the first time.
Amend is dangerous when: the commit has been pushed to a branch that teammates pull from, OR the commit is on main/develop/release, OR other people have based commits on top of yours, OR a CI pipeline has already run against the old commit.
Use revert instead when: the commit is on a shared branch, OR you want to undo the commit's changes while preserving history, OR the team policy prohibits force-pushing.
Use a new commit instead when: the change is logically separate from the last commit (even if it's small), OR you want to preserve the 'fix' as a visible history entry, OR you're on a shared branch where amend is forbidden.
The mental model: amend is for 'I made a mistake in the last 30 seconds.' If more than 30 seconds have passed — or if anyone else has seen the commit — create a new commit instead.
io/thecodeforge/git/AmendDecisionTree.shBASH
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
# io.thecodeforge — When to Amend vs WhenNOT to Amend
# ─────────────────────────────────────────────────────────────
# DECISION: Has the commit been pushed to a shared branch?
# ─────────────────────────────────────────────────────────────
# YES → DoNOT amend. Use one of these instead:
# Option A: Create a fixup commit (clean, trackable)
git add src/payments/RetryConfig.java
git commit --fixup HEAD
# Creates a commit with message 'fixup! <original message>'
# Later, during merge or rebase, Git auto-squashes fixup commits.
# Or: git rebase -i --autosquash (automatically orders fixup commits).
# Option B: Revert the commit (undo its changes, preserve history)
git revert HEAD
# Creates a new commit that undoes the last commit's changes.
# Safe to push. No force push required. History is preserved.
# Option C: Create a new commit with the fix (simplest)
git add src/payments/RetryConfig.java
git commit -m "fix(payment): add missing null check in RetryConfig"
# New commit, new message, no history rewrite. Safe everywhere.
# ─────────────────────────────────────────────────────────────
# DECISION: Is the commit on a branch only you use?
# ─────────────────────────────────────────────────────────────
# YES → Amend freely. Force push with --force-with-lease.
git add src/payments/RetryConfig.java
git commit --amend --no-edit
git push origin feature/payment-retry --force-with-lease
# ─────────────────────────────────────────────────────────────
# DECISION: Is the commit on main/develop/release?
# ─────────────────────────────────────────────────────────────
# NEVER amend on main, develop, or release branches.
# These branches are pulled by every developer on the team.
# Amending here will break everyone's local history.
# If you need to fix something on main:
git revert <commit-hash> # undo the change safely
# or
git cherry-pick <fix-commit> # bring a fix from another branch
# ─────────────────────────────────────────────────────────────
# FIXUPCOMMITS: The amend alternative for shared branches
# ─────────────────────────────────────────────────────────────
# Create a fixup commit that targets a specific older commit
git commit --fixup abc1234
# Creates: 'fixup! <original message of abc1234>'
# Later, during interactive rebase with autosquash:
git rebase -i --autosquash main
# Git automatically places the fixup commit next to its target
# and marks it as 'fixup' (squash without editing message).
# The result: the fix is baked into the original commit.
# This is how you 'amend' a commit that's already been pushed
# without force-pushing until the final rebase.
Safe: local-only branch, pre-push, nobody else has pulled
Dangerous: shared branch, main/develop/release, CI already ran
Alternative on shared branches: fixup commits (auto-squashed during rebase)
Alternative for undoing: revert (preserves history, no force push)
Production Insight
Fixup commits are the production-safe alternative to amend for shared branches. The workflow: create fixup commits during development, then git rebase -i --autosquash main before merging the PR. The PR shows the fixup commits (reviewers can see what was fixed), and the final merge has clean, squashed history. This gives you the cleanliness of amend without the danger of force-pushing. The key configuration: git config --global rebase.autoSquash true — this makes --autosquash the default for all interactive rebases.
Key Takeaway
Amend is for local-only branches where nobody else has seen the commit. On shared branches, use git commit --fixup to create fixup commits that auto-squash during rebase. On main/develop/release, never amend — use git revert to undo changes safely.
Undoing a Bad Amend: ORIG_HEAD and Reflog
You amended a commit and immediately realized you made it worse — wrong message, removed a needed file, or amended the wrong commit. How do you undo it?
Git saves ORIG_HEAD before every dangerous operation (amend, rebase, reset). After amending, ORIG_HEAD points to the commit you amended — the one with the original message and content. git reset --hard ORIG_HEAD restores your branch to the pre-amend state.
If you ran other Git commands after the amend (which overwrites ORIG_HEAD), use git reflog. The reflog records every HEAD movement for 90 days. Find the line with the original commit, copy its hash, and reset to it.
The window: 90 days. After that, orphaned commits are garbage-collected and unrecoverable. If you realize you amended the wrong commit two months later, the original may be gone.
io/thecodeforge/git/UndoAmend.shBASH
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
# io.thecodeforge — Undoing a BadAmend
# ─────────────────────────────────────────────────────────────
# SCENARIO1: You just amended and want to undo immediately.
# ORIG_HEAD is still available.
# ─────────────────────────────────────────────────────────────
# Check what ORIG_HEAD points to
git show ORIG_HEAD --oneline --no-patch
# Output: 9f2c4a1 feat: add retry config
# This is the commit BEFORE your amend.
# Undo the amend — restore the original commit
git reset --hard ORIG_HEAD
# Output: HEAD is now at 9f2c4a1 feat: add retry config
# Your branch is back to the pre-amend state.
# The amended commit is now orphaned (recoverable via reflog for90 days).
# ─────────────────────────────────────────────────────────────
# SCENARIO2: You ran other Git commands after amending.
# ORIG_HEAD has been overwritten. Use reflog.
# ─────────────────────────────────────────────────────────────
git reflog
# Output:
# a1b2c3d HEAD@{0}: commit (amend): feat(payment): add PaymentRetryService ← the amend
# 9f2c4a1 HEAD@{1}: commit: feat: add retry config ← THISISTHEORIGINAL
# 8d1e3f5 HEAD@{2}: checkout: moving from main to feature/payment-retry
# Reset to the original commit
git reset --hard 9f2c4a1
# Output: HEAD is now at 9f2c4a1 feat: add retry config
# ─────────────────────────────────────────────────────────────
# SCENARIO3: You amended, pushed, and now want to undo the push.
# ─────────────────────────────────────────────────────────────
# Option A: Force-push the original commit
git reset --hard ORIG_HEAD # or reflog hash
git push origin feature/payment-retry --force-with-lease
# Remote now has the original commit. The amended commit is gone from remote.
# Option B: Revert the amend (safer if others pulled the amended commit)
git revert HEAD
# Creates a new commit that undoes the amended commit's changes.
# Then create another commit with the correct changes.
# No force push required. History is preserved.
# ─────────────────────────────────────────────────────────────
# SCENARIO4: You amended multiple times and want a specific version.
# ─────────────────────────────────────────────────────────────
git reflog
# Output:
# c3d4e5f HEAD@{0}: commit (amend): feat(payment): third attempt at message
# b2c3d4e HEAD@{1}: commit (amend): feat(payment): second attempt at message
# a1b2c3d HEAD@{2}: commit (amend): feat(payment): first attempt at message
# 9f2c4a1 HEAD@{3}: commit: feat: add retry config ← the original
# Pick any version and reset to it
git reset --hard HEAD@{2}
# Output: HEAD is now at a1b2c3d feat(payment): first attempt at message
# You're back to the first amended version.
# 8d1e3f5 HEAD@{2}: checkout: moving from main to feature/payment-retry
ORIG_HEAD Is Your Undo Button — But It Expires
ORIG_HEAD is set before amend, rebase, and reset operations
It's overwritten by the next dangerous operation — not cumulative
Reflog records every HEAD movement for 90 days (configurable via gc.reflogExpire)
After 90 days, orphaned commits are garbage-collected and permanently unrecoverable
Production Insight
The reflog recovery window (90 days by default) is configurable via gc.reflogExpire and gc.reflogExpireUnreachable. For repositories with high commit velocity (monorepos, large teams), the reflog can grow large. The practical issue: git reflog output becomes noisy. Use git reflog --date=relative to see timestamps, and git reflog show <branch> to filter by branch. For critical repositories, consider setting gc.reflogExpire to 180 days. The trade-off: longer retention means more disk usage for the object database, but the recovery window is proportionally longer.
Key Takeaway
ORIG_HEAD is your immediate undo button — but it's overwritten by the next dangerous Git operation. For delayed recovery, git reflog records every HEAD movement for 90 days. After that, orphaned commits are garbage-collected permanently. If you amend and force-push, the old commit exists only in your local reflog — not on the remote.
Amending Merge Commits
You can amend a merge commit, but it works differently from amending a regular commit. A merge commit has two (or more) parents. git commit --amend on a merge commit lets you change the merge commit message, but it does NOT re-run the merge. The file content stays the same — you're only editing the message.
If you need to change the actual content of a merge commit (add a file that was missed, resolve a conflict differently), you make the changes, stage them, and then amend. The staged changes are folded into the merge commit.
The gotcha: amending a merge commit changes its SHA, which breaks the parent chain of every commit that came after it. This is the same problem as amending any other commit, but amplified because merge commits are often on shared branches (main, develop). Don't amend merge commits on shared branches.
io/thecodeforge/git/AmendMergeCommit.shBASH
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
# io.thecodeforge — AmendingMergeCommits
# ─────────────────────────────────────────────────────────────
# SCENARIO: You merged a feature branch into main and the merge
# commit message is the default'Merge branch feature/x into main'.
# You want a more descriptive message.
# ─────────────────────────────────────────────────────────────
# Step1: Verify the last commit is a merge commit
git log --oneline -1 --merges
# Output: a1b2c3d Merge branch 'feature/payment-retry' into main
# Step2: Amend the merge commit message
git commit --amend -m "Merge feature/payment-retry: Add PaymentRetryService with exponential backoff"
# New merge commit created with descriptive message.
# The file content is unchanged — only the message changed.
# ─────────────────────────────────────────────────────────────
# SCENARIO: You need to change content in a merge commit.
# ─────────────────────────────────────────────────────────────
# Step1: Make the changes
echo '// added during merge fix' >> src/payments/MergeNote.java
# Step2: Stage and amend
git add src/payments/MergeNote.java
git commit --amend --no-edit
# The file is now part of the merge commit.
# The merge commit's SHA changes.
# ─────────────────────────────────────────────────────────────
# WARNING: Amending merge commits on shared branches
# ─────────────────────────────────────────────────────────────
# If the merge commit is on main and teammates have pulled it:
# - Their local main has the old merge commit SHA
# - Your amended main has a new merge commit SHA
# - Histories have diverged
# - Force push required, breaks everyone
# Rule: amend merge commits ONLY before anyone else pulls them.
# If in doubt, create a follow-up commit instead:
git add src/payments/MergeNote.java
git commit -m "fix: add missing MergeNote.java from merge"
Amending a merge commit only changes the message and/or adds staged files
It does NOT re-run the merge or re-resolve conflicts
To re-resolve conflicts: git reset --hard HEAD~1 (undo merge) then re-merge
Merge commits are almost always on shared branches — amend is almost never safe here
Production Insight
The default merge commit message ('Merge branch X into Y') is a common source of noise in git log. Teams that care about history readability often configure git merge --no-ff with custom messages. The risk of amending merge commits on main is amplified because merge commits are integration points — they're referenced by CI systems, deployment trackers, and release tooling. Rewriting a merge commit SHA can break all of these simultaneously. The safe alternative: configure your merge tool to produce good messages upfront, rather than amending after the fact.
Key Takeaway
Amending a merge commit changes its message and/or adds staged files — but does NOT re-run the merge. Merge commits are almost always on shared branches (main, develop), so amending them is almost never safe. Configure your merge workflow to produce good messages upfront instead of amending after the fact.
Amend in CI/CD: Re-triggering Builds and Webhook Behavior
When you amend a pushed commit and force-push, most CI systems (GitHub Actions, GitLab CI, Jenkins) detect the push event and trigger a new build. The old build — which was running against the old commit — may still be running. You now have two builds: one for the old commit (which will complete but is irrelevant) and one for the new commit (which is the one you care about).
The problem: if your CI system cancels the old build automatically (GitHub Actions does this for the same branch), you're fine. If it doesn't (some Jenkins configurations), you get two builds running simultaneously, potentially deploying conflicting artifacts.
The other problem: CI webhooks include the commit SHA in their payload. If a downstream system (like a deployment tracker or a notification bot) recorded the old SHA, it now references a commit that no longer exists on the branch. This can cause 'commit not found' errors in downstream systems.
Best practice: if you amend and force-push, check that the CI build triggered for the new SHA. If you're using GitHub Actions, the old build is automatically cancelled. If you're using Jenkins, you may need to manually abort the old build.
io/thecodeforge/git/AmendInCI.shBASH
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
# io.thecodeforge — Amend and CI/CDInteraction
# ─────────────────────────────────────────────────────────────
# SCENARIO: You pushed a commit, CI started building.
# You noticed a typo, amended, and force-pushed.
# ─────────────────────────────────────────────────────────────
# Step1: Original push triggers CI
git push origin feature/payment-retry
# CI build #1042 starts — building commit 9f2c4a1
# Step2: You notice typo, amend, force-push
git commit --amend -m "feat(payment): Add PaymentRetryService"
git push origin feature/payment-retry --force-with-lease
# CI build #1043 starts — building commit a1b2c3d
# Build #1042 is still running (for the old commit)
# ─────────────────────────────────────────────────────────────
# GITHUBACTIONS: Auto-cancels previous builds on same branch
# ─────────────────────────────────────────────────────────────
# In your workflow YAML:
# concurrency:
# group: ${{ github.workflow }}-${{ github.ref }}
# cancel-in-progress: true
# This ensures only ONE build runs per branch at a time.
# When you force-push, the old build is cancelled automatically.
# ─────────────────────────────────────────────────────────────
# GITLABCI: Similar behaviour with resource_group
# ─────────────────────────────────────────────────────────────
# In .gitlab-ci.yml:
# deploy:
# resource_group: production
# script: ./deploy.sh
# resource_group ensures only one deploy job runs at a time.
# ─────────────────────────────────────────────────────────────
# JENKINS: Manual cancellation required
# ─────────────────────────────────────────────────────────────
# Jenkins does NOT auto-cancel builds on force-push by default.
# You need to manually abort the old build (#1042) from the UI.
# Or install the 'Discard Old Build' plugin with appropriate config.
# ─────────────────────────────────────────────────────────────
# VERIFY: Check that CI ran on the correct SHA
# ─────────────────────────────────────────────────────────────
git rev-parse HEAD
# Output: a1b2c3d ← this should match the SHA in your CI build logs
# IfCI shows 9f2c4a1, it's building the old commit — abort it.
Always Verify CI Ran on the New SHA After Amending
Force-push triggers a new CI build for the new SHA
The old build may still be running for the now-orphaned old SHA
GitHub Actions: configure concurrency groups for auto-cancellation
Jenkins: manual cancellation required — no default auto-cancel behavior
Production Insight
The CI/CD interaction has a subtle failure mode: deployment race conditions. If the old build completes and deploys before the new build starts, you deploy the old (now-orphaned) commit's artifacts. Then the new build deploys the amended commit's artifacts. The result: your production environment has the correct code, but the deployment history shows two deployments for what looks like the same feature — one with the old SHA (now dangling) and one with the new SHA. This confuses rollback procedures. If you need to roll back, which SHA do you revert to? The old one doesn't exist on the branch anymore. Configure CI to cancel in-progress builds on the same branch when a new push is detected.
Key Takeaway
Amend + force-push triggers a new CI build for the new SHA, but the old build may still be running for the orphaned old SHA. GitHub Actions can auto-cancel with concurrency groups. Jenkins requires manual cancellation. Always verify your CI dashboard shows the new SHA after amending. Downstream systems recording old SHAs will encounter dangling references.
Git Aliases for Amend: Speed Up Your Daily Workflow
If you amend multiple times a day (you will), setting up aliases saves keystrokes and prevents typos. Here are the aliases I use on every machine.
The most useful: an alias for 'amend with no editor and force-push with lease.' This is the 'oops, forgot a file' one-liner that turns a three-command sequence into one.
io/thecodeforge/git/AmendAliases.shBASH
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
# io.thecodeforge — GitAliasesforAmendWorkflows
# ─────────────────────────────────────────────────────────────
# ESSENTIALALIASES — add these to your ~/.gitconfig
# ─────────────────────────────────────────────────────────────
# Amend last commit without opening editor
git config --global alias.amend 'commit --amend --no-edit'
# Usage: git amend
# Equivalent to: git commit --amend --no-edit
# Amend last commit WITH editor
git config --global alias.amende 'commit --amend'
# Usage: git amende
# Opens editor to edit the message.
# Amend and force-push with lease in one command
git config --global alias.amendpush '!git commit --amend --no-edit && git push --force-with-lease'
# Usage: git amendpush
# Amends the last commit and force-pushes. Use only on your own branch.
# Amend with signoff
git config --global alias.amendsign 'commit --amend --signoff --no-edit'
# Usage: git amendsign
# AddsDCO sign-off to the last commit.
# Force push with lease (safer alternative to --force)
git config --global alias.pushf 'push --force-with-lease'
# Usage: git pushf origin feature/my-branch
# Undo the last amend (using ORIG_HEAD)
git config --global alias.undoamend 'reset --hard ORIG_HEAD'
# Usage: git undoamend
# WARNING: discards the amended commit. Use immediately after amend.
# Show what the last amend changed
git config --global alias.amenddiff 'diff ORIG_HEAD HEAD'
# Usage: git amenddiff
# Shows the diff between the original commit and the amended commit.
# Usefulfor verifying your amend did what you expected.
# ─────────────────────────────────────────────────────────────
# SHELLALTERNATIVE — add to ~/.bashrc or ~/.zshrc
# ─────────────────────────────────────────────────────────────
# One-liner: amend + push (for when you KNOW it's safe)
alias gamend='git commit --amend --no-edit && git push --force-with-lease'
# Quick message fix: amend with new message and push
alias gamendm='git commit --amend && git push --force-with-lease'
Shows the exact diff between ORIG_HEAD and current HEAD
Catches the common mistake: amending without staging the forgotten file first
Verifies the amend targeted the correct commit (not a different one)
Two-second sanity check that prevents hours of debugging later
Production Insight
The amendpush alias combines amend and force-push into one command. This is convenient but dangerous: it removes the pause between amend and push where you might realize the amend was wrong. The safer workflow: amend, verify with amenddiff, then push separately. The amendpush alias should certain nobody else has pulled. only be used on branches where you're absolutely For team-shared branches, never combine amend and push into a single command — the verification step is essential.
Key Takeaway
Aliases (amend, pushf, undoamend, amenddiff) reduce keystrokes and prevent --force accidents. The amenddiff alias is the most underrated — it verifies your amend did what you expected by showing the diff between the original and amended commits. Use it as a post-amend sanity check.
● Production incidentPOST-MORTEMseverity: high
Amend on Monorepo Main Branch: 40 Engineers, 6 Divergent Histories
Symptom
Multiple engineers reported 'Your branch and origin/main have diverged' on their next pull. Two engineers had local commits with parents pointing to a SHA that no longer existed on the remote. CI webhook payloads referenced a commit hash that returned 404 from the Git API.
Assumption
The junior developer assumed amending was safe because they were 'just fixing a typo in the message.' They did not realize that amend creates a new SHA, and that force-pushing to main would break every engineer's local tracking reference.
Root cause
1. The commit was on main — a branch pulled by every engineer on the team.
2. Amend created a new commit with a new SHA.
3. git push --force overwrote the remote's main branch pointer.
4. Six engineers had already pulled the old commit. Their local main now had a commit (the old one) that no longer existed on the remote.
5. Two of those six had created new commits on top of the old SHA. Those commits' parent pointers referenced a non-existent commit.
6. The CI system's webhook payload contained the old SHA. The deployment tracker could not resolve it.
Fix
1. Identified the old commit SHA from the CI webhook logs (9f2c4a1).
2. The team lead force-pushed the old commit back to main: git reset --hard 9f2c4a1 && git push origin main --force-with-lease.
3. For the two engineers with orphaned commits: used git rebase --onto main <old-parent> <branch-tip> to replay their commits on top of the restored main.
4. For the four engineers with only diverged local main: git fetch origin && git reset --hard origin/main.
5. Re-ran CI manually for the restored commit SHA.
6. Added branch protection rules requiring PR reviews for main — preventing direct pushes entirely.
Key lesson
Never amend on main, develop, or release branches. These are pulled by every developer. Amending here breaks everyone simultaneously.
git push --force without --force-with-lease destroyed the safety check that would have prevented this. --force-with-lease would have been irrelevant here (nobody else pushed), but the habit of using --force normalizes dangerous behavior.
Branch protection rules that prevent direct pushes to main would have prevented this entirely. The fix wasn't just technical — it was a process gap.
The CI webhook referencing the old SHA is an often-overlooked blast radius. Downstream systems that record commit SHAs will break when those SHAs are rewritten.
Production debug guideSystematic recovery paths for history rewrite failures.5 entries
Symptom · 01
git push rejected with 'Updates were rejected because the tip of your current branch is behind its remote counterpart'
→
Fix
1. This means the remote has commits your local branch doesn't have.
2. Run git fetch origin to see what the remote has.
3. Run git log HEAD..origin/<branch> --oneline to see the remote-only commits.
4. If you amended: rebase your amended commit on top of the remote's commits with git rebase origin/<branch>.
5. Then force-push with --force-with-lease.
6. If you did NOT amend: pull and merge instead of force-pushing.
Symptom · 02
Teammate reports 'Your branch and origin/<branch> have diverged' after your force-push
→
Fix
1. Your force-push rewrote history that the teammate had already pulled.
2. The teammate needs to reset their local branch to match the remote: git fetch origin && git reset --hard origin/<branch>.
3. If the teammate had local commits on top of the old SHA, those commits are now orphaned.
4. Have them run git reflog to find their orphaned commits, then cherry-pick or rebase them onto the new branch tip.
5. Prevention: never force-push to branches others pull from without coordinating first.
Symptom · 03
CI build references a commit SHA that returns 404 or 'object not found'
→
Fix
1. The commit SHA in the CI webhook payload was rewritten by an amend + force-push.
2. Check which branch the build was triggered for. Run git log --oneline on that branch to see the current SHAs.
3. If the old SHA is not in the log, check git reflog to confirm it was overwritten.
4. Re-trigger the CI build manually for the current HEAD SHA.
5. If using GitHub Actions with concurrency groups, the old build should have been auto-cancelled. Verify in the Actions tab.
Symptom · 04
You amended and now realize it was the wrong commit or wrong content
→
Fix
1. Immediately run git show ORIG_HEAD to see the pre-amend commit.
2. If ORIG_HEAD is correct: git reset --hard ORIG_HEAD to undo the amend.
3. If ORIG_HEAD was overwritten by subsequent Git commands: git reflog to find the original commit hash.
4. If you already force-pushed the bad amend: git reset --hard <original-hash> && git push origin <branch> --force-with-lease to restore the remote.
5. If others pulled the bad amend: coordinate with them to reset their local branches.
Symptom · 05
Interactive rebase produced conflicts during git rebase --continue after amending an older commit
→
Fix
1. The amended commit changed content that later commits depend on.
2. Open the conflicted files. Git marks conflicts with <<<<<<< / ======= / >>>>>>>.
3. Resolve the conflicts manually — decide which version to keep.
4. git add <resolved-files> && git rebase --continue.
5. If too many conflicts: git rebase --abort to return to the pre-rebase state. Consider creating a fixup commit instead of rewriting history.
★ Git Amend Triage Cheat SheetFast recovery for amend-related production incidents.
'Your branch and origin/<branch> have diverged' after teammate's force-push−
Immediate action
Reset local branch to match remote.
Commands
git fetch origin
git reset --hard origin/<branch>
Fix now
If you had local commits: git reflog to find them, then cherry-pick onto the reset branch.
git push rejected: 'non-fast-forward' or 'stale info'+
Immediate action
Remote has commits you don't have. Don't --force blindly.
git commit --amend creates a new commit object with a new SHA
it does NOT modify the existing commit. The old commit is orphaned and recoverable via reflog for 90 days.
2
Amend only commits that haven't been pushed to a shared branch. If the commit is on main, develop, or a branch teammates pull from, use git revert or git commit --fixup instead.
3
Always use --force-with-lease instead of --force. --force-with-lease checks that nobody else has pushed to the branch since your last fetch. --force overwrites blindly.
4
git commit --fixup <sha> is the safe alternative to amend on shared branches. It creates a fixup commit that gets auto-squashed during git rebase -i --autosquash.
5
ORIG_HEAD is your undo button
Git saves it before every amend, rebase, and reset. git reset --hard ORIG_HEAD immediately undoes the last amend. But it's overwritten by subsequent Git commands.
6
git reflog records every HEAD movement for 90 days. If ORIG_HEAD is gone, reflog is your last resort to find and recover the original commit.
7
Interactive rebase (git rebase -i) lets you amend older commits by marking them with 'edit'. But it rewrites every commit from the edit point onward
only safe on local-only branches.
8
The forgotten-file pattern (git add <file> && git commit --amend --no-edit) is the most common amend use case. Memorize it. You'll use it daily.
9
After amending a pushed commit, verify your CI triggered a new build for the new SHA. Old builds referencing the old SHA may still be running.
10
Set up git aliases (amend, pushf, undoamend) to speed up your daily workflow and prevent --force accidents.
11
Never amend on main, develop, or release branches. These are pulled by every developer. Amending here breaks everyone's local history simultaneously.
12
The mental model
amend is for 'I made a mistake in the last 30 seconds.' If more than 30 seconds have passed — or if anyone else has seen the commit — create a new commit instead.
INTERVIEW PREP · PRACTICE MODE
Interview Questions on This Topic
FAQ · 9 QUESTIONS
Frequently Asked Questions
01
Does git commit --amend create a new commit?
Yes. Amend creates an entirely new commit object with a new SHA hash. The old commit is orphaned — no branch points to it — but it still exists in Git's object database for 90 days and is recoverable via git reflog. This is why amending pushed commits requires a force push: the remote branch still points to the old SHA while your local branch points to the new one.
Was this helpful?
02
How do I amend a commit message without opening an editor?
Use git commit --amend -m 'your new message'. The -m flag provides the message inline so no editor is opened. For a one-character alias, add git config --global alias.amend 'commit --amend --no-edit' to your gitconfig.
Was this helpful?
03
How do I add a forgotten file to the last commit?
Stage the file with git add <filename>, then run git commit --amend --no-edit. The --no-edit flag preserves the existing commit message. This is the most common amend use case — you'll run this command multiple times a week.
Was this helpful?
04
What is the difference between --force and --force-with-lease?
--force overwrites the remote branch with your local branch, no questions asked. If a teammate pushed commits since your last fetch, --force silently destroys those commits. --force-with-lease does the same overwrite but first checks that the remote branch matches your local tracking reference (origin/branch-name). If someone else pushed since you last fetched, --force-with-lease rejects the push. Always use --force-with-lease.
Was this helpful?
05
How do I amend a commit that's not the most recent one?
Use git rebase -i HEAD~N (where N is the number of commits back). In the editor, change 'pick' to 'edit' on the commit you want to modify. Git pauses at that commit. Make your changes, stage them, run git commit --amend, then run git rebase --continue. This rewrites every commit from the edit point onward — only safe on local-only branches.
Was this helpful?
06
How do I undo a bad amend?
If you just amended: git reset --hard ORIG_HEAD restores the original commit. If you ran other Git commands since the amend: git reflog shows every HEAD movement for 90 days — find the original commit hash and reset to it. If you amended and force-pushed: either force-push the original commit (git reset --hard ORIG_HEAD && git push --force-with-lease) or create a revert commit (git revert HEAD) if others have already pulled the amended commit.
Was this helpful?
07
Can I amend a commit on a shared branch like main?
Technically yes, but you should never do it. Amending on main rewrites history that every developer on the team has in their local repository. Their next pull will show 'Your branch and 'origin/main' have diverged' and they'll need to reset their local main. Use git revert to undo changes on main, or git commit --fixup to create a fixup commit that gets squashed during the next rebase.
Was this helpful?
08
What happens to my CI build when I amend and force-push?
Most CI systems (GitHub Actions, GitLab CI) detect the new push and trigger a new build with the new SHA. The old build may still be running. GitHub Actions auto-cancels the old build if you configure concurrency groups. Jenkins does not auto-cancel — you need to manually abort the old build. Always verify your CI dashboard shows a build for the new SHA, not the old one.
Was this helpful?
09
What is a fixup commit and when should I use it instead of amend?
git commit --fixup <target-sha> creates a commit with the message 'fixup! <original message>'. During git rebase -i --autosquash, Git automatically places the fixup commit next to its target and squashes it. Use fixup commits when you can't amend (because the target commit is on a shared branch). The fixup is visible in the PR history, and it gets cleaned up automatically during the final rebase before merge.