← 返回首页
Add trailer support for commit creation · gitpython-developers/GitPython@7c5fbc6 · GitHub
Skip to content

Navigation Menu

Toggle navigation
Sign in
Appearance settings
Search or jump to...

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Include my email address so I can be contacted

Saved searches

Use saved searches to filter your results more quickly

Appearance settings
Resetting focus

Commit 7c5fbc6

Browse files
Add trailer support for commit creation
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 #1998
1 parent d0318a6 commit 7c5fbc6

3 files changed

Lines changed: 106 additions & 0 deletions

File tree

‎git/index/base.py‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1133,6 +1133,7 @@ def commit(
11331133
author_date: Union[datetime.datetime, str, None] = None,
11341134
commit_date: Union[datetime.datetime, str, None] = None,
11351135
skip_hooks: bool = False,
1136+
trailers: Union[None, "Dict[str, str]", "List[Tuple[str, str]]"] = None,
11361137
) -> Commit:
11371138
"""Commit the current default index file, creating a
11381139
:class:`~git.objects.commit.Commit` object.
@@ -1169,6 +1170,7 @@ def commit(
11691170
committer=committer,
11701171
author_date=author_date,
11711172
commit_date=commit_date,
1173+
trailers=trailers,
11721174
)
11731175
if not skip_hooks:
11741176
run_commit_hook("post-commit", self)

‎git/objects/commit.py‎

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -570,6 +570,7 @@ def create_from_tree(
570570
committer: Union[None, Actor] = None,
571571
author_date: Union[None, str, datetime.datetime] = None,
572572
commit_date: Union[None, str, datetime.datetime] = None,
573+
trailers: Union[None, Dict[str, str], List[Tuple[str, str]]] = None,
573574
) -> "Commit":
574575
"""Commit the given tree, creating a :class:`Commit` object.
575576
@@ -609,6 +610,14 @@ def create_from_tree(
609610
:param commit_date:
610611
The timestamp for the committer field.
611612
613+
:param trailers:
614+
Optional trailer key-value pairs to append to the commit message.
615+
Can be a dictionary mapping trailer keys to values, or a list of
616+
``(key, value)`` tuples (useful when the same key appears multiple
617+
times, e.g. multiple ``Signed-off-by`` trailers). Trailers are
618+
appended using ``git interpret-trailers``.
619+
See :manpage:`git-interpret-trailers(1)`.
620+
612621
:return:
613622
:class:`Commit` object representing the new commit.
614623
@@ -678,6 +687,27 @@ def create_from_tree(
678687
tree = repo.tree(tree)
679688
# END tree conversion
680689

690+
# APPLY TRAILERS
691+
if trailers:
692+
trailer_args: List[str] = []
693+
if isinstance(trailers, dict):
694+
for key, val in trailers.items():
695+
trailer_args.append("--trailer")
696+
trailer_args.append(f"{key}: {val}")
697+
else:
698+
for key, val in trailers:
699+
trailer_args.append("--trailer")
700+
trailer_args.append(f"{key}: {val}")
701+
702+
cmd = ["git", "interpret-trailers"] + trailer_args
703+
proc: Git.AutoInterrupt = repo.git.execute( # type: ignore[call-overload]
704+
cmd,
705+
as_process=True,
706+
istream=PIPE,
707+
)
708+
message = proc.communicate(str(message).encode())[0].decode("utf8")
709+
# END apply trailers
710+
681711
# CREATE NEW COMMIT
682712
new_commit = cls(
683713
repo,

‎test/test_commit.py‎

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -566,3 +566,77 @@ def test_commit_co_authors(self):
566566
Actor("test_user_2", "another_user-email@github.com"),
567567
Actor("test_user_3", "test_user_3@github.com"),
568568
]
569+
570+
@with_rw_directory
571+
def test_create_from_tree_with_trailers_dict(self, rw_dir):
572+
"""Test that create_from_tree supports adding trailers via a dict."""
573+
rw_repo = Repo.init(osp.join(rw_dir, "test_trailers_dict"))
574+
path = osp.join(str(rw_repo.working_tree_dir), "hello.txt")
575+
touch(path)
576+
rw_repo.index.add([path])
577+
tree = rw_repo.index.write_tree()
578+
579+
trailers = {"Issue": "123", "Signed-off-by": "Test User <test@test.com>"}
580+
commit = Commit.create_from_tree(
581+
rw_repo,
582+
tree,
583+
"Test commit with trailers",
584+
head=True,
585+
trailers=trailers,
586+
)
587+
588+
assert "Issue: 123" in commit.message
589+
assert "Signed-off-by: Test User <test@test.com>" in commit.message
590+
assert commit.trailers_dict == {
591+
"Issue": ["123"],
592+
"Signed-off-by": ["Test User <test@test.com>"],
593+
}
594+
595+
@with_rw_directory
596+
def test_create_from_tree_with_trailers_list(self, rw_dir):
597+
"""Test that create_from_tree supports adding trailers via a list of tuples."""
598+
rw_repo = Repo.init(osp.join(rw_dir, "test_trailers_list"))
599+
path = osp.join(str(rw_repo.working_tree_dir), "hello.txt")
600+
touch(path)
601+
rw_repo.index.add([path])
602+
tree = rw_repo.index.write_tree()
603+
604+
trailers = [
605+
("Signed-off-by", "Alice <alice@example.com>"),
606+
("Signed-off-by", "Bob <bob@example.com>"),
607+
("Issue", "456"),
608+
]
609+
commit = Commit.create_from_tree(
610+
rw_repo,
611+
tree,
612+
"Test commit with multiple trailers",
613+
head=True,
614+
trailers=trailers,
615+
)
616+
617+
assert "Signed-off-by: Alice <alice@example.com>" in commit.message
618+
assert "Signed-off-by: Bob <bob@example.com>" in commit.message
619+
assert "Issue: 456" in commit.message
620+
assert commit.trailers_dict == {
621+
"Signed-off-by": ["Alice <alice@example.com>", "Bob <bob@example.com>"],
622+
"Issue": ["456"],
623+
}
624+
625+
@with_rw_directory
626+
def test_index_commit_with_trailers(self, rw_dir):
627+
"""Test that IndexFile.commit() supports adding trailers."""
628+
rw_repo = Repo.init(osp.join(rw_dir, "test_index_trailers"))
629+
path = osp.join(str(rw_repo.working_tree_dir), "hello.txt")
630+
touch(path)
631+
rw_repo.index.add([path])
632+
633+
trailers = {"Reviewed-by": "Reviewer <reviewer@example.com>"}
634+
commit = rw_repo.index.commit(
635+
"Test index commit with trailers",
636+
trailers=trailers,
637+
)
638+
639+
assert "Reviewed-by: Reviewer <reviewer@example.com>" in commit.message
640+
assert commit.trailers_dict == {
641+
"Reviewed-by": ["Reviewer <reviewer@example.com>"],
642+
}

0 commit comments

Comments
 (0)

Footer

© 2026 GitHub, Inc.