diff --git a/tests/unit/test_merger_repo.py b/tests/unit/test_merger_repo.py index e4cc34e5a2..d73def8123 100644 --- a/tests/unit/test_merger_repo.py +++ b/tests/unit/test_merger_repo.py @@ -312,7 +312,7 @@ class TestMergerRepo(ZuulTestCase): 'none@example.org', 'User Name', '0', '0') self.waitUntilSettled() - # Break the gitmodules + # Break the gitmodules with uncommited changes path = work_repo.local_path with open(os.path.join(path, '.gitmodules'), 'w') as f: f.write('[submodule "libfoo"]\n' @@ -320,7 +320,21 @@ class TestMergerRepo(ZuulTestCase): '---\n' 'url = git://example.com/git/lib.git') - # And now reset the repo again. This should not crash + # And now reset the repo. This should not crash + work_repo.reset() + + # Break the gitmodules with a commit + path = work_repo.local_path + with open(os.path.join(path, '.gitmodules'), 'w') as f: + f.write('[submodule "libfoo"]\n' + 'path = include/foo\n' + '---\n' + 'url = git://example.com/git/lib.git') + git_repo = work_repo._createRepoObject(work_repo.local_path, + work_repo.env) + git_repo.git.add('.gitmodules') + git_repo.index.commit("Broken gitmodule") + # And now reset the repo. This should not crash work_repo.reset() def test_files_changes(self): diff --git a/zuul/merger/merger.py b/zuul/merger/merger.py index ffddb2ac24..f7017f9851 100644 --- a/zuul/merger/merger.py +++ b/zuul/merger/merger.py @@ -273,11 +273,26 @@ class Repo(object): if attempt < self.retry_attempts: if 'fatal: bad config' in e.stderr.lower(): # This error can be introduced by a merge conflict + # or someone committing faulty configuration # in the .gitmodules which was left by the last # merge operation. In this case reset and clean # the repo and try again immediately. - reset_repo_to_head(repo) - repo.git.clean('-x', '-f', '-d') + reset_ref = 'HEAD' + try: + if not repo.is_dirty(): + reset_ref = "{}^".format(repo.git.log( + '--diff-filter=A', + '-n', '1', + '--pretty=format:%H', + '--', '.gitmodules')) + repo.head.reset(reset_ref, working_tree=True) + repo.git.clean('-x', '-f', '-d') + except Exception: + # If we get here there probably isn't + # a valid commit we can easily find so + # delete the repo to make sure it doesn't + # get stuck in a broken state. + shutil.rmtree(self.local_path) elif 'fatal: not a git repository' in e.stderr.lower(): # If we get here the git.Repo object was happy with its # lightweight way of checking if this is a valid git