| Index: git_cl.py
|
| diff --git a/git_cl.py b/git_cl.py
|
| index 42af2a77f2bc6d09e8b9b2e02255e88e6644e9fd..8a5dc73ec8513e27f7171774e237c032151fdaa7 100755
|
| --- a/git_cl.py
|
| +++ b/git_cl.py
|
| @@ -114,20 +114,19 @@ def RunGit(args, **kwargs):
|
|
|
| def RunGitWithCode(args, suppress_stderr=False):
|
| """Returns return code and stdout."""
|
| + if suppress_stderr:
|
| + stderr = subprocess2.VOID
|
| + else:
|
| + stderr = sys.stderr
|
| try:
|
| - if suppress_stderr:
|
| - stderr = subprocess2.VOID
|
| - else:
|
| - stderr = sys.stderr
|
| - out, code = subprocess2.communicate(['git'] + args,
|
| - env=GetNoGitPagerEnv(),
|
| - stdout=subprocess2.PIPE,
|
| - stderr=stderr)
|
| - return code, out[0]
|
| - except ValueError:
|
| - # When the subprocess fails, it returns None. That triggers a ValueError
|
| - # when trying to unpack the return value into (out, code).
|
| - return 1, ''
|
| + (out, _), code = subprocess2.communicate(['git'] + args,
|
| + env=GetNoGitPagerEnv(),
|
| + stdout=subprocess2.PIPE,
|
| + stderr=stderr)
|
| + return code, out
|
| + except subprocess2.CalledProcessError as e:
|
| + logging.debug('Failed running %s', args)
|
| + return e.returncode, e.stdout
|
|
|
|
|
| def RunGitSilent(args):
|
| @@ -157,33 +156,72 @@ def ask_for_data(prompt):
|
| sys.exit(1)
|
|
|
|
|
| -def git_set_branch_value(key, value):
|
| - branch = GetCurrentBranch()
|
| - if not branch:
|
| - return
|
| +def _git_branch_config_key(branch, key):
|
| + """Helper method to return Git config key for a branch."""
|
| + assert branch, 'branch name is required to set git config for it'
|
| + return 'branch.%s.%s' % (branch, key)
|
|
|
| - cmd = ['config']
|
| - if isinstance(value, int):
|
| - cmd.append('--int')
|
| - git_key = 'branch.%s.%s' % (branch, key)
|
| - RunGit(cmd + [git_key, str(value)])
|
|
|
| +def _git_get_branch_config_value(key, default=None, value_type=str,
|
| + branch=False):
|
| + """Returns git config value of given or current branch if any.
|
|
|
| -def git_get_branch_default(key, default):
|
| - branch = GetCurrentBranch()
|
| - if branch:
|
| - git_key = 'branch.%s.%s' % (branch, key)
|
| - (_, stdout) = RunGitWithCode(['config', '--int', '--get', git_key])
|
| - try:
|
| - return int(stdout.strip())
|
| - except ValueError:
|
| - pass
|
| + Returns default in all other cases.
|
| + """
|
| + assert value_type in (int, str, bool)
|
| + if branch is False: # Distinguishing default arg value from None.
|
| + branch = GetCurrentBranch()
|
| +
|
| + if not branch:
|
| + return default
|
| +
|
| + args = ['config']
|
| + if value_type == int:
|
| + args.append('--int')
|
| + elif value_type == bool:
|
| + args.append('--bool')
|
| + args.append(_git_branch_config_key(branch, key))
|
| + code, out = RunGitWithCode(args)
|
| + if code == 0:
|
| + value = out.strip()
|
| + if value_type == int:
|
| + return int(value)
|
| + if value_type == bool:
|
| + return bool(value.lower() == 'true')
|
| + return value
|
| return default
|
|
|
|
|
| +def _git_set_branch_config_value(key, value, branch=None, **kwargs):
|
| + """Sets the value or unsets if it's None of a git branch config.
|
| +
|
| + Valid, though not necessarily existing, branch must be provided,
|
| + otherwise currently checked out branch is used.
|
| + """
|
| + if not branch:
|
| + branch = GetCurrentBranch()
|
| + assert branch, 'a branch name OR currently checked out branch is required'
|
| + args = ['config']
|
| + # Check for boolean first, becuase bool is int, but int is not bool.
|
| + if value is None:
|
| + args.append('--unset')
|
| + elif isinstance(value, bool):
|
| + args.append('--bool')
|
| + value = str(value).lower()
|
| + elif isinstance(value, int):
|
| + args.append('--int')
|
| + value = str(value)
|
| + else:
|
| + value = str(value)
|
| + args.append(_git_branch_config_key(branch, key))
|
| + if value is not None:
|
| + args.append(value)
|
| + RunGit(args, **kwargs)
|
| +
|
| +
|
| def add_git_similarity(parser):
|
| parser.add_option(
|
| - '--similarity', metavar='SIM', type='int', action='store',
|
| + '--similarity', metavar='SIM', type=int, action='store',
|
| help='Sets the percentage that a pair of files need to match in order to'
|
| ' be considered copies (default 50)')
|
| parser.add_option(
|
| @@ -198,19 +236,20 @@ def add_git_similarity(parser):
|
| options, args = old_parser_args(args)
|
|
|
| if options.similarity is None:
|
| - options.similarity = git_get_branch_default('git-cl-similarity', 50)
|
| + options.similarity = _git_get_branch_config_value(
|
| + 'git-cl-similarity', default=50, value_type=int)
|
| else:
|
| print('Note: Saving similarity of %d%% in git config.'
|
| % options.similarity)
|
| - git_set_branch_value('git-cl-similarity', options.similarity)
|
| + _git_set_branch_config_value('git-cl-similarity', options.similarity)
|
|
|
| options.similarity = max(0, min(options.similarity, 100))
|
|
|
| if options.find_copies is None:
|
| - options.find_copies = bool(
|
| - git_get_branch_default('git-find-copies', True))
|
| + options.find_copies = _git_get_branch_config_value(
|
| + 'git-find-copies', default=True, value_type=bool)
|
| else:
|
| - git_set_branch_value('git-find-copies', int(options.find_copies))
|
| + _git_set_branch_config_value('git-find-copies', bool(options.find_copies))
|
|
|
| print('Using %d%% similarity for rename/copy detection. '
|
| 'Override with --similarity.' % options.similarity)
|
| @@ -906,7 +945,7 @@ class Changelist(object):
|
| Notes:
|
| * Not safe for concurrent multi-{thread,process} use.
|
| * Caches values from current branch. Therefore, re-use after branch change
|
| - with care.
|
| + with great care.
|
| """
|
|
|
| def __init__(self, branchref=None, issue=None, codereview=None, **kwargs):
|
| @@ -966,14 +1005,15 @@ class Changelist(object):
|
| assert not self.issue
|
| # Whether we find issue or not, we are doing the lookup.
|
| self.lookedup_issue = True
|
| - for codereview, cls in _CODEREVIEW_IMPLEMENTATIONS.iteritems():
|
| - setting = cls.IssueSetting(self.GetBranch())
|
| - issue = RunGit(['config', setting], error_ok=True).strip()
|
| - if issue:
|
| - self._codereview = codereview
|
| - self._codereview_impl = cls(self, **kwargs)
|
| - self.issue = int(issue)
|
| - return
|
| + if self.GetBranch():
|
| + for codereview, cls in _CODEREVIEW_IMPLEMENTATIONS.iteritems():
|
| + issue = _git_get_branch_config_value(
|
| + cls.IssueConfigKey(), value_type=int, branch=self.GetBranch())
|
| + if issue:
|
| + self._codereview = codereview
|
| + self._codereview_impl = cls(self, **kwargs)
|
| + self.issue = int(issue)
|
| + return
|
|
|
| # No issue is set for this branch, so decide based on repo-wide settings.
|
| return self._load_codereview_impl(
|
| @@ -1025,16 +1065,31 @@ class Changelist(object):
|
| """Clears cached branch data of this object."""
|
| self.branch = self.branchref = None
|
|
|
| + def _GitGetBranchConfigValue(self, key, default=None, **kwargs):
|
| + assert 'branch' not in kwargs, 'this CL branch is used automatically'
|
| + kwargs['branch'] = self.GetBranch()
|
| + return _git_get_branch_config_value(key, default, **kwargs)
|
| +
|
| + def _GitSetBranchConfigValue(self, key, value, **kwargs):
|
| + assert 'branch' not in kwargs, 'this CL branch is used automatically'
|
| + assert self.GetBranch(), (
|
| + 'this CL must have an associated branch to %sset %s%s' %
|
| + ('un' if value is None else '',
|
| + key,
|
| + '' if value is None else ' to %r' % value))
|
| + kwargs['branch'] = self.GetBranch()
|
| + return _git_set_branch_config_value(key, value, **kwargs)
|
| +
|
| @staticmethod
|
| def FetchUpstreamTuple(branch):
|
| """Returns a tuple containing remote and remote ref,
|
| e.g. 'origin', 'refs/heads/master'
|
| """
|
| remote = '.'
|
| - upstream_branch = RunGit(['config', 'branch.%s.merge' % branch],
|
| - error_ok=True).strip()
|
| + upstream_branch = _git_get_branch_config_value('merge', branch=branch)
|
| +
|
| if upstream_branch:
|
| - remote = RunGit(['config', 'branch.%s.remote' % branch]).strip()
|
| + remote = _git_get_branch_config_value('remote', branch=branch)
|
| else:
|
| upstream_branch = RunGit(['config', 'rietveld.upstream-branch'],
|
| error_ok=True).strip()
|
| @@ -1168,8 +1223,7 @@ class Changelist(object):
|
|
|
| Returns None if it is not set.
|
| """
|
| - return RunGit(['config', 'branch.%s.base-url' % self.GetBranch()],
|
| - error_ok=True).strip()
|
| + return self._GitGetBranchConfigValue('base-url')
|
|
|
| def GetGitSvnRemoteUrl(self):
|
| """Return the configured git-svn remote URL parsed from git svn info.
|
| @@ -1202,10 +1256,8 @@ class Changelist(object):
|
| def GetIssue(self):
|
| """Returns the issue number as a int or None if not set."""
|
| if self.issue is None and not self.lookedup_issue:
|
| - issue = RunGit(['config',
|
| - self._codereview_impl.IssueSetting(self.GetBranch())],
|
| - error_ok=True).strip()
|
| - self.issue = int(issue) or None if issue else None
|
| + self.issue = self._GitGetBranchConfigValue(
|
| + self._codereview_impl.IssueConfigKey(), value_type=int)
|
| self.lookedup_issue = True
|
| return self.issue
|
|
|
| @@ -1230,41 +1282,44 @@ class Changelist(object):
|
| def GetPatchset(self):
|
| """Returns the patchset number as a int or None if not set."""
|
| if self.patchset is None and not self.lookedup_patchset:
|
| - patchset = RunGit(['config', self._codereview_impl.PatchsetSetting()],
|
| - error_ok=True).strip()
|
| - self.patchset = int(patchset) or None if patchset else None
|
| + self.patchset = self._GitGetBranchConfigValue(
|
| + self._codereview_impl.PatchsetConfigKey(), value_type=int)
|
| self.lookedup_patchset = True
|
| return self.patchset
|
|
|
| def SetPatchset(self, patchset):
|
| - """Set this branch's patchset. If patchset=0, clears the patchset."""
|
| - patchset_setting = self._codereview_impl.PatchsetSetting()
|
| - if patchset:
|
| - RunGit(['config', patchset_setting, str(patchset)])
|
| - self.patchset = patchset
|
| - else:
|
| - RunGit(['config', '--unset', patchset_setting],
|
| - stderr=subprocess2.PIPE, error_ok=True)
|
| + """Set this branch's patchset. If patchset=0, clears the patchset."""
|
| + assert self.GetBranch()
|
| + if not patchset:
|
| self.patchset = None
|
| + else:
|
| + self.patchset = int(patchset)
|
| + self._GitSetBranchConfigValue(
|
| + self._codereview_impl.PatchsetConfigKey(), self.patchset)
|
|
|
| def SetIssue(self, issue=None):
|
| - """Set this branch's issue. If issue isn't given, clears the issue."""
|
| - issue_setting = self._codereview_impl.IssueSetting(self.GetBranch())
|
| - codereview_setting = self._codereview_impl.GetCodereviewServerSetting()
|
| + """Set this branch's issue. If issue isn't given, clears the issue."""
|
| + assert self.GetBranch()
|
| if issue:
|
| + issue = int(issue)
|
| + self._GitSetBranchConfigValue(
|
| + self._codereview_impl.IssueConfigKey(), issue)
|
| self.issue = issue
|
| - RunGit(['config', issue_setting, str(issue)])
|
| codereview_server = self._codereview_impl.GetCodereviewServer()
|
| if codereview_server:
|
| - RunGit(['config', codereview_setting, codereview_server])
|
| + self._GitSetBranchConfigValue(
|
| + self._codereview_impl.CodereviewServerConfigKey(),
|
| + codereview_server)
|
| else:
|
| - # Reset it regardless. It doesn't hurt.
|
| - config_settings = [issue_setting, self._codereview_impl.PatchsetSetting()]
|
| - for prop in (['last-upload-hash'] +
|
| - self._codereview_impl._PostUnsetIssueProperties()):
|
| - config_settings.append('branch.%s.%s' % (self.GetBranch(), prop))
|
| - for setting in config_settings:
|
| - RunGit(['config', '--unset', setting], error_ok=True)
|
| + # Reset all of these just to be clean.
|
| + reset_suffixes = [
|
| + 'last-upload-hash',
|
| + self._codereview_impl.IssueConfigKey(),
|
| + self._codereview_impl.PatchsetConfigKey(),
|
| + self._codereview_impl.CodereviewServerConfigKey(),
|
| + ] + self._PostUnsetIssueProperties()
|
| + for prop in reset_suffixes:
|
| + self._GitSetBranchConfigValue(prop, None, error_ok=True)
|
| self.issue = None
|
| self.patchset = None
|
|
|
| @@ -1408,8 +1463,8 @@ class Changelist(object):
|
| elif options.cq_dry_run:
|
| self.SetCQState(_CQState.DRY_RUN)
|
|
|
| - git_set_branch_value('last-upload-hash',
|
| - RunGit(['rev-parse', 'HEAD']).strip())
|
| + _git_set_branch_config_value('last-upload-hash',
|
| + RunGit(['rev-parse', 'HEAD']).strip())
|
| # Run post upload hooks, if specified.
|
| if settings.GetRunPostUploadHook():
|
| presubmit_support.DoPostUploadExecuter(
|
| @@ -1496,27 +1551,24 @@ class _ChangelistCodereviewBase(object):
|
| """Fetches and returns description from the codereview server."""
|
| raise NotImplementedError()
|
|
|
| - def GetCodereviewServerSetting(self):
|
| - """Returns git config setting for the codereview server."""
|
| - raise NotImplementedError()
|
| -
|
| @classmethod
|
| - def IssueSetting(cls, branch):
|
| - return 'branch.%s.%s' % (branch, cls.IssueSettingSuffix())
|
| + def IssueConfigKey(cls):
|
| + """Returns branch setting storing issue number."""
|
| + raise NotImplementedError()
|
|
|
| @classmethod
|
| - def IssueSettingSuffix(cls):
|
| - """Returns name of git config setting which stores issue number for a given
|
| - branch."""
|
| + def PatchsetConfigKey(cls):
|
| + """Returns branch setting storing patchset number."""
|
| raise NotImplementedError()
|
|
|
| - def PatchsetSetting(self):
|
| - """Returns name of git config setting which stores issue number."""
|
| + @classmethod
|
| + def CodereviewServerConfigKey(cls):
|
| + """Returns branch setting storing codereview server."""
|
| raise NotImplementedError()
|
|
|
| def _PostUnsetIssueProperties(self):
|
| """Which branch-specific properties to erase when unsettin issue."""
|
| - raise NotImplementedError()
|
| + return []
|
|
|
| def GetRieveldObjForPresubmit(self):
|
| # This is an unfortunate Rietveld-embeddedness in presubmit.
|
| @@ -1603,10 +1655,8 @@ class _RietveldChangelistImpl(_ChangelistCodereviewBase):
|
| # If we're on a branch then get the server potentially associated
|
| # with that branch.
|
| if self.GetIssue():
|
| - rietveld_server_setting = self.GetCodereviewServerSetting()
|
| - if rietveld_server_setting:
|
| - self._rietveld_server = gclient_utils.UpgradeToHttps(RunGit(
|
| - ['config', rietveld_server_setting], error_ok=True).strip())
|
| + self._rietveld_server = gclient_utils.UpgradeToHttps(
|
| + self._GitGetBranchConfigValue(self.CodereviewServerConfigKey()))
|
| if not self._rietveld_server:
|
| self._rietveld_server = settings.GetDefaultServerUrl()
|
| return self._rietveld_server
|
| @@ -1760,23 +1810,16 @@ class _RietveldChangelistImpl(_ChangelistCodereviewBase):
|
| return self._rpc_server
|
|
|
| @classmethod
|
| - def IssueSettingSuffix(cls):
|
| + def IssueConfigKey(cls):
|
| return 'rietveldissue'
|
|
|
| - def PatchsetSetting(self):
|
| - """Return the git setting that stores this change's most recent patchset."""
|
| - return 'branch.%s.rietveldpatchset' % self.GetBranch()
|
| -
|
| - def GetCodereviewServerSetting(self):
|
| - """Returns the git setting that stores this change's rietveld server."""
|
| - branch = self.GetBranch()
|
| - if branch:
|
| - return 'branch.%s.rietveldserver' % branch
|
| - return None
|
| + @classmethod
|
| + def PatchsetConfigKey(cls):
|
| + return 'rietveldpatchset'
|
|
|
| - def _PostUnsetIssueProperties(self):
|
| - """Which branch-specific properties to erase when unsetting issue."""
|
| - return ['rietveldserver']
|
| + @classmethod
|
| + def CodereviewServerConfigKey(cls):
|
| + return 'rietveldserver'
|
|
|
| def GetRieveldObjForPresubmit(self):
|
| return self.RpcServer()
|
| @@ -2070,12 +2113,10 @@ class _GerritChangelistImpl(_ChangelistCodereviewBase):
|
| # If we're on a branch then get the server potentially associated
|
| # with that branch.
|
| if self.GetIssue():
|
| - gerrit_server_setting = self.GetCodereviewServerSetting()
|
| - if gerrit_server_setting:
|
| - self._gerrit_server = RunGit(['config', gerrit_server_setting],
|
| - error_ok=True).strip()
|
| - if self._gerrit_server:
|
| - self._gerrit_host = urlparse.urlparse(self._gerrit_server).netloc
|
| + self._gerrit_server = self._GitGetBranchConfigValue(
|
| + self.CodereviewServerConfigKey())
|
| + if self._gerrit_server:
|
| + self._gerrit_host = urlparse.urlparse(self._gerrit_server).netloc
|
| if not self._gerrit_server:
|
| # We assume repo to be hosted on Gerrit, and hence Gerrit server
|
| # has "-review" suffix for lowest level subdomain.
|
| @@ -2086,9 +2127,17 @@ class _GerritChangelistImpl(_ChangelistCodereviewBase):
|
| return self._gerrit_server
|
|
|
| @classmethod
|
| - def IssueSettingSuffix(cls):
|
| + def IssueConfigKey(cls):
|
| return 'gerritissue'
|
|
|
| + @classmethod
|
| + def PatchsetConfigKey(cls):
|
| + return 'gerritpatchset'
|
| +
|
| + @classmethod
|
| + def CodereviewServerConfigKey(cls):
|
| + return 'gerritserver'
|
| +
|
| def EnsureAuthenticated(self, force):
|
| """Best effort check that user is authenticated with Gerrit server."""
|
| if settings.GetGerritSkipEnsureAuthenticated():
|
| @@ -2134,24 +2183,9 @@ class _GerritChangelistImpl(_ChangelistCodereviewBase):
|
| cookie_auth.get_netrc_path(),
|
| cookie_auth.get_new_password_message(git_host)))
|
|
|
| -
|
| - def PatchsetSetting(self):
|
| - """Return the git setting that stores this change's most recent patchset."""
|
| - return 'branch.%s.gerritpatchset' % self.GetBranch()
|
| -
|
| - def GetCodereviewServerSetting(self):
|
| - """Returns the git setting that stores this change's Gerrit server."""
|
| - branch = self.GetBranch()
|
| - if branch:
|
| - return 'branch.%s.gerritserver' % branch
|
| - return None
|
| -
|
| def _PostUnsetIssueProperties(self):
|
| """Which branch-specific properties to erase when unsetting issue."""
|
| - return [
|
| - 'gerritserver',
|
| - 'gerritsquashhash',
|
| - ]
|
| + return ['gerritsquashhash']
|
|
|
| def GetRieveldObjForPresubmit(self):
|
| class ThisIsNotRietveldIssue(object):
|
| @@ -2274,8 +2308,7 @@ class _GerritChangelistImpl(_ChangelistCodereviewBase):
|
| 'Press Enter to continue, Ctrl+C to abort.')
|
|
|
| differs = True
|
| - last_upload = RunGit(['config',
|
| - 'branch.%s.gerritsquashhash' % self.GetBranch()],
|
| + last_upload = RunGit(['config', self._GitBranchSetting('gerritsquashhash')],
|
| error_ok=True).strip()
|
| # Note: git diff outputs nothing if there is no diff.
|
| if not last_upload or RunGit(['diff', last_upload]).strip():
|
| @@ -2578,8 +2611,7 @@ class _GerritChangelistImpl(_ChangelistCodereviewBase):
|
| ('Created|Updated %d issues on Gerrit, but only 1 expected.\n'
|
| 'Change-Id: %s') % (len(change_numbers), change_id))
|
| self.SetIssue(change_numbers[0])
|
| - RunGit(['config', 'branch.%s.gerritsquashhash' % self.GetBranch(),
|
| - ref_to_push])
|
| + self._GitSetBranchConfigValue('gerritsquashhash', ref_to_push)
|
| return 0
|
|
|
| def _AddChangeIdToCommitMessage(self, options, args):
|
| @@ -5079,7 +5111,7 @@ def CMDcheckout(parser, args):
|
|
|
| branches = []
|
| for cls in _CODEREVIEW_IMPLEMENTATIONS.values():
|
| - branches.extend(find_issues(cls.IssueSettingSuffix()))
|
| + branches.extend(find_issues(cls.IssueConfigKey()))
|
| if len(branches) == 0:
|
| print('No branch found for issue %s.' % target_issue)
|
| return 1
|
|
|