diff --git a/manifest_xml.py b/manifest_xml.py index 8e9efd13..be185477 100644 --- a/manifest_xml.py +++ b/manifest_xml.py @@ -123,7 +123,7 @@ class XmlManifest(object): if r.reviewUrl is not None: e.setAttribute('review', r.reviewUrl) - def Save(self, fd, peg_rev=False): + def Save(self, fd, peg_rev=False, peg_rev_upstream=True): """Write the current manifest out to the given file descriptor. """ mp = self.manifestProject @@ -197,11 +197,15 @@ class XmlManifest(object): e.setAttribute('remote', p.remote.name) if peg_rev: if self.IsMirror: - e.setAttribute('revision', - p.bare_git.rev_parse(p.revisionExpr + '^0')) + value = p.bare_git.rev_parse(p.revisionExpr + '^0') else: - e.setAttribute('revision', - p.work_git.rev_parse(HEAD + '^0')) + value = p.work_git.rev_parse(HEAD + '^0') + e.setAttribute('revision', value) + if peg_rev_upstream and value != p.revisionExpr: + # Only save the origin if the origin is not a sha1, and the default + # isn't our value, and the if the default doesn't already have that + # covered. + e.setAttribute('upstream', p.revisionExpr) elif not d.revisionExpr or p.revisionExpr != d.revisionExpr: e.setAttribute('revision', p.revisionExpr) @@ -573,6 +577,8 @@ class XmlManifest(object): else: sync_c = sync_c.lower() in ("yes", "true", "1") + upstream = node.getAttribute('upstream') + groups = '' if node.hasAttribute('groups'): groups = node.getAttribute('groups') @@ -599,7 +605,8 @@ class XmlManifest(object): revisionId = None, rebase = rebase, groups = groups, - sync_c = sync_c) + sync_c = sync_c, + upstream = upstream) for n in node.childNodes: if n.nodeName == 'copyfile': diff --git a/project.py b/project.py index 4621013c..04c43bb7 100644 --- a/project.py +++ b/project.py @@ -484,7 +484,8 @@ class Project(object): revisionId, rebase = True, groups = None, - sync_c = False): + sync_c = False, + upstream = None): self.manifest = manifest self.name = name self.remote = remote @@ -506,6 +507,7 @@ class Project(object): self.rebase = rebase self.groups = groups self.sync_c = sync_c + self.upstream = upstream self.snapshots = {} self.copyfiles = [] @@ -1373,6 +1375,16 @@ class Project(object): is_sha1 = False tag_name = None + def CheckForSha1(): + try: + # if revision (sha or tag) is not present then following function + # throws an error. + self.bare_git.rev_parse('--verify', '%s^0' % self.revisionExpr) + return True + except GitError: + # There is no such persistent revision. We have to fetch it. + return False + if current_branch_only: if ID_RE.match(self.revisionExpr) is not None: is_sha1 = True @@ -1381,14 +1393,10 @@ class Project(object): tag_name = self.revisionExpr[len(R_TAGS):] if is_sha1 or tag_name is not None: - try: - # if revision (sha or tag) is not present then following function - # throws an error. - self.bare_git.rev_parse('--verify', '%s^0' % self.revisionExpr) + if CheckForSha1(): return True - except GitError: - # There is no such persistent revision. We have to fetch it. - pass + if is_sha1 and (not self.upstream or ID_RE.match(self.upstream)): + current_branch_only = False if not name: name = self.remote.name @@ -1453,7 +1461,7 @@ class Project(object): cmd.append('--update-head-ok') cmd.append(name) - if not current_branch_only or is_sha1: + if not current_branch_only: # Fetch whole repo cmd.append('--tags') cmd.append((u'+refs/heads/*:') + remote.ToLocal('refs/heads/*')) @@ -1462,15 +1470,23 @@ class Project(object): cmd.append(tag_name) else: branch = self.revisionExpr + if is_sha1: + branch = self.upstream if branch.startswith(R_HEADS): branch = branch[len(R_HEADS):] cmd.append((u'+refs/heads/%s:' % branch) + remote.ToLocal('refs/heads/%s' % branch)) ok = False for i in range(2): - if GitCommand(self, cmd, bare=True, ssh_proxy=ssh_proxy).Wait() == 0: + ret = GitCommand(self, cmd, bare=True, ssh_proxy=ssh_proxy).Wait() + if ret == 0: ok = True break + elif current_branch_only and is_sha1 and ret == 128: + # Exit code 128 means "couldn't find the ref you asked for"; if we're in sha1 + # mode, we just tried sync'ing from the upstream field; it doesn't exist, thus + # abort the optimization attempt and do a full sync. + break time.sleep(random.randint(30, 45)) if initial: @@ -1480,6 +1496,15 @@ class Project(object): else: os.remove(packed_refs) self.bare_git.pack_refs('--all', '--prune') + + if is_sha1 and current_branch_only and self.upstream: + # We just synced the upstream given branch; verify we + # got what we wanted, else trigger a second run of all + # refs. + if not CheckForSha1(): + return self._RemoteFetch(name=name, current_branch_only=False, + initial=False, quiet=quiet, alt_dir=alt_dir) + return ok def _ApplyCloneBundle(self, initial=False, quiet=False): diff --git a/subcmds/manifest.py b/subcmds/manifest.py index cd196531..43887654 100644 --- a/subcmds/manifest.py +++ b/subcmds/manifest.py @@ -48,6 +48,11 @@ in a Git repository for use during future 'repo init' invocations. p.add_option('-r', '--revision-as-HEAD', dest='peg_rev', action='store_true', help='Save revisions as current HEAD') + p.add_option('--suppress-upstream-revision', dest='peg_rev_upstream', + default=True, action='store_false', + help='If in -r mode, do not write the upstream field. ' + 'Only of use if the branch names for a sha1 manifest are ' + 'sensitive.') p.add_option('-o', '--output-file', dest='output_file', default='-', @@ -60,7 +65,8 @@ in a Git repository for use during future 'repo init' invocations. else: fd = open(opt.output_file, 'w') self.manifest.Save(fd, - peg_rev = opt.peg_rev) + peg_rev = opt.peg_rev, + peg_rev_upstream = opt.peg_rev_upstream) fd.close() if opt.output_file != '-': print >>sys.stderr, 'Saved manifest to %s' % opt.output_file