Skip to main content
RunBook Academy

Git, CI/CD & GitOpsXVIII · Git RecoveryRecovery

Recovering an amended commit — the previous commit is in the reflog

Advanced⏱ ~22 mingit

What you'll learn

  • Apply the reflog recipe to recover a commit that has been amended with `git commit --amend`
  • Identify the original OID at `HEAD@{1}` as the recovery target and recognise that it becomes a dangling commit after the amend
  • Distinguish `git cherry-pick <oid>` (replay the original commit on the current branch) from `git branch <name> <oid>` (preserve the original commit as a named branch)
  • Recognise the 90-day retention boundary on the reflog entry and the recovery paths past the window

Prerequisites

Verified against Git 2.55.x teaching target; 2.40+ minimum · GitHub Actions continuous service; Aug 2026 documentation baseline · Argo CD v3.5.x teaching target; v3.0+ minimum · Flux v2.9.x · Sigstore Cosign v3.1.x · SLSA v1.2 · OCI Distribution Specification v1.1 · Git LFS v3.7.1 · Kubernetes (cross-course target) 1.36.x

Not yet marked complete on this device.

A git commit --amend is the most common recovery scenario because the amend is the most common history-rewriting command. The amend rewrites the current commit’s OID, the branch pointer advances to the new OID, and the original OID becomes a dangling commit in the object store. The dangling commit is reachable via the reflog at HEAD@{1} and is recoverable for the 90-day default window.

The surprise is what the original OID becomes. Engineers often believe the amend “edits” the commit; the data model shows otherwise. The amend creates a new commit and orphans the original. The original is gone from the branch view but preserved in the object store.

What the amend actually does

git commit --amend is shorthand for three operations: keep the current commit’s parent, keep the current commit’s author (unless --reset-author is passed), and replace the commit’s tree with the new index. The result is a new commit with the same parent and author but a new tree and therefore a new SHA. The branch pointer is moved to the new commit; the original commit is no longer reachable from the branch.

flowchart LR
    A["original commit (8a3f9d2)"] --> B["parent commit"]
    A --> C["amend: new tree from index"]
    C --> D["new commit (a1b2c3d)"]
    B --> D
    A -. "after amend" .-> E["dangling in object store"]
    A -. "reflog" .-> F["HEAD@{1} = 8a3f9d2"]
    D --> G["branch tip"]
    E -->|"git cherry-pick"| H["replay on current branch"]
    E -->|"git branch name"| I["preserve as branch"]

The fork in the diagram is the recovery path. The original commit is preserved in two places: the object store (as a dangling commit) and the reflog (as HEAD@{1}). The recovery is to read the reflog, copy the OID, and either cherry-pick or branch from it.

The recovery recipe

# Step 1: locate the original commit before the amend
git reflog -5
# a1b2c3d HEAD@{0}: commit (amend): rotate IAM credentials            <-- the amend
# 8a3f9d2 HEAD@{1}: commit: rotate IAM credentials                    <-- the original
# 3e4f5a6 HEAD@{2}: commit: break out IAM role into module

# Step 2: copy the OID to a durable location
ORIGINAL_OID=$(git reflog -1 HEAD@{1} | awk '{print $1}')
echo "Original commit: $ORIGINAL_OID"

# Step 3: verify the OID is the right one
git show --stat $ORIGINAL_OID
# confirms the commit before the amend

# Step 4: recover via cherry-pick or branch
git cherry-pick $ORIGINAL_OID
# or
git branch feature/iam-rotation-pre-amend $ORIGINAL_OID

The recipe has the same four-step shape as the other recovery recipes. The first command is git reflog. The recovery command is git cherry-pick or git branch.

git cherry-pick versus git branch

The two recovery commands have different shapes:

flowchart LR
    A["original OID (8a3f9d2)"] --> B["git cherry-pick 8a3f9d2"]
    A --> C["git branch name 8a3f9d2"]
    B --> D["new commit on current branch"]
    B --> E["tree changes replayed"]
    B --> F["author and message preserved"]
    C --> G["branch at 8a3f9d2"]
    C --> H["original commit reachable"]
    C --> I["no new commit created"]
  • git cherry-pick &lt;oid&gt; replays the tree changes of the original commit on the current branch as a new commit. The parent is the current branch tip; the tree is the original commit’s tree; the author and message are the original commit’s author and message. The cherry-pick is the additive recovery: the original commit is preserved semantically on the current branch.
  • git branch &lt;name&gt; &lt;oid&gt; creates a new branch pointing at the original commit. The original commit is reachable via the named branch; no new commit is created. The branch is the preservation recovery: the original commit is preserved as a recoverable reference.

