From d389946bcecbc339c4f6231b7918f84aca3c74eb Mon Sep 17 00:00:00 2001 From: Mateusz Sterczewski Date: Fri, 9 Jan 2026 11:32:06 +0100 Subject: [PATCH 1/6] CM-55207-Fix commit range parsing for empty remote --- .../files_collector/commit_range_documents.py | 17 ++++++ .../test_commit_range_documents.py | 60 +++++++++++++++++++ 2 files changed, 77 insertions(+) diff --git a/cycode/cli/files_collector/commit_range_documents.py b/cycode/cli/files_collector/commit_range_documents.py index d92aea81..1c375ae5 100644 --- a/cycode/cli/files_collector/commit_range_documents.py +++ b/cycode/cli/files_collector/commit_range_documents.py @@ -448,9 +448,26 @@ def parse_commit_range(commit_range: str, path: str) -> tuple[Optional[str], Opt - 'commit' (interpreted as 'commit..HEAD') - '..to' (interpreted as 'HEAD..to') - 'from..' (interpreted as 'from..HEAD') + - '--all' (interpreted as 'first_commit..HEAD' to scan all commits) """ repo = git_proxy.get_repo(path) + # Handle '--all' special case: scan all commits from first to HEAD + # Usually represents an empty remote repository + if commit_range == '--all': + try: + head_commit = repo.rev_parse(consts.GIT_HEAD_COMMIT_REV).hexsha + all_commits = repo.git.rev_list('--reverse', head_commit).strip() + if all_commits: + first_commit = all_commits.splitlines()[0] + return first_commit, head_commit, '..' + else: + logger.warning("No commits found for range '%s'", commit_range) + return None, None, None + except Exception as e: + logger.warning("Failed to parse commit range '%s'", commit_range, exc_info=e) + return None, None, None + separator = '..' if '...' in commit_range: from_spec, to_spec = commit_range.split('...', 1) diff --git a/tests/cli/files_collector/test_commit_range_documents.py b/tests/cli/files_collector/test_commit_range_documents.py index 0779f678..d8bd5bda 100644 --- a/tests/cli/files_collector/test_commit_range_documents.py +++ b/tests/cli/files_collector/test_commit_range_documents.py @@ -882,6 +882,66 @@ def test_single_commit_spec(self) -> None: parsed_from, parsed_to, separator = parse_commit_range(a, temp_dir) assert (parsed_from, parsed_to, separator) == (a, c, '..') + def test_parse_all_for_empty_remote_scenario(self) -> None: + """Test that '--all' is parsed correctly for empty remote repository. + This repository has one commit locally. + """ + with temporary_git_repository() as (temp_dir, repo): + # Create a local commit (simulating first commit to empty remote) + test_file = os.path.join(temp_dir, 'test.py') + with open(test_file, 'w') as f: + f.write("print('test')") + + repo.index.add(['test.py']) + commit = repo.index.commit('Initial commit') + + # Test that '--all' (returned by calculate_pre_push_commit_range for empty remote) + # can be parsed to a valid commit range + parsed_from, parsed_to, separator = parse_commit_range('--all', temp_dir) + + # Should return first commit to HEAD (which is the only commit in this case) + assert parsed_from == commit.hexsha + assert parsed_to == commit.hexsha + assert separator == '..' + + def test_parse_all_for_empty_remote_scenario_with_two_commits(self) -> None: + """Test that '--all' is parsed correctly for empty remote repository. + This repository has two commits locally. + """ + with temporary_git_repository() as (temp_dir, repo): + # Create first commit + test_file = os.path.join(temp_dir, 'test.py') + with open(test_file, 'w') as f: + f.write("print('test')") + + repo.index.add(['test.py']) + commit1 = repo.index.commit('First commit') + + # Create second commit + test_file2 = os.path.join(temp_dir, 'test2.py') + with open(test_file2, 'w') as f: + f.write("print('test2')") + + repo.index.add(['test2.py']) + commit2 = repo.index.commit('Second commit') + + # Test that '--all' returns first commit to HEAD (second commit) + parsed_from, parsed_to, separator = parse_commit_range('--all', temp_dir) + + # Should return first commit to HEAD (second commit) + assert parsed_from == commit1.hexsha # First commit + assert parsed_to == commit2.hexsha # HEAD (second commit) + assert separator == '..' + + def test_parse_all_with_empty_repository_returns_none(self) -> None: + """Test that '--all' returns None when repository has no commits.""" + with temporary_git_repository() as (temp_dir, repo): + # Empty repository with no commits + parsed_from, parsed_to, separator = parse_commit_range('--all', temp_dir) + # Should return None, None, None when HEAD doesn't exist + assert parsed_from is None + assert parsed_to is None + assert separator is None class TestParsePreReceiveInput: """Test the parse_pre_receive_input function with various pre-receive hook input scenarios.""" From 3edc87247a175d7ad35da2cea885e95834cfd348 Mon Sep 17 00:00:00 2001 From: Mateusz Sterczewski Date: Fri, 9 Jan 2026 11:34:22 +0100 Subject: [PATCH 2/6] CM-55207-Ruff fix --- .../files_collector/commit_range_documents.py | 5 ++--- .../test_commit_range_documents.py | 17 +++++++++-------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/cycode/cli/files_collector/commit_range_documents.py b/cycode/cli/files_collector/commit_range_documents.py index 1c375ae5..057d67d1 100644 --- a/cycode/cli/files_collector/commit_range_documents.py +++ b/cycode/cli/files_collector/commit_range_documents.py @@ -461,9 +461,8 @@ def parse_commit_range(commit_range: str, path: str) -> tuple[Optional[str], Opt if all_commits: first_commit = all_commits.splitlines()[0] return first_commit, head_commit, '..' - else: - logger.warning("No commits found for range '%s'", commit_range) - return None, None, None + logger.warning("No commits found for range '%s'", commit_range) + return None, None, None except Exception as e: logger.warning("Failed to parse commit range '%s'", commit_range, exc_info=e) return None, None, None diff --git a/tests/cli/files_collector/test_commit_range_documents.py b/tests/cli/files_collector/test_commit_range_documents.py index d8bd5bda..501c1811 100644 --- a/tests/cli/files_collector/test_commit_range_documents.py +++ b/tests/cli/files_collector/test_commit_range_documents.py @@ -891,14 +891,14 @@ def test_parse_all_for_empty_remote_scenario(self) -> None: test_file = os.path.join(temp_dir, 'test.py') with open(test_file, 'w') as f: f.write("print('test')") - + repo.index.add(['test.py']) commit = repo.index.commit('Initial commit') - + # Test that '--all' (returned by calculate_pre_push_commit_range for empty remote) # can be parsed to a valid commit range parsed_from, parsed_to, separator = parse_commit_range('--all', temp_dir) - + # Should return first commit to HEAD (which is the only commit in this case) assert parsed_from == commit.hexsha assert parsed_to == commit.hexsha @@ -913,21 +913,21 @@ def test_parse_all_for_empty_remote_scenario_with_two_commits(self) -> None: test_file = os.path.join(temp_dir, 'test.py') with open(test_file, 'w') as f: f.write("print('test')") - + repo.index.add(['test.py']) commit1 = repo.index.commit('First commit') - + # Create second commit test_file2 = os.path.join(temp_dir, 'test2.py') with open(test_file2, 'w') as f: f.write("print('test2')") - + repo.index.add(['test2.py']) commit2 = repo.index.commit('Second commit') - + # Test that '--all' returns first commit to HEAD (second commit) parsed_from, parsed_to, separator = parse_commit_range('--all', temp_dir) - + # Should return first commit to HEAD (second commit) assert parsed_from == commit1.hexsha # First commit assert parsed_to == commit2.hexsha # HEAD (second commit) @@ -943,6 +943,7 @@ def test_parse_all_with_empty_repository_returns_none(self) -> None: assert parsed_to is None assert separator is None + class TestParsePreReceiveInput: """Test the parse_pre_receive_input function with various pre-receive hook input scenarios.""" From 59ef251f72642cb6e93015b8bba091e7ab68880e Mon Sep 17 00:00:00 2001 From: Mateusz Sterczewski Date: Fri, 9 Jan 2026 12:33:00 +0100 Subject: [PATCH 3/6] CM-55207-Use consts --- cycode/cli/files_collector/commit_range_documents.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/cycode/cli/files_collector/commit_range_documents.py b/cycode/cli/files_collector/commit_range_documents.py index 057d67d1..c4f1e51a 100644 --- a/cycode/cli/files_collector/commit_range_documents.py +++ b/cycode/cli/files_collector/commit_range_documents.py @@ -21,6 +21,7 @@ logger = get_logger('Commit Range Collector') +COMMIT_RANGE_ALL = '--all' def get_safe_head_reference_for_diff(repo: 'Repo') -> str: """Get a safe reference to use for diffing against the current HEAD. @@ -351,10 +352,10 @@ def calculate_pre_push_commit_range(push_update_details: str) -> Optional[str]: return f'{merge_base}..{local_object_name}' logger.debug('Failed to find merge base with any default branch') - return '--all' + return COMMIT_RANGE_ALL except Exception as e: logger.debug('Failed to get repo for pre-push commit range calculation: %s', exc_info=e) - return '--all' + return COMMIT_RANGE_ALL # If deleting a branch (local_object_name is all zeros), no need to scan if local_object_name == consts.EMPTY_COMMIT_SHA: @@ -454,7 +455,7 @@ def parse_commit_range(commit_range: str, path: str) -> tuple[Optional[str], Opt # Handle '--all' special case: scan all commits from first to HEAD # Usually represents an empty remote repository - if commit_range == '--all': + if commit_range == COMMIT_RANGE_ALL: try: head_commit = repo.rev_parse(consts.GIT_HEAD_COMMIT_REV).hexsha all_commits = repo.git.rev_list('--reverse', head_commit).strip() From 6759467981e9cc195d897fbb2126ea2fc0ba62b5 Mon Sep 17 00:00:00 2001 From: Mateusz Sterczewski Date: Fri, 9 Jan 2026 12:34:20 +0100 Subject: [PATCH 4/6] CM-55207-Ruff fix --- cycode/cli/files_collector/commit_range_documents.py | 1 + 1 file changed, 1 insertion(+) diff --git a/cycode/cli/files_collector/commit_range_documents.py b/cycode/cli/files_collector/commit_range_documents.py index c4f1e51a..7ac5fab5 100644 --- a/cycode/cli/files_collector/commit_range_documents.py +++ b/cycode/cli/files_collector/commit_range_documents.py @@ -23,6 +23,7 @@ COMMIT_RANGE_ALL = '--all' + def get_safe_head_reference_for_diff(repo: 'Repo') -> str: """Get a safe reference to use for diffing against the current HEAD. In repositories with no commits, HEAD doesn't exist, so we return the empty tree hash. From 977b2dbbd93ac3d07857b9ac84d21de949fd081f Mon Sep 17 00:00:00 2001 From: Mateusz Sterczewski Date: Fri, 9 Jan 2026 12:43:52 +0100 Subject: [PATCH 5/6] CM-55207-Move consts --- cycode/cli/consts.py | 1 + cycode/cli/files_collector/commit_range_documents.py | 9 +++------ 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/cycode/cli/consts.py b/cycode/cli/consts.py index 1b1497bd..e06f9b00 100644 --- a/cycode/cli/consts.py +++ b/cycode/cli/consts.py @@ -269,6 +269,7 @@ # git consts COMMIT_DIFF_DELETED_FILE_CHANGE_TYPE = 'D' +COMMIT_RANGE_ALL_COMMITS = '--all' GIT_HEAD_COMMIT_REV = 'HEAD' GIT_EMPTY_TREE_OBJECT = '4b825dc642cb6eb9a060e54bf8d69288fbee4904' EMPTY_COMMIT_SHA = '0000000000000000000000000000000000000000' diff --git a/cycode/cli/files_collector/commit_range_documents.py b/cycode/cli/files_collector/commit_range_documents.py index 7ac5fab5..7579c052 100644 --- a/cycode/cli/files_collector/commit_range_documents.py +++ b/cycode/cli/files_collector/commit_range_documents.py @@ -21,9 +21,6 @@ logger = get_logger('Commit Range Collector') -COMMIT_RANGE_ALL = '--all' - - def get_safe_head_reference_for_diff(repo: 'Repo') -> str: """Get a safe reference to use for diffing against the current HEAD. In repositories with no commits, HEAD doesn't exist, so we return the empty tree hash. @@ -353,10 +350,10 @@ def calculate_pre_push_commit_range(push_update_details: str) -> Optional[str]: return f'{merge_base}..{local_object_name}' logger.debug('Failed to find merge base with any default branch') - return COMMIT_RANGE_ALL + return consts.COMMIT_RANGE_ALL_COMMITS except Exception as e: logger.debug('Failed to get repo for pre-push commit range calculation: %s', exc_info=e) - return COMMIT_RANGE_ALL + return consts.COMMIT_RANGE_ALL_COMMITS # If deleting a branch (local_object_name is all zeros), no need to scan if local_object_name == consts.EMPTY_COMMIT_SHA: @@ -456,7 +453,7 @@ def parse_commit_range(commit_range: str, path: str) -> tuple[Optional[str], Opt # Handle '--all' special case: scan all commits from first to HEAD # Usually represents an empty remote repository - if commit_range == COMMIT_RANGE_ALL: + if commit_range == consts.COMMIT_RANGE_ALL_COMMITS: try: head_commit = repo.rev_parse(consts.GIT_HEAD_COMMIT_REV).hexsha all_commits = repo.git.rev_list('--reverse', head_commit).strip() From c1b5d7eceba4a7f9c704e660d2ce547d1df3cd14 Mon Sep 17 00:00:00 2001 From: Mateusz Sterczewski Date: Fri, 9 Jan 2026 12:45:03 +0100 Subject: [PATCH 6/6] CM-55207-Ruff fix --- cycode/cli/files_collector/commit_range_documents.py | 1 + 1 file changed, 1 insertion(+) diff --git a/cycode/cli/files_collector/commit_range_documents.py b/cycode/cli/files_collector/commit_range_documents.py index 7579c052..a4a1a784 100644 --- a/cycode/cli/files_collector/commit_range_documents.py +++ b/cycode/cli/files_collector/commit_range_documents.py @@ -21,6 +21,7 @@ logger = get_logger('Commit Range Collector') + def get_safe_head_reference_for_diff(repo: 'Repo') -> str: """Get a safe reference to use for diffing against the current HEAD. In repositories with no commits, HEAD doesn't exist, so we return the empty tree hash.