Three release engineers compare staged, working-tree, and committed Git file versions during a source review.

Which File Version Did git commit --only Actually Save?

Tue, Oct 20, 2026

A commit command succeeds, the intended filename appears in its summary, and the developer moves on. Yet the new commit contains an edit made after review. Another change that the reviewer approved may still be staged, waiting for a later commit. The command completed its requested operation; the release process has lost the connection between approved content and recorded content.

This situation becomes visible when three versions of one tracked file coexist: A in HEAD, reviewed B in the index, and unreviewed C on disk. Add a second file with reviewed U1 staged over U0, and a filename alone cannot describe the intended release. The question is which complete tree the reviewer approved and whether the constructed commit contains it.

This playbook sets one explicit contract: accept the complete reviewed index tree, B/U1. It compares ordinary commit, a commit naming a path, explicit --only, --include, and --all in five disposable repositories. Each candidate is checked against an approval captured before construction, with readable B evidence retained outside its repository. The resulting ledger accounts for both committed files, both index entries, and the remaining working edit.

The outcome is a release decision supported by content evidence. You will distinguish an acceptable reviewed tree from an unreviewed tree, identify when the review selection needs repair, and recognize when missing evidence requires a rerun. A successful command or an empty status display cannot make that decision for you.

Name the reviewed artifact before choosing a commit command

Write the approval as a mapping: payload.txt must contain reviewed-B\n, and other.txt must contain other-U1\n. These are the complete contents of the two regular files in the approved laboratory tree. The final newline belongs to each value. Approval covers the full candidate, including the decision to include U1, rather than merely acknowledging that someone inspected the target filename.

The baseline is A/U0. The contributor stages B/U1, then edits the working payload to C. That later edit is available for continued development, but it has no approval under this contract. The review record must remain anchored to B/U1 even if a convenient invocation constructs something else.

For the main gate, require equality between the committed tree and the earlier approved index tree, together with intact saved B evidence. The tree comparison addresses the complete selected snapshot; the readable copy lets a reviewer examine the disputed bytes without depending on the current index. Neither a file count nor a commit message provides those two forms of evidence.

A team can legitimately want a different unit of work. Selecting only reviewed B while retaining U0 would define B/U0 as a different candidate. That choice requires its own review. It does not retroactively narrow an approval for B/U1. Make the scope explicit before choosing command options, so the release owner can evaluate a stable requirement rather than infer intent from whatever Git produced.

Distinguish HEAD, the index and the working file

The first reproducible setup assumes a local linear repository, two tracked regular files, and one sequential driver controlling all changes. The supplied October 20, 2026 preflight used Git 2.47.3 and CPython 3.13.5. A separate recorded execution during preparation used Git 2.51.1 and CPython 3.12.14; its matching results are separate observations, not a claim that the supplied runtime was rerun.

HEAD identifies the baseline committed state. The index holds the selected candidate. The working file holds the contributor's current editable bytes. Git's staging documentation explains that adding a file records its content at that time; subsequent edits require another staging action to replace that content in the index.

Our setup therefore has two independent differences. For the payload, A differs from B and B differs from C. For the other file, U0 differs from U1, while index and disk agree. Both paths remain tracked throughout. The related discussion of Git tracking and index membership addresses a separate question from the byte provenance tested here.

Use three different byte strings, not three labels for one value

The actual inputs are b"head-A\n", b"reviewed-B\n", and b"unreviewed-C\n". Distinct literals make source selection observable. If B and C were identical, a command choosing the working file could appear to preserve the staged version even though the experiment had never distinguished those sources.

At the setup boundary inside a laboratory repository, these read-only commands inspect three separate locations:

git show HEAD:payload.txt
git show :payload.txt
python -c 'from pathlib import Path; print(repr(Path("payload.txt").read_bytes()))'

Git show displays blob contents, while the object-name syntax distinguishes a path in a named revision from an index path. The driver captures Git output as bytes and reads the working payload directly with read_bytes(). Its comparisons retain the terminal newline; the shortened labels printed in JSON are presentation conveniences.

Use the comparisons for different questions. The Git 2.47.0 diff manual describes git diff --cached HEAD for index versus HEAD and ordinary git diff for working tree versus index. Here, the first boundary contains A-to-B and U0-to-U1; the second contains B-to-C. Reviewing only the working file collapses those separate decisions into one view and leaves the staged approval unspecified.