The branch form is preferred when the engineer wants to preserve the original commit for comparison or audit. The cherry-pick form is preferred when the engineer wants to fold the original commit back into the working branch.

The case where the engineer wants both branches

The most common production scenario is: the amend was a mistake (the engineer amended the wrong commit or amended with the wrong changes), and the engineer wants both the amended commit and the original commit available. The recovery is the branch form:

# Preserve the original as a branch
git branch feature/iam-rotation-pre-amend $ORIGINAL_OID

# The current branch is unchanged; the amended commit is the tip
git log --oneline -3
# a1b2c3d HEAD@{0}: commit (amend): rotate IAM credentials
# 7e8f9a0 HEAD@{1}: commit: bump ansible collection version
# 3e4f5a6 HEAD@{2}: commit: break out IAM role into module

# The original is on the recovery branch
git log --oneline -3 feature/iam-rotation-pre-amend
# 8a3f9d2 commit: rotate IAM credentials
# 7e8f9a0 commit: bump ansible collection version
# 3e4f5a6 commit: break out IAM role into module

The branches are now divergent. The engineer can compare the two commits (git diff feature/iam-rotation feature/iam-rotation-pre-amend), merge one into the other, or pick commits from one.

Recovery past the reflog window

Past the 90-day reflog window, the original commit is an unreachable object in the object store. The recovery paths are:

  1. Another clone. Any clone that fetched the branch before the amend may still have the OID in its reflog.
  2. Object store scan. git fsck --unreachable --no-reflogs on a clone that has the objects lists the dangling commit. The recovery is git branch recovered &lt;oid&gt;.
  3. Remote refs. The commit may be referenced by the forge’s PR ref, an older CI artifact, or a release tag.
  4. Backup. The bytes may be in a CI artifact store or a configuration backup.

The order of preference is the same as for the deleted branch. The reflog is the first path; the durable backup is the last.

Production discipline

  1. Never amend a pushed commit without coordinating with the team. The amend rewrites the public OID; any clone that fetched the branch has the old OID and will produce a non-fast-forward push or a forced merge on the next fetch. The team needs to be notified before the amend.
  2. Always copy the OID to a durable location before the amend. The OID at HEAD@{1} before the amend is the recovery target; copying it to a PR comment means the recovery is possible even if the reflog expires.
  3. Prefer git branch &lt;name&gt; &lt;oid&gt; over git cherry-pick &lt;oid&gt; for recovery. The branch form preserves the original commit as a named branch; the cherry-pick form creates a new commit on the current branch. The branch form is recoverable past the reflog window via the named branch; the cherry-pick form is not.
  4. Document the recovery in the runbook. The recipe above is a one-page runbook entry; paste it into the team’s on-call documentation.

Cross-course references

  • Git, CI/CD & GitOps — Part XII (Amend) — the amend mechanics; the prerequisite for this lesson.
  • Git, CI/CD & GitOps — Part XVII (Reflog) — the reflog’s location and scope; the prerequisite for the reading step.
  • GitOps with Argo CD — Part VI (MergeStrategies) — the GitOps controller’s commit lifecycle; the controller does not amend commits because the manifests are the source of truth, but the recovery recipe is the same when an operator amends locally.

Quiz

Knowledge check · 4 questions

  1. Q1. An engineer has just run `git commit --amend` and realised the original commit should have been preserved. The branch has not been pushed. What is the precise sequence to recover the original commit?

  2. Q2. After `git commit --amend`, the original commit's OID becomes a dangling commit in the object store and is reachable via the reflog at `HEAD@{1}` for the 90-day default retention window.

  3. Q3. What is the difference between `git cherry-pick &lt;oid&gt;` and `git branch &lt;name&gt; &lt;oid&gt;` for recovering an amended commit?

  4. Q4. Walk the recovery for an engineer who has amended the wrong commit and needs both the original and the amend available.

    An engineer on `feature/iam-rotation` has run `git commit --amend` because they wanted to add a forgotten Terraform change to the previous commit. The amend succeeded but the engineer then realised the original commit had the correct message and the amend introduced a typo. The branch has not been pushed. The engineer needs to preserve the original commit for comparison and to create a follow-up commit that fixes the typo.

Passing score: 75%. Answers are checked in this browser.