Skip to content

Commit a98ba2f

Browse files
committed
CM-53811: unified commit range parse
1 parent c0bf190 commit a98ba2f

File tree

3 files changed

+73
-23
lines changed

3 files changed

+73
-23
lines changed

cycode/cli/apps/scan/commit_range_scanner.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,7 @@
2626
get_diff_file_path,
2727
get_pre_commit_modified_documents,
2828
get_safe_head_reference_for_diff,
29-
parse_commit_range_sast,
30-
parse_commit_range_sca,
29+
parse_commit_range,
3130
)
3231
from cycode.cli.files_collector.documents_walk_ignore import filter_documents_with_cycodeignore
3332
from cycode.cli.files_collector.file_excluder import excluder
@@ -187,7 +186,7 @@ def _scan_commit_range_documents(
187186
def _scan_sca_commit_range(ctx: typer.Context, repo_path: str, commit_range: str, **_) -> None:
188187
scan_parameters = get_scan_parameters(ctx, (repo_path,))
189188

190-
from_commit_rev, to_commit_rev = parse_commit_range_sca(commit_range, repo_path)
189+
from_commit_rev, to_commit_rev = parse_commit_range(commit_range, repo_path)
191190
from_commit_documents, to_commit_documents, _ = get_commit_range_modified_documents(
192191
ctx.obj['progress_bar'], ScanProgressBarSection.PREPARE_LOCAL_FILES, repo_path, from_commit_rev, to_commit_rev
193192
)
@@ -228,7 +227,7 @@ def _scan_secret_commit_range(
228227
def _scan_sast_commit_range(ctx: typer.Context, repo_path: str, commit_range: str, **_) -> None:
229228
scan_parameters = get_scan_parameters(ctx, (repo_path,))
230229

231-
from_commit_rev, to_commit_rev = parse_commit_range_sast(commit_range, repo_path)
230+
from_commit_rev, to_commit_rev = parse_commit_range(commit_range, repo_path)
232231
_, commit_documents, diff_documents = get_commit_range_modified_documents(
233232
ctx.obj['progress_bar'],
234233
ScanProgressBarSection.PREPARE_LOCAL_FILES,

cycode/cli/files_collector/commit_range_documents.py

Lines changed: 1 addition & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -408,22 +408,7 @@ def get_pre_commit_modified_documents(
408408
return git_head_documents, pre_committed_documents, diff_documents
409409

410410

411-
def parse_commit_range_sca(commit_range: str, path: str) -> tuple[Optional[str], Optional[str]]:
412-
# FIXME(MarshalX): i truly believe that this function does NOT work as expected
413-
# it does not handle cases like 'A..B' correctly
414-
# i leave it as it for SCA to not break anything
415-
# the more correct approach is implemented for SAST
416-
from_commit_rev = to_commit_rev = None
417-
418-
for commit in git_proxy.get_repo(path).iter_commits(rev=commit_range):
419-
if not to_commit_rev:
420-
to_commit_rev = commit.hexsha
421-
from_commit_rev = commit.hexsha
422-
423-
return from_commit_rev, to_commit_rev
424-
425-
426-
def parse_commit_range_sast(commit_range: str, path: str) -> tuple[Optional[str], Optional[str]]:
411+
def parse_commit_range(commit_range: str, path: str) -> tuple[Optional[str], Optional[str]]:
427412
"""Parses a git commit range string and returns the full SHAs for the 'from' and 'to' commits.
428413
429414
Supports:

tests/cli/files_collector/test_commit_range_documents.py

Lines changed: 69 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
calculate_pre_push_commit_range,
1515
get_diff_file_path,
1616
get_safe_head_reference_for_diff,
17+
parse_commit_range,
1718
parse_pre_push_input,
1819
)
1920
from cycode.cli.utils.path_utils import get_path_by_os
@@ -22,7 +23,8 @@
2223
@contextmanager
2324
def git_repository(path: str) -> Generator[Repo, None, None]:
2425
"""Context manager for Git repositories that ensures proper cleanup on Windows."""
25-
repo = Repo.init(path)
26+
# Ensure the initialized repository uses 'main' as the default branch
27+
repo = Repo.init(path, b='main')
2628
try:
2729
yield repo
2830
finally:
@@ -539,8 +541,8 @@ def test_calculate_range_for_new_branch_with_merge_base(self) -> None:
539541
repo.index.add(['feature.py'])
540542
feature_commit = repo.index.commit('Add feature')
541543

542-
# Switch back to master to simulate we're pushing a feature branch
543-
repo.heads.master.checkout()
544+
# Switch back to the default branch to simulate pushing the feature branch
545+
repo.heads.main.checkout()
544546

545547
# Test new branch push
546548
push_details = f'refs/heads/feature {feature_commit.hexsha} refs/heads/feature {consts.EMPTY_COMMIT_SHA}'
@@ -805,3 +807,67 @@ def test_simulate_pre_push_hook_input_format(self) -> None:
805807
parts = push_input.split()
806808
expected_range = f'{parts[3]}..{parts[1]}'
807809
assert commit_range == expected_range
810+
811+
812+
class TestParseCommitRange:
813+
"""Tests to validate unified parse_commit_range behavior matches git semantics."""
814+
815+
def _make_linear_history(self, repo: Repo, base_dir: str) -> tuple[str, str, str]:
816+
"""Create three linear commits A -> B -> C and return their SHAs."""
817+
a_file = os.path.join(base_dir, 'a.txt')
818+
with open(a_file, 'w') as f:
819+
f.write('A')
820+
repo.index.add(['a.txt'])
821+
a = repo.index.commit('A')
822+
823+
with open(a_file, 'a') as f:
824+
f.write('B')
825+
repo.index.add(['a.txt'])
826+
b = repo.index.commit('B')
827+
828+
with open(a_file, 'a') as f:
829+
f.write('C')
830+
repo.index.add(['a.txt'])
831+
c = repo.index.commit('C')
832+
833+
return a.hexsha, b.hexsha, c.hexsha
834+
835+
def test_two_dot_linear_history(self) -> None:
836+
"""For 'A..C', expect (A,C) in linear history."""
837+
with temporary_git_repository() as (temp_dir, repo):
838+
a, b, c = self._make_linear_history(repo, temp_dir)
839+
840+
parsed_from, parsed_to = parse_commit_range(f'{a}..{c}', temp_dir)
841+
assert (parsed_from, parsed_to) == (a, c)
842+
843+
def test_three_dot_linear_history(self) -> None:
844+
"""For 'A...C' in linear history, expect (A,C)."""
845+
with temporary_git_repository() as (temp_dir, repo):
846+
a, b, c = self._make_linear_history(repo, temp_dir)
847+
848+
parsed_from, parsed_to = parse_commit_range(f'{a}...{c}', temp_dir)
849+
assert (parsed_from, parsed_to) == (a, c)
850+
851+
def test_open_right_linear_history(self) -> None:
852+
"""For 'A..', expect (A,HEAD=C)."""
853+
with temporary_git_repository() as (temp_dir, repo):
854+
a, b, c = self._make_linear_history(repo, temp_dir)
855+
856+
parsed_from, parsed_to = parse_commit_range(f'{a}..', temp_dir)
857+
assert (parsed_from, parsed_to) == (a, c)
858+
859+
def test_open_left_linear_history(self) -> None:
860+
"""For '..C' where HEAD==C, expect (HEAD=C,C)."""
861+
with temporary_git_repository() as (temp_dir, repo):
862+
a, b, c = self._make_linear_history(repo, temp_dir)
863+
864+
parsed_from, parsed_to = parse_commit_range(f'..{c}', temp_dir)
865+
assert (parsed_from, parsed_to) == (c, c)
866+
867+
def test_single_commit_spec(self) -> None:
868+
"""For 'A', expect (A,HEAD=C)."""
869+
with temporary_git_repository() as (temp_dir, repo):
870+
a, b, c = self._make_linear_history(repo, temp_dir)
871+
872+
parsed_from, parsed_to = parse_commit_range(a, temp_dir)
873+
assert (parsed_from, parsed_to) == (a, c)

0 commit comments

Comments
 (0)