The second file is a selection control, not incidental decoration. Its index and disk agree, so its later omission cannot be explained by choosing between two different current versions of that file. U0 in a candidate reveals that the staged update was withheld. That observation isolates whole-tree scope from the payload's B-versus-C source question.

Pin an isolated repository and remove unrelated configuration

Save the complete driver below as git_probe.py in an owned scratch directory. Use the supplied command from that directory:

python git_probe.py > git-results.json

The script creates its own temporary root and five new repositories beneath it. It accepts no repository argument and needs no production input. Never transplant its setup commands into a real checkout. The small files are synthetic release evidence, and their fixed names and literal contents are part of the experiment. Replacing them with customer data would introduce risk without improving this test.

Each repository receives a baseline commit and one candidate commit. A fresh repository per case prevents an earlier candidate from changing the starting index for the next option. The intended variable is the candidate command's selection mode. All five cases begin with the same A/B/C and U0/U1 relationships, so differences in their results can be attributed within that controlled setup.

The wrapper uses a temporary home directory, disables system and global Git configuration, and removes inherited GIT_* environment entries before establishing its own settings. Every Git invocation receives the fixture identity and an empty hooks directory. Automatic CRLF conversion and commit signing are disabled. These controls reduce unrelated influences; they do not qualify every possible machine configuration or replace inspection of the installed executable.

Author and committer dates are fixed at January 1, 2000. Those values are fixture inputs, not the experiment's execution date. The report records the actual Git and Python versions. The source fingerprint identifies the driver that ran, while the command log records arguments, repository paths, return codes, stdout, and stderr. Preserve them together so another engineer can distinguish a changed experiment from a changed result.

The references combine living Git manuals with the selected 2.47.0 diff manual. Those documentation branches differ from the executed builds. They establish the relevant contracts alongside measured observations; their version labels are neither a claim about the newest release nor a launch date for these options.

This discussion assumes everyday Git staging foundations. The additional discipline is controlling and documenting the content boundary under investigation.

After a successful run, inspect root in git-results.json. The full command log is in evidence.json under that root, beside the saved reviewed payload files. The root remains available for inspection; it is not automatically deleted. Retain the evidence needed by your workflow before removing only that known, owned laboratory. A temporary location is useful for isolation but is not an evidence-retention policy.

Build the full reproducible selection harness

The driver has two independent expectations. The cases table declares the literal committed bytes expected from each selection mode. Separately, approved_tree records the complete B/U1 index before the candidate is constructed. The first check establishes that the diagnostic behaved as designed. The second decides whether its output satisfies the release contract. Keeping them separate allows a deliberately unacceptable candidate to pass its diagnostic assertions.

Read the setup in order. The initial commit records A/U0. Two file writes and git add prepare B/U1. Another write changes only the working payload to C. The driver verifies A in HEAD and B in the index, preserves B outside the repository, and records the approved tree and initial status. Only then does it invoke the candidate command.

from pathlib import Path
import hashlib
import json
import os
import platform
import subprocess
import tempfile
def require(ok, message):
    if not ok:
        raise AssertionError(message)
root = Path(tempfile.mkdtemp(prefix="commit-selection-"))
(root / "home").mkdir()
(root / "hooks").mkdir()
env = {k: v for k, v in os.environ.items() if not k.startswith("GIT_")}
env.update(HOME=str(root / "home"), GIT_CONFIG_NOSYSTEM="1",
           GIT_CONFIG_GLOBAL=os.devnull, LC_ALL="C",
           GIT_AUTHOR_DATE="2000-01-01T00:00:00+0000",
           GIT_COMMITTER_DATE="2000-01-01T00:00:00+0000")
commands = []
def git(repo, *args):
    argv = ["git", "-c", "user.name=Fixture", "-c", "[email protected]",
            "-c", "commit.gpgsign=false", "-c", "core.autocrlf=false",
            "-c", f"core.hooksPath={root / 'hooks'}", *args]
    r = subprocess.run(argv, cwd=repo, env=env, capture_output=True, timeout=15)
    commands.append({"cwd": str(repo), "args": list(args), "rc": r.returncode,
                     "stdout": r.stdout.decode(), "stderr": r.stderr.decode()})
    require(r.returncode == 0, f"git failed: {args}: {r.stderr!r}")
    return r.stdout
