Skip to content

Commit a662f10

Browse files
Raise exception when repo is in a detached HEAD state (#626)
* Directly exit when detached HEAD is detected * Add test for detached head exception * Remove unused import in test_cli.py * Make pytests less verbose * Add sweep test to test_setup.py * Catch SystemExit exception * Add get_branch method + test. Replaced direct call to active_branch in branch.py * Fix type hint from review --------- Co-authored-by: jo-basevi <[email protected]>
1 parent 7ce8672 commit a662f10

File tree

7 files changed

+81
-10
lines changed

7 files changed

+81
-10
lines changed

.github/workflows/CI.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ jobs:
8787

8888
- name: Run tests
8989
run: |
90-
PYTHONPATH=. pytest --cov=payu -s test;
90+
PYTHONPATH=. pytest --cov=payu test;
9191
9292
- name: Coveralls
9393
uses: AndreMiras/coveralls-python-action@65c1672f0b8a201702d86c81b79187df74072505

payu/branch.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -442,7 +442,7 @@ def list_branches(config_path: Optional[Path] = None,
442442
control_path = get_control_path(config_path)
443443
git_repo = GitRepository(control_path)
444444

445-
current_branch = git_repo.repo.active_branch
445+
current_branch = git_repo.get_branch()
446446
print(f"* Current Branch: {current_branch.name}")
447447
print_branch_metadata(current_branch, verbose)
448448

payu/git_utils.py

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
import warnings
77
from pathlib import Path
8+
import sys
89
from typing import Optional, Union, List, Dict
910

1011
import git
@@ -57,17 +58,39 @@ def __init__(self,
5758
repo = get_git_repository(repo_path, catch_error=catch_error)
5859
self.repo = repo
5960

60-
def get_branch_name(self) -> Optional[str]:
61+
def get_branch(self) -> Optional[git.Head]:
6162
"""Return the current git branch or None if repository path is
6263
not a git repository"""
6364
if self.repo:
64-
return str(self.repo.active_branch)
65+
if self.repo.head.is_detached:
66+
sys.exit("\nRepo is in a detached HEAD state.\n"
67+
"Before running again checkout a branch using\n\n"
68+
" payu checkout <branch>\n\n")
69+
else:
70+
return self.repo.active_branch
71+
else:
72+
return None
73+
74+
def get_branch_name(self) -> Optional[str]:
75+
"""Return the current git branch or None if repository path is
76+
not a git repository"""
77+
78+
branch = self.get_branch()
79+
80+
if branch is not None:
81+
return branch.name
82+
else:
83+
return None
6584

6685
def get_hash(self) -> Optional[str]:
6786
"""Return the current git commit hash or None if repository path is
6887
not a git repository"""
6988
if self.repo:
70-
return self.repo.active_branch.object.hexsha
89+
branch = self.repo.get_branch()
90+
if branch is not None:
91+
return branch.object.hexsha
92+
else:
93+
return None
7194

7295
def get_origin_url(self) -> Optional[str]:
7396
"""Return url of remote origin if it exists"""

test/common.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
# Namespace clash if import setup_cmd.runcmd as setup. For
1111
# consistency use payu_ prefix for all commands
1212
from payu.subcommands.init_cmd import runcmd as payu_init
13-
from payu.subcommands.setup_cmd import runcmd as payu_setup_orignal
13+
from payu.subcommands.setup_cmd import runcmd as payu_setup_original
1414
from payu.subcommands.sweep_cmd import runcmd as payu_sweep
1515

1616
ctrldir_basename = 'ctrl'
@@ -127,7 +127,7 @@ def payu_setup(model_type=None,
127127
hard_sweep=False,
128128
lab_path=str(labdir),
129129
metadata_off=False)
130-
payu_setup_orignal(model_type,
130+
payu_setup_original(model_type,
131131
config_path,
132132
lab_path,
133133
force_archive,

test/test_cli.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import pdb
21
import pytest
32
import shlex
43

test/test_git_utils.py

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ def setup_and_teardown():
2929

3030
def create_new_repo(repo_path):
3131
"""Helper function to initialise a repo and create first commit"""
32-
repo = git.Repo.init(repo_path)
32+
repo = git.Repo.init(repo_path, initial_branch='main')
3333
init_file = repo_path / "init.txt"
3434
add_file_and_commit(repo, init_file)
3535
return repo
@@ -90,6 +90,14 @@ def test_get_git_user_info_config_set():
9090
assert value == 'TestUserName'
9191

9292

93+
def test_get_branch():
94+
# Setup
95+
repo_path = tmpdir / 'test_repo_get_branch'
96+
new_repo = GitRepository(repo_path, repo=create_new_repo(repo_path))
97+
98+
assert new_repo.get_branch_name() == 'main'
99+
100+
93101
@pytest.mark.parametrize("ref", ["branch", "hash", None])
94102
def test_git_checkout_new_branch_from_remote_ref(ref):
95103
# Setup
@@ -228,3 +236,32 @@ def test_git_checkout_missing_origin_repo():
228236

229237
# No remote branches found
230238
assert repo.remote_branches_dict() == {}
239+
240+
241+
def test_git_get_branch_detached_head():
242+
"""In Issue #625 there was an unhelpful exception message
243+
when payu sweep was run from a repo with a detached HEAD.
244+
Now throw exception will be caught in the caller."""
245+
246+
# Setup
247+
detached_repo_path = tmpdir / 'detachedRepo'
248+
create_new_repo(detached_repo_path)
249+
250+
detached = GitRepository(detached_repo_path)
251+
252+
assert detached.get_branch_name() == "main"
253+
254+
# Checkout HEAD commit to make detached state
255+
detached.repo.git.checkout(detached.repo.commit("HEAD"))
256+
257+
assert detached.repo.head.is_detached
258+
259+
expected_msg = ("\nRepo is in a detached HEAD state.\n"
260+
"Checkout a branch before running again.\n")
261+
262+
expected_msg = ("\nRepo is in a detached HEAD state.\n"
263+
"Before running again checkout a branch using"
264+
"\n\n payu checkout <branch>\n\n")
265+
266+
with pytest.raises(SystemExit, match=expected_msg):
267+
detached.get_branch_name()

test/test_setup.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212

1313
from .common import cd, make_random_file, get_manifests
1414
from .common import tmpdir, ctrldir, labdir, workdir
15-
from .common import payu_init, payu_setup
15+
from .common import payu_init, payu_setup, sweep_work
1616
from .common import config as config_orig
1717
from .common import write_config
1818
from .common import make_exe, make_inputs, make_expt_archive_dir
@@ -189,6 +189,18 @@ def test_setup():
189189
assert workdir.is_symlink and workdir.is_dir()
190190

191191

192+
def test_sweep():
193+
"""Test sweeping ephemeral work directory functions
194+
correctly"""
195+
run_payu_setup(create_inputs=True, create_config_files=True)
196+
197+
assert workdir.is_symlink and workdir.is_dir()
198+
199+
sweep_work()
200+
201+
assert not workdir.exists()
202+
203+
192204
@pytest.mark.parametrize(
193205
"current_version, min_version",
194206
[

0 commit comments

Comments
 (0)