Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 25 additions & 17 deletions src/git/src/mcp_server_git/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -169,31 +169,36 @@ def git_log(repo: git.Repo, max_count: int = 10, start_timestamp: Optional[str]
args.extend(['--since', start_timestamp])
if end_timestamp:
args.extend(['--until', end_timestamp])
args.extend(['--format=%H%n%an%n%ad%n%s%n'])
args.extend([f'-n{max_count}', '--format=%x1e%H%x1f%an <%ae>%x1f%aI%x1f%s'])

log_output = repo.git.log(*args).split('\n')
raw_output = repo.git.log(*args)
if not raw_output.strip():
return []

log = []
# Process commits in groups of 4 (hash, author, date, message)
for i in range(0, len(log_output), 4):
if i + 3 < len(log_output) and len(log) < max_count:
for record in raw_output.strip('\n\x1e').split('\x1e'):
parts = record.split('\x1f')
if len(parts) >= 4:
log.append(
f"Commit: {log_output[i]}\n"
f"Author: {log_output[i+1]}\n"
f"Date: {log_output[i+2]}\n"
f"Message: {log_output[i+3]}\n"
f"Commit: {parts[0]}\n"
f"Author: {parts[1]}\n"
f"Date: {parts[2]}\n"
f"Message: {parts[3]}\n"
)
return log
else:
# Use existing logic for simple log without date filtering
commits = list(repo.iter_commits(max_count=max_count))
log = []
for commit in commits:
author_str = f"{commit.author.name} <{commit.author.email}>" if commit.author else "Unknown"
date_str = commit.authored_datetime.isoformat() if hasattr(commit, "authored_datetime") else str(commit.committed_datetime)
message_str = commit.message.strip() if isinstance(commit.message, str) else str(commit.message)
log.append(
f"Commit: {commit.hexsha!r}\n"
f"Author: {commit.author!r}\n"
f"Date: {commit.authored_datetime}\n"
f"Message: {commit.message!r}\n"
f"Commit: {commit.hexsha}\n"
f"Author: {author_str}\n"
f"Date: {date_str}\n"
f"Message: {message_str}\n"
)
return log

Expand Down Expand Up @@ -228,11 +233,14 @@ def git_show(repo: git.Repo, revision: str) -> str:
if revision.startswith("-"):
raise BadName(f"Invalid revision: '{revision}' - cannot start with '-'")
commit = repo.commit(revision)
author_str = f"{commit.author.name} <{commit.author.email}>" if commit.author else "Unknown"
date_str = commit.authored_datetime.isoformat() if hasattr(commit, "authored_datetime") else str(commit.committed_datetime)
message_str = commit.message.strip() if isinstance(commit.message, str) else str(commit.message)
output = [
f"Commit: {commit.hexsha!r}\n"
f"Author: {commit.author!r}\n"
f"Date: {commit.authored_datetime!r}\n"
f"Message: {commit.message!r}\n"
f"Commit: {commit.hexsha}\n"
f"Author: {author_str}\n"
f"Date: {date_str}\n"
f"Message: {message_str}\n"
]
if commit.parents:
parent = commit.parents[0]
Expand Down
34 changes: 28 additions & 6 deletions src/git/tests/test_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -227,12 +227,34 @@ def test_git_log(test_repository):
assert "Date:" in result[0]
assert "Message:" in result[0]

def test_git_log_default(test_repository):
result = git_log(test_repository)

assert isinstance(result, list)
assert len(result) >= 1
assert "initial commit" in result[0]
def test_git_log_schema_consistency(test_repository):
for i in range(3):
file_path = Path(test_repository.working_dir) / f"log_consistent_{i}.txt"
file_path.write_text(f"content {i}")
test_repository.index.add([f"log_consistent_{i}.txt"])
test_repository.index.commit(f"commit consistent {i}")

unfiltered = git_log(test_repository, max_count=2)
filtered = git_log(test_repository, max_count=2, start_timestamp="2020-01-01")

assert len(unfiltered) == 2
assert len(filtered) == 2

# Both must NOT have quotes or raw repr around hexsha/message
assert "'" not in unfiltered[0].split("\n")[0]
assert "'" not in filtered[0].split("\n")[0]

# Check that field structure is identical across both
for entry in [unfiltered[0], filtered[0]]:
lines = entry.strip().split("\n")
assert lines[0].startswith("Commit: ")
assert lines[1].startswith("Author: ")
assert lines[2].startswith("Date: ")
assert lines[3].startswith("Message: ")
# Hexsha must be 40 chars clean
sha = lines[0].replace("Commit: ", "").strip()
assert len(sha) == 40
assert not sha.startswith("'") and not sha.endswith("'")

def test_git_create_branch(test_repository):
result = git_create_branch(test_repository, "new-feature-branch")
Expand Down