cases = [
    ("G1", [], (b"reviewed-B\n", b"other-U1\n"), b"reviewed-B\n"),
    ("G2", ["--", "payload.txt"], (b"unreviewed-C\n", b"other-U0\n"), b"unreviewed-C\n"),
    ("G3", ["--only", "--", "payload.txt"], (b"unreviewed-C\n", b"other-U0\n"), b"unreviewed-C\n"),
    ("G4", ["--include", "--", "payload.txt"], (b"unreviewed-C\n", b"other-U1\n"), b"unreviewed-C\n"),
    ("G5", ["--all"], (b"unreviewed-C\n", b"other-U1\n"), b"unreviewed-C\n"),
]
rows = []
for case, options, expected, expected_index_payload in cases:
    repo = root / case
    repo.mkdir()
    git(repo, "init", "-q")
    payload, other = repo / "payload.txt", repo / "other.txt"
    payload.write_bytes(b"head-A\n")
    other.write_bytes(b"other-U0\n")
    git(repo, "add", "--", "payload.txt", "other.txt")
    git(repo, "commit", "-qm", "baseline")
    payload.write_bytes(b"reviewed-B\n")
    other.write_bytes(b"other-U1\n")
    git(repo, "add", "--", "payload.txt", "other.txt")
    payload.write_bytes(b"unreviewed-C\n")
    require(git(repo, "show", "HEAD:payload.txt") == b"head-A\n", "HEAD baseline")
    require(git(repo, "show", ":payload.txt") == b"reviewed-B\n", "index baseline")
    saved = root / f"{case}-reviewed-payload.txt"
    saved.write_bytes(git(repo, "show", ":payload.txt"))
    approved_tree = git(repo, "write-tree").decode().strip()
    before_status = git(repo, "status", "--porcelain=v1").decode()
    git(repo, "commit", "-qm", "candidate", *options)
    actual = (git(repo, "show", "HEAD:payload.txt"), git(repo, "show", "HEAD:other.txt"))
    require(actual == expected, f"{case}: committed bytes")
    require(git(repo, "show", ":payload.txt") == expected_index_payload, "post index payload")
    require(git(repo, "show", ":other.txt") == b"other-U1\n", "post index other")
    require(payload.read_bytes() == b"unreviewed-C\n", "working bytes changed")
    require(saved.read_bytes() == b"reviewed-B\n", "saved review evidence changed")
    actual_tree = git(repo, "rev-parse", "HEAD^{tree}").decode().strip()
    accepted = actual_tree == approved_tree
    require(accepted == (case == "G1"), "reviewed-tree gate")
    rows.append({"case": case, "options": options, "committed": [b.decode().strip() for b in actual],
                 "approved_tree": approved_tree, "actual_tree": actual_tree,
                 "post_index_payload": expected_index_payload.decode().strip(),
                 "before_status": before_status,
                 "after_status": git(repo, "status", "--porcelain=v1").decode(),
                 "decision": "ACCEPT_REVIEWED_TREE" if accepted else "HOLD_UNREVIEWED_TREE"})
report = {"python": platform.python_version(), "git": git(root, "--version").decode().strip(),
          "source_sha256": hashlib.sha256(Path(__file__).read_bytes()).hexdigest(),
          "root": str(root), "rows": rows, "commands": commands, "status": "PASS_5_CASES"}
(root / "evidence.json").write_text(json.dumps(report, indent=2))
print(json.dumps({k: v for k, v in report.items() if k != "commands"}, indent=2))

Save the approved index before construction mutates it

The supplied code writes C before saving B and calling write-tree. Writing C changes only the worktree; without another staging action, the index still represents B/U1. Evidence is captured before the candidate command can replace an index entry. The saved file therefore preserves staged B independently of the current working payload.

Git write-tree creates a tree object from the current, fully merged index. Capturing its identifier establishes a comparison target here. It does not create a permanent named recovery reference. The readable G1-reviewed-payload.txt through G5-reviewed-payload.txt files supply separate B evidence, but they also remain under the temporary root until retained elsewhere.

