summaryrefslogtreecommitdiff
path: root/tools/patman/gitutil.py
diff options
context:
space:
mode:
Diffstat (limited to 'tools/patman/gitutil.py')
-rw-r--r--tools/patman/gitutil.py136
1 files changed, 132 insertions, 4 deletions
diff --git a/tools/patman/gitutil.py b/tools/patman/gitutil.py
index ca3ba4a..4e21d4c 100644
--- a/tools/patman/gitutil.py
+++ b/tools/patman/gitutil.py
@@ -23,11 +23,12 @@ import command
import re
import os
import series
-import settings
import subprocess
import sys
import terminal
+import settings
+
def CountCommitsToBranch():
"""Returns number of commits between HEAD and the tracking branch.
@@ -40,10 +41,123 @@ def CountCommitsToBranch():
"""
pipe = [['git', 'log', '--no-color', '--oneline', '@{upstream}..'],
['wc', '-l']]
- stdout = command.RunPipe(pipe, capture=True, oneline=True)
+ stdout = command.RunPipe(pipe, capture=True, oneline=True).stdout
patch_count = int(stdout)
return patch_count
+def GetUpstream(git_dir, branch):
+ """Returns the name of the upstream for a branch
+
+ Args:
+ git_dir: Git directory containing repo
+ branch: Name of branch
+
+ Returns:
+ Name of upstream branch (e.g. 'upstream/master') or None if none
+ """
+ remote = command.OutputOneLine('git', '--git-dir', git_dir, 'config',
+ 'branch.%s.remote' % branch)
+ merge = command.OutputOneLine('git', '--git-dir', git_dir, 'config',
+ 'branch.%s.merge' % branch)
+ if remote == '.':
+ return merge
+ elif remote and merge:
+ leaf = merge.split('/')[-1]
+ return '%s/%s' % (remote, leaf)
+ else:
+ raise ValueError, ("Cannot determine upstream branch for branch "
+ "'%s' remote='%s', merge='%s'" % (branch, remote, merge))
+
+
+def GetRangeInBranch(git_dir, branch, include_upstream=False):
+ """Returns an expression for the commits in the given branch.
+
+ Args:
+ git_dir: Directory containing git repo
+ branch: Name of branch
+ Return:
+ Expression in the form 'upstream..branch' which can be used to
+ access the commits.
+ """
+ upstream = GetUpstream(git_dir, branch)
+ return '%s%s..%s' % (upstream, '~' if include_upstream else '', branch)
+
+def CountCommitsInBranch(git_dir, branch, include_upstream=False):
+ """Returns the number of commits in the given branch.
+
+ Args:
+ git_dir: Directory containing git repo
+ branch: Name of branch
+ Return:
+ Number of patches that exist on top of the branch
+ """
+ range_expr = GetRangeInBranch(git_dir, branch, include_upstream)
+ pipe = [['git', '--git-dir', git_dir, 'log', '--oneline', range_expr],
+ ['wc', '-l']]
+ result = command.RunPipe(pipe, capture=True, oneline=True)
+ patch_count = int(result.stdout)
+ return patch_count
+
+def CountCommits(commit_range):
+ """Returns the number of commits in the given range.
+
+ Args:
+ commit_range: Range of commits to count (e.g. 'HEAD..base')
+ Return:
+ Number of patches that exist on top of the branch
+ """
+ pipe = [['git', 'log', '--oneline', commit_range],
+ ['wc', '-l']]
+ stdout = command.RunPipe(pipe, capture=True, oneline=True).stdout
+ patch_count = int(stdout)
+ return patch_count
+
+def Checkout(commit_hash, git_dir=None, work_tree=None, force=False):
+ """Checkout the selected commit for this build
+
+ Args:
+ commit_hash: Commit hash to check out
+ """
+ pipe = ['git']
+ if git_dir:
+ pipe.extend(['--git-dir', git_dir])
+ if work_tree:
+ pipe.extend(['--work-tree', work_tree])
+ pipe.append('checkout')
+ if force:
+ pipe.append('-f')
+ pipe.append(commit_hash)
+ result = command.RunPipe([pipe], capture=True, raise_on_error=False)
+ if result.return_code != 0:
+ raise OSError, 'git checkout (%s): %s' % (pipe, result.stderr)
+
+def Clone(git_dir, output_dir):
+ """Checkout the selected commit for this build
+
+ Args:
+ commit_hash: Commit hash to check out
+ """
+ pipe = ['git', 'clone', git_dir, '.']
+ result = command.RunPipe([pipe], capture=True, cwd=output_dir)
+ if result.return_code != 0:
+ raise OSError, 'git clone: %s' % result.stderr
+
+def Fetch(git_dir=None, work_tree=None):
+ """Fetch from the origin repo
+
+ Args:
+ commit_hash: Commit hash to check out
+ """
+ pipe = ['git']
+ if git_dir:
+ pipe.extend(['--git-dir', git_dir])
+ if work_tree:
+ pipe.extend(['--work-tree', work_tree])
+ pipe.append('fetch')
+ result = command.RunPipe([pipe], capture=True)
+ if result.return_code != 0:
+ raise OSError, 'git fetch: %s' % result.stderr
+
def CreatePatches(start, count, series):
"""Create a series of patches from the top of the current branch.
@@ -203,7 +317,7 @@ def BuildEmailList(in_list, tag=None, alias=None):
return result
def EmailPatches(series, cover_fname, args, dry_run, cc_fname,
- self_only=False, alias=None):
+ self_only=False, alias=None, in_reply_to=None):
"""Email a patch series.
Args:
@@ -213,6 +327,8 @@ def EmailPatches(series, cover_fname, args, dry_run, cc_fname,
dry_run: Just return the command that would be run
cc_fname: Filename of Cc file for per-commit Cc
self_only: True to just email to yourself as a test
+ in_reply_to: If set we'll pass this to git as --in-reply-to.
+ Should be a message ID that this is in reply to.
Returns:
Git command that was/would be run
@@ -262,6 +378,9 @@ def EmailPatches(series, cover_fname, args, dry_run, cc_fname,
to = BuildEmailList([os.getenv('USER')], '--to', alias)
cc = []
cmd = ['git', 'send-email', '--annotate']
+ if in_reply_to:
+ cmd.append('--in-reply-to="%s"' % in_reply_to)
+
cmd += to
cmd += cc
cmd += ['--cc-cmd', '"%s --cc-cmd %s"' % (sys.argv[0], cc_fname)]
@@ -359,7 +478,8 @@ def GetAliasFile():
Returns:
Filename of git alias file, or None if none
"""
- fname = command.OutputOneLine('git', 'config', 'sendemail.aliasesfile')
+ fname = command.OutputOneLine('git', 'config', 'sendemail.aliasesfile',
+ raise_on_error=False)
if fname:
fname = os.path.join(GetTopLevel(), fname.strip())
return fname
@@ -389,6 +509,14 @@ def Setup():
if alias_fname:
settings.ReadGitAliases(alias_fname)
+def GetHead():
+ """Get the hash of the current HEAD
+
+ Returns:
+ Hash of HEAD
+ """
+ return command.OutputOneLine('git', 'show', '-s', '--pretty=format:%H')
+
if __name__ == "__main__":
import doctest