Skip to content

Add trailer support for commit creation#2116

Merged
Byron merged 2 commits intogitpython-developers:mainfrom
Krishnachaitanyakc:add-trailer-support-for-commit-creation
Apr 12, 2026
Merged

Add trailer support for commit creation#2116
Byron merged 2 commits intogitpython-developers:mainfrom
Krishnachaitanyakc:add-trailer-support-for-commit-creation

Conversation

@Krishnachaitanyakc
Copy link
Copy Markdown
Contributor

Summary

  • Adds a trailers parameter to Commit.create_from_tree() and IndexFile.commit() enabling trailer creation (e.g. Signed-off-by, Issue) when committing programmatically
  • Accepts either a dict or a list of (key, value) tuples (for duplicate keys like multiple Signed-off-by)
  • Uses git interpret-trailers for formatting, consistent with existing trailer parsing in Commit.trailers_list

Closes #1998

Motivation

GitPython already supports parsing trailers from existing commits, but did not support adding them when creating new commits. The git commit CLI supports this via --trailer, e.g.:

git commit --message "Did a thing" --trailer "Issue: abc"

This change brings equivalent functionality to GitPython's Python API.

Usage

# Using a dict
repo.index.commit("Fix bug", trailers={"Issue": "123"})

# Using a list of tuples (for duplicate keys)
repo.index.commit("Fix bug", trailers=[
    ("Signed-off-by", "Alice <alice@example.com>"),
    ("Signed-off-by", "Bob <bob@example.com>"),
])

# Via Commit.create_from_tree directly
Commit.create_from_tree(repo, tree, "Fix bug", trailers={"Reviewed-by": "Eve"})

Test plan

  • test_create_from_tree_with_trailers_dict - verifies dict-based trailer creation and round-trip parsing
  • test_create_from_tree_with_trailers_list - verifies list-of-tuples trailer creation with duplicate keys
  • test_index_commit_with_trailers - verifies IndexFile.commit() passes trailers through correctly
  • All 3 new tests pass locally

Add a `trailers` parameter to `Commit.create_from_tree()` and
`IndexFile.commit()` that allows appending trailer key-value pairs
(e.g. Signed-off-by, Issue) to the commit message at creation time.

Trailers can be passed as either a dict or a list of (key, value) tuples,
the latter being useful when duplicate keys are needed. The implementation
uses `git interpret-trailers` for proper formatting, consistent with
the existing trailer parsing in `Commit.trailers_list`.

Closes gitpython-developers#1998
@Byron Byron requested a review from Copilot March 25, 2026 12:01
@Byron
Copy link
Copy Markdown
Member

Byron commented Mar 25, 2026

@codex review

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds support for creating commit trailers when programmatically generating commits, aligning commit creation with GitPython’s existing trailer parsing capabilities.

Changes:

  • Add a trailers parameter to Commit.create_from_tree() to append trailers via git interpret-trailers.
  • Thread trailers through IndexFile.commit() so index commits can include trailers.
  • Add tests covering dict-based trailers, list-of-tuples trailers (duplicate keys), and the index commit plumbing.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.

File Description
git/objects/commit.py Adds trailers support in Commit.create_from_tree() by invoking git interpret-trailers.
git/index/base.py Exposes trailers in IndexFile.commit() and forwards it to Commit.create_from_tree().
test/test_commit.py Adds round-trip tests validating trailer creation and parsing for both APIs.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link
Copy Markdown
Member

@Byron Byron left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks a lot, this looks good to me.

The Copilot comments should be addressed and then it can be merged.

@Krishnachaitanyakc
Copy link
Copy Markdown
Contributor Author

@Byron addressed those comments, can you please review?

@Byron
Copy link
Copy Markdown
Member

Byron commented Apr 6, 2026

It doesn't look like they are resolved, nor was anything pushed. Please also resolve them. Thank you.

@Krishnachaitanyakc Krishnachaitanyakc force-pushed the add-trailer-support-for-commit-creation branch from 4da170c to af0933c Compare April 6, 2026 15:59
@Krishnachaitanyakc
Copy link
Copy Markdown
Contributor Author

@Byron sorry, missed pushing them. Can you review now?

Copy link
Copy Markdown
Member

@Byron Byron left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks a lot, looks good to me!

It's a shame that it's shelling out to build a string, but better to have a slow but correct version, than nothing at all in that regard.

@Byron Byron merged commit b5b87dd into gitpython-developers:main Apr 12, 2026
34 checks passed
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +708 to +710
stdout_bytes, _ = proc.communicate(str(message).encode())
finalize_process(proc)
message = stdout_bytes.decode("utf8")
Copy link

Copilot AI Apr 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

interpret-trailers I/O is hard-coded to UTF-8 (.encode() default + .decode('utf8')), but this method already resolves conf_encoding for the commit’s declared encoding. If i18n.commitencoding is not UTF-8, this can raise decode errors or corrupt non-ASCII characters; use the resolved encoding for both encoding stdin and decoding stdout (and consider an explicit error strategy).

Suggested change
stdout_bytes, _ = proc.communicate(str(message).encode())
finalize_process(proc)
message = stdout_bytes.decode("utf8")
stdout_bytes, _ = proc.communicate(str(message).encode(conf_encoding, errors="strict"))
finalize_process(proc)
message = stdout_bytes.decode(conf_encoding, errors="strict")

Copilot uses AI. Check for mistakes.
Comment on lines +707 to +709
)
stdout_bytes, _ = proc.communicate(str(message).encode())
finalize_process(proc)
Copy link

Copilot AI Apr 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This uses as_process=True, which makes Git.execute(..., with_exceptions=True) ineffective (see Git.execute docs), but the code never checks proc.returncode / stderr after communicate(). If interpret-trailers fails (e.g., unsupported --trailer on an older Git), message may be replaced with empty/partial stdout and the commit is still created; please detect non-zero exit status and raise (or preserve the original message on failure).

Suggested change
)
stdout_bytes, _ = proc.communicate(str(message).encode())
finalize_process(proc)
stderr=PIPE,
)
stdout_bytes, stderr_bytes = proc.communicate(str(message).encode())
finalize_process(proc)
if proc.returncode:
stderr = stderr_bytes.decode("utf8", errors="replace") if stderr_bytes else ""
raise RuntimeError(
"git interpret-trailers failed with exit code "
f"{proc.returncode}: {stderr or 'no error output'}"
)

Copilot uses AI. Check for mistakes.
Comment on lines +702 to +704
cmd = [repo.git.GIT_PYTHON_GIT_EXECUTABLE, "interpret-trailers"] + trailer_args
proc: Git.AutoInterrupt = repo.git.execute( # type: ignore[call-overload]
cmd,
Copy link

Copilot AI Apr 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The command uses repo.git.GIT_PYTHON_GIT_EXECUTABLE here, but Commit.trailers_list/trailers_dict currently invoke interpret-trailers via a hard-coded ['git', ...]. If a caller overrides GIT_PYTHON_GIT_EXECUTABLE, trailer creation and trailer parsing can end up using different Git binaries; consider aligning both paths on the same executable selection approach for consistent round-trips.

Suggested change
cmd = [repo.git.GIT_PYTHON_GIT_EXECUTABLE, "interpret-trailers"] + trailer_args
proc: Git.AutoInterrupt = repo.git.execute( # type: ignore[call-overload]
cmd,
proc: Git.AutoInterrupt = repo.git._call_process( # type: ignore[attr-defined]
"interpret-trailers",
*trailer_args,

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

Ability to add trailers when creating a commit

3 participants