After construction, the driver reads both committed blobs and both index entries. It checks that disk still contains C and the saved file still contains B. The compact row's post_index_payload is an expected label recorded only after an independent index read matched it. U1 is likewise asserted in the other index entry, although the compact row has no separate field for that value.

Every Git subprocess has a fifteen-second timeout and must return zero. The explicit require helper raises on a mismatch. The complete evidence.json is written only after the loop succeeds; a timeout or assertion failure must not be interpreted as a completed five-case report. Keep failure output and investigate the first unmet condition before rerunning.

Compare the reported source fingerprint with the recorded digest of the supplied driver. This identifies the experiment's source; it is separate from the commit-content oracle. Likewise, the JSON's stripped display strings are not newline-sensitive comparisons. Those distinctions keep the readable report convenient without weakening the actual byte assertions or silently changing the approved artifact.

The unchanged supplied source has SHA-256 58cf27d00ae9da18618f0dede08b5c54f888930a26d01bbe45adee23333331cb. Treat a different fingerprint as a prompt to inspect the source difference before comparing runs. A harmless formatting edit and a changed expected literal have different consequences, but both need an accurate source identity in the retained report.

Use a plain commit as the reviewed-index control

G1 invokes ordinary commit without a selection-changing option or a path argument. The git-commit documentation identifies the index as the content source for this ordinary operation. In the experiment, that source contains B/U1, although the working payload already contains C.

The measured candidate contains reviewed-B\n and other-U1\n. Its tree identifier equals the approval captured earlier. The saved B file is intact, and both index entries still match the reviewed values. This is the positive control for ACCEPT_REVIEWED_TREE: the mechanism selected for construction delivered the complete artifact that the gate was asked to approve.

The working payload remains C. Consequently, the recorded post-commit status is " M payload.txt\n": the payload has an unstaged working change. Nothing in this outcome requires treating C as lost, defective, or accidentally left behind. It is the known continuation of development after staging. The release gate approves B/U1 while preserving C for its owner to evaluate separately.

This control prevents two mistaken repairs. First, do not stage the remaining C merely to make the status display empty. That would change the candidate for subsequent work and would require its own review. Second, do not replace the working file with B simply because B was committed. The approved commit and a developer's continuing edit can coexist; preserving their separation is the purpose of the setup.

Read all the evidence before calling G1 a pass. Seeing B in the committed payload is necessary but insufficient because U1 is also part of the contract. Seeing B/U1 in the index afterwards is useful reconciliation evidence but cannot substitute for reading the commit. In this case, both checks agree, and the earlier approved tree provides the fixed comparison target.

The distinction also helps a reviewer explain the result to another engineer. “The commit matches approval, and C remains an unstaged edit” is an actionable handoff. “The repository is dirty” omits the approved artifact entirely. Record the accepted tree separately from pending work so the contributor can continue without the release owner accidentally broadening what has been authorized. G1 establishes that distinction under the laboratory assumptions; it is not a blanket acceptance rule for every ordinary commit.

G1 also tests the repaired selection procedure from the same initial conditions: use ordinary commit for the approved B/U1 index. Its result is compared with the independently retained approval. This supports the command choice before construction; it does not demonstrate an in-place correction of the already constructed G2–G5 histories.

Pass a path and observe the working-tree version

G2 adds -- payload.txt to the candidate invocation. G3 adds explicit --only -- payload.txt. Git documents path-limited commit and --only as selecting the named paths' current working-tree contents while withholding unrelated staged changes. Naming paths normally supplies this mode implicitly; the explicit option makes the intent visible.

Both measured commits contain C/U0. The target payload changed to C, and the other file retained its baseline U0 in the new commit. These are two separate failures against B/U1: the target version crossed the wrong review boundary, and the second approved change did not cross the commit boundary at all. A reviewer inspecting only payload.txt would miss the latter.

The index left behind is C/U1. The driver reads :payload.txt after construction and finds C, so B is no longer the target's current staged version. It independently reads :other.txt and confirms that U1 remains staged. The observed "M  other.txt\n" status reconciles that remaining index change with U0 in the new HEAD.

