diff -Nru repo-1.12.32/command.py repo-1.12.37/command.py --- repo-1.12.32/command.py 2015-12-06 22:02:47.000000000 -0600 +++ repo-1.12.37/command.py 2016-12-18 14:14:05.000000000 -0600 @@ -31,7 +31,7 @@ manifest = None _optparse = None - def WantPager(self, opt): + def WantPager(self, _opt): return False def ReadEnvironmentOptions(self, opts): @@ -63,7 +63,7 @@ usage = self.helpUsage.strip().replace('%prog', me) except AttributeError: usage = 'repo %s' % self.NAME - self._optparse = optparse.OptionParser(usage = usage) + self._optparse = optparse.OptionParser(usage=usage) self._Options(self._optparse) return self._optparse @@ -110,15 +110,20 @@ project = None if os.path.exists(path): oldpath = None - while path \ - and path != oldpath \ - and path != manifest.topdir: + while path and \ + path != oldpath and \ + path != manifest.topdir: try: project = self._by_path[path] break except KeyError: oldpath = path path = os.path.dirname(path) + if not project and path == manifest.topdir: + try: + project = self._by_path[path] + except KeyError: + pass else: try: project = self._by_path[path] @@ -138,7 +143,7 @@ mp = manifest.manifestProject if not groups: - groups = mp.config.GetString('manifest.groups') + groups = mp.config.GetString('manifest.groups') if not groups: groups = 'default,platform-' + platform.system().lower() groups = [x for x in re.split(r'[,\s]+', groups) if x] @@ -151,8 +156,7 @@ for p in project.GetDerivedSubprojects()) all_projects_list.extend(derived_projects.values()) for project in all_projects_list: - if ((missing_ok or project.Exists) and - project.MatchesGroups(groups)): + if (missing_ok or project.Exists) and project.MatchesGroups(groups): result.append(project) else: self._ResetPathToProjectMap(all_projects_list) @@ -166,8 +170,8 @@ # If it's not a derived project, update path->project mapping and # search again, as arg might actually point to a derived subproject. - if (project and not project.Derived and - (submodules_ok or project.sync_s)): + if (project and not project.Derived and (submodules_ok or + project.sync_s)): search_again = False for subproject in project.GetDerivedSubprojects(): self._UpdatePathToProjectMap(subproject) @@ -194,17 +198,24 @@ result.sort(key=_getpath) return result - def FindProjects(self, args): + def FindProjects(self, args, inverse=False): result = [] patterns = [re.compile(r'%s' % a, re.IGNORECASE) for a in args] for project in self.GetProjects(''): for pattern in patterns: - if pattern.search(project.name) or pattern.search(project.relpath): + match = pattern.search(project.name) or pattern.search(project.relpath) + if not inverse and match: result.append(project) break + if inverse and match: + break + else: + if inverse: + result.append(project) result.sort(key=lambda project: project.relpath) return result + # pylint: disable=W0223 # Pylint warns that the `InteractiveCommand` and `PagedCommand` classes do not # override method `Execute` which is abstract in `Command`. Since that method @@ -214,28 +225,32 @@ """Command which requires user interaction on the tty and must not run within a pager, even if the user asks to. """ - def WantPager(self, opt): + def WantPager(self, _opt): return False + class PagedCommand(Command): """Command which defaults to output in a pager, as its display tends to be larger than one screen full. """ - def WantPager(self, opt): + def WantPager(self, _opt): return True # pylint: enable=W0223 + class MirrorSafeCommand(object): """Command permits itself to run within a mirror, and does not require a working directory. """ + class GitcAvailableCommand(object): """Command that requires GITC to be available, but does not require the local client to be a GITC client. """ + class GitcClientCommand(object): """Command that requires the local client to be a GITC client. diff -Nru repo-1.12.32/debian/changelog repo-1.12.37/debian/changelog --- repo-1.12.32/debian/changelog 2016-06-30 12:05:44.000000000 -0500 +++ repo-1.12.37/debian/changelog 2016-12-26 15:37:57.000000000 -0600 @@ -1,3 +1,20 @@ +repo (1.12.37-1ubuntu1) zesty; urgency=medium + + * Merge from Debian Sid. Remaining changes: + - debian/control: + + Add Breaks/Replaces on older versions of phablet-tools that used + to also ship a /usr/bin/repo file. Can drop this delta after the + next LTS. + + -- Simon Quigley Mon, 26 Dec 2016 15:37:57 -0600 + +repo (1.12.37-1) unstable; urgency=medium + + * New upstream release + * Standards-Version: 3.9.8 + + -- Hans-Christoph Steiner Sun, 18 Dec 2016 21:45:53 +0100 + repo (1.12.32-2ubuntu1) yakkety; urgency=medium * debian/control: diff -Nru repo-1.12.32/debian/control repo-1.12.37/debian/control --- repo-1.12.32/debian/control 2016-06-30 12:05:44.000000000 -0500 +++ repo-1.12.37/debian/control 2016-12-26 15:37:12.000000000 -0600 @@ -5,7 +5,7 @@ XSBC-Original-Maintainer: Android tools Maintainer Uploaders: Hans-Christoph Steiner Build-Depends: debhelper (>= 9) -Standards-Version: 3.9.6 +Standards-Version: 3.9.8 Homepage: https://source.android.com/source/developing.html Vcs-Git: https://alioth.debian.org/anonscm/git/android-tools/repo.git Vcs-Browser: https://anonscm.debian.org/gitweb/?p=android-tools/repo.git diff -Nru repo-1.12.32/debian/patches/set-python-shebang.patch repo-1.12.37/debian/patches/set-python-shebang.patch --- repo-1.12.32/debian/patches/set-python-shebang.patch 2015-07-10 22:46:53.000000000 -0500 +++ repo-1.12.37/debian/patches/set-python-shebang.patch 2016-12-18 14:45:53.000000000 -0600 @@ -10,5 +10,5 @@ -#!/usr/bin/env python +#!/usr/bin/python - ## repo default configuration - ## + # repo default configuration + # diff -Nru repo-1.12.32/debian/rules repo-1.12.37/debian/rules --- repo-1.12.32/debian/rules 2015-12-07 01:44:31.000000000 -0600 +++ repo-1.12.37/debian/rules 2016-12-18 14:45:41.000000000 -0600 @@ -6,7 +6,6 @@ dh $@ --without python --without python2 get-orig-source: v$(DEB_VERSION_UPSTREAM).tar.gz - mk-origtargz --repack --compression xz $< v$(DEB_VERSION_UPSTREAM).tar.gz: wget https://android.googlesource.com/tools/repo/+archive/$@ diff -Nru repo-1.12.32/docs/manifest-format.txt repo-1.12.37/docs/manifest-format.txt --- repo-1.12.32/docs/manifest-format.txt 2015-12-06 22:02:47.000000000 -0600 +++ repo-1.12.37/docs/manifest-format.txt 2016-12-18 14:14:05.000000000 -0600 @@ -35,6 +35,7 @@ + @@ -47,12 +48,12 @@ - + + copyfile*, + linkfile*)> @@ -71,14 +72,14 @@ - - + + - - + + - + @@ -125,6 +126,12 @@ this remote. Each project's name is appended to this prefix to form the actual URL used to clone the project. +Attribute `pushurl`: The Git "push" URL prefix for all projects +which use this remote. Each project's name is appended to this +prefix to form the actual URL used to "git push" the project. +This attribute is optional; if not specified then "git push" +will use the same URL as the `fetch` attribute. + Attribute `review`: Hostname of the Gerrit server where reviews are uploaded to by `repo upload`. This attribute is optional; if not specified then `repo upload` will not function. @@ -175,7 +182,8 @@ GetApprovedManifest(branch, target) Return a manifest in which each project is pegged to a known good revision -for the current branch and target. +for the current branch and target. This is used by repo sync when the +--smart-sync option is given. The target to use is defined by environment variables TARGET_PRODUCT and TARGET_BUILD_VARIANT. These variables are used to create a string @@ -187,7 +195,8 @@ GetManifest(tag) Return a manifest in which each project is pegged to the revision at -the specified tag. +the specified tag. This is used by repo sync when the --smart-tag option +is given. Element project diff -Nru repo-1.12.32/.flake8 repo-1.12.37/.flake8 --- repo-1.12.32/.flake8 1969-12-31 18:00:00.000000000 -0600 +++ repo-1.12.37/.flake8 2016-12-18 14:14:05.000000000 -0600 @@ -0,0 +1,3 @@ +[flake8] +max-line-length=80 +ignore=E111,E114,E402 diff -Nru repo-1.12.32/.gitattributes repo-1.12.37/.gitattributes --- repo-1.12.32/.gitattributes 2015-12-06 22:02:47.000000000 -0600 +++ repo-1.12.37/.gitattributes 2016-12-18 14:14:05.000000000 -0600 @@ -1,4 +1,4 @@ # Prevent /bin/sh scripts from being clobbered by autocrlf=true git_ssh text eol=lf -main.py text eol=lf repo text eol=lf +hooks/* text eol=lf diff -Nru repo-1.12.32/git_command.py repo-1.12.37/git_command.py --- repo-1.12.32/git_command.py 2015-12-06 22:02:47.000000000 -0600 +++ repo-1.12.37/git_command.py 2016-12-18 14:14:05.000000000 -0600 @@ -168,6 +168,9 @@ if p is not None: s = p + ' ' + s _setenv(env, 'GIT_CONFIG_PARAMETERS', s) + if 'GIT_ALLOW_PROTOCOL' not in env: + _setenv(env, 'GIT_ALLOW_PROTOCOL', + 'file:git:http:https:ssh:persistent-http:persistent-https:sso:rpc') if project: if not cwd: diff -Nru repo-1.12.32/git_config.py repo-1.12.37/git_config.py --- repo-1.12.32/git_config.py 2015-12-06 22:02:47.000000000 -0600 +++ repo-1.12.37/git_config.py 2016-12-18 14:14:05.000000000 -0600 @@ -464,9 +464,13 @@ % (host,port, str(e)), file=sys.stderr) return False + time.sleep(1) + ssh_died = (p.poll() is not None) + if ssh_died: + return False + _master_processes.append(p) _master_keys.add(key) - time.sleep(1) return True finally: _master_keys_lock.release() @@ -568,6 +572,7 @@ self._config = config self.name = name self.url = self._Get('url') + self.pushUrl = self._Get('pushurl') self.review = self._Get('review') self.projectname = self._Get('projectname') self.fetch = list(map(RefSpec.FromString, @@ -694,6 +699,10 @@ """Save this remote to the configuration. """ self._Set('url', self.url) + if self.pushUrl is not None: + self._Set('pushurl', self.pushUrl + '/' + self.projectname) + else: + self._Set('pushurl', self.pushUrl) self._Set('review', self.review) self._Set('projectname', self.projectname) self._Set('fetch', list(map(str, self.fetch))) diff -Nru repo-1.12.32/gitc_utils.py repo-1.12.37/gitc_utils.py --- repo-1.12.32/gitc_utils.py 2015-12-06 22:02:47.000000000 -0600 +++ repo-1.12.37/gitc_utils.py 2016-12-18 14:14:05.000000000 -0600 @@ -24,7 +24,9 @@ import git_config import wrapper -NUM_BATCH_RETRIEVE_REVISIONID = 300 +from error import ManifestParseError + +NUM_BATCH_RETRIEVE_REVISIONID = 32 def get_gitc_manifest_dir(): return wrapper.Wrapper().get_gitc_manifest_dir() @@ -54,7 +56,11 @@ if gitcmd.Wait(): print('FATAL: Failed to retrieve revisionExpr for %s' % proj) sys.exit(1) - proj.revisionExpr = gitcmd.stdout.split('\t')[0] + revisionExpr = gitcmd.stdout.split('\t')[0] + if not revisionExpr: + raise(ManifestParseError('Invalid SHA-1 revision project %s (%s)' % + (proj.remote.url, proj.revisionExpr))) + proj.revisionExpr = revisionExpr def _manifest_groups(manifest): """Returns the manifest group string that should be synced @@ -127,7 +133,7 @@ repo_proj.revisionExpr = None # Convert URLs from relative to absolute. - for name, remote in manifest.remotes.iteritems(): + for _name, remote in manifest.remotes.iteritems(): remote.fetchUrl = remote.resolvedFetchUrl # Save the manifest. diff -Nru repo-1.12.32/hooks/commit-msg repo-1.12.37/hooks/commit-msg --- repo-1.12.32/hooks/commit-msg 2015-12-06 22:02:47.000000000 -0600 +++ repo-1.12.37/hooks/commit-msg 2016-12-18 14:14:05.000000000 -0600 @@ -1,6 +1,7 @@ #!/bin/sh +# From Gerrit Code Review 2.12.1 # -# Part of Gerrit Code Review (http://code.google.com/p/gerrit/) +# Part of Gerrit Code Review (https://www.gerritcodereview.com/) # # Copyright (C) 2009 The Android Open Source Project # @@ -19,7 +20,7 @@ unset GREP_OPTIONS -CHANGE_ID_AFTER="Bug|Issue" +CHANGE_ID_AFTER="Bug|Issue|Test" MSG="$1" # Check for, and add if missing, a unique Change-Id @@ -38,6 +39,12 @@ return fi + # Do not add Change-Id to temp commits + if echo "$clean_message" | head -1 | grep -q '^\(fixup\|squash\)!' + then + return + fi + if test "false" = "`git config --bool --get gerrit.createChangeId`" then return @@ -57,6 +64,10 @@ AWK=/usr/xpg4/bin/awk fi + # Get core.commentChar from git config or use default symbol + commentChar=`git config --get core.commentChar` + commentChar=${commentChar:-#} + # How this works: # - parse the commit message as (textLine+ blankLine*)* # - assume textLine+ to be a footer until proven otherwise @@ -75,8 +86,8 @@ blankLines = 0 } - # Skip lines starting with "#" without any spaces before it. - /^#/ { next } + # Skip lines starting with commentChar without any spaces before it. + /^'"$commentChar"'/ { next } # Skip the line starting with the diff command and everything after it, # up to the end of the file, assuming it is only patch data. diff -Nru repo-1.12.32/.mailmap repo-1.12.37/.mailmap --- repo-1.12.32/.mailmap 1969-12-31 18:00:00.000000000 -0600 +++ repo-1.12.37/.mailmap 2016-12-18 14:14:05.000000000 -0600 @@ -0,0 +1,11 @@ +Anthony Newnam Anthony +Hu Xiuyun Hu xiuyun +Hu Xiuyun Hu Xiuyun +Jelly Chen chenguodong +Jia Bi bijia +JoonCheol Park Jooncheol Park +Sergii Pylypenko pelya +Shawn Pearce Shawn O. Pearce +Ulrik Sjölin Ulrik Sjolin +Ulrik Sjölin Ulrik Sjolin +Ulrik Sjölin Ulrik Sjölin diff -Nru repo-1.12.32/main.py repo-1.12.37/main.py --- repo-1.12.32/main.py 2015-12-06 22:02:47.000000000 -0600 +++ repo-1.12.37/main.py 2016-12-18 14:14:05.000000000 -0600 @@ -379,7 +379,7 @@ self.context = None self.handler_order = urllib.request.BaseHandler.handler_order - 50 - def http_error_401(self, req, fp, code, msg, headers): + def http_error_401(self, req, fp, code, msg, headers): # pylint:disable=unused-argument host = req.get_host() retry = self.http_error_auth_reqed('www-authenticate', host, req, headers) return retry diff -Nru repo-1.12.32/manifest_xml.py repo-1.12.37/manifest_xml.py --- repo-1.12.32/manifest_xml.py 2015-12-06 22:02:47.000000000 -0600 +++ repo-1.12.37/manifest_xml.py 2016-12-18 14:14:05.000000000 -0600 @@ -64,11 +64,13 @@ name, alias=None, fetch=None, + pushUrl=None, manifestUrl=None, review=None, revision=None): self.name = name self.fetchUrl = fetch + self.pushUrl = pushUrl self.manifestUrl = manifestUrl self.remoteAlias = alias self.reviewUrl = review @@ -102,7 +104,11 @@ remoteName = self.name if self.remoteAlias: remoteName = self.remoteAlias - return RemoteSpec(remoteName, url, self.reviewUrl) + return RemoteSpec(remoteName, + url=url, + pushUrl=self.pushUrl, + review=self.reviewUrl, + orig_name=self.name) class XmlManifest(object): """manages the repo configuration file""" @@ -157,6 +163,8 @@ root.appendChild(e) e.setAttribute('name', r.name) e.setAttribute('fetch', r.fetchUrl) + if r.pushUrl is not None: + e.setAttribute('pushurl', r.pushUrl) if r.remoteAlias is not None: e.setAttribute('alias', r.remoteAlias) if r.reviewUrl is not None: @@ -249,9 +257,9 @@ e.setAttribute('path', relpath) remoteName = None if d.remote: - remoteName = d.remote.remoteAlias or d.remote.name - if not d.remote or p.remote.name != remoteName: - remoteName = p.remote.name + remoteName = d.remote.name + if not d.remote or p.remote.orig_name != remoteName: + remoteName = p.remote.orig_name e.setAttribute('remote', remoteName) if peg_rev: if self.IsMirror: @@ -267,7 +275,7 @@ # isn't our value e.setAttribute('upstream', p.revisionExpr) else: - revision = self.remotes[remoteName].revision or d.revisionExpr + revision = self.remotes[p.remote.orig_name].revision or d.revisionExpr if not revision or revision != p.revisionExpr: e.setAttribute('revision', p.revisionExpr) if p.upstream and p.upstream != p.revisionExpr: @@ -636,6 +644,9 @@ if alias == '': alias = None fetch = self._reqatt(node, 'fetch') + pushUrl = node.getAttribute('pushurl') + if pushUrl == '': + pushUrl = None review = node.getAttribute('review') if review == '': review = None @@ -643,7 +654,7 @@ if revision == '': revision = None manifestUrl = self.manifestProject.config.GetString('remote.origin.url') - return _XmlRemote(name, alias, fetch, manifestUrl, review, revision) + return _XmlRemote(name, alias, fetch, pushUrl, manifestUrl, review, revision) def _ParseDefault(self, node): """ @@ -969,5 +980,5 @@ def _output_manifest_project_extras(self, p, e): """Output GITC Specific Project attributes""" if p.old_revision: - e.setAttribute('old-revision', str(p.old_revision)) + e.setAttribute('old-revision', str(p.old_revision)) diff -Nru repo-1.12.32/project.py repo-1.12.37/project.py --- repo-1.12.32/project.py 2015-12-06 22:02:47.000000000 -0600 +++ repo-1.12.37/project.py 2016-12-18 14:14:05.000000000 -0600 @@ -30,7 +30,8 @@ from color import Coloring from git_command import GitCommand, git_require -from git_config import GitConfig, IsId, GetSchemeFromUrl, GetUrlCookieFile, ID_RE +from git_config import GitConfig, IsId, GetSchemeFromUrl, GetUrlCookieFile, \ + ID_RE from error import GitError, HookError, UploadError, DownloadError from error import ManifestInvalidRevisionError from error import NoManifestException @@ -39,11 +40,18 @@ from git_refs import GitRefs, HEAD, R_HEADS, R_TAGS, R_PUB, R_M from pyversion import is_python3 -if not is_python3(): +if is_python3(): + import urllib.parse +else: + import imp + import urlparse + urllib = imp.new_module('urllib') + urllib.parse = urlparse # pylint:disable=W0622 input = raw_input # pylint:enable=W0622 + def _lwrite(path, content): lock = '%s.lock' % path @@ -59,21 +67,27 @@ os.remove(lock) raise + def _error(fmt, *args): msg = fmt % args print('error: %s' % msg, file=sys.stderr) + def _warn(fmt, *args): msg = fmt % args print('warn: %s' % msg, file=sys.stderr) + def not_rev(r): return '^' + r + def sq(r): return "'" + r.replace("'", "'\''") + "'" _project_hook_list = None + + def _ProjectHooks(): """List the hooks present in the 'hooks' directory. @@ -107,15 +121,14 @@ @property def commits(self): if self._commit_cache is None: - self._commit_cache = self.project.bare_git.rev_list( - '--abbrev=8', - '--abbrev-commit', - '--pretty=oneline', - '--reverse', - '--date-order', - not_rev(self.base), - self.commit, - '--') + self._commit_cache = self.project.bare_git.rev_list('--abbrev=8', + '--abbrev-commit', + '--pretty=oneline', + '--reverse', + '--date-order', + not_rev(self.base), + self.commit, + '--') return self._commit_cache @@ -134,36 +147,36 @@ @property def commits(self): if self._commit_cache is None: - self._commit_cache = self.project.bare_git.rev_list( - '--abbrev=8', - '--abbrev-commit', - '--pretty=oneline', - '--reverse', - '--date-order', - not_rev(self.base), - R_HEADS + self.name, - '--') + self._commit_cache = self.project.bare_git.rev_list('--abbrev=8', + '--abbrev-commit', + '--pretty=oneline', + '--reverse', + '--date-order', + not_rev(self.base), + R_HEADS + self.name, + '--') return self._commit_cache @property def unabbrev_commits(self): r = dict() - for commit in self.project.bare_git.rev_list( - not_rev(self.base), - R_HEADS + self.name, - '--'): + for commit in self.project.bare_git.rev_list(not_rev(self.base), + R_HEADS + self.name, + '--'): r[commit[0:8]] = commit return r @property def date(self): - return self.project.bare_git.log( - '--pretty=format:%cd', - '-n', '1', - R_HEADS + self.name, - '--') + return self.project.bare_git.log('--pretty=format:%cd', + '-n', '1', + R_HEADS + self.name, + '--') - def UploadForReview(self, people, auto_topic=False, draft=False, dest_branch=None): + def UploadForReview(self, people, + auto_topic=False, + draft=False, + dest_branch=None): self.project.UploadForReview(self.name, people, auto_topic=auto_topic, @@ -173,8 +186,8 @@ def GetPublishedRefs(self): refs = {} output = self.project.bare_git.ls_remote( - self.branch.remote.SshReviewUrl(self.project.UserEmail), - 'refs/changes/*') + self.branch.remote.SshReviewUrl(self.project.UserEmail), + 'refs/changes/*') for line in output.split('\n'): try: (sha, ref) = line.split() @@ -184,7 +197,9 @@ return refs + class StatusColoring(Coloring): + def __init__(self, config): Coloring.__init__(self, config, 'status') self.project = self.printer('header', attr='bold') @@ -198,17 +213,22 @@ class DiffColoring(Coloring): + def __init__(self, config): Coloring.__init__(self, config, 'diff') self.project = self.printer('header', attr='bold') + class _Annotation(object): + def __init__(self, name, value, keep): self.name = name self.value = value self.keep = keep + class _CopyFile(object): + def __init__(self, src, dest, abssrc, absdest): self.src = src self.dest = dest @@ -236,7 +256,9 @@ except IOError: _error('Cannot copy file %s to %s', src, dest) + class _LinkFile(object): + def __init__(self, git_worktree, src, dest, relsrc, absdest): self.git_worktree = git_worktree self.src = src @@ -249,7 +271,7 @@ if not os.path.islink(absDest) or (os.readlink(absDest) != relSrc): try: # remove existing file first, since it might be read-only - if os.path.exists(absDest): + if os.path.lexists(absDest): os.remove(absDest) else: dest_dir = os.path.dirname(absDest) @@ -275,7 +297,7 @@ absDestDir = self.abs_dest if os.path.exists(absDestDir) and not os.path.isdir(absDestDir): _error('Link error: src with wildcard, %s must be a directory', - absDestDir) + absDestDir) else: absSrcFiles = glob.glob(absSrc) for absSrcFile in absSrcFiles: @@ -292,18 +314,26 @@ relSrc = os.path.join(relSrcDir, srcFile) self.__linkIt(relSrc, absDest) + class RemoteSpec(object): + def __init__(self, name, url=None, + pushUrl=None, review=None, - revision=None): + revision=None, + orig_name=None): self.name = name self.url = url + self.pushUrl = pushUrl self.review = review self.revision = revision + self.orig_name = orig_name + class RepoHook(object): + """A RepoHook contains information about a script to run as a hook. Hooks are used to run a python script before running an upload (for instance, @@ -316,10 +346,12 @@ Hooks are always python. When a hook is run, we will load the hook into the interpreter and execute its main() function. """ + def __init__(self, hook_type, hooks_project, topdir, + manifest_url, abort_if_user_denies=False): """RepoHook constructor. @@ -333,11 +365,13 @@ topdir: Repo's top directory (the one containing the .repo directory). Scripts will run with CWD as this directory. If you have a manifest, this is manifest.topdir + manifest_url: The URL to the manifest git repo. abort_if_user_denies: If True, we'll throw a HookError() if the user doesn't allow us to run the hook. """ self._hook_type = hook_type self._hooks_project = hooks_project + self._manifest_url = manifest_url self._topdir = topdir self._abort_if_user_denies = abort_if_user_denies @@ -386,9 +420,9 @@ def _CheckForHookApproval(self): """Check to see whether this hook has been approved. - We'll look at the hash of all of the hooks. If this matches the hash that - the user last approved, we're done. If it doesn't, we'll ask the user - about approval. + We'll accept approval of manifest URLs if they're using secure transports. + This way the user can say they trust the manifest hoster. For insecure + hosts, we fall back to checking the hash of the hooks repo. Note that we ask permission for each individual hook even though we use the hash of all hooks when detecting changes. We'd like the user to be @@ -402,44 +436,58 @@ HookError: Raised if the user doesn't approve and abort_if_user_denies was passed to the consturctor. """ - hooks_config = self._hooks_project.config - git_approval_key = 'repo.hooks.%s.approvedhash' % self._hook_type + if self._ManifestUrlHasSecureScheme(): + return self._CheckForHookApprovalManifest() + else: + return self._CheckForHookApprovalHash() + + def _CheckForHookApprovalHelper(self, subkey, new_val, main_prompt, + changed_prompt): + """Check for approval for a particular attribute and hook. + + Args: + subkey: The git config key under [repo.hooks.] to store the + last approved string. + new_val: The new value to compare against the last approved one. + main_prompt: Message to display to the user to ask for approval. + changed_prompt: Message explaining why we're re-asking for approval. + + Returns: + True if this hook is approved to run; False otherwise. - # Get the last hash that the user approved for this hook; may be None. - old_hash = hooks_config.GetString(git_approval_key) + Raises: + HookError: Raised if the user doesn't approve and abort_if_user_denies + was passed to the consturctor. + """ + hooks_config = self._hooks_project.config + git_approval_key = 'repo.hooks.%s.%s' % (self._hook_type, subkey) - # Get the current hash so we can tell if scripts changed since approval. - new_hash = self._GetHash() + # Get the last value that the user approved for this hook; may be None. + old_val = hooks_config.GetString(git_approval_key) - if old_hash is not None: + if old_val is not None: # User previously approved hook and asked not to be prompted again. - if new_hash == old_hash: + if new_val == old_val: # Approval matched. We're done. return True else: # Give the user a reason why we're prompting, since they last told # us to "never ask again". - prompt = 'WARNING: Scripts have changed since %s was allowed.\n\n' % ( - self._hook_type) + prompt = 'WARNING: %s\n\n' % (changed_prompt,) else: prompt = '' # Prompt the user if we're not on a tty; on a tty we'll assume "no". if sys.stdout.isatty(): - prompt += ('Repo %s run the script:\n' - ' %s\n' - '\n' - 'Do you want to allow this script to run ' - '(yes/yes-never-ask-again/NO)? ') % ( - self._GetMustVerb(), self._script_fullpath) + prompt += main_prompt + ' (yes/always/NO)? ' response = input(prompt).lower() print() # User is doing a one-time approval. if response in ('y', 'yes'): return True - elif response == 'yes-never-ask-again': - hooks_config.SetString(git_approval_key, new_hash) + elif response == 'always': + hooks_config.SetString(git_approval_key, new_val) return True # For anything else, we'll assume no approval. @@ -449,6 +497,40 @@ return False + def _ManifestUrlHasSecureScheme(self): + """Check if the URI for the manifest is a secure transport.""" + secure_schemes = ('file', 'https', 'ssh', 'persistent-https', 'sso', 'rpc') + parse_results = urllib.parse.urlparse(self._manifest_url) + return parse_results.scheme in secure_schemes + + def _CheckForHookApprovalManifest(self): + """Check whether the user has approved this manifest host. + + Returns: + True if this hook is approved to run; False otherwise. + """ + return self._CheckForHookApprovalHelper( + 'approvedmanifest', + self._manifest_url, + 'Run hook scripts from %s' % (self._manifest_url,), + 'Manifest URL has changed since %s was allowed.' % (self._hook_type,)) + + def _CheckForHookApprovalHash(self): + """Check whether the user has approved the hooks repo. + + Returns: + True if this hook is approved to run; False otherwise. + """ + prompt = ('Repo %s run the script:\n' + ' %s\n' + '\n' + 'Do you want to allow this script to run') + return self._CheckForHookApprovalHelper( + 'approvedhash', + self._GetHash(), + prompt % (self._GetMustVerb(), self._script_fullpath), + 'Scripts have changed since %s was allowed.' % (self._hook_type,)) + def _ExecuteHook(self, **kwargs): """Actually execute the given hook. @@ -475,19 +557,18 @@ # Exec, storing global context in the context dict. We catch exceptions # and convert to a HookError w/ just the failing traceback. - context = {} + context = {'__file__': self._script_fullpath} try: exec(compile(open(self._script_fullpath).read(), self._script_fullpath, 'exec'), context) except Exception: - raise HookError('%s\nFailed to import %s hook; see traceback above.' % ( - traceback.format_exc(), self._hook_type)) + raise HookError('%s\nFailed to import %s hook; see traceback above.' % + (traceback.format_exc(), self._hook_type)) # Running the script should have defined a main() function. if 'main' not in context: raise HookError('Missing main() in: "%s"' % self._script_fullpath) - # Add 'hook_should_take_kwargs' to the arguments to be passed to main. # We don't actually want hooks to define their main with this argument-- # it's there to remind them that their hook should always take **kwargs. @@ -505,8 +586,8 @@ context['main'](**kwargs) except Exception: raise HookError('%s\nFailed to run main() for %s hook; see traceback ' - 'above.' % ( - traceback.format_exc(), self._hook_type)) + 'above.' % (traceback.format_exc(), + self._hook_type)) finally: # Restore sys.path and CWD. sys.path = orig_syspath @@ -530,8 +611,8 @@ to run a required hook (from _CheckForHookApproval). """ # No-op if there is no hooks project or if hook is disabled. - if ((not self._hooks_project) or - (self._hook_type not in self._hooks_project.enabled_repo_hooks)): + if ((not self._hooks_project) or (self._hook_type not in + self._hooks_project.enabled_repo_hooks)): return # Bail with a nice error if we can't find the hook. @@ -553,6 +634,7 @@ # These objects can only be used by a single working tree. working_tree_files = ['config', 'packed-refs', 'shallow'] working_tree_dirs = ['logs', 'refs'] + def __init__(self, manifest, name, @@ -605,15 +687,15 @@ self.gitdir = gitdir.replace('\\', '/') self.objdir = objdir.replace('\\', '/') if worktree: - self.worktree = worktree.replace('\\', '/') + self.worktree = os.path.normpath(worktree.replace('\\', '/')) else: self.worktree = None self.relpath = relpath self.revisionExpr = revisionExpr - if revisionId is None \ - and revisionExpr \ - and IsId(revisionExpr): + if revisionId is None \ + and revisionExpr \ + and IsId(revisionExpr): self.revisionId = revisionExpr else: self.revisionId = revisionId @@ -633,9 +715,8 @@ self.copyfiles = [] self.linkfiles = [] self.annotations = [] - self.config = GitConfig.ForRepository( - gitdir=self.gitdir, - defaults=self.manifest.globalConfig) + self.config = GitConfig.ForRepository(gitdir=self.gitdir, + defaults=self.manifest.globalConfig) if self.worktree: self.work_git = self._GitGetByExec(self, bare=False, gitdir=gitdir) @@ -773,7 +854,7 @@ """ expanded_manifest_groups = manifest_groups or ['default'] expanded_project_groups = ['all'] + (self.groups or []) - if not 'notdefault' in expanded_project_groups: + if 'notdefault' not in expanded_project_groups: expanded_project_groups += ['default'] matched = False @@ -785,7 +866,7 @@ return matched -## Status Display ## +# Status Display ## def UncommitedFiles(self, get_all=True): """Returns a list of strings, uncommitted files in the git tree. @@ -837,7 +918,7 @@ output: If specified, redirect the output to this object. """ if not os.path.isdir(self.worktree): - if output_redir == None: + if output_redir is None: output_redir = sys.stdout print(file=output_redir) print('project %s/' % self.relpath, file=output_redir) @@ -856,7 +937,7 @@ return 'CLEAN' out = StatusColoring(self.config) - if not output_redir == None: + if output_redir is not None: out.redirect(output_redir) out.project('project %-40s', self.relpath + '/ ') @@ -899,7 +980,7 @@ if i and i.src_path: line = ' %s%s\t%s => %s (%s%%)' % (i_status, f_status, - i.src_path, p, i.level) + i.src_path, p, i.level) else: line = ' %s%s\t%s' % (i_status, f_status, p) @@ -942,7 +1023,7 @@ p.Wait() -## Publish / Upload ## +# Publish / Upload ## def WasPublished(self, branch, all_refs=None): """Was the branch published (uploaded) for code review? @@ -1085,7 +1166,7 @@ message=msg) -## Sync ## +# Sync ## def _ExtractArchive(self, tarpath, path=None): """Extract the given tar on its current location @@ -1103,14 +1184,15 @@ return False def Sync_NetworkHalf(self, - quiet=False, - is_new=None, - current_branch_only=False, - force_sync=False, - clone_bundle=True, - no_tags=False, - archive=False, - optimized_fetch=False): + quiet=False, + is_new=None, + current_branch_only=False, + force_sync=False, + clone_bundle=True, + no_tags=False, + archive=False, + optimized_fetch=False, + prune=False): """Perform only the network IO portion of the sync process. Local working directory/branch state is not affected. """ @@ -1163,8 +1245,8 @@ alt_dir = None if clone_bundle \ - and alt_dir is None \ - and self._ApplyCloneBundle(initial=is_new, quiet=quiet): + and alt_dir is None \ + and self._ApplyCloneBundle(initial=is_new, quiet=quiet): is_new = False if not current_branch_only: @@ -1176,12 +1258,13 @@ elif self.manifest.default.sync_c: current_branch_only = True - need_to_fetch = not (optimized_fetch and \ - (ID_RE.match(self.revisionExpr) and self._CheckForSha1())) - if (need_to_fetch - and not self._RemoteFetch(initial=is_new, quiet=quiet, alt_dir=alt_dir, - current_branch_only=current_branch_only, - no_tags=no_tags)): + need_to_fetch = not (optimized_fetch and + (ID_RE.match(self.revisionExpr) and + self._CheckForSha1())) + if (need_to_fetch and + not self._RemoteFetch(initial=is_new, quiet=quiet, alt_dir=alt_dir, + current_branch_only=current_branch_only, + no_tags=no_tags, prune=prune)): return False if self.worktree: @@ -1218,9 +1301,8 @@ try: return self.bare_git.rev_list(self.revisionExpr, '-1')[0] except GitError: - raise ManifestInvalidRevisionError( - 'revision %s in %s not found' % (self.revisionExpr, - self.name)) + raise ManifestInvalidRevisionError('revision %s in %s not found' % + (self.revisionExpr, self.name)) def GetRevisionId(self, all_refs=None): if self.revisionId: @@ -1235,9 +1317,8 @@ try: return self.bare_git.rev_parse('--verify', '%s^0' % rev) except GitError: - raise ManifestInvalidRevisionError( - 'revision %s in %s not found' % (self.revisionExpr, - self.name)) + raise ManifestInvalidRevisionError('revision %s in %s not found' % + (self.revisionExpr, self.name)) def Sync_LocalHalf(self, syncbuf, force_sync=False): """Perform only the local IO portion of the sync process. @@ -1326,8 +1407,8 @@ # to rewrite the published commits so we punt. # syncbuf.fail(self, - "branch %s is published (but not merged) and is now %d commits behind" - % (branch.name, len(upstream_gain))) + "branch %s is published (but not merged) and is now " + "%d commits behind" % (branch.name, len(upstream_gain))) return elif pub == head: # All published commits are merged, and thus we are a @@ -1421,7 +1502,7 @@ remote = self.GetRemote(self.remote.name) cmd = ['fetch', remote.name] - cmd.append('refs/changes/%2.2d/%d/%d' \ + cmd.append('refs/changes/%2.2d/%d/%d' % (change_id % 100, change_id, patch_id)) if GitCommand(self, cmd, bare=True).Wait() != 0: return None @@ -1432,7 +1513,7 @@ self.bare_git.rev_parse('FETCH_HEAD')) -## Branch Management ## +# Branch Management ## def StartBranch(self, name, branch_merge=''): """Create a new branch off the manifest's revision. @@ -1582,8 +1663,6 @@ if kill: old = self.bare_git.GetHead() - if old is None: - old = 'refs/heads/please_never_use_this_as_a_branch_name' try: self.bare_git.DetachHead(rev) @@ -1595,7 +1674,10 @@ capture_stderr=True) b.Wait() finally: - self.bare_git.SetHead(old) + if ID_RE.match(old): + self.bare_git.DetachHead(old) + else: + self.bare_git.SetHead(old) left = self._allrefs for branch in kill: @@ -1618,10 +1700,11 @@ return kept -## Submodule Management ## +# Submodule Management ## def GetRegisteredSubprojects(self): result = [] + def rec(subprojects): if not subprojects: return @@ -1656,6 +1739,7 @@ re_path = re.compile(r'^submodule\.([^.]+)\.path=(.*)$') re_url = re.compile(r'^submodule\.([^.]+)\.url=(.*)$') + def parse_gitmodules(gitdir, rev): cmd = ['cat-file', 'blob', '%s:.gitmodules' % rev] try: @@ -1743,6 +1827,7 @@ remote = RemoteSpec(self.remote.name, url=url, + pushUrl=self.remote.pushUrl, review=self.remote.review, revision=self.remote.revision) subproject = Project(manifest=self.manifest, @@ -1752,7 +1837,7 @@ objdir=objdir, worktree=worktree, relpath=relpath, - revisionExpr=self.revisionExpr, + revisionExpr=rev, revisionId=rev, rebase=self.rebase, groups=self.groups, @@ -1765,7 +1850,7 @@ return result -## Direct Git Commands ## +# Direct Git Commands ## def _CheckForSha1(self): try: # if revision (sha or tag) is not present then following function @@ -1789,13 +1874,13 @@ if command.Wait() != 0: raise GitError('git archive %s: %s' % (self.name, command.stderr)) - def _RemoteFetch(self, name=None, current_branch_only=False, initial=False, quiet=False, alt_dir=None, - no_tags=False): + no_tags=False, + prune=False): is_sha1 = False tag_name = None @@ -1835,7 +1920,10 @@ # will fail. # * otherwise, fetch all branches to make sure we end up with the # specific commit. - current_branch_only = self.upstream and not ID_RE.match(self.upstream) + if self.upstream: + current_branch_only = not ID_RE.match(self.upstream) + else: + current_branch_only = False if not name: name = self.remote.name @@ -1908,6 +1996,9 @@ else: cmd.append('--tags') + if prune: + cmd.append('--prune') + spec = [] if not current_branch_only: # Fetch whole repo @@ -1949,9 +2040,9 @@ break continue 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. + # 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 elif ret < 0: # Git died with a signal, exit immediately @@ -1978,20 +2069,24 @@ initial=False, quiet=quiet, alt_dir=alt_dir) if self.clone_depth: self.clone_depth = None - return self._RemoteFetch(name=name, current_branch_only=current_branch_only, + return self._RemoteFetch(name=name, + current_branch_only=current_branch_only, initial=False, quiet=quiet, alt_dir=alt_dir) return ok def _ApplyCloneBundle(self, initial=False, quiet=False): - if initial and (self.manifest.manifestProject.config.GetString('repo.depth') or self.clone_depth): + if initial and \ + (self.manifest.manifestProject.config.GetString('repo.depth') or + self.clone_depth): return False remote = self.GetRemote(self.remote.name) bundle_url = remote.url + '/clone.bundle' bundle_url = GitConfig.ForUser().UrlInsteadOf(bundle_url) - if GetSchemeFromUrl(bundle_url) not in ( - 'http', 'https', 'persistent-http', 'persistent-https'): + if GetSchemeFromUrl(bundle_url) not in ('http', 'https', + 'persistent-http', + 'persistent-https'): return False bundle_dst = os.path.join(self.gitdir, 'clone.bundle') @@ -2040,7 +2135,7 @@ os.remove(tmpPath) if 'http_proxy' in os.environ and 'darwin' == sys.platform: cmd += ['--proxy', os.environ['http_proxy']] - with GetUrlCookieFile(srcUrl, quiet) as (cookiefile, proxy): + with GetUrlCookieFile(srcUrl, quiet) as (cookiefile, _proxy): if cookiefile: cmd += ['--cookie', cookiefile, '--cookie-jar', cookiefile] if srcUrl.startswith('persistent-'): @@ -2159,11 +2254,12 @@ self._CheckDirReference(self.objdir, self.gitdir, share_refs=False) except GitError as e: if force_sync: - print("Retrying clone after deleting %s" % self.gitdir, file=sys.stderr) + print("Retrying clone after deleting %s" % + self.gitdir, file=sys.stderr) try: shutil.rmtree(os.path.realpath(self.gitdir)) - if self.worktree and os.path.exists( - os.path.realpath(self.worktree)): + if self.worktree and os.path.exists(os.path.realpath + (self.worktree)): shutil.rmtree(os.path.realpath(self.worktree)) return self._InitGitDir(mirror_git=mirror_git, force_sync=False) except: @@ -2199,6 +2295,7 @@ for key in ['user.name', 'user.email']: if m.Has(key, include_defaults=False): self.config.SetString(key, m.GetString(key)) + self.config.SetString('filter.lfs.smudge', 'git-lfs smudge --skip -- %f') if self.manifest.IsMirror: self.config.SetString('core.bare', 'true') else: @@ -2222,7 +2319,7 @@ name = os.path.basename(stock_hook) if name in ('commit-msg',) and not self.remote.review \ - and not self is self.manifest.manifestProject: + and self is not self.manifest.manifestProject: # Don't install a Gerrit Code Review hook if this # project does not appear to use it for reviews. # @@ -2237,7 +2334,8 @@ if filecmp.cmp(stock_hook, dst, shallow=False): os.remove(dst) else: - _warn("%s: Not replacing locally modified %s hook", self.relpath, name) + _warn("%s: Not replacing locally modified %s hook", + self.relpath, name) continue try: os.symlink(os.path.relpath(stock_hook, os.path.dirname(dst)), dst) @@ -2251,6 +2349,7 @@ if self.remote.url: remote = self.GetRemote(self.remote.name) remote.url = self.remote.url + remote.pushUrl = self.remote.pushUrl remote.review = self.remote.review remote.projectname = self.name @@ -2283,8 +2382,8 @@ self.bare_git.symbolic_ref('-m', msg, ref, dst) def _CheckDirReference(self, srcdir, destdir, share_refs): - symlink_files = self.shareable_files - symlink_dirs = self.shareable_dirs + symlink_files = self.shareable_files[:] + symlink_dirs = self.shareable_dirs[:] if share_refs: symlink_files += self.working_tree_files symlink_dirs += self.working_tree_dirs @@ -2312,8 +2411,8 @@ copy_all: If true, copy all remaining files from |gitdir| -> |dotgit|. This saves you the effort of initializing |dotgit| yourself. """ - symlink_files = self.shareable_files - symlink_dirs = self.shareable_dirs + symlink_files = self.shareable_files[:] + symlink_dirs = self.shareable_dirs[:] if share_refs: symlink_files += self.working_tree_files symlink_dirs += self.working_tree_dirs @@ -2405,7 +2504,7 @@ def _allrefs(self): return self.bare_ref.all - def _getLogs(self, rev1, rev2, oneline=False, color=True): + def _getLogs(self, rev1, rev2, oneline=False, color=True, pretty_format=None): """Get logs between two revisions of this project.""" comp = '..' if rev1: @@ -2416,6 +2515,8 @@ out = DiffColoring(self.config) if out.is_on and color: cmd.append('--color') + if pretty_format is not None: + cmd.append('--pretty=format:%s' % pretty_format) if oneline: cmd.append('--oneline') @@ -2432,17 +2533,21 @@ raise return None - def getAddedAndRemovedLogs(self, toProject, oneline=False, color=True): + def getAddedAndRemovedLogs(self, toProject, oneline=False, color=True, + pretty_format=None): """Get the list of logs from this revision to given revisionId""" logs = {} selfId = self.GetRevisionId(self._allrefs) toId = toProject.GetRevisionId(toProject._allrefs) - logs['added'] = self._getLogs(selfId, toId, oneline=oneline, color=color) - logs['removed'] = self._getLogs(toId, selfId, oneline=oneline, color=color) + logs['added'] = self._getLogs(selfId, toId, oneline=oneline, color=color, + pretty_format=pretty_format) + logs['removed'] = self._getLogs(toId, selfId, oneline=oneline, color=color, + pretty_format=pretty_format) return logs class _GitGetByExec(object): + def __init__(self, project, bare, gitdir): self._project = project self._bare = bare @@ -2461,8 +2566,8 @@ if p.Wait() == 0: out = p.stdout if out: + # Backslash is not anomalous return out[:-1].split('\0') # pylint: disable=W1401 - # Backslash is not anomalous return [] def DiffZ(self, name, *args): @@ -2488,6 +2593,7 @@ break class _Info(object): + def __init__(self, path, omode, nmode, oid, nid, state): self.path = path self.src_path = None @@ -2590,10 +2696,8 @@ line = line[:-1] r.append(line) if p.Wait() != 0: - raise GitError('%s rev-list %s: %s' % ( - self._project.name, - str(args), - p.stderr)) + raise GitError('%s rev-list %s: %s' % + (self._project.name, str(args), p.stderr)) return r def __getattr__(self, name): @@ -2616,6 +2720,7 @@ A callable object that will try to call git with the named command. """ name = name.replace('_', '-') + def runner(*args, **kwargs): cmdv = [] config = kwargs.pop('config', None) @@ -2638,10 +2743,8 @@ capture_stdout=True, capture_stderr=True) if p.Wait() != 0: - raise GitError('%s %s: %s' % ( - self._project.name, - name, - p.stderr)) + raise GitError('%s %s: %s' % + (self._project.name, name, p.stderr)) r = p.stdout try: r = r.decode('utf-8') @@ -2654,14 +2757,19 @@ class _PriorSyncFailedError(Exception): + def __str__(self): return 'prior sync failed; rebase still in progress' + class _DirtyError(Exception): + def __str__(self): return 'contains uncommitted changes' + class _InfoMessage(object): + def __init__(self, project, text): self.project = project self.text = text @@ -2670,7 +2778,9 @@ syncbuf.out.info('%s/: %s', self.project.relpath, self.text) syncbuf.out.nl() + class _Failure(object): + def __init__(self, project, why): self.project = project self.why = why @@ -2681,7 +2791,9 @@ str(self.why)) syncbuf.out.nl() + class _Later(object): + def __init__(self, project, action): self.project = project self.action = action @@ -2698,14 +2810,18 @@ out.nl() return False + class _SyncColoring(Coloring): + def __init__(self, config): Coloring.__init__(self, config, 'reposync') self.project = self.printer('header', attr='bold') self.info = self.printer('info') self.fail = self.printer('fail', fg='red') + class SyncBuffer(object): + def __init__(self, config, detach_head=False): self._messages = [] self._failures = [] @@ -2761,8 +2877,10 @@ class MetaProject(Project): + """A special project housed under .repo. """ + def __init__(self, manifest, name, gitdir, worktree): Project.__init__(self, manifest=manifest, @@ -2796,10 +2914,9 @@ syncbuf.Finish() return GitCommand(self, - ['update-ref', '-d', 'refs/heads/default'], - capture_stdout=True, - capture_stderr=True).Wait() == 0 - + ['update-ref', '-d', 'refs/heads/default'], + capture_stdout=True, + capture_stderr=True).Wait() == 0 @property def LastFetch(self): diff -Nru repo-1.12.32/.pylintrc repo-1.12.37/.pylintrc --- repo-1.12.32/.pylintrc 2015-12-06 22:02:47.000000000 -0600 +++ repo-1.12.37/.pylintrc 1969-12-31 18:00:00.000000000 -0600 @@ -1,298 +0,0 @@ -# lint Python modules using external checkers. -# -# This is the main checker controling the other ones and the reports -# generation. It is itself both a raw checker and an astng checker in order -# to: -# * handle message activation / deactivation at the module level -# * handle some basic but necessary stats'data (number of classes, methods...) -# -[MASTER] - -# Specify a configuration file. -#rcfile= - -# Python code to execute, usually for sys.path manipulation such as -# pygtk.require(). -#init-hook= - -# Profiled execution. -profile=no - -# Add to the black list. It should be a base name, not a -# path. You may set this option multiple times. -ignore=SVN - -# Pickle collected data for later comparisons. -persistent=yes - -# Set the cache size for astng objects. -cache-size=500 - -# List of plugins (as comma separated values of python modules names) to load, -# usually to register additional checkers. -load-plugins= - - -[MESSAGES CONTROL] - -# Enable only checker(s) with the given id(s). This option conflicts with the -# disable-checker option -#enable-checker= - -# Enable all checker(s) except those with the given id(s). This option -# conflicts with the enable-checker option -#disable-checker= - -# Enable all messages in the listed categories. -#enable-msg-cat= - -# Disable all messages in the listed categories. -#disable-msg-cat= - -# Enable the message(s) with the given id(s). -enable=RP0004 - -# Disable the message(s) with the given id(s). -disable=R0903,R0912,R0913,R0914,R0915,W0141,C0111,C0103,W0603,W0703,R0911,C0301,C0302,R0902,R0904,W0142,W0212,E1101,E1103,R0201,W0201,W0122,W0232,RP0001,RP0003,RP0101,RP0002,RP0401,RP0701,RP0801,F0401,E0611,R0801,I0011 - -[REPORTS] - -# set the output format. Available formats are text, parseable, colorized, msvs -# (visual studio) and html -output-format=text - -# Put messages in a separate file for each module / package specified on the -# command line instead of printing them on stdout. Reports (if any) will be -# written in a file name "pylint_global.[txt|html]". -files-output=no - -# Tells whether to display a full report or only the messages -reports=yes - -# Python expression which should return a note less than 10 (10 is the highest -# note).You have access to the variables errors warning, statement which -# respectivly contain the number of errors / warnings messages and the total -# number of statements analyzed. This is used by the global evaluation report -# (R0004). -evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10) - -# Add a comment according to your evaluation note. This is used by the global -# evaluation report (R0004). -comment=no - -# checks for -# * unused variables / imports -# * undefined variables -# * redefinition of variable from builtins or from an outer scope -# * use of variable before assigment -# -[VARIABLES] - -# Tells whether we should check for unused import in __init__ files. -init-import=no - -# A regular expression matching names used for dummy variables (i.e. not used). -dummy-variables-rgx=_|dummy - -# List of additional names supposed to be defined in builtins. Remember that -# you should avoid to define new builtins when possible. -additional-builtins= - - -# try to find bugs in the code using type inference -# -[TYPECHECK] - -# Tells whether missing members accessed in mixin class should be ignored. A -# mixin class is detected if its name ends with "mixin" (case insensitive). -ignore-mixin-members=yes - -# List of classes names for which member attributes should not be checked -# (useful for classes with attributes dynamicaly set). -ignored-classes=SQLObject - -# When zope mode is activated, consider the acquired-members option to ignore -# access to some undefined attributes. -zope=no - -# List of members which are usually get through zope's acquisition mecanism and -# so shouldn't trigger E0201 when accessed (need zope=yes to be considered). -acquired-members=REQUEST,acl_users,aq_parent - - -# checks for : -# * doc strings -# * modules / classes / functions / methods / arguments / variables name -# * number of arguments, local variables, branchs, returns and statements in -# functions, methods -# * required module attributes -# * dangerous default values as arguments -# * redefinition of function / method / class -# * uses of the global statement -# -[BASIC] - -# Required attributes for module, separated by a comma -required-attributes= - -# Regular expression which should only match functions or classes name which do -# not require a docstring -no-docstring-rgx=_main|__.*__ - -# Regular expression which should only match correct module names -module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$ - -# Regular expression which should only match correct module level names -const-rgx=(([A-Z_][A-Z1-9_]*)|(__.*__))|(log)$ - -# Regular expression which should only match correct class names -class-rgx=[A-Z_][a-zA-Z0-9]+$ - -# Regular expression which should only match correct function names -function-rgx=[a-z_][a-z0-9_]{2,30}$ - -# Regular expression which should only match correct method names -method-rgx=[a-z_][a-z0-9_]{2,30}$ - -# Regular expression which should only match correct instance attribute names -attr-rgx=[a-z_][a-z0-9_]{2,30}$ - -# Regular expression which should only match correct argument names -argument-rgx=[a-z_][a-z0-9_]{2,30}$ - -# Regular expression which should only match correct variable names -variable-rgx=[a-z_][a-z0-9_]{2,30}$ - -# Regular expression which should only match correct list comprehension / -# generator expression variable names -inlinevar-rgx=[A-Za-z_][A-Za-z0-9_]*$ - -# Good variable names which should always be accepted, separated by a comma -good-names=i,j,k,ex,Run,_,e,d1,d2,v,f,l,d - -# Bad variable names which should always be refused, separated by a comma -bad-names=foo,bar,baz,toto,tutu,tata - -# List of builtins function names that should not be used, separated by a comma -bad-functions=map,filter,apply,input - - -# checks for sign of poor/misdesign: -# * number of methods, attributes, local variables... -# * size, complexity of functions, methods -# -[DESIGN] - -# Maximum number of arguments for function / method -max-args=5 - -# Maximum number of locals for function / method body -max-locals=15 - -# Maximum number of return / yield for function / method body -max-returns=6 - -# Maximum number of branch for function / method body -max-branchs=12 - -# Maximum number of statements in function / method body -max-statements=50 - -# Maximum number of parents for a class (see R0901). -max-parents=7 - -# Maximum number of attributes for a class (see R0902). -max-attributes=20 - -# Minimum number of public methods for a class (see R0903). -min-public-methods=2 - -# Maximum number of public methods for a class (see R0904). -max-public-methods=30 - - -# checks for -# * external modules dependencies -# * relative / wildcard imports -# * cyclic imports -# * uses of deprecated modules -# -[IMPORTS] - -# Deprecated modules which should not be used, separated by a comma -deprecated-modules=regsub,string,TERMIOS,Bastion,rexec - -# Create a graph of every (i.e. internal and external) dependencies in the -# given file (report R0402 must not be disabled) -import-graph= - -# Create a graph of external dependencies in the given file (report R0402 must -# not be disabled) -ext-import-graph= - -# Create a graph of internal dependencies in the given file (report R0402 must -# not be disabled) -int-import-graph= - - -# checks for : -# * methods without self as first argument -# * overridden methods signature -# * access only to existant members via self -# * attributes not defined in the __init__ method -# * supported interfaces implementation -# * unreachable code -# -[CLASSES] - -# List of interface methods to ignore, separated by a comma. This is used for -# instance to not check methods defines in Zope's Interface base class. -ignore-iface-methods=isImplementedBy,deferred,extends,names,namesAndDescriptions,queryDescriptionFor,getBases,getDescriptionFor,getDoc,getName,getTaggedValue,getTaggedValueTags,isEqualOrExtendedBy,setTaggedValue,isImplementedByInstancesOf,adaptWith,is_implemented_by - -# List of method names used to declare (i.e. assign) instance attributes. -defining-attr-methods=__init__,__new__,setUp - - -# checks for similarities and duplicated code. This computation may be -# memory / CPU intensive, so you should disable it if you experiments some -# problems. -# -[SIMILARITIES] - -# Minimum lines number of a similarity. -min-similarity-lines=4 - -# Ignore comments when computing similarities. -ignore-comments=yes - -# Ignore docstrings when computing similarities. -ignore-docstrings=yes - - -# checks for: -# * warning notes in the code like FIXME, XXX -# * PEP 263: source code with non ascii character but no encoding declaration -# -[MISCELLANEOUS] - -# List of note tags to take in consideration, separated by a comma. -notes=FIXME,XXX,TODO - - -# checks for : -# * unauthorized constructions -# * strict indentation -# * line length -# * use of <> instead of != -# -[FORMAT] - -# Maximum number of characters on a single line. -max-line-length=80 - -# Maximum number of lines in a module -max-module-lines=1000 - -# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1 -# tab). In repo it is 2 spaces. -indent-string=' ' diff -Nru repo-1.12.32/README.md repo-1.12.37/README.md --- repo-1.12.32/README.md 1969-12-31 18:00:00.000000000 -0600 +++ repo-1.12.37/README.md 2016-12-18 14:14:05.000000000 -0600 @@ -0,0 +1,14 @@ +# repo + +Repo is a tool built on top of Git. Repo helps manage many Git repositories, +does the uploads to revision control systems, and automates parts of the +development workflow. Repo is not meant to replace Git, only to make it +easier to work with Git. The repo command is an executable Python script +that you can put anywhere in your path. + +* Homepage: https://code.google.com/p/git-repo/ +* Bug reports: https://code.google.com/p/git-repo/issues/ +* Source: https://code.google.com/p/git-repo/ +* Overview: https://source.android.com/source/developing.html +* Docs: https://source.android.com/source/using-repo.html +* [Submitting patches](./SUBMITTING_PATCHES.md) diff -Nru repo-1.12.32/repo repo-1.12.37/repo --- repo-1.12.32/repo 2015-12-06 22:02:47.000000000 -0600 +++ repo-1.12.37/repo 2016-12-18 14:14:05.000000000 -0600 @@ -1,8 +1,11 @@ #!/usr/bin/env python -## repo default configuration -## -REPO_URL = 'https://gerrit.googlesource.com/git-repo' +# repo default configuration +# +import os +REPO_URL = os.environ.get('REPO_URL', None) +if not REPO_URL: + REPO_URL = 'https://gerrit.googlesource.com/git-repo' REPO_REV = 'stable' # Copyright (C) 2008 Google Inc. @@ -20,10 +23,13 @@ # limitations under the License. # increment this whenever we make important changes to this script -VERSION = (1, 22) +VERSION = (1, 23) # increment this if the MAINTAINER_KEYS block is modified KEYRING_VERSION = (1, 2) + +# Each individual key entry is created by using: +# gpg --armor --export keyid MAINTAINER_KEYS = """ Repo Maintainer @@ -101,20 +107,19 @@ -----END PGP PUBLIC KEY BLOCK----- """ -GIT = 'git' # our git command -MIN_GIT_VERSION = (1, 7, 2) # minimum supported git version -repodir = '.repo' # name of repo's private directory -S_repo = 'repo' # special repo repository -S_manifests = 'manifests' # special manifest repository -REPO_MAIN = S_repo + '/main.py' # main script -MIN_PYTHON_VERSION = (2, 6) # minimum supported python version +GIT = 'git' # our git command +MIN_GIT_VERSION = (1, 7, 2) # minimum supported git version +repodir = '.repo' # name of repo's private directory +S_repo = 'repo' # special repo repository +S_manifests = 'manifests' # special manifest repository +REPO_MAIN = S_repo + '/main.py' # main script +MIN_PYTHON_VERSION = (2, 6) # minimum supported python version GITC_CONFIG_FILE = '/gitc/.config' GITC_FS_ROOT_DIR = '/gitc/manifest-rw/' import errno import optparse -import os import re import shutil import stat @@ -194,6 +199,9 @@ help='restrict manifest projects to ones with a specified ' 'platform group [auto|all|none|linux|darwin|...]', metavar='PLATFORM') +group.add_option('--no-clone-bundle', + dest='no_clone_bundle', action='store_true', + help='disable use of /clone.bundle on HTTP/HTTPS') # Tool @@ -214,9 +222,10 @@ dest='config_name', action="store_true", default=False, help='Always prompt for name/e-mail') -def _GitcInitOptions(init_optparse): - init_optparse.set_usage("repo gitc-init -u url -c client [options]") - g = init_optparse.add_option_group('GITC options') + +def _GitcInitOptions(init_optparse_arg): + init_optparse_arg.set_usage("repo gitc-init -u url -c client [options]") + g = init_optparse_arg.add_option_group('GITC options') g.add_option('-f', '--manifest-file', dest='manifest_file', help='Optional manifest file to use for this GITC client.') @@ -225,6 +234,8 @@ help='The name of the gitc_client instance to create or modify.') _gitc_manifest_dir = None + + def get_gitc_manifest_dir(): global _gitc_manifest_dir if _gitc_manifest_dir is None: @@ -239,6 +250,7 @@ pass return _gitc_manifest_dir + def gitc_parse_clientdir(gitc_fs_path): """Parse a path in the GITC FS and return its client name. @@ -261,7 +273,9 @@ return gitc_fs_path.split(manifest_dir)[1].split('/')[0] return gitc_fs_path.split(GITC_FS_ROOT_DIR)[1].split('/')[0] + class CloneFailure(Exception): + """Indicate the remote clone of repo itself failed. """ @@ -331,7 +345,7 @@ can_verify = True dst = os.path.abspath(os.path.join(repodir, S_repo)) - _Clone(url, dst, opt.quiet) + _Clone(url, dst, opt.quiet, not opt.no_clone_bundle) if can_verify and not opt.no_repo_verify: rev = _Verify(dst, branch, opt.quiet) @@ -424,13 +438,16 @@ sys.exit(1) env = os.environ.copy() - env['GNUPGHOME'] = gpg_dir.encode() + try: + env['GNUPGHOME'] = gpg_dir + except UnicodeEncodeError: + env['GNUPGHOME'] = gpg_dir.encode() cmd = ['gpg', '--import'] try: proc = subprocess.Popen(cmd, - env = env, - stdin = subprocess.PIPE) + env=env, + stdin=subprocess.PIPE) except OSError as e: if not quiet: _print('warning: gpg (GnuPG) is not available.', file=sys.stderr) @@ -456,7 +473,7 @@ """Set a git configuration option to the specified value. """ cmd = [GIT, 'config', name, value] - if subprocess.Popen(cmd, cwd = local).wait() != 0: + if subprocess.Popen(cmd, cwd=local).wait() != 0: raise CloneFailure() @@ -469,9 +486,9 @@ n = netrc.netrc() for host in n.hosts: p = n.hosts[host] - mgr.add_password(p[1], 'http://%s/' % host, p[0], p[2]) + mgr.add_password(p[1], 'http://%s/' % host, p[0], p[2]) mgr.add_password(p[1], 'https://%s/' % host, p[0], p[2]) - except: + except: # pylint: disable=bare-except pass handlers.append(urllib.request.HTTPBasicAuthHandler(mgr)) handlers.append(urllib.request.HTTPDigestAuthHandler(mgr)) @@ -484,6 +501,7 @@ handlers.append(urllib.request.HTTPSHandler(debuglevel=1)) urllib.request.install_opener(urllib.request.build_opener(*handlers)) + def _Fetch(url, local, src, quiet): if not quiet: _print('Get %s' % url, file=sys.stderr) @@ -498,22 +516,23 @@ cmd.append('+refs/heads/*:refs/remotes/origin/*') cmd.append('refs/tags/*:refs/tags/*') - proc = subprocess.Popen(cmd, cwd = local, stderr = err) + proc = subprocess.Popen(cmd, cwd=local, stderr=err) if err: proc.stderr.read() proc.stderr.close() if proc.wait() != 0: raise CloneFailure() + def _DownloadBundle(url, local, quiet): if not url.endswith('/'): url += '/' url += 'clone.bundle' proc = subprocess.Popen( - [GIT, 'config', '--get-regexp', 'url.*.insteadof'], - cwd = local, - stdout = subprocess.PIPE) + [GIT, 'config', '--get-regexp', 'url.*.insteadof'], + cwd=local, + stdout=subprocess.PIPE) for line in proc.stdout: m = re.compile(r'^url\.(.*)\.insteadof (.*)$').match(line) if m: @@ -533,7 +552,7 @@ try: r = urllib.request.urlopen(url) except urllib.error.HTTPError as e: - if e.code in [401, 403, 404]: + if e.code in [401, 403, 404, 501]: return False _print('fatal: Cannot get %s' % url, file=sys.stderr) _print('fatal: HTTP error %s' % e.code, file=sys.stderr) @@ -555,6 +574,7 @@ finally: dest.close() + def _ImportBundle(local): path = os.path.join(local, '.git', 'clone.bundle') try: @@ -562,7 +582,8 @@ finally: os.remove(path) -def _Clone(url, local, quiet): + +def _Clone(url, local, quiet, clone_bundle): """Clones a git repository to a new subdirectory of repodir """ try: @@ -574,14 +595,14 @@ cmd = [GIT, 'init', '--quiet'] try: - proc = subprocess.Popen(cmd, cwd = local) + proc = subprocess.Popen(cmd, cwd=local) except OSError as e: _print(file=sys.stderr) _print("fatal: '%s' is not available" % GIT, file=sys.stderr) _print('fatal: %s' % e, file=sys.stderr) _print(file=sys.stderr) _print('Please make sure %s is installed and in your path.' % GIT, - file=sys.stderr) + file=sys.stderr) raise CloneFailure() if proc.wait() != 0: _print('fatal: could not create %s' % local, file=sys.stderr) @@ -589,9 +610,10 @@ _InitHttp() _SetConfig(local, 'remote.origin.url', url) - _SetConfig(local, 'remote.origin.fetch', - '+refs/heads/*:refs/remotes/origin/*') - if _DownloadBundle(url, local, quiet): + _SetConfig(local, + 'remote.origin.fetch', + '+refs/heads/*:refs/remotes/origin/*') + if clone_bundle and _DownloadBundle(url, local, quiet): _ImportBundle(local) _Fetch(url, local, 'origin', quiet) @@ -603,7 +625,7 @@ proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, - cwd = cwd) + cwd=cwd) cur = proc.stdout.read().strip() proc.stdout.close() @@ -621,18 +643,21 @@ if not quiet: _print(file=sys.stderr) _print("info: Ignoring branch '%s'; using tagged release '%s'" - % (branch, cur), file=sys.stderr) + % (branch, cur), file=sys.stderr) _print(file=sys.stderr) env = os.environ.copy() - env['GNUPGHOME'] = gpg_dir.encode() + try: + env['GNUPGHOME'] = gpg_dir + except UnicodeEncodeError: + env['GNUPGHOME'] = gpg_dir.encode() cmd = [GIT, 'tag', '-v', cur] proc = subprocess.Popen(cmd, - stdout = subprocess.PIPE, - stderr = subprocess.PIPE, - cwd = cwd, - env = env) + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + cwd=cwd, + env=env) out = proc.stdout.read() proc.stdout.close() @@ -652,21 +677,21 @@ """Checkout an upstream branch into the repository and track it. """ cmd = [GIT, 'update-ref', 'refs/heads/default', rev] - if subprocess.Popen(cmd, cwd = cwd).wait() != 0: + if subprocess.Popen(cmd, cwd=cwd).wait() != 0: raise CloneFailure() _SetConfig(cwd, 'branch.default.remote', 'origin') _SetConfig(cwd, 'branch.default.merge', 'refs/heads/%s' % branch) cmd = [GIT, 'symbolic-ref', 'HEAD', 'refs/heads/default'] - if subprocess.Popen(cmd, cwd = cwd).wait() != 0: + if subprocess.Popen(cmd, cwd=cwd).wait() != 0: raise CloneFailure() cmd = [GIT, 'read-tree', '--reset', '-u'] if not quiet: cmd.append('-v') cmd.append('HEAD') - if subprocess.Popen(cmd, cwd = cwd).wait() != 0: + if subprocess.Popen(cmd, cwd=cwd).wait() != 0: raise CloneFailure() @@ -678,8 +703,8 @@ olddir = None while curdir != '/' \ - and curdir != olddir \ - and not repo: + and curdir != olddir \ + and not repo: repo = os.path.join(curdir, repodir, REPO_MAIN) if not os.path.isfile(repo): repo = None @@ -688,7 +713,7 @@ return (repo, os.path.join(curdir, repodir)) -class _Options: +class _Options(object): help = False @@ -715,7 +740,7 @@ gitc_usage = " gitc-init Initialize a GITC Client.\n" _print( -"""usage: repo COMMAND [ARGS] + """usage: repo COMMAND [ARGS] repo is not yet installed. Use "repo init" to install it here. @@ -723,7 +748,7 @@ init Install repo in the current working directory """ + gitc_usage + -""" help Display detailed help on a command + """ help Display detailed help on a command For access to the full online help, install repo ("repo init"). """, file=sys.stderr) @@ -784,8 +809,8 @@ '--git-dir=%s' % gitdir, 'symbolic-ref', 'HEAD'], - stdout = subprocess.PIPE, - stderr = subprocess.PIPE) + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) REPO_REV = proc.stdout.read().strip() proc.stdout.close() @@ -812,7 +837,8 @@ if get_gitc_manifest_dir() and cwd.startswith(get_gitc_manifest_dir()): _print('error: repo cannot be used in the GITC local manifest directory.' '\nIf you want to work on this GITC client please rerun this ' - 'command from the corresponding client under /gitc/', file=sys.stderr) + 'command from the corresponding client under /gitc/', + file=sys.stderr) sys.exit(1) if not repo_main: if opt.help: diff -Nru repo-1.12.32/subcmds/diffmanifests.py repo-1.12.37/subcmds/diffmanifests.py --- repo-1.12.32/subcmds/diffmanifests.py 2015-12-06 22:02:47.000000000 -0600 +++ repo-1.12.37/subcmds/diffmanifests.py 2016-12-18 14:14:05.000000000 -0600 @@ -71,6 +71,10 @@ p.add_option('--no-color', dest='color', action='store_false', default=True, help='does not display the diff in color.') + p.add_option('--pretty-format', + dest='pretty_format', action='store', + metavar='', + help='print the log using a custom git pretty format string') def _printRawDiff(self, diff): for project in diff['added']: @@ -92,7 +96,7 @@ otherProject.revisionExpr)) self.out.nl() - def _printDiff(self, diff, color=True): + def _printDiff(self, diff, color=True, pretty_format=None): if diff['added']: self.out.nl() self.printText('added projects : \n') @@ -124,7 +128,8 @@ self.printText(' to ') self.printRevision(otherProject.revisionExpr) self.out.nl() - self._printLogs(project, otherProject, raw=False, color=color) + self._printLogs(project, otherProject, raw=False, color=color, + pretty_format=pretty_format) self.out.nl() if diff['unreachable']: @@ -139,9 +144,13 @@ self.printText(' not found') self.out.nl() - def _printLogs(self, project, otherProject, raw=False, color=True): - logs = project.getAddedAndRemovedLogs(otherProject, oneline=True, - color=color) + def _printLogs(self, project, otherProject, raw=False, color=True, + pretty_format=None): + + logs = project.getAddedAndRemovedLogs(otherProject, + oneline=(pretty_format is None), + color=color, + pretty_format=pretty_format) if logs['removed']: removedLogs = logs['removed'].split('\n') for log in removedLogs: @@ -192,4 +201,4 @@ if opt.raw: self._printRawDiff(diff) else: - self._printDiff(diff, color=opt.color) + self._printDiff(diff, color=opt.color, pretty_format=opt.pretty_format) diff -Nru repo-1.12.32/subcmds/forall.py repo-1.12.37/subcmds/forall.py --- repo-1.12.32/subcmds/forall.py 2015-12-06 22:02:47.000000000 -0600 +++ repo-1.12.37/subcmds/forall.py 2016-12-18 14:14:05.000000000 -0600 @@ -120,6 +120,9 @@ p.add_option('-r', '--regex', dest='regex', action='store_true', help="Execute the command only on projects matching regex or wildcard expression") + p.add_option('-i', '--inverse-regex', + dest='inverse_regex', action='store_true', + help="Execute the command only on projects not matching regex or wildcard expression") p.add_option('-g', '--groups', dest='groups', help="Execute the command only on projects matching the specified groups") @@ -215,10 +218,12 @@ if os.path.isfile(smart_sync_manifest_path): self.manifest.Override(smart_sync_manifest_path) - if not opt.regex: - projects = self.GetProjects(args, groups=opt.groups) - else: + if opt.regex: projects = self.FindProjects(args) + elif opt.inverse_regex: + projects = self.FindProjects(args, inverse=True) + else: + projects = self.GetProjects(args, groups=opt.groups) os.environ['REPO_COUNT'] = str(len(projects)) @@ -240,7 +245,8 @@ rc = rc or errno.EINTR except Exception as e: # Catch any other exceptions raised - print('Got an error, terminating the pool: %r' % e, + print('Got an error, terminating the pool: %s: %s' % + (type(e).__name__, e), file=sys.stderr) pool.terminate() rc = rc or getattr(e, 'errno', 1) @@ -254,7 +260,8 @@ try: project = self._SerializeProject(p) except Exception as e: - print('Project list error: %r' % e, + print('Project list error on project %s: %s: %s' % + (p.name, type(e).__name__, e), file=sys.stderr) return except KeyboardInterrupt: diff -Nru repo-1.12.32/subcmds/init.py repo-1.12.37/subcmds/init.py --- repo-1.12.32/subcmds/init.py 2015-12-06 22:02:47.000000000 -0600 +++ repo-1.12.37/subcmds/init.py 2016-12-18 14:14:05.000000000 -0600 @@ -61,6 +61,11 @@ directory when fetching from the server. This will make the sync go a lot faster by reducing data traffic on the network. +The --no-clone-bundle option disables any attempt to use +$URL/clone.bundle to bootstrap a new Git repository from a +resumeable bundle file on a content delivery network. This +may be necessary if there are problems with the local Python +HTTP client or proxy configuration, but the Git binary works. Switching Manifest Branches --------------------------- @@ -113,6 +118,9 @@ help='restrict manifest projects to ones with a specified ' 'platform group [auto|all|none|linux|darwin|...]', metavar='PLATFORM') + g.add_option('--no-clone-bundle', + dest='no_clone_bundle', action='store_true', + help='disable use of /clone.bundle on HTTP/HTTPS') # Tool g = p.add_option_group('repo Version options') @@ -179,7 +187,7 @@ r.Save() groups = re.split(r'[,\s]+', opt.groups) - all_platforms = ['linux', 'darwin'] + all_platforms = ['linux', 'darwin', 'windows'] platformize = lambda x: 'platform-' + x if opt.platform == 'auto': if (not opt.mirror and @@ -188,7 +196,7 @@ elif opt.platform == 'all': groups.extend(map(platformize, all_platforms)) elif opt.platform in all_platforms: - groups.extend(platformize(opt.platform)) + groups.append(platformize(opt.platform)) elif opt.platform != 'none': print('fatal: invalid platform flag', file=sys.stderr) sys.exit(1) @@ -222,7 +230,8 @@ 'in another location.', file=sys.stderr) sys.exit(1) - if not m.Sync_NetworkHalf(is_new=is_new): + if not m.Sync_NetworkHalf(is_new=is_new, quiet=opt.quiet, + clone_bundle=not opt.no_clone_bundle): r = m.GetRemote(m.remote.name) print('fatal: cannot obtain manifest %s' % r.url, file=sys.stderr) diff -Nru repo-1.12.32/subcmds/rebase.py repo-1.12.37/subcmds/rebase.py --- repo-1.12.32/subcmds/rebase.py 2015-12-06 22:02:47.000000000 -0600 +++ repo-1.12.37/subcmds/rebase.py 2016-12-18 14:14:05.000000000 -0600 @@ -54,6 +54,11 @@ p.add_option('--auto-stash', dest='auto_stash', action='store_true', help='Stash local modifications before starting') + p.add_option('-m', '--onto-manifest', + dest='onto_manifest', action='store_true', + help='Rebase onto the manifest version instead of upstream ' + 'HEAD. This helps to make sure the local tree stays ' + 'consistent if you previously synced to a manifest.') def Execute(self, opt, args): all_projects = self.GetProjects(args) @@ -106,6 +111,10 @@ if opt.interactive: args.append("-i") + if opt.onto_manifest: + args.append('--onto') + args.append(project.revisionExpr) + args.append(upbranch.LocalMerge) print('# %s: rebasing %s -> %s' diff -Nru repo-1.12.32/subcmds/start.py repo-1.12.37/subcmds/start.py --- repo-1.12.32/subcmds/start.py 2015-12-06 22:02:47.000000000 -0600 +++ repo-1.12.37/subcmds/start.py 2016-12-18 14:14:05.000000000 -0600 @@ -54,13 +54,17 @@ if not opt.all: projects = args[1:] if len(projects) < 1: - print("error: at least one project must be specified", file=sys.stderr) - sys.exit(1) + projects = ['.',] # start it in the local project by default + all_projects = self.GetProjects(projects, + missing_ok=bool(self.gitc_manifest)) + + # This must happen after we find all_projects, since GetProjects may need + # the local directory, which will disappear once we save the GITC manifest. if self.gitc_manifest: - all_projects = self.GetProjects(projects, manifest=self.gitc_manifest, - missing_ok=True) - for project in all_projects: + gitc_projects = self.GetProjects(projects, manifest=self.gitc_manifest, + missing_ok=True) + for project in gitc_projects: if project.old_revision: project.already_synced = True else: @@ -70,8 +74,10 @@ # Save the GITC manifest. gitc_utils.save_manifest(self.gitc_manifest) - all_projects = self.GetProjects(projects, - missing_ok=bool(self.gitc_manifest)) + # Make sure we have a valid CWD + if not os.path.exists(os.getcwd()): + os.chdir(self.manifest.topdir) + pm = Progress('Starting %s' % nb, len(all_projects)) for project in all_projects: pm.update() diff -Nru repo-1.12.32/subcmds/sync.py repo-1.12.37/subcmds/sync.py --- repo-1.12.32/subcmds/sync.py 2015-12-06 22:02:47.000000000 -0600 +++ repo-1.12.37/subcmds/sync.py 2016-12-18 14:14:05.000000000 -0600 @@ -151,6 +151,9 @@ are fixed to a sha1 revision if the sha1 revision does not already exist locally. +The --prune option can be used to remove any refs that no longer +exist on the remote. + SSH Connections --------------- @@ -234,10 +237,12 @@ p.add_option('--optimized-fetch', dest='optimized_fetch', action='store_true', help='only fetch projects fixed to sha1 if revision does not exist locally') + p.add_option('--prune', dest='prune', action='store_true', + help='delete refs that no longer exist on the remote') if show_smart: p.add_option('-s', '--smart-sync', dest='smart_sync', action='store_true', - help='smart sync using manifest from a known good build') + help='smart sync using manifest from the latest known good build') p.add_option('-t', '--smart-tag', dest='smart_tag', action='store', help='smart sync using manifest from a known tag') @@ -305,7 +310,8 @@ force_sync=opt.force_sync, clone_bundle=not opt.no_clone_bundle, no_tags=opt.no_tags, archive=self.manifest.IsArchive, - optimized_fetch=opt.optimized_fetch) + optimized_fetch=opt.optimized_fetch, + prune=opt.prune) self._fetch_times.Set(project, time.time() - start) # Lock around all the rest of the code, since printing, updating a set @@ -314,6 +320,7 @@ did_lock = True if not success: + err_event.set() print('error: Cannot fetch %s' % project.name, file=sys.stderr) if opt.force_broken: print('warn: --force-broken, continuing to sync', @@ -324,7 +331,7 @@ fetched.add(project.gitdir) pm.update() except _FetchError: - err_event.set() + pass except Exception as e: print('error: Cannot fetch %s (%s: %s)' \ % (project.name, type(e).__name__, str(e)), file=sys.stderr) @@ -390,9 +397,12 @@ return fetched def _GCProjects(self, projects): - gitdirs = {} + gc_gitdirs = {} for project in projects: - gitdirs[project.gitdir] = project.bare_git + if len(project.manifest.GetProjectsWithName(project.name)) > 1: + print('Shared project %s found, disabling pruning.' % project.name) + project.bare_git.config('--replace-all', 'gc.pruneExpire', 'never') + gc_gitdirs[project.gitdir] = project.bare_git has_dash_c = git_require((1, 7, 2)) if multiprocessing and has_dash_c: @@ -402,7 +412,7 @@ jobs = min(self.jobs, cpu_count) if jobs < 2: - for bare_git in gitdirs.values(): + for bare_git in gc_gitdirs.values(): bare_git.gc('--auto') return @@ -424,7 +434,7 @@ finally: sem.release() - for bare_git in gitdirs.values(): + for bare_git in gc_gitdirs.values(): if err_event.isSet(): break sem.acquire() @@ -447,6 +457,65 @@ else: self.manifest._Unload() + def _DeleteProject(self, path): + print('Deleting obsolete path %s' % path, file=sys.stderr) + + # Delete the .git directory first, so we're less likely to have a partially + # working git repository around. There shouldn't be any git projects here, + # so rmtree works. + try: + shutil.rmtree(os.path.join(path, '.git')) + except OSError: + print('Failed to remove %s' % os.path.join(path, '.git'), file=sys.stderr) + print('error: Failed to delete obsolete path %s' % path, file=sys.stderr) + print(' remove manually, then run sync again', file=sys.stderr) + return -1 + + # Delete everything under the worktree, except for directories that contain + # another git project + dirs_to_remove = [] + failed = False + for root, dirs, files in os.walk(path): + for f in files: + try: + os.remove(os.path.join(root, f)) + except OSError: + print('Failed to remove %s' % os.path.join(root, f), file=sys.stderr) + failed = True + dirs[:] = [d for d in dirs + if not os.path.lexists(os.path.join(root, d, '.git'))] + dirs_to_remove += [os.path.join(root, d) for d in dirs + if os.path.join(root, d) not in dirs_to_remove] + for d in reversed(dirs_to_remove): + if os.path.islink(d): + try: + os.remove(d) + except OSError: + print('Failed to remove %s' % os.path.join(root, d), file=sys.stderr) + failed = True + elif len(os.listdir(d)) == 0: + try: + os.rmdir(d) + except OSError: + print('Failed to remove %s' % os.path.join(root, d), file=sys.stderr) + failed = True + continue + if failed: + print('error: Failed to delete obsolete path %s' % path, file=sys.stderr) + print(' remove manually, then run sync again', file=sys.stderr) + return -1 + + # Try deleting parent dirs if they are empty + project_dir = path + while project_dir != self.manifest.topdir: + if len(os.listdir(project_dir)) == 0: + os.rmdir(project_dir) + else: + break + project_dir = os.path.dirname(project_dir) + + return 0 + def UpdateProjectList(self): new_project_paths = [] for project in self.GetProjects(None, missing_ok=True): @@ -467,8 +536,8 @@ continue if path not in new_project_paths: # If the path has already been deleted, we don't need to do it - if os.path.exists(self.manifest.topdir + '/' + path): - gitdir = os.path.join(self.manifest.topdir, path, '.git') + gitdir = os.path.join(self.manifest.topdir, path, '.git') + if os.path.exists(gitdir): project = Project( manifest = self.manifest, name = path, @@ -487,18 +556,8 @@ print(' commit changes, then run sync again', file=sys.stderr) return -1 - else: - print('Deleting obsolete path %s' % project.worktree, - file=sys.stderr) - shutil.rmtree(project.worktree) - # Try deleting parent subdirs if they are empty - project_dir = os.path.dirname(project.worktree) - while project_dir != self.manifest.topdir: - try: - os.rmdir(project_dir) - except OSError: - break - project_dir = os.path.dirname(project_dir) + elif self._DeleteProject(project.worktree): + return -1 new_project_paths.sort() fd = open(file_path, 'w') diff -Nru repo-1.12.32/subcmds/upload.py repo-1.12.37/subcmds/upload.py --- repo-1.12.32/subcmds/upload.py 2015-12-06 22:02:47.000000000 -0600 +++ repo-1.12.37/subcmds/upload.py 2016-12-18 14:14:05.000000000 -0600 @@ -454,9 +454,15 @@ if avail: pending.append((project, avail)) - if pending and (not opt.bypass_hooks): + if not pending: + print("no branches ready for upload", file=sys.stderr) + return + + if not opt.bypass_hooks: hook = RepoHook('pre-upload', self.manifest.repo_hooks_project, - self.manifest.topdir, abort_if_user_denies=True) + self.manifest.topdir, + self.manifest.manifestProject.GetRemote('origin').url, + abort_if_user_denies=True) pending_proj_names = [project.name for (project, avail) in pending] pending_worktrees = [project.worktree for (project, avail) in pending] try: @@ -472,9 +478,7 @@ cc = _SplitEmails(opt.cc) people = (reviewers, cc) - if not pending: - print("no branches ready for upload", file=sys.stderr) - elif len(pending) == 1 and len(pending[0][1]) == 1: + if len(pending) == 1 and len(pending[0][1]) == 1: self._SingleBranch(opt, pending[0][1][0], people) else: self._MultipleBranches(opt, pending, people) diff -Nru repo-1.12.32/SUBMITTING_PATCHES repo-1.12.37/SUBMITTING_PATCHES --- repo-1.12.32/SUBMITTING_PATCHES 2015-12-06 22:02:47.000000000 -0600 +++ repo-1.12.37/SUBMITTING_PATCHES 1969-12-31 18:00:00.000000000 -0600 @@ -1,87 +0,0 @@ -Short Version: - - - Make small logical changes. - - Provide a meaningful commit message. - - Check for coding errors with pylint - - Make sure all code is under the Apache License, 2.0. - - Publish your changes for review: - - git push https://gerrit-review.googlesource.com/git-repo HEAD:refs/for/master - - -Long Version: - -I wanted a file describing how to submit patches for repo, -so I started with the one found in the core Git distribution -(Documentation/SubmittingPatches), which itself was based on the -patch submission guidelines for the Linux kernel. - -However there are some differences, so please review and familiarize -yourself with the following relevant bits: - - -(1) Make separate commits for logically separate changes. - -Unless your patch is really trivial, you should not be sending -out a patch that was generated between your working tree and your -commit head. Instead, always make a commit with complete commit -message and generate a series of patches from your repository. -It is a good discipline. - -Describe the technical detail of the change(s). - -If your description starts to get too long, that's a sign that you -probably need to split up your commit to finer grained pieces. - - -(2) Check for coding errors with pylint - -Run pylint on changed modules using the provided configuration: - - pylint --rcfile=.pylintrc file.py - - -(3) Check the license - -repo is licensed under the Apache License, 2.0. - -Because of this licensing model *every* file within the project -*must* list the license that covers it in the header of the file. -Any new contributions to an existing file *must* be submitted under -the current license of that file. Any new files *must* clearly -indicate which license they are provided under in the file header. - -Please verify that you are legally allowed and willing to submit your -changes under the license covering each file *prior* to submitting -your patch. It is virtually impossible to remove a patch once it -has been applied and pushed out. - - -(4) Sending your patches. - -Do not email your patches to anyone. - -Instead, login to the Gerrit Code Review tool at: - - https://gerrit-review.googlesource.com/ - -Ensure you have completed one of the necessary contributor -agreements, providing documentation to the project maintainers that -they have right to redistribute your work under the Apache License: - - https://gerrit-review.googlesource.com/#/settings/agreements - -Ensure you have obtained an HTTP password to authenticate: - - https://gerrit-review.googlesource.com/new-password - -Push your patches over HTTPS to the review server, possibly through -a remembered remote to make this easier in the future: - - git config remote.review.url https://gerrit-review.googlesource.com/git-repo - git config remote.review.push HEAD:refs/for/master - - git push review - -You will be automatically emailed a copy of your commits, and any -comments made by the project maintainers. diff -Nru repo-1.12.32/SUBMITTING_PATCHES.md repo-1.12.37/SUBMITTING_PATCHES.md --- repo-1.12.32/SUBMITTING_PATCHES.md 1969-12-31 18:00:00.000000000 -0600 +++ repo-1.12.37/SUBMITTING_PATCHES.md 2016-12-18 14:14:05.000000000 -0600 @@ -0,0 +1,135 @@ +# Short Version + + - Make small logical changes. + - Provide a meaningful commit message. + - Check for coding errors and style nits with pyflakes and flake8 + - Make sure all code is under the Apache License, 2.0. + - Publish your changes for review. + - Make corrections if requested. + - Verify your changes on gerrit so they can be submitted. + + `git push https://gerrit-review.googlesource.com/git-repo HEAD:refs/for/master` + + +# Long Version + +I wanted a file describing how to submit patches for repo, +so I started with the one found in the core Git distribution +(Documentation/SubmittingPatches), which itself was based on the +patch submission guidelines for the Linux kernel. + +However there are some differences, so please review and familiarize +yourself with the following relevant bits. + + +## Make separate commits for logically separate changes. + +Unless your patch is really trivial, you should not be sending +out a patch that was generated between your working tree and your +commit head. Instead, always make a commit with complete commit +message and generate a series of patches from your repository. +It is a good discipline. + +Describe the technical detail of the change(s). + +If your description starts to get too long, that's a sign that you +probably need to split up your commit to finer grained pieces. + + +## Check for coding errors and style nits with pyflakes and flake8 + +### Coding errors + +Run `pyflakes` on changed modules: + + pyflakes file.py + +Ideally there should be no new errors or warnings introduced. + +### Style violations + +Run `flake8` on changes modules: + + flake8 file.py + +Note that repo generally follows [Google's python style guide] +(https://google.github.io/styleguide/pyguide.html) rather than [PEP 8] +(https://www.python.org/dev/peps/pep-0008/), so it's possible that +the output of `flake8` will be quite noisy. It's not mandatory to +avoid all warnings, but at least the maximum line length should be +followed. + +If there are many occurrences of the same warning that cannot be +avoided without going against the Google style guide, these may be +suppressed in the included `.flake8` file. + +## Check the license + +repo is licensed under the Apache License, 2.0. + +Because of this licensing model *every* file within the project +*must* list the license that covers it in the header of the file. +Any new contributions to an existing file *must* be submitted under +the current license of that file. Any new files *must* clearly +indicate which license they are provided under in the file header. + +Please verify that you are legally allowed and willing to submit your +changes under the license covering each file *prior* to submitting +your patch. It is virtually impossible to remove a patch once it +has been applied and pushed out. + + +## Sending your patches. + +Do not email your patches to anyone. + +Instead, login to the Gerrit Code Review tool at: + + https://gerrit-review.googlesource.com/ + +Ensure you have completed one of the necessary contributor +agreements, providing documentation to the project maintainers that +they have right to redistribute your work under the Apache License: + + https://gerrit-review.googlesource.com/#/settings/agreements + +Ensure you have obtained an HTTP password to authenticate: + + https://gerrit-review.googlesource.com/new-password + +Ensure that you have the local commit hook installed to automatically +add a ChangeId to your commits: + + curl -Lo `git rev-parse --git-dir`/hooks/commit-msg https://gerrit-review.googlesource.com/tools/hooks/commit-msg + chmod +x `git rev-parse --git-dir`/hooks/commit-msg + +If you have already committed your changes you will need to amend the commit +to get the ChangeId added. + + git commit --amend + +Push your patches over HTTPS to the review server, possibly through +a remembered remote to make this easier in the future: + + git config remote.review.url https://gerrit-review.googlesource.com/git-repo + git config remote.review.push HEAD:refs/for/master + + git push review + +You will be automatically emailed a copy of your commits, and any +comments made by the project maintainers. + + +## Make changes if requested + +The project maintainer who reviews your changes might request changes to your +commit. If you make the requested changes you will need to amend your commit +and push it to the review server again. + + +## Verify your changes on gerrit + +After you receive a Code-Review+2 from the maintainer, select the Verified +button on the gerrit page for the change. This verifies that you have tested +your changes and notifies the maintainer that they are ready to be submitted. +The maintainer will then submit your changes to the repository.