Merge pull request #3789 from ei-grad/attribute-co-authored-by

feat: Add Co-authored-by attribution for AI commits
This commit is contained in:
paul-gauthier 2025-05-07 16:54:46 -07:00 committed by GitHub
commit c6954f9972
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 353 additions and 56 deletions

View file

@ -427,14 +427,20 @@ def get_parser(default_config_files, git_root):
group.add_argument( group.add_argument(
"--attribute-author", "--attribute-author",
action=argparse.BooleanOptionalAction, action=argparse.BooleanOptionalAction,
default=True, default=None,
help="Attribute aider code changes in the git author name (default: True)", help=(
"Attribute aider code changes in the git author name (default: True). If explicitly set"
" to True, overrides --attribute-co-authored-by precedence."
),
) )
group.add_argument( group.add_argument(
"--attribute-committer", "--attribute-committer",
action=argparse.BooleanOptionalAction, action=argparse.BooleanOptionalAction,
default=True, default=None,
help="Attribute aider commits in the git committer name (default: True)", help=(
"Attribute aider commits in the git committer name (default: True). If explicitly set"
" to True, overrides --attribute-co-authored-by precedence for aider edits."
),
) )
group.add_argument( group.add_argument(
"--attribute-commit-message-author", "--attribute-commit-message-author",
@ -448,6 +454,16 @@ def get_parser(default_config_files, git_root):
default=False, default=False,
help="Prefix all commit messages with 'aider: ' (default: False)", help="Prefix all commit messages with 'aider: ' (default: False)",
) )
group.add_argument(
"--attribute-co-authored-by",
action=argparse.BooleanOptionalAction,
default=False,
help=(
"Attribute aider edits using the Co-authored-by trailer in the commit message"
" (default: False). If True, this takes precedence over default --attribute-author and"
" --attribute-committer behavior unless they are explicitly set to True."
),
)
group.add_argument( group.add_argument(
"--git-commit-verify", "--git-commit-verify",
action=argparse.BooleanOptionalAction, action=argparse.BooleanOptionalAction,

View file

@ -2331,7 +2331,7 @@ class Coder:
context = self.get_context_from_history(self.cur_messages) context = self.get_context_from_history(self.cur_messages)
try: try:
res = self.repo.commit(fnames=edited, context=context, aider_edits=True) res = self.repo.commit(fnames=edited, context=context, aider_edits=True, coder=self)
if res: if res:
self.show_auto_commit_outcome(res) self.show_auto_commit_outcome(res)
commit_hash, commit_message = res commit_hash, commit_message = res
@ -2367,7 +2367,7 @@ class Coder:
if not self.repo: if not self.repo:
return return
self.repo.commit(fnames=self.need_commit_before_edits) self.repo.commit(fnames=self.need_commit_before_edits, coder=self)
# files changed, move cur messages back behind the files messages # files changed, move cur messages back behind the files messages
# self.move_back_cur_messages(self.gpt_prompts.files_content_local_edits) # self.move_back_cur_messages(self.gpt_prompts.files_content_local_edits)

View file

@ -904,6 +904,7 @@ def main(argv=None, input=None, output=None, force_git_root=None, return_coder=F
commit_prompt=args.commit_prompt, commit_prompt=args.commit_prompt,
subtree_only=args.subtree_only, subtree_only=args.subtree_only,
git_commit_verify=args.git_commit_verify, git_commit_verify=args.git_commit_verify,
attribute_co_authored_by=args.attribute_co_authored_by, # Pass the arg
) )
except FileNotFoundError: except FileNotFoundError:
pass pass

View file

@ -1,3 +1,4 @@
import contextlib
import os import os
import time import time
from pathlib import Path, PurePosixPath from pathlib import Path, PurePosixPath
@ -34,6 +35,19 @@ ANY_GIT_ERROR += [
ANY_GIT_ERROR = tuple(ANY_GIT_ERROR) ANY_GIT_ERROR = tuple(ANY_GIT_ERROR)
@contextlib.contextmanager
def set_git_env(var_name, value, original_value):
"""Temporarily set a Git environment variable."""
os.environ[var_name] = value
try:
yield
finally:
if original_value is not None:
os.environ[var_name] = original_value
elif var_name in os.environ:
del os.environ[var_name]
class GitRepo: class GitRepo:
repo = None repo = None
aider_ignore_file = None aider_ignore_file = None
@ -58,6 +72,7 @@ class GitRepo:
commit_prompt=None, commit_prompt=None,
subtree_only=False, subtree_only=False,
git_commit_verify=True, git_commit_verify=True,
attribute_co_authored_by=False, # Added parameter
): ):
self.io = io self.io = io
self.models = models self.models = models
@ -69,6 +84,7 @@ class GitRepo:
self.attribute_committer = attribute_committer self.attribute_committer = attribute_committer
self.attribute_commit_message_author = attribute_commit_message_author self.attribute_commit_message_author = attribute_commit_message_author
self.attribute_commit_message_committer = attribute_commit_message_committer self.attribute_commit_message_committer = attribute_commit_message_committer
self.attribute_co_authored_by = attribute_co_authored_by # Assign from parameter
self.commit_prompt = commit_prompt self.commit_prompt = commit_prompt
self.subtree_only = subtree_only self.subtree_only = subtree_only
self.git_commit_verify = git_commit_verify self.git_commit_verify = git_commit_verify
@ -111,7 +127,71 @@ class GitRepo:
if aider_ignore_file: if aider_ignore_file:
self.aider_ignore_file = Path(aider_ignore_file) self.aider_ignore_file = Path(aider_ignore_file)
def commit(self, fnames=None, context=None, message=None, aider_edits=False): def commit(self, fnames=None, context=None, message=None, aider_edits=False, coder=None):
"""
Commit the specified files or all dirty files if none are specified.
Args:
fnames (list, optional): List of filenames to commit. Defaults to None (commit all
dirty files).
context (str, optional): Context for generating the commit message. Defaults to None.
message (str, optional): Explicit commit message. Defaults to None (generate message).
aider_edits (bool, optional): Whether the changes were made by Aider. Defaults to False.
This affects attribution logic.
coder (Coder, optional): The Coder instance, used to access config and model info.
Defaults to None.
Returns:
tuple(str, str) or None: The commit hash and commit message if successful, else None.
Attribution Logic:
------------------
This method handles Git commit attribution based on configuration flags and whether
Aider generated the changes (`aider_edits`).
Key Concepts:
- Author: The person who originally wrote the code changes.
- Committer: The person who last applied the commit to the repository.
- aider_edits=True: Changes were generated by Aider (LLM).
- aider_edits=False: Commit is user-driven (e.g., /commit manually staged changes).
- Explicit Setting: A flag (--attribute-...) is set to True or False via command line
or config file.
- Implicit Default: A flag is not explicitly set, defaulting to None in args, which is
interpreted as True unless overridden by other logic.
Flags:
- --attribute-author: Modify Author name to "User Name (aider)".
- --attribute-committer: Modify Committer name to "User Name (aider)".
- --attribute-co-authored-by: Add "Co-authored-by: aider (<model>) <noreply@aider.chat>"
trailer to the commit message.
Behavior Summary:
1. When aider_edits = True (AI Changes):
- If --attribute-co-authored-by=True:
- Co-authored-by trailer IS ADDED.
- Author/Committer names are NOT modified by default (co-authored-by takes precedence).
- EXCEPTION: If --attribute-author/--attribute-committer is EXPLICITLY True,
the respective name IS modified (explicit overrides precedence).
- If --attribute-co-authored-by=False:
- Co-authored-by trailer is NOT added.
- Author/Committer names ARE modified by default (implicit True).
- EXCEPTION: If --attribute-author/--attribute-committer is EXPLICITLY False,
the respective name is NOT modified.
2. When aider_edits = False (User Changes):
- --attribute-co-authored-by is IGNORED (trailer never added).
- Author name is NEVER modified (--attribute-author ignored).
- Committer name IS modified by default (implicit True, as Aider runs `git commit`).
- EXCEPTION: If --attribute-committer is EXPLICITLY False, the name is NOT modified.
Resulting Scenarios:
- Standard AI edit (defaults): Co-authored-by=False -> Author=You(aider), Committer=You(aider)
- AI edit with Co-authored-by (default): Co-authored-by=True -> Author=You, Committer=You, Trailer added
- AI edit with Co-authored-by + Explicit Author: Co-authored-by=True, --attribute-author -> Author=You(aider), Committer=You, Trailer added
- User commit (defaults): aider_edits=False -> Author=You, Committer=You(aider)
- User commit with explicit no-committer: aider_edits=False, --no-attribute-committer -> Author=You, Committer=You
"""
if not fnames and not self.repo.is_dirty(): if not fnames and not self.repo.is_dirty():
return return
@ -124,17 +204,68 @@ class GitRepo:
else: else:
commit_message = self.get_commit_message(diffs, context) commit_message = self.get_commit_message(diffs, context)
if aider_edits and self.attribute_commit_message_author: # Retrieve attribute settings, prioritizing coder.args if available
commit_message = "aider: " + commit_message if coder and hasattr(coder, "args"):
elif self.attribute_commit_message_committer: attribute_author = coder.args.attribute_author
commit_message = "aider: " + commit_message attribute_committer = coder.args.attribute_committer
attribute_commit_message_author = coder.args.attribute_commit_message_author
attribute_commit_message_committer = coder.args.attribute_commit_message_committer
attribute_co_authored_by = coder.args.attribute_co_authored_by
else:
# Fallback to self attributes (initialized from config/defaults)
attribute_author = self.attribute_author
attribute_committer = self.attribute_committer
attribute_commit_message_author = self.attribute_commit_message_author
attribute_commit_message_committer = self.attribute_commit_message_committer
attribute_co_authored_by = self.attribute_co_authored_by
# Determine explicit settings (None means use default behavior)
author_explicit = attribute_author is not None
committer_explicit = attribute_committer is not None
# Determine effective settings (apply default True if not explicit)
effective_author = True if attribute_author is None else attribute_author
effective_committer = True if attribute_committer is None else attribute_committer
# Determine commit message prefixing
prefix_commit_message = aider_edits and (
attribute_commit_message_author or attribute_commit_message_committer
)
# Determine Co-authored-by trailer
commit_message_trailer = ""
if aider_edits and attribute_co_authored_by:
model_name = "unknown-model"
if coder and hasattr(coder, "main_model") and coder.main_model.name:
model_name = coder.main_model.name
commit_message_trailer = (
f"\n\nCo-authored-by: aider ({model_name}) <noreply@aider.chat>"
)
# Determine if author/committer names should be modified
# Author modification applies only to aider edits.
# It's used if effective_author is True AND (co-authored-by is False OR author was explicitly set).
use_attribute_author = (
aider_edits
and effective_author
and (not attribute_co_authored_by or author_explicit)
)
# Committer modification applies regardless of aider_edits (based on tests).
# It's used if effective_committer is True AND (it's not an aider edit with co-authored-by OR committer was explicitly set).
use_attribute_committer = effective_committer and (
not (aider_edits and attribute_co_authored_by) or committer_explicit
)
if not commit_message: if not commit_message:
commit_message = "(no commit message provided)" commit_message = "(no commit message provided)"
full_commit_message = commit_message if prefix_commit_message:
# if context: commit_message = "aider: " + commit_message
# full_commit_message += "\n\n# Aider chat conversation:\n\n" + context
full_commit_message = commit_message + commit_message_trailer
cmd = ["-m", full_commit_message] cmd = ["-m", full_commit_message]
if not self.git_commit_verify: if not self.git_commit_verify:
@ -152,36 +283,30 @@ class GitRepo:
original_user_name = self.repo.git.config("--get", "user.name") original_user_name = self.repo.git.config("--get", "user.name")
original_committer_name_env = os.environ.get("GIT_COMMITTER_NAME") original_committer_name_env = os.environ.get("GIT_COMMITTER_NAME")
original_author_name_env = os.environ.get("GIT_AUTHOR_NAME")
committer_name = f"{original_user_name} (aider)" committer_name = f"{original_user_name} (aider)"
if self.attribute_committer:
os.environ["GIT_COMMITTER_NAME"] = committer_name
if aider_edits and self.attribute_author:
original_author_name_env = os.environ.get("GIT_AUTHOR_NAME")
os.environ["GIT_AUTHOR_NAME"] = committer_name
try: try:
self.repo.git.commit(cmd) # Use context managers to handle environment variables
commit_hash = self.get_head_commit_sha(short=True) with contextlib.ExitStack() as stack:
self.io.tool_output(f"Commit {commit_hash} {commit_message}", bold=True) if use_attribute_committer:
return commit_hash, commit_message stack.enter_context(
set_git_env("GIT_COMMITTER_NAME", committer_name, original_committer_name_env)
)
if use_attribute_author:
stack.enter_context(
set_git_env("GIT_AUTHOR_NAME", committer_name, original_author_name_env)
)
# Perform the commit
self.repo.git.commit(cmd)
commit_hash = self.get_head_commit_sha(short=True)
self.io.tool_output(f"Commit {commit_hash} {commit_message}", bold=True)
return commit_hash, commit_message
except ANY_GIT_ERROR as err: except ANY_GIT_ERROR as err:
self.io.tool_error(f"Unable to commit: {err}") self.io.tool_error(f"Unable to commit: {err}")
finally: # No return here, implicitly returns None
# Restore the env
if self.attribute_committer:
if original_committer_name_env is not None:
os.environ["GIT_COMMITTER_NAME"] = original_committer_name_env
else:
del os.environ["GIT_COMMITTER_NAME"]
if aider_edits and self.attribute_author:
if original_author_name_env is not None:
os.environ["GIT_AUTHOR_NAME"] = original_author_name_env
else:
del os.environ["GIT_AUTHOR_NAME"]
def get_rel_repo_dir(self): def get_rel_repo_dir(self):
try: try:

