Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

git: do not let git clones clobber the target directory (by default) #312

Merged
merged 1 commit into from
Aug 23, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@
2.3b5 (unreleased)
------------------

- Nothing changed yet.
- Do not clobber git clones by default. Add new option `clobber` to allow
specifying that a git clone may clobber the target directory.
([#298](https://github.com/flyingcircusio/batou/issues/298))


2.3b4 (2022-08-22)
Expand Down
27 changes: 20 additions & 7 deletions src/batou/lib/git.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ class Clone(Component):
revision = None
branch = None
vcs_update = True
clobber = False

def configure(self):
if (not self.revision_or_branch) or (self.revision and self.branch):
Expand Down Expand Up @@ -64,13 +65,25 @@ def verify(self):
)

if self.has_changes():
output.annotate(
"Git clone at {} is dirty, going to lose changes.".format(
self.target
),
red=True,
)
raise UpdateNeeded()
if self.clobber:
output.annotate(
"Git clone at {} is dirty, going to lose changes.".format(
self.target
),
red=True,
)
raise UpdateNeeded()
else:
output.annotate(
"Git clone at {} is dirty - refusing to clobber. "
"Use `clobber=True` if this is intentional .".format(
self.target
),
red=True,
)
raise RuntimeError(
"Refusing to clobber dirty work directory."
)

if self.revision and self.current_revision() != self.revision:
raise UpdateNeeded()
Expand Down
63 changes: 58 additions & 5 deletions src/batou/lib/tests/test_git.py
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ def test_clean_clone_updates_on_incoming_changes(root, repos_path):


@pytest.mark.slow
def test_changes_lost_on_update_with_incoming(root, repos_path):
def test_no_clobber_changes_protected_on_update_with_incoming(root, repos_path):
root.component += batou.lib.git.Clone(
repos_path, target="clone", branch="master"
)
Expand All @@ -170,16 +170,69 @@ def test_changes_lost_on_update_with_incoming(root, repos_path):
)
)
cmd("cd {dir}/clone; echo foobar >foo".format(dir=root.workdir))
with pytest.raises(RuntimeError) as e:
root.component.deploy()
assert e.value.args[0] == "Refusing to clobber dirty work directory."
with open(root.component.map("clone/foo")) as f:
assert f.read() == "foobar\n"


@pytest.mark.slow
def test_no_clobber_changes_protected_on_update_without_incoming(
root, repos_path
):
root.component += batou.lib.git.Clone(
repos_path, target="clone", branch="master"
)
root.component.deploy()
cmd("cd {dir}/clone; echo foobar >foo".format(dir=root.workdir))
with pytest.raises(RuntimeError) as e:
root.component.deploy()
assert e.value.args[0] == "Refusing to clobber dirty work directory."
with open(root.component.map("clone/foo")) as f:
assert f.read() == "foobar\n"


@pytest.mark.slow
def test_no_clobber_untracked_files_are_kept_on_update(root, repos_path):
root.component += batou.lib.git.Clone(
repos_path, target="clone", branch="master"
)
root.component.deploy()
cmd(
"cd {dir}/clone; mkdir bar; echo foobar >bar/baz".format(
dir=root.workdir
)
)
with pytest.raises(RuntimeError) as e:
root.component.deploy()
assert e.value.args[0] == "Refusing to clobber dirty work directory."
with open(root.component.map("clone/bar/baz")) as f:
assert f.read() == "foobar\n"


@pytest.mark.slow
def test_clobber_changes_lost_on_update_with_incoming(root, repos_path):
root.component += batou.lib.git.Clone(
repos_path, target="clone", branch="master", clobber=True
)
root.component.deploy()
cmd(
'cd {dir}; touch bar; git add .; git commit -m "commit"'.format(
dir=repos_path
)
)
cmd("cd {dir}/clone; echo foobar >foo".format(dir=root.workdir))
root.component.deploy()
assert os.path.exists(root.component.map("clone/bar"))
with open(root.component.map("clone/foo")) as f:
assert not f.read()


@pytest.mark.slow
def test_changes_lost_on_update_without_incoming(root, repos_path):
def test_clobber_changes_lost_on_update_without_incoming(root, repos_path):
root.component += batou.lib.git.Clone(
repos_path, target="clone", branch="master"
repos_path, target="clone", branch="master", clobber=True
)
root.component.deploy()
cmd("cd {dir}/clone; echo foobar >foo".format(dir=root.workdir))
Expand All @@ -189,9 +242,9 @@ def test_changes_lost_on_update_without_incoming(root, repos_path):


@pytest.mark.slow
def test_untracked_files_are_removed_on_update(root, repos_path):
def test_clobber_untracked_files_are_removed_on_update(root, repos_path):
root.component += batou.lib.git.Clone(
repos_path, target="clone", branch="master"
repos_path, target="clone", branch="master", clobber=True
)
root.component.deploy()
cmd(
Expand Down