This matters operationally because rerunning an ordinary commit immediately afterwards would start from a different index than the original review. The existence of pending staged work does not imply that the index still represents B/U1. Preserve the earlier approval and inspect both current entries before deciding what a later commit should contain.

Why --only is not a selector for staged hunks

The word “only” limits the selected paths in this invocation; it does not identify an earlier staged payload as the source. In G3, the filename is correct and the target is already tracked, yet the candidate contains C. Treating --only as “commit only the staged changes” misstates the selection contract.

Path-limited commit remains valid when the intended selection is the current working version of the named file and unrelated staged work should wait. Git did not corrupt B into C. The contributor supplied C on disk and chose an operation whose documented input includes it. The error belongs to substituting that operation for an approval of B/U1.

The -- separator also supplies no review protection. It ends option parsing; it does not preserve a previously staged version. In both path-limited cases, the release decision stays HOLD_UNREVIEWED_TREE until the selected artifact is deliberately reconciled with an explicit approval.

Compare --include and --all without changing the oracle

G4 invokes --include -- payload.txt; G5 invokes --all. Git's descriptions of these options distinguish staging named working-tree contents alongside existing staged work from staging tracked modifications and deletions more broadly. Neither option promises to preserve an older reviewed index unchanged.

Both measured candidates contain C/U1. U1 reaches the commit, which makes these rows different from G2/G3, but the payload is still the unreviewed C. Both index entries match the new commit, and the working files match those entries. The recorded status is therefore empty in each case. The complete tree still differs from the approved B/U1 tree.

G4 isolates the named-path update. The other file was already staged at U1, and no later working edit changed it. Including the working payload replaces the B selection with C while retaining the second staged change. The result looks complete if a reviewer asks only whether both changed files were included. Byte approval asks the additional question that catches this row: included with which contents?

G5 reaches the same result through a broader selection rule. In this two-file setup there are no other tracked modifications for --all to collect. That restriction explains the matching outcomes without making the options interchangeable. Outside the laboratory, another tracked modification or deletion can expand the effect of --all; a review contract must account for that scope before adopting it.

Do not change the oracle because C/U1 seems like a useful combined change. A contributor may decide that C is better than B. That assessment can justify a new review, but it cannot supply the missing approval retrospectively. Record the original mismatch first, preserve the data, and then decide whether the desired repair restores B/U1 or creates a newly reviewed candidate.

The negative control is strongest when the command appears successful in ordinary workflow terms. G4 and G5 have successful return codes, include the second change, and leave no tracked difference in the status display. All three observations are compatible with a failed content gate. The experiment therefore tests whether the release procedure can reject a plausible but unapproved result, rather than merely recognize a command failure. Removing these rows would weaken the evidence precisely where a hurried handoff is most likely to rely on the wrong signal.

Reconcile the commit and the index left behind

The following ledger combines the supplied preflight outcomes with the independently matching recorded run. B, C, U0, and U1 refer to the newline-terminated literals defined earlier. The status cells preserve JSON escaping and leading spaces; an empty string means the recorded output was empty.

Case

Candidate selection

Commit payload / other

Index payload / other

Post-status string

Reviewed-tree decision

G1

Plain commit

B / U1

B / U1

" M payload.txt\n"

ACCEPT_REVIEWED_TREE

G2

-- payload.txt

C / U0

C / U1

"M  other.txt\n"

HOLD_UNREVIEWED_TREE

G3

--only -- payload.txt

C / U0

C / U1

"M  other.txt\n"

HOLD_UNREVIEWED_TREE

G4

--include -- payload.txt

C / U1

C / U1

""

HOLD_UNREVIEWED_TREE

G5

--all

C / U1

C / U1

""

HOLD_UNREVIEWED_TREE

The recorded preparation run reported "M  other.txt\nMM payload.txt\n" before every candidate. In Git's porcelain status notation, the first column describes the index difference and the second describes the working-tree difference in this non-conflicted setting. The driver records these strings; it does not contain a separate assertion over their exact text.

The recorded preparation execution produced these full source-tree identifiers:

  • Approved B/U1, also G1's actual tree: 16ab9a9269b559908b1311b46b159388b620a0dc.

  • G2/G3's actual C/U0 tree: a7f558960894252fdd646875400a68e6dcbad920.

  • G4/G5's actual C/U1 tree: 97143557958cd8823df62b22f5b80c066735730b.

