Skip to content

Commit bcc8506

Browse files
CM-55207-Fix commit range parsing for empty remote (#371)
1 parent 348852c commit bcc8506

File tree

3 files changed

+80
-2
lines changed

3 files changed

+80
-2
lines changed

cycode/cli/consts.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,7 @@
269269

270270
# git consts
271271
COMMIT_DIFF_DELETED_FILE_CHANGE_TYPE = 'D'
272+
COMMIT_RANGE_ALL_COMMITS = '--all'
272273
GIT_HEAD_COMMIT_REV = 'HEAD'
273274
GIT_EMPTY_TREE_OBJECT = '4b825dc642cb6eb9a060e54bf8d69288fbee4904'
274275
EMPTY_COMMIT_SHA = '0000000000000000000000000000000000000000'

cycode/cli/files_collector/commit_range_documents.py

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -351,10 +351,10 @@ def calculate_pre_push_commit_range(push_update_details: str) -> Optional[str]:
351351
return f'{merge_base}..{local_object_name}'
352352

353353
logger.debug('Failed to find merge base with any default branch')
354-
return '--all'
354+
return consts.COMMIT_RANGE_ALL_COMMITS
355355
except Exception as e:
356356
logger.debug('Failed to get repo for pre-push commit range calculation: %s', exc_info=e)
357-
return '--all'
357+
return consts.COMMIT_RANGE_ALL_COMMITS
358358

359359
# If deleting a branch (local_object_name is all zeros), no need to scan
360360
if local_object_name == consts.EMPTY_COMMIT_SHA:
@@ -448,9 +448,25 @@ def parse_commit_range(commit_range: str, path: str) -> tuple[Optional[str], Opt
448448
- 'commit' (interpreted as 'commit..HEAD')
449449
- '..to' (interpreted as 'HEAD..to')
450450
- 'from..' (interpreted as 'from..HEAD')
451+
- '--all' (interpreted as 'first_commit..HEAD' to scan all commits)
451452
"""
452453
repo = git_proxy.get_repo(path)
453454

455+
# Handle '--all' special case: scan all commits from first to HEAD
456+
# Usually represents an empty remote repository
457+
if commit_range == consts.COMMIT_RANGE_ALL_COMMITS:
458+
try:
459+
head_commit = repo.rev_parse(consts.GIT_HEAD_COMMIT_REV).hexsha
460+
all_commits = repo.git.rev_list('--reverse', head_commit).strip()
461+
if all_commits:
462+
first_commit = all_commits.splitlines()[0]
463+
return first_commit, head_commit, '..'
464+
logger.warning("No commits found for range '%s'", commit_range)
465+
return None, None, None
466+
except Exception as e:
467+
logger.warning("Failed to parse commit range '%s'", commit_range, exc_info=e)
468+
return None, None, None
469+
454470
separator = '..'
455471
if '...' in commit_range:
456472
from_spec, to_spec = commit_range.split('...', 1)

tests/cli/files_collector/test_commit_range_documents.py

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -882,6 +882,67 @@ def test_single_commit_spec(self) -> None:
882882
parsed_from, parsed_to, separator = parse_commit_range(a, temp_dir)
883883
assert (parsed_from, parsed_to, separator) == (a, c, '..')
884884

885+
def test_parse_all_for_empty_remote_scenario(self) -> None:
886+
"""Test that '--all' is parsed correctly for empty remote repository.
887+
This repository has one commit locally.
888+
"""
889+
with temporary_git_repository() as (temp_dir, repo):
890+
# Create a local commit (simulating first commit to empty remote)
891+
test_file = os.path.join(temp_dir, 'test.py')
892+
with open(test_file, 'w') as f:
893+
f.write("print('test')")
894+
895+
repo.index.add(['test.py'])
896+
commit = repo.index.commit('Initial commit')
897+
898+
# Test that '--all' (returned by calculate_pre_push_commit_range for empty remote)
899+
# can be parsed to a valid commit range
900+
parsed_from, parsed_to, separator = parse_commit_range('--all', temp_dir)
901+
902+
# Should return first commit to HEAD (which is the only commit in this case)
903+
assert parsed_from == commit.hexsha
904+
assert parsed_to == commit.hexsha
905+
assert separator == '..'
906+
907+
def test_parse_all_for_empty_remote_scenario_with_two_commits(self) -> None:
908+
"""Test that '--all' is parsed correctly for empty remote repository.
909+
This repository has two commits locally.
910+
"""
911+
with temporary_git_repository() as (temp_dir, repo):
912+
# Create first commit
913+
test_file = os.path.join(temp_dir, 'test.py')
914+
with open(test_file, 'w') as f:
915+
f.write("print('test')")
916+
917+
repo.index.add(['test.py'])
918+
commit1 = repo.index.commit('First commit')
919+
920+
# Create second commit
921+
test_file2 = os.path.join(temp_dir, 'test2.py')
922+
with open(test_file2, 'w') as f:
923+
f.write("print('test2')")
924+
925+
repo.index.add(['test2.py'])
926+
commit2 = repo.index.commit('Second commit')
927+
928+
# Test that '--all' returns first commit to HEAD (second commit)
929+
parsed_from, parsed_to, separator = parse_commit_range('--all', temp_dir)
930+
931+
# Should return first commit to HEAD (second commit)
932+
assert parsed_from == commit1.hexsha # First commit
933+
assert parsed_to == commit2.hexsha # HEAD (second commit)
934+
assert separator == '..'
935+
936+
def test_parse_all_with_empty_repository_returns_none(self) -> None:
937+
"""Test that '--all' returns None when repository has no commits."""
938+
with temporary_git_repository() as (temp_dir, repo):
939+
# Empty repository with no commits
940+
parsed_from, parsed_to, separator = parse_commit_range('--all', temp_dir)
941+
# Should return None, None, None when HEAD doesn't exist
942+
assert parsed_from is None
943+
assert parsed_to is None
944+
assert separator is None
945+
885946

886947
class TestParsePreReceiveInput:
887948
"""Test the parse_pre_receive_input function with various pre-receive hook input scenarios."""

0 commit comments

Comments
 (0)