← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] ~cjwatson/git-build-recipe:tolerate-missing-HEAD into git-build-recipe:master

 

Colin Watson has proposed merging ~cjwatson/git-build-recipe:tolerate-missing-HEAD into git-build-recipe:master.

Commit message:
Tolerate remote repositories with no HEAD

We want to fetch the remote HEAD if it exists, but if the recipe doesn't
rely on it then a failure to do so doesn't need to be fatal.

LP: #1683913

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)
Related bugs:
  Bug #1683913 in git-build-recipe: "git-build-recipe assumes that HEAD exists in the source repositories"
  https://bugs.launchpad.net/git-build-recipe/+bug/1683913

For more details, see:
https://code.launchpad.net/~cjwatson/git-build-recipe/+git/git-build-recipe/+merge/368050
-- 
Your team Launchpad code reviewers is requested to review the proposed merge of ~cjwatson/git-build-recipe:tolerate-missing-HEAD into git-build-recipe:master.
diff --git a/gitbuildrecipe/recipe.py b/gitbuildrecipe/recipe.py
index d1700f2..0ff215f 100644
--- a/gitbuildrecipe/recipe.py
+++ b/gitbuildrecipe/recipe.py
@@ -327,12 +327,22 @@ def fetch_branches(child_branch):
     parsed_url = urlparse(url)
     if not parsed_url.scheme and not parsed_url.path.startswith("/"):
         url = os.path.abspath(url)
-    # Fetch all remote branches, the remote HEAD, and (implicitly) any
-    # tags that reference commits in those refs. Tags that aren't on a
-    # branch won't be fetched.
+    # Fetch the remote HEAD.  This may not exist, which is OK as long as the
+    # recipe uses explicit branch names.
+    try:
+        child_branch.git_call(
+            "fetch", url,
+            "HEAD:refs/remotes/%s/HEAD" % child_branch.remote_name,
+            silent=True)
+    except subprocess.CalledProcessError as e:
+        logging.info(e.output)
+        logging.info(
+            "Failed to fetch HEAD; recipe instructions for this repository "
+            "that do not specify a branch name will fail.")
+    # Fetch all remote branches and (implicitly) any tags that reference
+    # commits in those refs. Tags that aren't on a branch won't be fetched.
     child_branch.git_call(
         "fetch", url,
-        "HEAD:refs/remotes/%s/HEAD" % child_branch.remote_name,
         "refs/heads/*:refs/remotes/%s/*" % child_branch.remote_name)
 
 
@@ -650,10 +660,10 @@ class RecipeBranch:
             output = subprocess.check_output(
                 cmd, stderr=subprocess.STDOUT, universal_newlines=True,
                 **kwargs)
-            if silent:
+            if not silent:
                 sys.stdout.write(output)
         except subprocess.CalledProcessError as e:
-            if silent:
+            if not silent:
                 sys.stderr.write(e.output)
             raise
 
diff --git a/gitbuildrecipe/tests/__init__.py b/gitbuildrecipe/tests/__init__.py
index 4962e50..6b41396 100644
--- a/gitbuildrecipe/tests/__init__.py
+++ b/gitbuildrecipe/tests/__init__.py
@@ -58,6 +58,9 @@ class GitRepository:
         self._git_call("commit", "-q", "--allow-empty", "-m", message, env=env)
         return self.last_revision()
 
+    def branch(self, branch_name, commit):
+        self._git_call("branch", branch_name, commit)
+
     def tag(self, tag_name, commit, force=False):
         args = ["tag"]
         if force:
@@ -65,6 +68,9 @@ class GitRepository:
         args.extend([tag_name, commit])
         self._git_call(*args, stdout=subprocess.DEVNULL)
 
+    def set_head(self, ref_path):
+        self._git_call("symbolic-ref", "HEAD", ref_path)
+
     def get_parents(self, commit):
         return self._git_output(
             "log", "-1", "--format=%P", commit).rstrip("\n").split()
diff --git a/gitbuildrecipe/tests/test_recipe.py b/gitbuildrecipe/tests/test_recipe.py
index ed7634d..7628a45 100644
--- a/gitbuildrecipe/tests/test_recipe.py
+++ b/gitbuildrecipe/tests/test_recipe.py
@@ -891,6 +891,20 @@ class BuildTreeTests(GitTestCase):
         self.assertRaises(
             TargetAlreadyExists, pull_or_clone, base_branch, "target")
 
+    def test_pull_or_clone_with_no_HEAD(self):
+        source = GitRepository("source")
+        source.build_tree(["a"])
+        source.add(["a"])
+        commit = source.commit("one")
+        source.branch("source/master", commit)
+        source.set_head("refs/heads/nonexistent")
+        base_branch = BaseRecipeBranch(
+            "source", "1", 0.2, revspec="source/master")
+        pull_or_clone(base_branch, "target")
+        target = GitRepository("target", allow_create=False)
+        self.assertEqual(commit, target.last_revision())
+        self.assertEqual(commit, target.rev_parse("source/master"))
+
     def test_build_tree_runs_commands(self):
         source = GitRepository("source")
         commit = source.commit("one")

Follow ups