Skip to content

Commit ff1c300

Browse files
fix: better detect merge-commits (#397)
Apparently a common thing for CIs to do is create a merge-commit for changes in a branch before running tests and stuff. This means that - especially for not-directly-supported CIs - we would maybe return a SHA for a commit that didn't exist in the branch. These changes fix that by checking to see if the current commit is a merge commit. If it is we return the second parent, the most recent commit before the current one. Q: What if the current latest commit in the branch is a merge commit? Well in this case the parent, which is also part of the branch, will have the coverage. Users can still provide a commit sha value to override this behavior. closes #372
1 parent 92d7e89 commit ff1c300

3 files changed

Lines changed: 36 additions & 9 deletions

File tree

codecov_cli/helpers/versioning_systems.py

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,20 @@ def is_available(cls):
4242

4343
def get_fallback_value(self, fallback_field: FallbackFieldEnum):
4444
if fallback_field == FallbackFieldEnum.commit_sha:
45+
# here we will get the commit SHA of the latest commit
46+
# that is NOT a merge commit
47+
p = subprocess.run(
48+
# List current commit parent's SHA
49+
["git", "rev-parse", "HEAD^@"],
50+
capture_output=True,
51+
)
52+
parents_hash = p.stdout.decode().strip().splitlines()
53+
if len(parents_hash) == 2:
54+
# IFF the current commit is a merge commit it will have 2 parents
55+
# We return the 2nd one - The commit that came from the branch merged into ours
56+
return parents_hash[1]
57+
# At this point we know the current commit is not a merge commit
58+
# so we get it's SHA and return that
4559
p = subprocess.run(["git", "log", "-1", "--format=%H"], capture_output=True)
4660
if p.stdout:
4761
return p.stdout.decode().strip()
@@ -56,7 +70,7 @@ def get_fallback_value(self, fallback_field: FallbackFieldEnum):
5670
return branch_name if branch_name != "HEAD" else None
5771

5872
if fallback_field == FallbackFieldEnum.slug:
59-
# if there are multiple remotes, we will prioritize using the one called 'origin' if it exsits, else we will use the first one in 'git remote' list
73+
# if there are multiple remotes, we will prioritize using the one called 'origin' if it exists, else we will use the first one in 'git remote' list
6074

6175
p = subprocess.run(["git", "remote"], capture_output=True)
6276

@@ -78,7 +92,7 @@ def get_fallback_value(self, fallback_field: FallbackFieldEnum):
7892
return parse_slug(remote_url)
7993

8094
if fallback_field == FallbackFieldEnum.git_service:
81-
# if there are multiple remotes, we will prioritize using the one called 'origin' if it exsits, else we will use the first one in 'git remote' list
95+
# if there are multiple remotes, we will prioritize using the one called 'origin' if it exists, else we will use the first one in 'git remote' list
8296

8397
p = subprocess.run(["git", "remote"], capture_output=True)
8498
if not p.stdout:

tests/helpers/test_versioning_systems.py

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,17 +8,28 @@
88

99
class TestGitVersioningSystem(object):
1010
@pytest.mark.parametrize(
11-
"commit_sha,expected", [("", None), (b" random_sha ", "random_sha")]
11+
"runs_output,expected",
12+
[
13+
# No output for parents nor commit
14+
([b"", b""], None),
15+
# No output for parents, commit has SHA
16+
([b"", b" random_sha"], "random_sha"),
17+
# Commit is NOT a merge-commit
18+
([b" parent_sha", b" random_sha "], "random_sha"),
19+
# Commit IS a merge-commit
20+
([b" parent_sha0\nparent_sha1", b" random_sha"], "parent_sha1"),
21+
],
1222
)
13-
def test_commit_sha(self, mocker, commit_sha, expected):
14-
mocked_subprocess = MagicMock()
23+
def test_commit_sha(self, mocker, runs_output, expected):
24+
mocked_subprocess = [
25+
MagicMock(**{"stdout": runs_output[0]}),
26+
MagicMock(**{"stdout": runs_output[1]}),
27+
]
1528
mocker.patch(
1629
"codecov_cli.helpers.versioning_systems.subprocess.run",
17-
return_value=mocked_subprocess,
30+
side_effect=mocked_subprocess,
1831
)
1932

20-
mocked_subprocess.stdout = commit_sha
21-
2233
assert (
2334
GitVersioningSystem().get_fallback_value(FallbackFieldEnum.commit_sha)
2435
== expected

tests/services/upload/test_coverage_file_finder.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -295,7 +295,9 @@ def test_find_coverage_files_with_user_specified_files_not_found(self):
295295
expected_paths = sorted([file.get_filename() for file in expected])
296296
self.assertEqual(result, expected_paths)
297297

298-
def test_find_coverage_files_with_user_specified_files_in_default_ignored_folder(self):
298+
def test_find_coverage_files_with_user_specified_files_in_default_ignored_folder(
299+
self,
300+
):
299301
# Create some sample coverage files
300302
coverage_files = [
301303
self.project_root / "coverage.xml",

0 commit comments

Comments
 (0)