Reset repos with files named 'HEAD'

If a git repo has the misfortune to have a file with the name "HEAD"
at the root level of the repo, git will return an error because it
is unsure whether the file or ref is meant.

Call 'git reset' with '--' at the end of the command to disambiguate
and instruct git that the HEAD ref is meant.

Change-Id: Icb19063afafa1e0719b3354470c14df8168a64b5
This commit is contained in:
James E. Blair 2015-07-17 14:04:49 -07:00
parent 1c42a2ab47
commit 879dafb5b9
3 changed files with 47 additions and 7 deletions

View File

@ -47,8 +47,9 @@ import zuul.webapp
import zuul.rpclistener
import zuul.launcher.gearman
import zuul.lib.swift
import zuul.merger.server
import zuul.merger.client
import zuul.merger.merger
import zuul.merger.server
import zuul.reporter.gerrit
import zuul.reporter.smtp
import zuul.trigger.gerrit
@ -145,7 +146,7 @@ class FakeChange(object):
self.latest_patchset),
'refs/tags/init')
repo.head.reference = ref
repo.head.reset(index=True, working_tree=True)
zuul.merger.merger.reset_repo_to_head(repo)
repo.git.clean('-x', '-f', '-d')
path = os.path.join(self.upstream_root, self.project)
@ -167,7 +168,7 @@ class FakeChange(object):
r = repo.index.commit(msg)
repo.head.reference = 'master'
repo.head.reset(index=True, working_tree=True)
zuul.merger.merger.reset_repo_to_head(repo)
repo.git.clean('-x', '-f', '-d')
repo.heads['master'].checkout()
return r
@ -1045,7 +1046,7 @@ class ZuulTestCase(BaseTestCase):
repo.create_tag('init')
repo.head.reference = master
repo.head.reset(index=True, working_tree=True)
zuul.merger.merger.reset_repo_to_head(repo)
repo.git.clean('-x', '-f', '-d')
self.create_branch(project, 'mp')
@ -1064,7 +1065,7 @@ class ZuulTestCase(BaseTestCase):
repo.index.commit('%s commit' % branch)
repo.head.reference = repo.heads['master']
repo.head.reset(index=True, working_tree=True)
zuul.merger.merger.reset_repo_to_head(repo)
repo.git.clean('-x', '-f', '-d')
def ref_has_change(self, ref, change):

View File

@ -2036,6 +2036,30 @@ class TestScheduler(ZuulTestCase):
self.assertEqual(self.history[0].name, 'gate-noop')
self.assertEqual(self.history[0].result, 'SUCCESS')
def test_file_head(self):
# This is a regression test for an observed bug. A change
# with a file named "HEAD" in the root directory of the repo
# was processed by a merger. It then was unable to reset the
# repo because of:
# GitCommandError: 'git reset --hard HEAD' returned
# with exit code 128
# stderr: 'fatal: ambiguous argument 'HEAD': both revision
# and filename
# Use '--' to separate filenames from revisions'
A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
A.addPatchset(['HEAD'])
B = self.fake_gerrit.addFakeChange('org/project', 'master', 'B')
self.fake_gerrit.addEvent(A.getPatchsetCreatedEvent(2))
self.waitUntilSettled()
self.fake_gerrit.addEvent(B.getPatchsetCreatedEvent(1))
self.waitUntilSettled()
self.assertIn('Build succeeded', A.messages[0])
self.assertIn('Build succeeded', B.messages[0])
def test_file_jobs(self):
"Test that file jobs run only when appropriate"
A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')

View File

@ -20,6 +20,21 @@ import logging
import zuul.model
def reset_repo_to_head(repo):
# This lets us reset the repo even if there is a file in the root
# directory named 'HEAD'. Currently, GitPython does not allow us
# to instruct it to always include the '--' to disambiguate. This
# should no longer be necessary if this PR merges:
# https://github.com/gitpython-developers/GitPython/pull/319
try:
repo.git.reset('--hard', 'HEAD', '--')
except git.GitCommandError as e:
# git nowadays may use 1 as status to indicate there are still unstaged
# modifications after the reset
if e.status != 1:
raise
class ZuulReference(git.Reference):
_common_path_default = "refs/zuul"
_points_to_commits_only = True
@ -82,7 +97,7 @@ class Repo(object):
# Reset to remote HEAD (usually origin/master)
repo.head.reference = origin.refs['HEAD']
repo.head.reset(index=True, working_tree=True)
reset_repo_to_head(repo)
repo.git.clean('-x', '-f', '-d')
def prune(self):
@ -114,7 +129,7 @@ class Repo(object):
repo = self.createRepoObject()
self.log.debug("Checking out %s" % ref)
repo.head.reference = ref
repo.head.reset(index=True, working_tree=True)
reset_repo_to_head(repo)
def cherryPick(self, ref):
repo = self.createRepoObject()