View file

@ -4,7 +4,7 @@ import tempfile
import time import time
import unittest import unittest
from pathlib import Path from pathlib import Path
from unittest.mock import patch from unittest.mock import MagicMock, patch
import git import git
@ -165,14 +165,11 @@ class TestRepo(unittest.TestCase):
args = mock_send.call_args[0] # Get positional args args = mock_send.call_args[0] # Get positional args
self.assertEqual(args[0][0]["content"], custom_prompt) # Check first message content self.assertEqual(args[0][0]["content"], custom_prompt) # Check first message content
@unittest.skipIf(platform.system() == "Windows", "Git env var behavior differs on Windows")
@patch("aider.repo.GitRepo.get_commit_message") @patch("aider.repo.GitRepo.get_commit_message")
def test_commit_with_custom_committer_name(self, mock_send): def test_commit_with_custom_committer_name(self, mock_send):
mock_send.return_value = '"a good commit message"' mock_send.return_value = '"a good commit message"'
# Cleanup of the git temp dir explodes on windows
if platform.system() == "Windows":
return
with GitTemporaryDirectory(): with GitTemporaryDirectory():
# new repo # new repo
raw_repo = git.Repo() raw_repo = git.Repo()
@ -185,32 +182,192 @@ class TestRepo(unittest.TestCase):
raw_repo.git.commit("-m", "initial commit") raw_repo.git.commit("-m", "initial commit")
io = InputOutput() io = InputOutput()
git_repo = GitRepo(io, None, None) # Initialize GitRepo with default None values for attributes
git_repo = GitRepo(io, None, None, attribute_author=None, attribute_committer=None)
# commit a change # commit a change with aider_edits=True (using default attributes)
fname.write_text("new content") fname.write_text("new content")
git_repo.commit(fnames=[str(fname)], aider_edits=True) commit_result = git_repo.commit(fnames=[str(fname)], aider_edits=True)
self.assertIsNotNone(commit_result)
# check the committer name # check the committer name (defaults interpreted as True)
commit = raw_repo.head.commit commit = raw_repo.head.commit
self.assertEqual(commit.author.name, "Test User (aider)") self.assertEqual(commit.author.name, "Test User (aider)")
self.assertEqual(commit.committer.name, "Test User (aider)") self.assertEqual(commit.committer.name, "Test User (aider)")
# commit a change without aider_edits # commit a change without aider_edits (using default attributes)
fname.write_text("new content again!") fname.write_text("new content again!")
git_repo.commit(fnames=[str(fname)], aider_edits=False) commit_result = git_repo.commit(fnames=[str(fname)], aider_edits=False)
self.assertIsNotNone(commit_result)
# check the committer name # check the committer name (author not modified, committer still modified by default)
commit = raw_repo.head.commit commit = raw_repo.head.commit
self.assertEqual(commit.author.name, "Test User") self.assertEqual(commit.author.name, "Test User")
self.assertEqual(commit.committer.name, "Test User (aider)") self.assertEqual(commit.committer.name, "Test User (aider)")
# Now test with explicit False
git_repo_explicit_false = GitRepo(io, None, None, attribute_author=False, attribute_committer=False)
fname.write_text("explicit false content")
commit_result = git_repo_explicit_false.commit(fnames=[str(fname)], aider_edits=True)
self.assertIsNotNone(commit_result)
commit = raw_repo.head.commit
self.assertEqual(commit.author.name, "Test User") # Explicit False
self.assertEqual(commit.committer.name, "Test User") # Explicit False
# check that the original committer name is restored # check that the original committer name is restored
original_committer_name = os.environ.get("GIT_COMMITTER_NAME") original_committer_name = os.environ.get("GIT_COMMITTER_NAME")
self.assertIsNone(original_committer_name) self.assertIsNone(original_committer_name)
original_author_name = os.environ.get("GIT_AUTHOR_NAME") original_author_name = os.environ.get("GIT_AUTHOR_NAME")
self.assertIsNone(original_author_name) self.assertIsNone(original_author_name)
# Test user commit with explicit no-committer attribution
git_repo_user_no_committer = GitRepo(io, None, None, attribute_committer=False)
fname.write_text("user no committer content")
commit_result = git_repo_user_no_committer.commit(fnames=[str(fname)], aider_edits=False)
self.assertIsNotNone(commit_result)
commit = raw_repo.head.commit
self.assertEqual(commit.author.name, "Test User", msg="Author name should not be modified for user commits")
self.assertEqual(commit.committer.name, "Test User", msg="Committer name should not be modified when attribute_committer=False")
@unittest.skipIf(platform.system() == "Windows", "Git env var behavior differs on Windows")
def test_commit_with_co_authored_by(self):
with GitTemporaryDirectory():
# new repo
raw_repo = git.Repo()
raw_repo.config_writer().set_value("user", "name", "Test User").release()
raw_repo.config_writer().set_value("user", "email", "test@example.com").release()
# add a file and commit it
fname = Path("file.txt")
fname.touch()
raw_repo.git.add(str(fname))
raw_repo.git.commit("-m", "initial commit")
# Mock coder args: Co-authored-by enabled, author/committer use default (None)
mock_coder = MagicMock()
mock_coder.args.attribute_co_authored_by = True
mock_coder.args.attribute_author = None # Default
mock_coder.args.attribute_committer = None # Default
mock_coder.args.attribute_commit_message_author = False
mock_coder.args.attribute_commit_message_committer = False
# The code uses coder.main_model.name for the co-authored-by line
mock_coder.main_model = MagicMock()
mock_coder.main_model.name = "gpt-test"
io = InputOutput()
git_repo = GitRepo(io, None, None)
# commit a change with aider_edits=True and co-authored-by flag
fname.write_text("new content")
commit_result = git_repo.commit(fnames=[str(fname)], aider_edits=True, coder=mock_coder, message="Aider edit")
self.assertIsNotNone(commit_result)
# check the commit message and author/committer
commit = raw_repo.head.commit
self.assertIn("Co-authored-by: aider (gpt-test) <noreply@aider.chat>", commit.message)
self.assertEqual(commit.message.splitlines()[0], "Aider edit")
# With default (None), co-authored-by takes precedence
self.assertEqual(commit.author.name, "Test User", msg="Author name should not be modified when co-authored-by takes precedence")
self.assertEqual(commit.committer.name, "Test User", msg="Committer name should not be modified when co-authored-by takes precedence")
@unittest.skipIf(platform.system() == "Windows", "Git env var behavior differs on Windows")
def test_commit_co_authored_by_with_explicit_name_modification(self):
# Test scenario where Co-authored-by is true AND author/committer modification are explicitly True
with GitTemporaryDirectory():
# Setup repo...
# new repo
raw_repo = git.Repo()
raw_repo.config_writer().set_value("user", "name", "Test User").release()
raw_repo.config_writer().set_value("user", "email", "test@example.com").release()
# add a file and commit it
fname = Path("file.txt")
fname.touch()
raw_repo.git.add(str(fname))
raw_repo.git.commit("-m", "initial commit")
# Mock coder args: Co-authored-by enabled, author/committer modification explicitly enabled
mock_coder = MagicMock()
mock_coder.args.attribute_co_authored_by = True
mock_coder.args.attribute_author = True # Explicitly enable
mock_coder.args.attribute_committer = True # Explicitly enable
mock_coder.args.attribute_commit_message_author = False
mock_coder.args.attribute_commit_message_committer = False
mock_coder.main_model = MagicMock()
mock_coder.main_model.name = "gpt-test-combo"
io = InputOutput()
git_repo = GitRepo(io, None, None)
# commit a change with aider_edits=True and combo flags
fname.write_text("new content combo")
commit_result = git_repo.commit(fnames=[str(fname)], aider_edits=True, coder=mock_coder, message="Aider combo edit")
self.assertIsNotNone(commit_result)
# check the commit message and author/committer
commit = raw_repo.head.commit
self.assertIn("Co-authored-by: aider (gpt-test-combo) <noreply@aider.chat>", commit.message)
self.assertEqual(commit.message.splitlines()[0], "Aider combo edit")
# When co-authored-by is true BUT author/committer are explicit True, modification SHOULD happen
self.assertEqual(commit.author.name, "Test User (aider)", msg="Author name should be modified when explicitly True, even with co-author")
self.assertEqual(commit.committer.name, "Test User (aider)", msg="Committer name should be modified when explicitly True, even with co-author")
@unittest.skipIf(platform.system() == "Windows", "Git env var behavior differs on Windows")
def test_commit_ai_edits_no_coauthor_explicit_false(self):
# Test AI edits (aider_edits=True) when co-authored-by is False,
# but author or committer attribution is explicitly disabled.
with GitTemporaryDirectory():
# Setup repo
raw_repo = git.Repo()
raw_repo.config_writer().set_value("user", "name", "Test User").release()
raw_repo.config_writer().set_value("user", "email", "test@example.com").release()
fname = Path("file.txt")
fname.touch()
raw_repo.git.add(str(fname))
raw_repo.git.commit("-m", "initial commit")
io = InputOutput()
# Case 1: attribute_author = False, attribute_committer = None (default True)
mock_coder_no_author = MagicMock()
mock_coder_no_author.args.attribute_co_authored_by = False
mock_coder_no_author.args.attribute_author = False # Explicit False
mock_coder_no_author.args.attribute_committer = None # Default True
mock_coder_no_author.args.attribute_commit_message_author = False
mock_coder_no_author.args.attribute_commit_message_committer = False
mock_coder_no_author.main_model = MagicMock()
mock_coder_no_author.main_model.name = "gpt-test-no-author"
git_repo_no_author = GitRepo(io, None, None)
fname.write_text("no author content")
commit_result = git_repo_no_author.commit(fnames=[str(fname)], aider_edits=True, coder=mock_coder_no_author, message="Aider no author")
self.assertIsNotNone(commit_result)
commit = raw_repo.head.commit
self.assertNotIn("Co-authored-by:", commit.message)
self.assertEqual(commit.author.name, "Test User") # Explicit False
self.assertEqual(commit.committer.name, "Test User (aider)") # Default True
# Case 2: attribute_author = None (default True), attribute_committer = False
mock_coder_no_committer = MagicMock()
mock_coder_no_committer.args.attribute_co_authored_by = False
mock_coder_no_committer.args.attribute_author = None # Default True
mock_coder_no_committer.args.attribute_committer = False # Explicit False
mock_coder_no_committer.args.attribute_commit_message_author = False
mock_coder_no_committer.args.attribute_commit_message_committer = False
mock_coder_no_committer.main_model = MagicMock()
mock_coder_no_committer.main_model.name = "gpt-test-no-committer"
git_repo_no_committer = GitRepo(io, None, None)
fname.write_text("no committer content")
commit_result = git_repo_no_committer.commit(fnames=[str(fname)], aider_edits=True, coder=mock_coder_no_committer, message="Aider no committer")
self.assertIsNotNone(commit_result)
commit = raw_repo.head.commit
self.assertNotIn("Co-authored-by:", commit.message)
self.assertEqual(commit.author.name, "Test User (aider)", msg="Author name should be modified (default True) when co-author=False")
self.assertEqual(commit.committer.name, "Test User", msg="Committer name should not be modified (explicit False) when co-author=False")
def test_get_tracked_files(self): def test_get_tracked_files(self):
# Create a temporary directory # Create a temporary directory
tempdir = Path(tempfile.mkdtemp()) tempdir = Path(tempfile.mkdtemp())
@ -404,14 +561,12 @@ class TestRepo(unittest.TestCase):
git_repo = GitRepo(InputOutput(), None, None) git_repo = GitRepo(InputOutput(), None, None)
git_repo.commit(fnames=[str(fname)]) commit_result = git_repo.commit(fnames=[str(fname)])
self.assertIsNone(commit_result)
@unittest.skipIf(platform.system() == "Windows", "Git hook execution differs on Windows")
def test_git_commit_verify(self): def test_git_commit_verify(self):
"""Test that git_commit_verify controls whether --no-verify is passed to git commit""" """Test that git_commit_verify controls whether --no-verify is passed to git commit"""
# Skip on Windows as hook execution works differently
if platform.system() == "Windows":
return
with GitTemporaryDirectory(): with GitTemporaryDirectory():
# Create a new repo # Create a new repo
raw_repo = git.Repo() raw_repo = git.Repo()