import re
import subprocess
import pytest
from .conftest import strip_ansi_colours
@pytest.mark.parametrize("format_flag", ["", "--format=full", "--format=fuller"])
def test_log(commit_env_config, git2cpp_path, tmp_path, format_flag):
cmd_init = [git2cpp_path, "init", "."]
p_init = subprocess.run(cmd_init, capture_output=True, cwd=tmp_path)
assert p_init.returncode == 0
p = tmp_path / "mook_file.txt"
p.write_text("")
cmd_add = [git2cpp_path, "add", "mook_file.txt"]
p_add = subprocess.run(cmd_add, cwd=tmp_path, text=True)
assert p_add.returncode == 0
cmd_commit = [git2cpp_path, "commit", "-m", "test commit"]
p_commit = subprocess.run(cmd_commit, cwd=tmp_path, text=True)
assert p_commit.returncode == 0
cmd_log = [git2cpp_path, "log"]
if format_flag != "":
cmd_log.append(format_flag)
p_log = subprocess.run(cmd_log, capture_output=True, cwd=tmp_path, text=True)
assert p_log.returncode == 0
assert "Jane Doe" in p_log.stdout
assert "test commit" in p_log.stdout
if format_flag == "":
assert "Commit" not in p_log.stdout
else:
assert "Commit" in p_log.stdout
if format_flag == "--format=full":
assert "Date" not in p_log.stdout
else:
assert "CommitDate" in p_log.stdout
def test_log_nogit(commit_env_config, git2cpp_path, tmp_path):
cmd_log = [git2cpp_path, "log"]
p_log = subprocess.run(cmd_log, capture_output=True, cwd=tmp_path, text=True)
assert p_log.returncode != 0
assert "error: could not find repository at" in p_log.stderr
@pytest.mark.parametrize("max_count_flag", ["", "-n", "--max-count"])
def test_max_count(
repo_init_with_commit, commit_env_config, git2cpp_path, tmp_path, max_count_flag
):
assert (tmp_path / "initial.txt").exists()
p2 = tmp_path / "second.txt"
p2.write_text("second file")
cmd_add2 = [git2cpp_path, "add", "second.txt"]
subprocess.run(cmd_add2, capture_output=True, cwd=tmp_path, text=True, check=True)
cmd_commit2 = [git2cpp_path, "commit", "-m", "Second commit"]
subprocess.run(cmd_commit2, capture_output=True, cwd=tmp_path, text=True, check=True)
p3 = tmp_path / "third.txt"
p3.write_text("third file")
cmd_add3 = [git2cpp_path, "add", "third.txt"]
subprocess.run(cmd_add3, capture_output=True, cwd=tmp_path, text=True, check=True)
cmd_commit3 = [git2cpp_path, "commit", "-m", "Third commit"]
subprocess.run(cmd_commit3, capture_output=True, cwd=tmp_path, text=True, check=True)
cmd_log = [git2cpp_path, "log"]
if max_count_flag != "":
cmd_log.append(max_count_flag)
cmd_log.append("2")
p_log = subprocess.run(cmd_log, capture_output=True, cwd=tmp_path, text=True)
assert p_log.returncode == 0
if max_count_flag == "":
assert p_log.stdout.count("Author") > 2
else:
assert p_log.stdout.count("Author") == 2
def test_log_with_head_reference(repo_init_with_commit, commit_env_config, git2cpp_path, tmp_path):
"""Test that HEAD reference is shown on the latest commit."""
assert (tmp_path / "initial.txt").exists()
# Create a new commit
p = tmp_path / "test_file.txt"
p.write_text("test content")
subprocess.run([git2cpp_path, "add", "test_file.txt"], cwd=tmp_path, check=True)
subprocess.run([git2cpp_path, "commit", "-m", "test commit"], cwd=tmp_path, check=True)
# Run log with max count 1 to get only the latest commit
p_log = subprocess.run(
[git2cpp_path, "log", "-n", "1"], capture_output=True, cwd=tmp_path, text=True
)
assert p_log.returncode == 0
# Check that HEAD reference is shown
assert "HEAD ->" in strip_ansi_colours(p_log.stdout)
assert "master" in p_log.stdout or "main" in p_log.stdout
def test_log_with_tag(commit_env_config, git2cpp_path, tmp_path):
"""Test that tags are shown in log output."""
cmd_init = [git2cpp_path, "init", "."]
p_init = subprocess.run(cmd_init, capture_output=True, cwd=tmp_path)
assert p_init.returncode == 0
# Create a commit and tag it
p = tmp_path / "tagged_file.txt"
p.write_text("tagged content")
subprocess.run([git2cpp_path, "add", "tagged_file.txt"], cwd=tmp_path, check=True)
subprocess.run([git2cpp_path, "commit", "-m", "tagged commit"], cwd=tmp_path, check=True)
# Create a tag (using git command since git2cpp might not have tag creation yet)
subprocess.run([git2cpp_path, "tag", "v1.0.0"], cwd=tmp_path, check=True)
# Run log
p_log = subprocess.run(
[git2cpp_path, "log", "-n", "1"], capture_output=True, cwd=tmp_path, text=True
)
assert p_log.returncode == 0
# Check that tag is shown
assert "tag: v1.0.0" in p_log.stdout
def test_log_with_multiple_tags(commit_env_config, git2cpp_path, tmp_path):
"""Test that multiple tags on the same commit are all shown."""
cmd_init = [git2cpp_path, "init", "."]
p_init = subprocess.run(cmd_init, capture_output=True, cwd=tmp_path)
assert p_init.returncode == 0
# Create a commit
p = tmp_path / "multi_tag_file.txt"
p.write_text("content")
subprocess.run([git2cpp_path, "add", "multi_tag_file.txt"], cwd=tmp_path, check=True)
subprocess.run([git2cpp_path, "commit", "-m", "multi tag commit"], cwd=tmp_path, check=True)
# Create multiple tags
subprocess.run([git2cpp_path, "tag", "v1.0.0"], cwd=tmp_path, check=True)
subprocess.run([git2cpp_path, "tag", "stable"], cwd=tmp_path, check=True)
subprocess.run([git2cpp_path, "tag", "latest"], cwd=tmp_path, check=True)
# Run log
p_log = subprocess.run(
[git2cpp_path, "log", "-n", "1"], capture_output=True, cwd=tmp_path, text=True
)
assert p_log.returncode == 0
# Check that all tags are shown
assert "tag: v1.0.0" in p_log.stdout
assert "tag: stable" in p_log.stdout
assert "tag: latest" in p_log.stdout
def test_log_with_annotated_tag(commit_env_config, git2cpp_path, tmp_path):
"""Test that annotated tags are shown in log output."""
cmd_init = [git2cpp_path, "init", "."]
p_init = subprocess.run(cmd_init, capture_output=True, cwd=tmp_path)
assert p_init.returncode == 0
# Create a commit
p = tmp_path / "annotated_tag_file.txt"
p.write_text("content")
subprocess.run([git2cpp_path, "add", "annotated_tag_file.txt"], cwd=tmp_path, check=True)
subprocess.run([git2cpp_path, "commit", "-m", "annotated tag commit"], cwd=tmp_path, check=True)
# Create an annotated tag
subprocess.run(
[git2cpp_path, "tag", "-a", "v2.0.0", "-m", "Version 2.0.0"],
cwd=tmp_path,
check=True,
)
# Run log
p_log = subprocess.run(
[git2cpp_path, "log", "-n", "1"], capture_output=True, cwd=tmp_path, text=True
)
assert p_log.returncode == 0
# Check that annotated tag is shown
assert "tag: v2.0.0" in p_log.stdout
def test_log_with_branch(commit_env_config, git2cpp_path, tmp_path):
"""Test that branches are shown in log output."""
cmd_init = [git2cpp_path, "init", "."]
p_init = subprocess.run(cmd_init, capture_output=True, cwd=tmp_path)
assert p_init.returncode == 0
# Create a commit
p = tmp_path / "branch_file.txt"
p.write_text("content")
subprocess.run([git2cpp_path, "add", "branch_file.txt"], cwd=tmp_path, check=True)
subprocess.run([git2cpp_path, "commit", "-m", "branch commit"], cwd=tmp_path, check=True)
# Create a new branch pointing to HEAD
subprocess.run([git2cpp_path, "branch", "feature-branch"], cwd=tmp_path, check=True)
# Run log
p_log = subprocess.run(
[git2cpp_path, "log", "-n", "1"], capture_output=True, cwd=tmp_path, text=True
)
assert p_log.returncode == 0
# Check that both branches are shown (HEAD -> master/main and feature-branch)
assert "feature-branch" in p_log.stdout
def test_log_with_remote_branches(xtl_clone, commit_env_config, git2cpp_path, tmp_path):
"""Test that remote branches are shown in log output."""
assert (tmp_path / "xtl").exists()
xtl_path = tmp_path / "xtl"
# The xtl_clone fixture already has remote branches (origin/master, etc.)
# Run log to check they appear
p_log = subprocess.run(
[git2cpp_path, "log", "-n", "1"], capture_output=True, cwd=xtl_path, text=True
)
assert p_log.returncode == 0
# Check that origin remote branches are shown
assert "origin/master" in p_log.stdout
def test_log_commit_without_references(commit_env_config, git2cpp_path, tmp_path):
"""Test that commits without any references don't show empty parentheses."""
cmd_init = [git2cpp_path, "init", "."]
p_init = subprocess.run(cmd_init, capture_output=True, cwd=tmp_path)
assert p_init.returncode == 0
# Create two commits - the second one will have refs, the first won't
for i in range(2):
p = tmp_path / f"file_{i}.txt"
p.write_text(f"content {i}")
subprocess.run([git2cpp_path, "add", f"file_{i}.txt"], cwd=tmp_path, check=True)
subprocess.run([git2cpp_path, "commit", "-m", f"commit {i}"], cwd=tmp_path, check=True)
# Run log with 2 commits
p_log = subprocess.run(
[git2cpp_path, "log", "-n", "2"], capture_output=True, cwd=tmp_path, text=True
)
assert p_log.returncode == 0
# First commit line should have references
lines = strip_ansi_colours(p_log.stdout).split("\n")
first_commit_line = next(line for line in lines if line.startswith("commit"))
assert "(" in first_commit_line # Has references
# Second commit (older one) should not have empty parentheses
second_commit_line = [line for line in lines if line.startswith("commit")][1]
# Should either have no parentheses or have actual references
if "(" in second_commit_line:
# If it has parentheses, they shouldn't be empty
assert "()" not in second_commit_line
@pytest.mark.parametrize("abbrev_commit_flag", ["", "--abbrev-commit", "--no-abbrev-commit"])
@pytest.mark.parametrize("abbrev_flag", ["", "--abbrev=10"])
def test_log_abbrev_commit_flags(
repo_init_with_commit,
commit_env_config,
git2cpp_path,
tmp_path,
abbrev_commit_flag,
abbrev_flag,
):
"""Test for --abbrev-commit, --abbrev= (only applies when --abbrev-commit is set) and --no-abbrev-commit"""
assert (tmp_path / "initial.txt").exists()
cmd = [git2cpp_path, "log", "-n", "1"]
if abbrev_commit_flag != "":
cmd.append(abbrev_commit_flag)
if abbrev_flag != "":
cmd.append(abbrev_flag)
p = subprocess.run(cmd, capture_output=True, cwd=tmp_path, text=True)
assert p.returncode == 0
m = re.search(r"^commit\s+([0-9a-fA-F]+)", strip_ansi_colours(p.stdout), flags=re.MULTILINE)
if abbrev_commit_flag in ["", "--no-abbrev-commit"]:
assert len(m.group(1)) == 40
else:
if abbrev_flag == "--abbrev=10":
assert len(m.group(1)) == 10
else:
assert len(m.group(1)) == 7
def test_log_format_oneline(repo_init_with_commit, git2cpp_path, tmp_path):
"""Test --format=oneline prints the full sha"""
assert (tmp_path / "initial.txt").exists()
p = subprocess.run(
[git2cpp_path, "rev-parse", "HEAD"],
cwd=tmp_path,
capture_output=True,
text=True,
)
assert p.returncode == 0
full_sha = p.stdout.strip()
p = subprocess.run(
[git2cpp_path, "log", "--format=oneline", "-n", "1"],
cwd=tmp_path,
capture_output=True,
text=True,
)
assert p.returncode == 0
# assert: should contain full 40-hex sha and subject
assert full_sha in p.stdout
assert "Initial commit" in p.stdout
def test_log_oneline(repo_init_with_commit, git2cpp_path, tmp_path):
"""Test --oneline prints the default length sha (i.e 7)"""
assert (tmp_path / "initial.txt").exists()
p = subprocess.run(
[git2cpp_path, "rev-parse", "HEAD"],
cwd=tmp_path,
capture_output=True,
text=True,
)
assert p.returncode == 0
full_sha = str(p.stdout.strip())
abbrev7 = full_sha[:7]
subprocess.run([git2cpp_path, "tag", "v1"], capture_output=True, cwd=tmp_path, check=True)
p = subprocess.run(
[git2cpp_path, "log", "--oneline", "-n", "1"],
cwd=tmp_path,
capture_output=True,
text=True,
)
assert p.returncode == 0
assert abbrev7 in p.stdout
assert full_sha not in p.stdout
assert "tag: v1" in p.stdout
assert "Initial commit" in p.stdout
def test_log_oneline_no_abbrev_commit(repo_init_with_commit, git2cpp_path, tmp_path):
"""Test --oneline prints prints the full sha when using --no-abbrev-commit"""
assert (tmp_path / "initial.txt").exists()
p = subprocess.run(
[git2cpp_path, "rev-parse", "HEAD"],
cwd=tmp_path,
capture_output=True,
text=True,
)
assert p.returncode == 0
full_sha = str(p.stdout.strip())
p = subprocess.run(
[git2cpp_path, "log", "--oneline", "--no-abbrev-commit", "-n", "1"],
cwd=tmp_path,
capture_output=True,
text=True,
)
assert p.returncode == 0
assert full_sha in p.stdout
assert "Initial commit" in p.stdout