Simplify GIT.Capture() code to always redirect stderr by default and always throw an exception on failure.

Make gclient_scm_test silent.

Replace raise Exception() with raise gclient_utils.Error().

BUG=54084
TEST=none

Review URL: http://codereview.chromium.org/3353018

git-svn-id: svn://svn.chromium.org/chrome/trunk/tools/depot_tools@58936 0039d316-1c4b-4281-b951-d872f2087c98
experimental/szager/collated-output
maruel@chromium.org 15 years ago
parent 389d6dea37
commit ad80e3b96e

@ -10,8 +10,7 @@ details on the presubmit API built into gcl.
UNIT_TESTS = [ UNIT_TESTS = [
'tests.gcl_unittest', 'tests.gcl_unittest',
# The git tests are broken. 'tests.gclient_scm_test',
#'tests.gclient_scm_test',
'tests.gclient_smoketest', 'tests.gclient_smoketest',
'tests.gclient_utils_test', 'tests.gclient_utils_test',
'tests.presubmit_unittest', 'tests.presubmit_unittest',

@ -258,10 +258,9 @@ class GitWrapper(SCMWrapper):
backoff_time = 5 backoff_time = 5
for _ in range(10): for _ in range(10):
try: try:
remote_output, remote_err = scm.GIT.Capture( remote_output = scm.GIT.Capture(
['remote'] + verbose + ['update'], ['remote'] + verbose + ['update'],
self.checkout_path, cwd=self.checkout_path)
print_error=False)
break break
except gclient_utils.CheckCallError, e: except gclient_utils.CheckCallError, e:
# Hackish but at that point, git is known to work so just checking for # Hackish but at that point, git is known to work so just checking for
@ -276,9 +275,7 @@ class GitWrapper(SCMWrapper):
raise raise
if verbose: if verbose:
options.stdout.write(remote_output.strip() + '\n') options.stdout.write(remote_output)
# git remote update prints to stderr when used with --verbose
options.stdout.write(remote_err.strip() + '\n')
# This is a big hammer, debatable if it should even be here... # This is a big hammer, debatable if it should even be here...
if options.force or options.reset: if options.force or options.reset:
@ -331,10 +328,8 @@ class GitWrapper(SCMWrapper):
options.stdout.write('Trying fast-forward merge to branch : %s\n' % options.stdout.write('Trying fast-forward merge to branch : %s\n' %
upstream_branch) upstream_branch)
try: try:
merge_output, merge_err = scm.GIT.Capture(['merge', '--ff-only', merge_output = scm.GIT.Capture(['merge', '--ff-only', upstream_branch],
upstream_branch], cwd=self.checkout_path)
self.checkout_path,
print_error=False)
except gclient_utils.CheckCallError, e: except gclient_utils.CheckCallError, e:
if re.match('fatal: Not possible to fast-forward, aborting.', e.stderr): if re.match('fatal: Not possible to fast-forward, aborting.', e.stderr):
if not printed_path: if not printed_path:
@ -342,7 +337,7 @@ class GitWrapper(SCMWrapper):
printed_path = True printed_path = True
while True: while True:
try: try:
# TODO(maruel): That can't work. # TODO(maruel): That can't work with --jobs.
action = str(raw_input("Cannot fast-forward merge, attempt to " action = str(raw_input("Cannot fast-forward merge, attempt to "
"rebase? (y)es / (q)uit / (s)kip : ")) "rebase? (y)es / (q)uit / (s)kip : "))
except ValueError: except ValueError:
@ -382,10 +377,7 @@ class GitWrapper(SCMWrapper):
if not printed_path: if not printed_path:
options.stdout.write('\n_____ %s%s\n' % (self.relpath, rev_str)) options.stdout.write('\n_____ %s%s\n' % (self.relpath, rev_str))
printed_path = True printed_path = True
print merge_output.strip() options.stdout.write(merge_output)
if merge_err:
options.stdout.write('Merge produced error output:\n%s\n' %
merge_err.strip())
if not verbose: if not verbose:
# Make the output a little prettier. It's nice to have some # Make the output a little prettier. It's nice to have some
# whitespace between projects when syncing. # whitespace between projects when syncing.
@ -530,13 +522,11 @@ class GitWrapper(SCMWrapper):
rebase_cmd.append(branch) rebase_cmd.append(branch)
try: try:
rebase_output, rebase_err = scm.GIT.Capture(rebase_cmd, rebase_output = scm.GIT.Capture(rebase_cmd, cwd=self.checkout_path)
self.checkout_path,
print_error=False)
except gclient_utils.CheckCallError, e: except gclient_utils.CheckCallError, e:
if re.match(r'cannot rebase: you have unstaged changes', e.stderr) or \ if (re.match(r'cannot rebase: you have unstaged changes', e.stderr) or
re.match(r'cannot rebase: your index contains uncommitted changes', re.match(r'cannot rebase: your index contains uncommitted changes',
e.stderr): e.stderr)):
while True: while True:
rebase_action = str(raw_input("Cannot rebase because of unstaged " rebase_action = str(raw_input("Cannot rebase because of unstaged "
"changes.\n'git reset --hard HEAD' ?\n" "changes.\n'git reset --hard HEAD' ?\n"
@ -546,8 +536,7 @@ class GitWrapper(SCMWrapper):
if re.match(r'yes|y', rebase_action, re.I): if re.match(r'yes|y', rebase_action, re.I):
self._Run(['reset', '--hard', 'HEAD'], options) self._Run(['reset', '--hard', 'HEAD'], options)
# Should this be recursive? # Should this be recursive?
rebase_output, rebase_err = scm.GIT.Capture(rebase_cmd, rebase_output = scm.GIT.Capture(rebase_cmd, cwd=self.checkout_path)
self.checkout_path)
break break
elif re.match(r'quit|q', rebase_action, re.I): elif re.match(r'quit|q', rebase_action, re.I):
raise gclient_utils.Error("Please merge or rebase manually\n" raise gclient_utils.Error("Please merge or rebase manually\n"
@ -572,10 +561,7 @@ class GitWrapper(SCMWrapper):
self.checkout_path self.checkout_path
+ "%s" % ' '.join(rebase_cmd)) + "%s" % ' '.join(rebase_cmd))
print rebase_output.strip() options.stdout.write(rebase_output)
if rebase_err:
options.stdout.write('Rebase produced error output:\n%s\n' %
rebase_err.strip())
if not options.verbose: if not options.verbose:
# Make the output a little prettier. It's nice to have some # Make the output a little prettier. It's nice to have some
# whitespace between projects when syncing. # whitespace between projects when syncing.
@ -601,7 +587,7 @@ class GitWrapper(SCMWrapper):
# Make sure the tree is clean; see git-rebase.sh for reference # Make sure the tree is clean; see git-rebase.sh for reference
try: try:
scm.GIT.Capture(['update-index', '--ignore-submodules', '--refresh'], scm.GIT.Capture(['update-index', '--ignore-submodules', '--refresh'],
self.checkout_path, print_error=False) cwd=self.checkout_path)
except gclient_utils.CheckCallError: except gclient_utils.CheckCallError:
raise gclient_utils.Error('\n____ %s%s\n' raise gclient_utils.Error('\n____ %s%s\n'
'\tYou have unstaged changes.\n' '\tYou have unstaged changes.\n'
@ -609,8 +595,8 @@ class GitWrapper(SCMWrapper):
% (self.relpath, rev_str)) % (self.relpath, rev_str))
try: try:
scm.GIT.Capture(['diff-index', '--cached', '--name-status', '-r', scm.GIT.Capture(['diff-index', '--cached', '--name-status', '-r',
'--ignore-submodules', 'HEAD', '--'], self.checkout_path, '--ignore-submodules', 'HEAD', '--'],
print_error=False) cwd=self.checkout_path)
except gclient_utils.CheckCallError: except gclient_utils.CheckCallError:
raise gclient_utils.Error('\n____ %s%s\n' raise gclient_utils.Error('\n____ %s%s\n'
'\tYour index contains uncommitted changes\n' '\tYour index contains uncommitted changes\n'
@ -622,10 +608,8 @@ class GitWrapper(SCMWrapper):
# reference by a commit). If not, error out -- most likely a rebase is # reference by a commit). If not, error out -- most likely a rebase is
# in progress, try to detect so we can give a better error. # in progress, try to detect so we can give a better error.
try: try:
_, _ = scm.GIT.Capture( scm.GIT.Capture(['name-rev', '--no-undefined', 'HEAD'],
['name-rev', '--no-undefined', 'HEAD'], cwd=self.checkout_path)
self.checkout_path,
print_error=False)
except gclient_utils.CheckCallError: except gclient_utils.CheckCallError:
# Commit is not contained by any rev. See if the user is rebasing: # Commit is not contained by any rev. See if the user is rebasing:
if self._IsRebasing(): if self._IsRebasing():
@ -652,8 +636,8 @@ class GitWrapper(SCMWrapper):
return branch return branch
def _Capture(self, args): def _Capture(self, args):
return gclient_utils.CheckCall(['git'] + args, return gclient_utils.CheckCall(
cwd=self.checkout_path)[0].strip() ['git'] + args, cwd=self.checkout_path, print_error=False)[0].strip()
def _Run(self, args, options, **kwargs): def _Run(self, args, options, **kwargs):
kwargs.setdefault('cwd', self.checkout_path) kwargs.setdefault('cwd', self.checkout_path)

@ -36,7 +36,7 @@ class CheckCallError(OSError, Error):
"""CheckCall() returned non-0.""" """CheckCall() returned non-0."""
def __init__(self, command, cwd, returncode, stdout, stderr=None): def __init__(self, command, cwd, returncode, stdout, stderr=None):
OSError.__init__(self, command, cwd, returncode, stdout, stderr) OSError.__init__(self, command, cwd, returncode, stdout, stderr)
Error.__init__(self) Error.__init__(self, command)
self.command = command self.command = command
self.cwd = cwd self.cwd = cwd
self.returncode = returncode self.returncode = returncode

@ -66,22 +66,9 @@ def GenFakeDiff(filename):
class GIT(object): class GIT(object):
@staticmethod @staticmethod
def Capture(args, in_directory=None, print_error=True, error_ok=False): def Capture(args, **kwargs):
"""Runs git, capturing output sent to stdout as a string. return gclient_utils.CheckCall(['git'] + args, print_error=False,
**kwargs)[0]
Args:
args: A sequence of command line parameters to be passed to git.
in_directory: The directory where git is to be run.
Returns:
The output sent to stdout as a string.
"""
try:
return gclient_utils.CheckCall(['git'] + args, in_directory, print_error)
except gclient_utils.CheckCallError:
if error_ok:
return ('', '')
raise
@staticmethod @staticmethod
def CaptureStatus(files, upstream_branch=None): def CaptureStatus(files, upstream_branch=None):
@ -93,32 +80,34 @@ class GIT(object):
if upstream_branch is None: if upstream_branch is None:
upstream_branch = GIT.GetUpstreamBranch(os.getcwd()) upstream_branch = GIT.GetUpstreamBranch(os.getcwd())
if upstream_branch is None: if upstream_branch is None:
raise Exception("Cannot determine upstream branch") raise gclient_utils.Error('Cannot determine upstream branch')
command = ["diff", "--name-status", "-r", "%s..." % upstream_branch] command = ['diff', '--name-status', '-r', '%s...' % upstream_branch]
if not files: if not files:
pass pass
elif isinstance(files, basestring): elif isinstance(files, basestring):
command.append(files) command.append(files)
else: else:
command.extend(files) command.extend(files)
status = GIT.Capture(command).rstrip()
status = GIT.Capture(command)[0].rstrip()
results = [] results = []
if status: if status:
for statusline in status.split('\n'): for statusline in status.splitlines():
m = re.match('^(\w)\t(.+)$', statusline) m = re.match('^(\w)\t(.+)$', statusline)
if not m: if not m:
raise Exception("status currently unsupported: %s" % statusline) raise gclient_utils.Error(
'status currently unsupported: %s' % statusline)
results.append(('%s ' % m.group(1), m.group(2))) results.append(('%s ' % m.group(1), m.group(2)))
return results return results
@staticmethod @staticmethod
def GetEmail(repo_root): def GetEmail(cwd):
"""Retrieves the user email address if known.""" """Retrieves the user email address if known."""
# We could want to look at the svn cred when it has a svn remote but it # We could want to look at the svn cred when it has a svn remote but it
# should be fine for now, users should simply configure their git settings. # should be fine for now, users should simply configure their git settings.
return GIT.Capture(['config', 'user.email'], try:
repo_root, error_ok=True)[0].strip() return GIT.Capture(['config', 'user.email'], cwd=cwd).strip()
except gclient_utils.CheckCallError:
return ''
@staticmethod @staticmethod
def ShortBranchName(branch): def ShortBranchName(branch):
@ -128,7 +117,7 @@ class GIT(object):
@staticmethod @staticmethod
def GetBranchRef(cwd): def GetBranchRef(cwd):
"""Returns the full branch reference, e.g. 'refs/heads/master'.""" """Returns the full branch reference, e.g. 'refs/heads/master'."""
return GIT.Capture(['symbolic-ref', 'HEAD'], cwd)[0].strip() return GIT.Capture(['symbolic-ref', 'HEAD'], cwd=cwd).strip()
@staticmethod @staticmethod
def GetBranch(cwd): def GetBranch(cwd):
@ -140,7 +129,7 @@ class GIT(object):
"""Returns true if this repo looks like it's using git-svn.""" """Returns true if this repo looks like it's using git-svn."""
# If you have any "svn-remote.*" config keys, we think you're using svn. # If you have any "svn-remote.*" config keys, we think you're using svn.
try: try:
GIT.Capture(['config', '--get-regexp', r'^svn-remote\.'], cwd) GIT.Capture(['config', '--get-regexp', r'^svn-remote\.'], cwd=cwd)
return True return True
except gclient_utils.CheckCallError: except gclient_utils.CheckCallError:
return False return False
@ -159,11 +148,11 @@ class GIT(object):
# Get the refname and svn url for all refs/remotes/*. # Get the refname and svn url for all refs/remotes/*.
remotes = GIT.Capture( remotes = GIT.Capture(
['for-each-ref', '--format=%(refname)', 'refs/remotes'], ['for-each-ref', '--format=%(refname)', 'refs/remotes'],
cwd)[0].splitlines() cwd=cwd).splitlines()
svn_refs = {} svn_refs = {}
for ref in remotes: for ref in remotes:
match = git_svn_re.search( match = git_svn_re.search(
GIT.Capture(['cat-file', '-p', ref], cwd)[0]) GIT.Capture(['cat-file', '-p', ref], cwd=cwd))
# Prefer origin/HEAD over all others. # Prefer origin/HEAD over all others.
if match and (match.group(1) not in svn_refs or if match and (match.group(1) not in svn_refs or
ref == "refs/remotes/origin/HEAD"): ref == "refs/remotes/origin/HEAD"):
@ -198,22 +187,24 @@ class GIT(object):
""" """
remote = '.' remote = '.'
branch = GIT.GetBranch(cwd) branch = GIT.GetBranch(cwd)
upstream_branch = None try:
upstream_branch = GIT.Capture( upstream_branch = GIT.Capture(
['config', 'branch.%s.merge' % branch], in_directory=cwd, ['config', 'branch.%s.merge' % branch], cwd=cwd).strip()
error_ok=True)[0].strip() except gclient_utils.Error:
upstream_branch = None
if upstream_branch: if upstream_branch:
remote = GIT.Capture( try:
['config', 'branch.%s.remote' % branch], remote = GIT.Capture(
in_directory=cwd, error_ok=True)[0].strip() ['config', 'branch.%s.remote' % branch], cwd=cwd).strip()
except gclient_utils.Error:
pass
else: else:
# Fall back on trying a git-svn upstream branch. # Fall back on trying a git-svn upstream branch.
if GIT.IsGitSvn(cwd): if GIT.IsGitSvn(cwd):
upstream_branch = GIT.GetSVNBranch(cwd) upstream_branch = GIT.GetSVNBranch(cwd)
else: else:
# Else, try to guess the origin remote. # Else, try to guess the origin remote.
remote_branches = GIT.Capture( remote_branches = GIT.Capture(['branch', '-r'], cwd=cwd).split()
['branch', '-r'], in_directory=cwd)[0].split()
if 'origin/master' in remote_branches: if 'origin/master' in remote_branches:
# Fall back on origin/master if it exits. # Fall back on origin/master if it exits.
remote = 'origin' remote = 'origin'
@ -254,7 +245,7 @@ class GIT(object):
if files: if files:
command.append('--') command.append('--')
command.extend(files) command.extend(files)
diff = GIT.Capture(command, cwd)[0].splitlines(True) diff = GIT.Capture(command, cwd=cwd).splitlines(True)
for i in range(len(diff)): for i in range(len(diff)):
# In the case of added files, replace /dev/null with the path to the # In the case of added files, replace /dev/null with the path to the
# file being added. # file being added.
@ -268,20 +259,20 @@ class GIT(object):
if not branch: if not branch:
branch = GIT.GetUpstreamBranch(cwd) branch = GIT.GetUpstreamBranch(cwd)
command = ['diff', '--name-only', branch + "..." + branch_head] command = ['diff', '--name-only', branch + "..." + branch_head]
return GIT.Capture(command, cwd)[0].splitlines(False) return GIT.Capture(command, cwd=cwd).splitlines(False)
@staticmethod @staticmethod
def GetPatchName(cwd): def GetPatchName(cwd):
"""Constructs a name for this patch.""" """Constructs a name for this patch."""
short_sha = GIT.Capture(['rev-parse', '--short=4', 'HEAD'], cwd)[0].strip() short_sha = GIT.Capture(['rev-parse', '--short=4', 'HEAD'], cwd=cwd).strip()
return "%s#%s" % (GIT.GetBranch(cwd), short_sha) return "%s#%s" % (GIT.GetBranch(cwd), short_sha)
@staticmethod @staticmethod
def GetCheckoutRoot(path): def GetCheckoutRoot(cwd):
"""Returns the top level directory of a git checkout as an absolute path. """Returns the top level directory of a git checkout as an absolute path.
""" """
root = GIT.Capture(['rev-parse', '--show-cdup'], path)[0].strip() root = GIT.Capture(['rev-parse', '--show-cdup'], cwd=cwd).strip()
return os.path.abspath(os.path.join(path, root)) return os.path.abspath(os.path.join(cwd, root))
@staticmethod @staticmethod
def AssertVersion(min_version): def AssertVersion(min_version):
@ -291,7 +282,7 @@ class GIT(object):
return int(val) return int(val)
else: else:
return 0 return 0
current_version = GIT.Capture(['--version'])[0].split()[-1] current_version = GIT.Capture(['--version']).split()[-1]
current_version_list = map(only_int, current_version.split('.')) current_version_list = map(only_int, current_version.split('.'))
for min_ver in map(int, min_version.split('.')): for min_ver in map(int, min_version.split('.')):
ver = current_version_list.pop(0) ver = current_version_list.pop(0)

@ -630,7 +630,9 @@ from :3
scm.diff(options, self.args, file_list) scm.diff(options, self.args, file_list)
self.assertEquals(file_list, []) self.assertEquals(file_list, [])
self.checkstdout( self.checkstdout(
('\n_____ . at refs/heads/master\n\n\n' ('\n_____ . at refs/heads/master\nUpdating 069c602..a7142dc\n'
'Fast-forward\n a | 1 +\n b | 1 +\n'
' 2 files changed, 2 insertions(+), 0 deletions(-)\n\n\n'
'________ running \'git reset --hard origin/master\' in \'%s\'\n' '________ running \'git reset --hard origin/master\' in \'%s\'\n'
'HEAD is now at a7142dc Personalized\n') % 'HEAD is now at a7142dc Personalized\n') %
gclient_scm.os.path.join(self.root_dir, '.')) gclient_scm.os.path.join(self.root_dir, '.'))
@ -649,7 +651,9 @@ from :3
self.assertEquals(scm.revinfo(options, self.args, None), self.assertEquals(scm.revinfo(options, self.args, None),
'a7142dc9f0009350b96a11f372b6ea658592aa95') 'a7142dc9f0009350b96a11f372b6ea658592aa95')
self.checkstdout( self.checkstdout(
('\n_____ . at refs/heads/master\n\n\n' ('\n_____ . at refs/heads/master\nUpdating 069c602..a7142dc\n'
'Fast-forward\n a | 1 +\n b | 1 +\n'
' 2 files changed, 2 insertions(+), 0 deletions(-)\n\n\n'
'________ running \'git reset --hard origin/master\' in \'%s\'\n' '________ running \'git reset --hard origin/master\' in \'%s\'\n'
'HEAD is now at a7142dc Personalized\n') % 'HEAD is now at a7142dc Personalized\n') %
gclient_scm.os.path.join(self.root_dir, '.')) gclient_scm.os.path.join(self.root_dir, '.'))
@ -673,7 +677,9 @@ from :3
self.assertEquals(scm.revinfo(options, self.args, None), self.assertEquals(scm.revinfo(options, self.args, None),
'a7142dc9f0009350b96a11f372b6ea658592aa95') 'a7142dc9f0009350b96a11f372b6ea658592aa95')
self.checkstdout( self.checkstdout(
('\n_____ . at refs/heads/master\n\n\n' ('\n_____ . at refs/heads/master\nUpdating 069c602..a7142dc\n'
'Fast-forward\n a | 1 +\n b | 1 +\n'
' 2 files changed, 2 insertions(+), 0 deletions(-)\n\n\n'
'________ running \'git reset --hard origin/master\' in \'%s\'\n' '________ running \'git reset --hard origin/master\' in \'%s\'\n'
'HEAD is now at a7142dc Personalized\n') % 'HEAD is now at a7142dc Personalized\n') %
gclient_scm.os.path.join(self.root_dir, '.')) gclient_scm.os.path.join(self.root_dir, '.'))
@ -701,7 +707,9 @@ from :3
self.assertEquals(scm.revinfo(options, self.args, None), self.assertEquals(scm.revinfo(options, self.args, None),
'a7142dc9f0009350b96a11f372b6ea658592aa95') 'a7142dc9f0009350b96a11f372b6ea658592aa95')
self.checkstdout( self.checkstdout(
('\n_____ . at refs/heads/master\n\n\n' ('\n_____ . at refs/heads/master\nUpdating 069c602..a7142dc\n'
'Fast-forward\n a | 1 +\n b | 1 +\n'
' 2 files changed, 2 insertions(+), 0 deletions(-)\n\n\n'
'________ running \'git reset --hard origin/master\' in \'%s\'\n' '________ running \'git reset --hard origin/master\' in \'%s\'\n'
'HEAD is now at a7142dc Personalized\n') % 'HEAD is now at a7142dc Personalized\n') %
gclient_scm.os.path.join(self.root_dir, '.')) gclient_scm.os.path.join(self.root_dir, '.'))
@ -784,7 +792,10 @@ from :3
self.assertEquals(file_list, expected_file_list) self.assertEquals(file_list, expected_file_list)
self.assertEquals(scm.revinfo(options, (), None), self.assertEquals(scm.revinfo(options, (), None),
'a7142dc9f0009350b96a11f372b6ea658592aa95') 'a7142dc9f0009350b96a11f372b6ea658592aa95')
self.checkstdout('\n_____ . at refs/heads/master\n\n') self.checkstdout(
'\n_____ . at refs/heads/master\n'
'Updating 069c602..a7142dc\nFast-forward\n a | 1 +\n b | 1 +\n'
' 2 files changed, 2 insertions(+), 0 deletions(-)\n\n')
def testUpdateUnstagedConflict(self): def testUpdateUnstagedConflict(self):
if not self.enabled: if not self.enabled:

@ -60,8 +60,8 @@ class GitWrapperTestCase(BaseSCMTestCase):
def testGetEmail(self): def testGetEmail(self):
self.mox.StubOutWithMock(scm.GIT, 'Capture') self.mox.StubOutWithMock(scm.GIT, 'Capture')
scm.GIT.Capture(['config', 'user.email'], self.root_dir, error_ok=True scm.GIT.Capture(['config', 'user.email'], cwd=self.root_dir
).AndReturn(['mini@me.com', '']) ).AndReturn('mini@me.com')
self.mox.ReplayAll() self.mox.ReplayAll()
self.assertEqual(scm.GIT.GetEmail(self.root_dir), 'mini@me.com') self.assertEqual(scm.GIT.GetEmail(self.root_dir), 'mini@me.com')

Loading…
Cancel
Save