cmd = ['git']
if git_dir:
cmd += ['--git-dir', git_dir]
- cmd += ['log', '--no-color']
+ cmd += ['--no-pager', 'log', '--no-color']
if oneline:
cmd.append('--oneline')
if use_no_decorate:
patch_count = int(stdout)
return patch_count
+def NameRevision(commit_hash):
+ """Gets the revision name for a commit
+
+ Args:
+ commit_hash: Commit hash to look up
+
+ Return:
+ Name of revision, if any, else None
+ """
+ pipe = ['git', 'name-rev', commit_hash]
+ stdout = command.RunPipe([pipe], capture=True, oneline=True).stdout
+
+ # We expect a commit, a space, then a revision name
+ name = stdout.split(' ')[1].strip()
+ return name
+
+def GuessUpstream(git_dir, branch):
+ """Tries to guess the upstream for a branch
+
+ This lists out top commits on a branch and tries to find a suitable
+ upstream. It does this by looking for the first commit where
+ 'git name-rev' returns a plain branch name, with no ! or ^ modifiers.
+
+ Args:
+ git_dir: Git directory containing repo
+ branch: Name of branch
+
+ Returns:
+ Tuple:
+ Name of upstream branch (e.g. 'upstream/master') or None if none
+ Warning/error message, or None if none
+ """
+ pipe = [LogCmd(branch, git_dir=git_dir, oneline=True, count=100)]
+ result = command.RunPipe(pipe, capture=True, capture_stderr=True,
+ raise_on_error=False)
+ if result.return_code:
+ return None, "Branch '%s' not found" % branch
+ for line in result.stdout.splitlines()[1:]:
+ commit_hash = line.split(' ')[0]
+ name = NameRevision(commit_hash)
+ if '~' not in name and '^' not in name:
+ if name.startswith('remotes/'):
+ name = name[8:]
+ return name, "Guessing upstream as '%s'" % name
+ return None, "Cannot find a suitable upstream for branch '%s'" % branch
+
def GetUpstream(git_dir, branch):
"""Returns the name of the upstream for a branch
branch: Name of branch
Returns:
- Name of upstream branch (e.g. 'upstream/master') or None if none
+ Tuple:
+ Name of upstream branch (e.g. 'upstream/master') or None if none
+ Warning/error message, or None if none
"""
try:
remote = command.OutputOneLine('git', '--git-dir', git_dir, 'config',
merge = command.OutputOneLine('git', '--git-dir', git_dir, 'config',
'branch.%s.merge' % branch)
except:
- return None
+ upstream, msg = GuessUpstream(git_dir, branch)
+ return upstream, msg
if remote == '.':
return merge
elif remote and merge:
leaf = merge.split('/')[-1]
- return '%s/%s' % (remote, leaf)
+ return '%s/%s' % (remote, leaf), None
else:
raise ValueError, ("Cannot determine upstream branch for branch "
"'%s' remote='%s', merge='%s'" % (branch, remote, merge))
Expression in the form 'upstream..branch' which can be used to
access the commits. If the branch does not exist, returns None.
"""
- upstream = GetUpstream(git_dir, branch)
+ upstream, msg = GetUpstream(git_dir, branch)
if not upstream:
- return None
- return '%s%s..%s' % (upstream, '~' if include_upstream else '', branch)
+ return None, msg
+ rstr = '%s%s..%s' % (upstream, '~' if include_upstream else '', branch)
+ return rstr, msg
def CountCommitsInBranch(git_dir, branch, include_upstream=False):
"""Returns the number of commits in the given branch.
Number of patches that exist on top of the branch, or None if the
branch does not exist.
"""
- range_expr = GetRangeInBranch(git_dir, branch, include_upstream)
+ range_expr, msg = GetRangeInBranch(git_dir, branch, include_upstream)
if not range_expr:
- return None
+ return None, msg
pipe = [LogCmd(range_expr, git_dir=git_dir, oneline=True),
['wc', '-l']]
result = command.RunPipe(pipe, capture=True, oneline=True)
patch_count = int(result.stdout)
- return patch_count
+ return patch_count, msg
def CountCommits(commit_range):
"""Returns the number of commits in the given range.
if force:
pipe.append('-f')
pipe.append(commit_hash)
- result = command.RunPipe([pipe], capture=True, raise_on_error=False)
+ result = command.RunPipe([pipe], capture=True, raise_on_error=False,
+ capture_stderr=True)
if result.return_code != 0:
raise OSError, 'git checkout (%s): %s' % (pipe, result.stderr)
commit_hash: Commit hash to check out
"""
pipe = ['git', 'clone', git_dir, '.']
- result = command.RunPipe([pipe], capture=True, cwd=output_dir)
+ result = command.RunPipe([pipe], capture=True, cwd=output_dir,
+ capture_stderr=True)
if result.return_code != 0:
raise OSError, 'git clone: %s' % result.stderr
if work_tree:
pipe.extend(['--work-tree', work_tree])
pipe.append('fetch')
- result = command.RunPipe([pipe], capture=True)
+ result = command.RunPipe([pipe], capture=True, capture_stderr=True)
if result.return_code != 0:
raise OSError, 'git fetch: %s' % result.stderr
else:
return None, files
-def ApplyPatch(verbose, fname):
- """Apply a patch with git am to test it
-
- TODO: Convert these to use command, with stderr option
-
- Args:
- fname: filename of patch file to apply
- """
- col = terminal.Color()
- cmd = ['git', 'am', fname]
- pipe = subprocess.Popen(cmd, stdout=subprocess.PIPE,
- stderr=subprocess.PIPE)
- stdout, stderr = pipe.communicate()
- re_error = re.compile('^error: patch failed: (.+):(\d+)')
- for line in stderr.splitlines():
- if verbose:
- print line
- match = re_error.match(line)
- if match:
- print checkpatch.GetWarningMsg(col, 'warning', match.group(1),
- int(match.group(2)), 'Patch failed')
- return pipe.returncode == 0, stdout
-
-def ApplyPatches(verbose, args, start_point):
- """Apply the patches with git am to make sure all is well
-
- Args:
- verbose: Print out 'git am' output verbatim
- args: List of patch files to apply
- start_point: Number of commits back from HEAD to start applying.
- Normally this is len(args), but it can be larger if a start
- offset was given.
- """
- error_count = 0
- col = terminal.Color()
-
- # Figure out our current position
- cmd = ['git', 'name-rev', 'HEAD', '--name-only']
- pipe = subprocess.Popen(cmd, stdout=subprocess.PIPE)
- stdout, stderr = pipe.communicate()
- if pipe.returncode:
- str = 'Could not find current commit name'
- print col.Color(col.RED, str)
- print stdout
- return False
- old_head = stdout.splitlines()[0]
- if old_head == 'undefined':
- str = "Invalid HEAD '%s'" % stdout.strip()
- print col.Color(col.RED, str)
- return False
-
- # Checkout the required start point
- cmd = ['git', 'checkout', 'HEAD~%d' % start_point]
- pipe = subprocess.Popen(cmd, stdout=subprocess.PIPE,
- stderr=subprocess.PIPE)
- stdout, stderr = pipe.communicate()
- if pipe.returncode:
- str = 'Could not move to commit before patch series'
- print col.Color(col.RED, str)
- print stdout, stderr
- return False
-
- # Apply all the patches
- for fname in args:
- ok, stdout = ApplyPatch(verbose, fname)
- if not ok:
- print col.Color(col.RED, 'git am returned errors for %s: will '
- 'skip this patch' % fname)
- if verbose:
- print stdout
- error_count += 1
- cmd = ['git', 'am', '--skip']
- pipe = subprocess.Popen(cmd, stdout=subprocess.PIPE)
- stdout, stderr = pipe.communicate()
- if pipe.returncode != 0:
- print col.Color(col.RED, 'Unable to skip patch! Aborting...')
- print stdout
- break
-
- # Return to our previous position
- cmd = ['git', 'checkout', old_head]
- pipe = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
- stdout, stderr = pipe.communicate()
- if pipe.returncode:
- print col.Color(col.RED, 'Could not move back to head commit')
- print stdout, stderr
- return error_count == 0
-
def BuildEmailList(in_list, tag=None, alias=None, raise_on_error=True):
"""Build a list of email addresses based on an input list.
...
OSError: Recursive email alias at 'other'
>>> LookupEmail('odd', alias, raise_on_error=False)
- \033[1;31mAlias 'odd' not found\033[0m
+ Alias 'odd' not found
[]
>>> # In this case the loop part will effectively be ignored.
>>> LookupEmail('loop', alias, raise_on_error=False)
- \033[1;31mRecursive email alias at 'other'\033[0m
- \033[1;31mRecursive email alias at 'john'\033[0m
- \033[1;31mRecursive email alias at 'mary'\033[0m
+ Recursive email alias at 'other'
+ Recursive email alias at 'john'
+ Recursive email alias at 'mary'
"""
if not alias:
def Setup():
"""Set up git utils, by reading the alias files."""
# Check for a git alias file also
+ global use_no_decorate
+
alias_fname = GetAliasFile()
if alias_fname:
settings.ReadGitAliases(alias_fname)