These values are observations from that recorded run, not identifiers supplied with the earlier preflight. The report fields approved_tree and actual_tree are the authoritative pairing for each execution. Do not replace another run's measured approval with a copied identifier merely because the sample files have familiar names.

The supplied preflight reported successful return codes for every recorded Git command. The preparation run separately recorded 81 Git commands, all returning zero. That agreement supports the reproducibility of the diagnostic sequence; the accept-or-hold result still comes from comparing the selected contents with the previously approved tree.

Reconcile the residual data independently. Disk retains C in all five cases. The saved external file retains B in all five. The index retains U1 in all five, including the two commits that excluded it. Those relationships explain where pending work remains and where the current index no longer preserves the original reviewed selection.

A clean status can accompany an unreviewed tree

G4 and G5 demonstrate a specific limitation: agreement between current HEAD, index, and working files does not establish agreement with an earlier approval. The reviewed tree is a separate comparison endpoint. Clean status is useful evidence about current differences, but it contains no independent decision about whether C was reviewed.

Keep the status check because it explains what work remains. Pair it with the tree gate because that answers what crossed the approval boundary. The preparation run's PASS_5_CASES confirms that all five diagnostic expectations held, including four intentional release rejections. It is not a count of five acceptable commits.

Choose a deliberate strategy for staged-only subsets

A request to commit only reviewed B is incomplete until it states what the rest of the tree should contain. In this laboratory, keeping the second file unchanged would produce B/U0. Including its approved update would produce B/U1. Those are different complete candidates even though both contain the same target payload. The reviewer must know which one is being proposed.

Start by preserving the available states: B from the retained review evidence, C from the working file, and U1 from its current staged source. Record the baseline and current commit identities. Then declare the intended full mapping, including the choice for other.txt. This is a review-selection decision before it is a command-selection decision.

For a staged-only subset, require a deliberately prepared candidate index whose contents match that mapping. Review that candidate as a whole before commitment, and retain its tree identity as the new approval. Use your team's established preparation procedure in an owned workspace. This five-case driver does not implement or qualify an alternate-index configuration, and --only cannot replace that missing qualification.

The handoff should state which pending work remains outside the candidate and how its bytes are retained. “Only the payload” does not explain whether U1 was deliberately deferred or overlooked. An explicit B/U0 proposal does. If the owner instead confirms that U1 belongs with B, the original B/U1 contract remains the appropriate target; unnecessary subset construction would add work without changing the requirement.

Review the complete differences against the declared base, not just the target's name. The subset may be syntactically smaller while changing the meaning of the proposed update because its companion file remains at U0. The fixture assigns no application semantics to U0 or U1, so it cannot decide whether that pairing is functionally sound. That judgment belongs to the reviewer of the actual source.

After candidate construction, compare the new commit's tree with the explicitly approved subset tree. A match to B/U0 would satisfy that new contract, but it would still fail the principal B/U1 contract used throughout this article. Keep both decisions identifiable rather than overwriting the old evidence with a newly convenient expectation.

This is REPAIR_REVIEW_SELECTION: clarify and prepare the artifact the team actually wants, obtain approval for it, and then validate its construction. It is not permission to accept C because the command selected it. The repair succeeds only when the command, the declared candidate, and the retained review evidence refer to the same complete source tree.

Preserve evidence before repairing a local mistake

When the candidate fails the tree gate, stop its promotion and preserve evidence before another mutating command. Retain the earlier approval, readable B, the current working C, the current index contents for both paths, and the identity and contents of the constructed commit. Include the command invocation and the comparison result. These records explain both the release mismatch and the pending work that a correction must protect.

In G2/G3, the current index contains C/U1, so reading it now cannot recreate the original B/U1 approval by itself. In G4/G5, a clean status makes the missing approval even less visible. The saved B file is useful in both situations because it was selected before candidate construction replaced the index payload. Preserve C too; its lack of release approval does not make it disposable.

Determine whether the mistaken candidate is unpublished local work or has entered shared history. An unpublished correction must follow the team's local-history policy and preserve pending data. A published correction must follow its shared-history policy, including coordination over the replacement release candidate. This playbook provides no automatic history-rewrite sequence. The evidence needed to make either decision is the same; the authority and permitted remediation differ.

