Home / Alt manpages / git-patch-id(1)

  • git-patch-id(1)
  • User command
  • linux

Compare Git Changes Reliably with git patch-id

You will finish with a repeatable way to turn a Git patch into an identifier, compare likely duplicate changes, and keep a patch ID mapped to the commit that produced it. The examples use Git 2.43.0, the version installed on this machine.

Allow about ten minutes. You need Git, a repository containing the commit or patch you want to inspect, and permission to read it. The commands below do not need sudo, do not alter repository history, and do not write to the repository.

1. Check the installed contract

Confirm which Git is being used and read the option set exposed by that installation:

$ git --version
git version 2.43.0
$ git patch-id -h
usage: git patch-id [--stable | --unstable | --verbatim]

git patch-id reads a patch from standard input. It does not take a commit name as its input argument. Feed it output from a Git diff command or redirect a patch file.

Checkpoint: if git --version reports a different release, keep the local help output as the authority for flags. Do not assume that an option from a newer Git installation exists here.

2. Produce a patch for one commit

Use git diff-tree when you want the result to include the commit object name alongside the patch ID. Replace COMMIT with an actual commit ID, tag, or other revision accepted by Git:

$ git diff-tree --root -p COMMIT | git patch-id --stable
5bc1c98b7ad46ead6fd09ef25064e858e9dae1ef 2240bf50f987f4d9bc44996e5522b9a23fca84ae

The first 40-character hexadecimal value is the patch ID. The second is the commit ID. The example values are illustrative: your repository will produce different values.

--root makes the command work for a root commit as well as an ordinary commit. The -p option asks diff-tree for the patch itself. If the revision is invalid, Git reports that before git patch-id has anything to hash.

Verify the commit independently when the mapping matters:

$ git rev-parse --verify COMMIT^{commit}
2240bf50f987f4d9bc44996e5522b9a23fca84ae

3. Compare two changes without comparing commit IDs

A commit ID changes when metadata or the parent changes. A patch ID is intended to identify the file changes, with line numbers ignored. That makes it useful for finding a change that was applied again by cherry-pick or manual backport.

Calculate the IDs for two commits and compare only the first field:

$ git diff-tree --root -p COMMIT_A | git patch-id --stable
7b3c... 1111...
$ git diff-tree --root -p COMMIT_B | git patch-id --stable
7b3c... 2222...

Matching patch IDs are strong evidence that the changes are equivalent for this purpose, but treat them as a practical duplicate key rather than a proof of identity. Keep the second field, or store the complete pair, so you can trace the result back to its source commit.

For a patch already saved on disk, the input side is simpler:

$ git patch-id --stable < change.patch
7b3c... 0000000000000000000000000000000000000000

A plain patch has no commit object name for Git to report, so the second field is forty zeroes. That is expected, not a failed hash.

4. Choose the hashing mode deliberately

Git 2.43.0 accepts three mutually exclusive modes. The default is --unstable, unless your Git configuration selects another default:

  • --unstable is compatible with patch IDs produced by Git 1.9 and older. Whitespace is ignored, but reordering the file diffs can change the result.
  • --stable ignores whitespace and makes the result independent of the order of the file diffs. It produces values different from the historical and unstable algorithms, so use it consistently for a new database.
  • --verbatim hashes the input as given and does not strip whitespace. Use it when whitespace changes are part of the distinction you need to preserve.

For a new duplicate-detection job, choose --stable explicitly in the script and record that choice. Do not mix stable and unstable values in one lookup table. If you must compare with an old store made by Git 1.9 or older, use --unstable for compatibility and document the decision.

Configuration can change the default. Inspect the relevant settings without changing them:

$ git config --show-origin --get-regexp '^patchid\.(stable|verbatim)$'
file:/home/alice/.gitconfig patchid.stable true

No output means those settings are not present in the configuration scopes Git searched. An explicit command-line mode is easier to audit than relying on a user's global configuration.

5. Keep the pipeline safe in scripts

Patch IDs are calculated from standard input, so a shell pipeline is the natural interface. Keep the revision fixed and quote values that come from variables:

$ commit='COMMIT'
$ git diff-tree --root -p "$commit" -- | git patch-id --stable
7b3c... 1111...

Do not pass an untrusted string as a set of Git options. If a revision comes from a user or an external system, validate it as a commit first and use a separate -- boundary where the producing Git command supports it. A patch ID does not authenticate its input and should not be used as a security credential.

Also remember that the command reads all patch data from standard input. An empty or truncated stream can produce no useful mapping, while a syntactically valid but unexpected patch can produce a perfectly valid ID. Check the producing command's exit status and review the diff when the source is not fully trusted.

6. Recover from the common mistakes

If no line is printed, inspect the producer before changing hash modes:

$ git diff-tree --root -p COMMIT > /tmp/commit.patch
$ test -s /tmp/commit.patch && echo 'patch is non-empty'
$ git patch-id --stable < /tmp/commit.patch

The temporary file is only a diagnostic copy. Remove it when you have finished checking it; do not delete the repository's original data. If the producer failed, fix the revision or repository access first.

If two known-equivalent changes have different IDs, check the mode, whitespace, file order and actual file content. Re-run both with the same explicit option. --stable handles file-diff order, but it does not make unrelated changes equal and it does not make verbatim whitespace significant.

There is no undo step for these examples. They read Git objects and write only the explicitly requested temporary patch copy. If that copy contains sensitive source, treat it like the original and remove it securely according to your system's policy rather than leaving it in a shared temporary directory.

Done means

  • You confirmed the installed Git version and local option syntax.
  • You fed a patch to git patch-id through standard input.
  • You can distinguish the patch ID from the optional source commit ID.
  • You selected one mode consistently, rather than mixing stable and historical values.
  • You understand that whitespace, file order and commit metadata affect different modes differently.
  • Your workflow remains read-only apart from any deliberately created temporary patch file.