Merge ~rhansen/git-build-recipe:pristine-tar-refs into git-build-recipe:master

Proposed by Richard Hansen
Status: Needs review
Proposed branch: ~rhansen/git-build-recipe:pristine-tar-refs
Merge into: git-build-recipe:master
Diff against target: 91 lines (+67/-2)
2 files modified
gitbuildrecipe/recipe.py (+25/-2)
gitbuildrecipe/tests/test_functional.py (+42/-0)
Reviewer Review Type Date Requested Status
Clinton Fung Pending
William Grant Pending
Canonical Launchpad Engineering Pending
Loïc Minier Pending
Review via email: mp+446358@code.launchpad.net

Description of the change

Fetch all references so that the pristine-tar trees are guaranteed to be fetched.

This is an alternative to:
https://code.launchpad.net/~lool/git-build-recipe/+git/git-build-recipe/+merge/443942

Changes from that merge request:
  - Tags are put in refs/tags/<remote>/* instead of refs/remotes/<remote>/* (to avoid potential name collisions).
  - I added some comments explaining why fetching is done the way it is.
  - I added tests.

To post a comment you must log in.

Unmerged commits

39f83d9... by Richard Hansen

fetch everything for pristine-tar

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/gitbuildrecipe/recipe.py b/gitbuildrecipe/recipe.py
2index f932ca7..f8ffd45 100644
3--- a/gitbuildrecipe/recipe.py
4+++ b/gitbuildrecipe/recipe.py
5@@ -339,11 +339,34 @@ def fetch_branches(child_branch):
6 logging.info(
7 "Failed to fetch HEAD; recipe instructions for this repository "
8 "that do not specify a branch name will fail.")
9- # Fetch all remote branches and (implicitly) any tags that reference
10- # commits in those refs. Tags that aren't on a branch won't be fetched.
11+ # Fetch all remote branches and (implicitly) any tags in the commit history
12+ # of the remote branches. Tags that aren't on a branch will be fetched in a
13+ # separate fetch call below. Note that if two remotes have an identically
14+ # named tag, the tag from the first fetched remote wins; the second fetch
15+ # will silently skip the tag (fetch will not return an error).
16+ # Note: extract_upstream_tarball looks for the tags upstream/<version> and
17+ # upstream-<version> if the <remote>/pristine-tar branch doesn't exist.
18 child_branch.git_call(
19 "fetch", url,
20 "refs/heads/*:refs/remotes/%s/*" % child_branch.remote_name)
21+ # All refs must be fetched for pristine-tar to work because pristine-tar
22+ # stores Git tree object IDs in blobs, not as tree objects referenced from
23+ # the pristine-tar commits. Git has no way to know that the blobs reference
24+ # the tree objects, so it won't include the trees in the fetched packfile
25+ # unless another fetched ref brings them along. The trees are likely to be
26+ # in the upstream branch (already fetched above), but they could
27+ # theoretically be anywhere.
28+ #
29+ # The tags are prefixed with the remote name to avoid collision with other
30+ # remotes. The refspec could instead be the usual
31+ # "refs/tags/*:refs/tags/*", but then fetch would fail if two remotes have
32+ # an identically named tag pointing at different commits. The refspec could
33+ # be prefixed with "+" (or pass "--force"), but that would change the
34+ # semantics from first-fetched-wins to last-fetched-wins, which might break
35+ # existing recipes.
36+ child_branch.git_call(
37+ "fetch", url,
38+ "refs/tags/*:refs/tags/%s/*" % child_branch.remote_name)
39
40
41 @lru_cache(maxsize=1)
42diff --git a/gitbuildrecipe/tests/test_functional.py b/gitbuildrecipe/tests/test_functional.py
43index 56fd216..33ed43d 100644
44--- a/gitbuildrecipe/tests/test_functional.py
45+++ b/gitbuildrecipe/tests/test_functional.py
46@@ -579,3 +579,45 @@ class FunctionalBuilderTests(GitTestCase):
47 out, err = self.run_recipe(
48 "--allow-fallback-to-native test.recipe working", retcode=1)
49 self.assertIn("Unknown source format 2.0\n", err)
50+
51+ def test_all_refs_fetched(self):
52+ source = self.make_simple_package("source")
53+ main_branch = source._git_output(
54+ "branch", "--show-current").rstrip("\n")
55+ source._git_call("switch", "--orphan", "branch-for-unconnected-tag")
56+ source.build_tree(["a"])
57+ source.add(["a"])
58+ commit = source.commit("unconnected tag")
59+ source.tag("unconnected-tag", commit)
60+ source._git_call("switch", main_branch)
61+ source._git_call("branch", "-D", "branch-for-unconnected-tag")
62+ with open("package-0.1.recipe", "w") as recipe:
63+ recipe.write(
64+ "# git-build-recipe format 0.4 deb-version 0.1-1\n"
65+ "source\n")
66+ self.run_recipe("--allow-fallback-to-native package-0.1.recipe working")
67+ working = GitRepository("working/package-0.1", allow_create=False)
68+ working.rev_parse(commit)
69+ working.rev_parse("refs/tags/unconnected-tag")
70+
71+ def test_tag_conflict_first_fetched_wins(self):
72+ # If a tag exists in two remote repositories pointing to different
73+ # revisions, the first fetched repository "wins" (its tag appears in the
74+ # clone), and the losing tag is silently ignored. This is probably not
75+ # the ideal behavior, but changing it would require bumping the format
76+ # version number or else it might break existing recipes.
77+ source = GitRepository("source")
78+ source.build_tree(["a"])
79+ source.add(["a"])
80+ commit = source.commit("pristine")
81+ source.tag("upstream/0.1", commit)
82+ debianized = self.make_simple_package("debianized")
83+ debianized.tag("upstream/0.1", "HEAD")
84+ with open("package-0.1.recipe", "w") as recipe:
85+ recipe.write(
86+ "# git-build-recipe format 0.4 deb-version 0.1-1\n"
87+ "source\n"
88+ "nest-part debianized debianized debian\n")
89+ self.run_recipe("package-0.1.recipe working")
90+ working = GitRepository("working/package-0.1", allow_create=False)
91+ self.assertEqual(working.rev_parse("refs/tags/upstream/0.1"), commit)

Subscribers

People subscribed via source and target branches