Once the team chooses a correction, construct the intended candidate from retained inputs and review it. Validate the result against the original approved B/U1 tree if that remains the contract, or against a newly approved tree if the intended source changed. Do not generate the expectation from the corrected commit itself and then call the comparison independent.

Do not depend on the reflog to recover every staged-only byte

Git's reflog documentation describes records of reference-tip updates. That is not a general archive of every value that previously occupied the index. The garbage-collection documentation also discusses unreachable objects created by earlier staging operations. A remembered tree identifier is therefore not an indefinite recovery promise.

This fixture retains one readable reviewed payload per case, a report, and the temporary repositories. It does not create an external readable backup of every index entry or a permanent reference for the approved tree. Production retention needs an approved durable evidence artifact containing the required bytes, identities, scope, and approval record, with a retention period and an accountable owner.

For a different incident involving saved pending state, use conflicted stash recovery as a separate workflow. The present correction depends on evidence retained for commit selection. Keep its repair bounded to preserving those inputs and qualifying the next explicitly intended candidate.

Carry the selected commit identity into packaging

After accepting a tree, record the exact commit selected for release and associate it with the review record. Tree equality answers whether the approved snapshot was constructed. The selected commit identity answers which historical object the downstream process should use. In this local linear fixture, the candidate is the new HEAD immediately after construction; a production handoff should preserve its full identity instead of relying on that moving description.

The driver records the approved and actual tree identifiers, but its compact report does not retain a full candidate commit identifier. Capture that additional evidence before handing a real candidate onward. A read-only command inside the selected laboratory repository is:

git rev-parse HEAD

Record the returned value as the selected commit, together with the corresponding HEAD^{tree} value and approval identity. This is an additional handoff step, not an assertion already present in the five-case JSON. The distinction matters when readers adapt a diagnostic report into a release record: retain what the report actually measures and add the fields that the next consumer needs.

For the recorded G1 result, the approved tree and actual tree are equal while the working payload still contains C. A packaging process that simply reads the contributor's current working directory could consume different bytes from the accepted commit. The next process must therefore declare and verify its source identity instead of inheriting an informal assurance that the contributor just committed the reviewed work.

Keep downstream qualification separate. Git archive file-selection validation addresses the later question of which files an export actually contains. This article stops at commit construction. It does not run a packaging command, inspect an archive, or establish that an eventual package contains every required source path.

A useful release record connects the reviewer-approved tree, the selected commit, and the eventual artifact identifier. Each transition needs its own evidence. The first relationship is tested here. The later relationship must be checked where the package is produced. Recording one identifier for each object prevents a successful source review from being treated as proof of an unobserved packaging result.

If another source change is introduced before packaging, evaluate its exact candidate and approval rather than reusing the earlier gate result by association. The test should fail closed on a missing or mismatched identity. That gives the release owner a concrete action: resolve the source selection and repeat the relevant comparison before promoting the resulting artifact.

Define a review-to-commit handoff that can be audited

Assign three responsibilities explicitly. The contributor prepares the candidate and identifies remaining work. The reviewer approves a complete tree and records the basis for that approval. The release owner verifies the constructed commit against the approved evidence before allowing downstream use. One person may hold multiple roles in a small team, but the decisions should remain distinguishable in the record.

The contributor's handoff should identify the repository, baseline commit, intended tree, target paths, and retained pending bytes. The review should identify its scope, approver, and retained content. The construction record should identify the command, runtime, resulting commit and tree, and remaining index/worktree differences. Together, these fields let a second engineer evaluate the claim without reconstructing intent from a chat message or a filename list.

Retain the evidence in a durable location approved by the team, with clear ownership and access appropriate to the source. Include readable contents or a retained source artifact sufficient to inspect the complete approved selection. A bare digest is useful for comparison but does not give a future reviewer the missing bytes. Establish retention before treating an expired workspace as recoverable evidence.

Define when the handoff must be reconsidered. A changed index tree invalidates a claim that the current index still equals the recorded approval. A changed working file invalidates any approval that depended on those working bytes being selected. It does not automatically invalidate a captured index approval: G1 deliberately demonstrates that distinction. State which source the construction procedure is authorized to read, then test the resulting tree.

