v5: Align object encoding with upstream#2065
Merged
Merged
Conversation
Signed-off-by: Paulo Gomes <paulo@entire.io>
Signed-off-by: Paulo Gomes <paulo@entire.io>
Signed-off-by: Paulo Gomes <paulo@entire.io>
Replace the per-iteration flag soup in (*Commit).Decode with a func-based stateFn loop that mirrors upstream Git's parsing posture. The new state machine encodes "where we are in the buffer" as the function being called rather than as a bag of mutable booleans. Conformance changes vs upstream commit.c: - tree must be the first header, missing tree errors with ErrMalformedCommit (matches the "bogus commit object" check at commit.c:508-510). - Parents are accepted only contiguously after tree; out-of-position parent lines are silently dropped (matches parse_commit_buffer's parent loop exiting at the first non-parent line and read_commit_extra_header_lines filtering parents from extras). - Author and committer are accepted only at their canonical positions (immediately after parents, immediately after author). Out-of-place occurrences are dropped, mirroring parse_commit_date silently returning 0 plus the standard_header_field filter. - Encoding/mergetag/gpgsig/gpgsig-sha256 follow first-wins; duplicate sig/mergetag continuation lines are discarded by a dedicated scanSkipCont state. - Continuation strip uses line[1:] (single space) instead of bytes.TrimLeft, mirroring upstream's `line + 1` at commit.c:1509. - Header/body boundary is the literal "\n" only, matching *line == '\n' at commit.c:1502. Tests: - TestDecodeRequiresTreeFirst covers the four hard-error inputs (missing tree, parent before tree, extra before tree, empty buffer). - TestDecodeFirstOccurrenceWins covers the lenient drop semantics for duplicate tree/author/committer/gpgsig/gpgsig-sha256, parents after author or interleaved with extras, missing committer, encoding between author and committer (drops the misplaced committer), and author at a non-canonical position (drops author and committer). - TestMalformedHeader is back to expecting NoError (lenient parsing of garbage author/committer values). The state machine lives in plumbing/object/commit_scanner.go. Assisted-by: Claude Opus 4.7 <noreply@anthropic.com> Signed-off-by: Paulo Gomes <paulo@entire.io>
Signed-off-by: Paulo Gomes <paulo@entire.io>
Tag verification re-encoded from struct fields, so any non-canonical bytes the signature was computed over (duplicate headers, mutation-only fields like Signature/SignatureSHA256, etc.) were lost from the payload and signatures that were valid in upstream Git failed in go-git. Mirror the approach already used by Commit: retain a reference to the source plumbing.EncodedObject on Decode, add matchesSource() so EncodeWithoutSignature can stream the raw bytes (with the inline trailing PGP block truncated and gpgsig/gpgsig-sha256 headers stripped) when the struct still matches the source. Mutating struct fields still falls back to the struct-encoded payload. Assisted-by: Claude Opus 4.7 <noreply@anthropic.com> Signed-off-by: Paulo Gomes <paulo@entire.io>
Align Commit.Verify with upstream's parse_buffer_signed_by_header (commit.c:1186) and parse_gpg_output rejection (gpg-interface.c:257-269). The commit scanner now concatenates every gpgsig/gpgsig-sha256 header into a single signature buffer instead of first-wins, and Commit.Verify returns the new ErrMultipleSignatures when that buffer carries more than one armored block. Assisted-by: Claude Opus 4.7 <noreply@anthropic.com> Signed-off-by: Paulo Gomes <paulo@entire.io>
Signed-off-by: Paulo Gomes <paulo@entire.io>
Signed-off-by: Paulo Gomes <paulo@entire.io>
Signed-off-by: Paulo Gomes <paulo@entire.io>
Signed-off-by: Paulo Gomes <paulo@entire.io>
Signed-off-by: Paulo Gomes <paulo@entire.io>
Signed-off-by: Paulo Gomes <paulo@entire.io>
Contributor
There was a problem hiding this comment.
Pull request overview
This PR aligns go-git’s Commit/Tag/Tree decoding, signature payload generation, and verification behavior with upstream git, and adds conformance tests to ensure Verify verdicts match git verify-commit / git verify-tag.
Changes:
- Commit/Tag: implement stricter upstream-like header interpretation (canonical order + first-wins) and make
EncodeWithoutSignaturepreserve exact signed payload bytes by stripping signatures from the original decoded bytes when unmodified. - Tree: canonicalize entry modes, detect malformed trees, track whether entries are sorted, and short-circuit lookups for unsorted trees to match upstream behavior.
- Add Linux-only conformance tests that compare
gitvs go-git verification verdicts (GPG-backed), and allowtests:as a commit message prefix in CI validation.
Reviewed changes
Copilot reviewed 15 out of 15 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
tests/objectverify/main_test.go |
Test harness: repo init, isolated GPG env, helpers to compare git vs go-git verification. |
tests/objectverify/commit_test.go |
Conformance cases for git verify-commit vs Commit.Verify. |
tests/objectverify/tag_test.go |
Conformance cases for git verify-tag vs Tag.Verify. |
plumbing/object/commit.go |
Commit decode/encode/signature-payload alignment, raw-source preservation, multi-signature rejection in Verify. |
plumbing/object/commit_scanner.go |
New commit header state machine to enforce upstream-like parsing and first-wins semantics. |
plumbing/object/commit_sha256_test.go |
SHA-256 object-id decoding test coverage for commits. |
plumbing/object/commit_test.go |
Expanded unit tests for strict header parsing and EncodeWithoutSignature payload behavior. |
plumbing/object/tag.go |
Tag decode/encode/signature-payload alignment, raw-source preservation, optional tagger encoding. |
plumbing/object/tag_scanner.go |
New tag header state machine to enforce upstream-like parsing and header skipping. |
plumbing/object/tag_test.go |
Expanded unit tests for tag parsing, signature handling, and EncodeWithoutSignature. |
plumbing/object/signature.go |
Shared helpers for signature detection/counting and streaming signature stripping from raw objects. |
plumbing/object/tree.go |
Tree decode hardened (malformed detection), mode canonicalization, sortedness tracking, and lookup changes. |
plumbing/object/tree_test.go |
Adds tests for malformed tree rejection, canonical mode mapping, cache/state clearing, and duplicate handling. |
plumbing/object/object_test.go |
Updates tree lookup assertions to use the new lookup path. |
.github/workflows/pr-validation.yml |
Allows tests: prefix in commit message validation pattern. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Maks1mS
pushed a commit
to stplr-dev/stplr
that referenced
this pull request
May 10, 2026
This PR contains the following updates: | Package | Type | Update | Change | OpenSSF | |---|---|---|---|---| | [github.com/go-git/go-git/v5](https://github.com/go-git/go-git) | require | minor | `v5.18.0` → `v5.19.0` | [](https://securityscorecards.dev/viewer/?uri=github.com/go-git/go-git) | --- >⚠️ **Warning** > > Some dependencies could not be looked up. Check the [Dependency Dashboard](issues/23) for more information. --- ### Release Notes <details> <summary>go-git/go-git (github.com/go-git/go-git/v5)</summary> ### [`v5.19.0`](https://github.com/go-git/go-git/releases/tag/v5.19.0) [Compare Source](go-git/go-git@v5.18.0...v5.19.0) #### What's Changed - build: Update module github.com/go-git/go-git/v5 to v5.18.0 \[SECURITY] (releases/v5.x) by [@​go-git-renovate](https://github.com/go-git-renovate)\[bot] in [#​2010](go-git/go-git#2010) - v5: Bump sha1cd and go-billy by [@​pjbgf](https://github.com/pjbgf) in [#​2060](go-git/go-git#2060) - v5: Align object encoding with upstream by [@​pjbgf](https://github.com/pjbgf) in [#​2065](go-git/go-git#2065) **Full Changelog**: <go-git/go-git@v5.18.0...v5.19.0> </details> --- ### Configuration 📅 **Schedule**: (UTC) - Branch creation - At 12:00 AM through 04:59 AM and 10:00 PM through 11:59 PM, Monday through Friday (`* 0-4,22-23 * * 1-5`) - Only on Sunday and Saturday (`* * * * 0,6`) - Automerge - At any time (no schedule defined) 🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied. ♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box --- This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate). <!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4xMzIuMyIsInVwZGF0ZWRJblZlciI6IjQzLjEzMi4zIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJLaW5kL0RlcGVuZGVuY2llcyJdfQ==--> Reviewed-on: https://altlinux.space/stapler/stplr/pulls/427
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
CommitandTagobjects return their raw bytes (minusgpgsignandgpgsign-sha256headers).CommitandTaginterpretation with upstream: first instance of standard headers wins.git. Note that duplicate signatures were valid back onv2.11, which is why that test is skipped during capability mode.Treeinterpretation with upstream: first wins and unsorted tree is short-circuited.