Mark concurrency, hooks and filters as separate qualification boundaries

No hooks, attributes or filters, merges, signing, shared index mutation, or alternate-index configuration are exercised here. The driver owns its repositories and executes each command sequentially. Its observations establish the stated selection behavior within those conditions; they do not solve races or certify repository-specific transformations.

If the real release procedure introduces any of those features, qualify the expanded procedure before extending this acceptance claim. Preserve the same independently chosen content oracle, add controls for the new boundary, and examine its measured effects. Do not interpret a passing isolated laboratory as evidence that an untested production step preserves the approval.

Make the handoff rule enforceable as a comparison, not a promise that developers will remember the correct option. A recorded mismatch should identify the approved tree, actual tree, affected paths, and next owner. A missing record should trigger evidence collection. That response keeps an audit useful to the people performing the work and avoids turning a precise content gate into a generic successful-command checkbox.

Decide whether to accept, hold, repair the review or rerun

Apply the decision to the proposed release artifact, with the evidence package beside it. The principal acceptance boundary remains the complete reviewed B/U1 tree and intact saved B. The command's exit status is execution evidence; it is not an approval token. A failed or incomplete experiment cannot supply the missing content evidence either.

Decision

Evidence condition

Required action

ACCEPT_REVIEWED_TREE

Committed tree equals the independently approved full tree; saved B is intact; required evidence is complete.

Retain the selected commit identity and pass it to the next gate.

HOLD_UNREVIEWED_TREE

Complete observations show a tree mismatch, including C replacing B or U1 being omitted.

Stop promotion, preserve pending data, and assign a correction owner.

REPAIR_REVIEW_SELECTION

Intended scope differs from the recorded approval or remains ambiguous; relevant inputs are retained.

Declare and review the complete intended candidate before constructing it.

RERUN_MISSING_EVIDENCE

Runtime, source, approval, byte observations, or preservation checks are absent or inconclusive.

Restore an observable qualification setup and rerun the required checks.

G1 satisfies the first row in this fixture. G2/G3 fail on two content choices; G4/G5 fail on the payload despite their clean status. Their mismatch is already demonstrated, so asking for a rerun solely to avoid a hold would discard useful evidence. Preserve the finding while carrying out any justified follow-up experiment.

The repair decision concerns the intended selection. It does not release the currently rejected commit. If the owner chooses B/U0, that candidate needs a new approval; if B/U1 remains required, the corrected construction must match the retained original approval. A change to the review contract should be visible as a new decision, not hidden inside a revised comparison script.

Use the rerun decision for uncertainty rather than demonstrated equality or inequality. If B evidence is missing, the next action may first be preserving what remains and obtaining a reviewable candidate. Rerunning a synthetic laboratory cannot recover absent production approval. The evidence must correspond to the actual release source before the gate can accept it.

The same distinction is useful when reviewing infrastructure as code: approve identified source, validate what was constructed from it, and carry unresolved differences to an owner. This application concerns source provenance; it does not qualify infrastructure execution. Record a reason with every decision so the next engineer knows whether the problem is content, selection, preservation, or missing observation.

Connect Git review discipline with wider DevOps practice

The release skill demonstrated here is naming the artifact at each boundary and checking that the intended content crossed it. The staged review identifies B/U1. Commit construction produces a measurable tree. The release handoff carries a particular commit onward. Keeping those objects distinct makes a precise approval possible even while development continues in the working directory.

The five cases give that discipline a concrete test. Ordinary commit preserves the reviewed selection in this setup and leaves C available as pending work. Path-limited selection chooses C and defers U1. Including the working payload or staging all tracked modifications produces C/U1 with clean status. Each result becomes understandable when its source and the unchanged approval are visible together.

Retain the evidence before correcting a mismatch, require a new review when the intended candidate changes, and qualify additional production features separately. These practices give contributors room to continue editing while giving release owners a definite artifact to accept or hold. A future engineer should be able to read the record and identify exactly what was approved, what was constructed, and what remained pending.

For wider Git, CI/CD, and infrastructure-review skills, explore the DevOps Engineering Program and check the current cohort requirements. Its published curriculum includes Git/GitHub and CI/CD.