blob: e2b4959d58b377dab1546a470df1fa6b07e89af7 [file] [log] [blame]
Simon Glass0d24de92012-01-14 15:12:45 +00001# Copyright (c) 2011 The Chromium OS Authors.
2#
Wolfgang Denk1a459662013-07-08 09:37:19 +02003# SPDX-License-Identifier: GPL-2.0+
Simon Glass0d24de92012-01-14 15:12:45 +00004#
5
6import command
7import re
8import os
9import series
Simon Glass0d24de92012-01-14 15:12:45 +000010import subprocess
11import sys
12import terminal
13
Simon Glass757f64a2014-04-20 10:50:13 -060014import checkpatch
Simon Glass5f6a1c42012-12-15 10:42:07 +000015import settings
16
Simon Glasse49f14a2014-08-09 15:33:11 -060017# True to use --no-decorate - we check this in Setup()
18use_no_decorate = True
19
Simon Glasscda2a612014-08-09 15:33:10 -060020def LogCmd(commit_range, git_dir=None, oneline=False, reverse=False,
21 count=None):
22 """Create a command to perform a 'git log'
23
24 Args:
25 commit_range: Range expression to use for log, None for none
26 git_dir: Path to git repositiory (None to use default)
27 oneline: True to use --oneline, else False
28 reverse: True to reverse the log (--reverse)
29 count: Number of commits to list, or None for no limit
30 Return:
31 List containing command and arguments to run
32 """
33 cmd = ['git']
34 if git_dir:
35 cmd += ['--git-dir', git_dir]
36 cmd += ['log', '--no-color']
37 if oneline:
38 cmd.append('--oneline')
Simon Glasse49f14a2014-08-09 15:33:11 -060039 if use_no_decorate:
40 cmd.append('--no-decorate')
Simon Glass042a7322014-08-14 21:59:11 -060041 if reverse:
42 cmd.append('--reverse')
Simon Glasscda2a612014-08-09 15:33:10 -060043 if count is not None:
44 cmd.append('-n%d' % count)
45 if commit_range:
46 cmd.append(commit_range)
47 return cmd
Simon Glass0d24de92012-01-14 15:12:45 +000048
49def CountCommitsToBranch():
50 """Returns number of commits between HEAD and the tracking branch.
51
52 This looks back to the tracking branch and works out the number of commits
53 since then.
54
55 Return:
56 Number of patches that exist on top of the branch
57 """
Simon Glasscda2a612014-08-09 15:33:10 -060058 pipe = [LogCmd('@{upstream}..', oneline=True),
Simon Glass0d24de92012-01-14 15:12:45 +000059 ['wc', '-l']]
Simon Glassa10fd932012-12-15 10:42:04 +000060 stdout = command.RunPipe(pipe, capture=True, oneline=True).stdout
Simon Glass0d24de92012-01-14 15:12:45 +000061 patch_count = int(stdout)
62 return patch_count
63
Simon Glass5f6a1c42012-12-15 10:42:07 +000064def GetUpstream(git_dir, branch):
65 """Returns the name of the upstream for a branch
66
67 Args:
68 git_dir: Git directory containing repo
69 branch: Name of branch
70
71 Returns:
72 Name of upstream branch (e.g. 'upstream/master') or None if none
73 """
Simon Glasscce717a2013-05-08 08:06:08 +000074 try:
75 remote = command.OutputOneLine('git', '--git-dir', git_dir, 'config',
76 'branch.%s.remote' % branch)
77 merge = command.OutputOneLine('git', '--git-dir', git_dir, 'config',
78 'branch.%s.merge' % branch)
79 except:
80 return None
81
Simon Glass5f6a1c42012-12-15 10:42:07 +000082 if remote == '.':
83 return merge
84 elif remote and merge:
85 leaf = merge.split('/')[-1]
86 return '%s/%s' % (remote, leaf)
87 else:
88 raise ValueError, ("Cannot determine upstream branch for branch "
89 "'%s' remote='%s', merge='%s'" % (branch, remote, merge))
90
91
92def GetRangeInBranch(git_dir, branch, include_upstream=False):
93 """Returns an expression for the commits in the given branch.
94
95 Args:
96 git_dir: Directory containing git repo
97 branch: Name of branch
98 Return:
99 Expression in the form 'upstream..branch' which can be used to
Simon Glasscce717a2013-05-08 08:06:08 +0000100 access the commits. If the branch does not exist, returns None.
Simon Glass5f6a1c42012-12-15 10:42:07 +0000101 """
102 upstream = GetUpstream(git_dir, branch)
Simon Glasscce717a2013-05-08 08:06:08 +0000103 if not upstream:
104 return None
Simon Glass5f6a1c42012-12-15 10:42:07 +0000105 return '%s%s..%s' % (upstream, '~' if include_upstream else '', branch)
106
107def CountCommitsInBranch(git_dir, branch, include_upstream=False):
108 """Returns the number of commits in the given branch.
109
110 Args:
111 git_dir: Directory containing git repo
112 branch: Name of branch
113 Return:
Simon Glasscce717a2013-05-08 08:06:08 +0000114 Number of patches that exist on top of the branch, or None if the
115 branch does not exist.
Simon Glass5f6a1c42012-12-15 10:42:07 +0000116 """
117 range_expr = GetRangeInBranch(git_dir, branch, include_upstream)
Simon Glasscce717a2013-05-08 08:06:08 +0000118 if not range_expr:
119 return None
Simon Glasscda2a612014-08-09 15:33:10 -0600120 pipe = [LogCmd(range_expr, git_dir=git_dir, oneline=True),
Simon Glass5f6a1c42012-12-15 10:42:07 +0000121 ['wc', '-l']]
122 result = command.RunPipe(pipe, capture=True, oneline=True)
123 patch_count = int(result.stdout)
124 return patch_count
125
126def CountCommits(commit_range):
127 """Returns the number of commits in the given range.
128
129 Args:
130 commit_range: Range of commits to count (e.g. 'HEAD..base')
131 Return:
132 Number of patches that exist on top of the branch
133 """
Simon Glasscda2a612014-08-09 15:33:10 -0600134 pipe = [LogCmd(commit_range, oneline=True),
Simon Glass5f6a1c42012-12-15 10:42:07 +0000135 ['wc', '-l']]
136 stdout = command.RunPipe(pipe, capture=True, oneline=True).stdout
137 patch_count = int(stdout)
138 return patch_count
139
140def Checkout(commit_hash, git_dir=None, work_tree=None, force=False):
141 """Checkout the selected commit for this build
142
143 Args:
144 commit_hash: Commit hash to check out
145 """
146 pipe = ['git']
147 if git_dir:
148 pipe.extend(['--git-dir', git_dir])
149 if work_tree:
150 pipe.extend(['--work-tree', work_tree])
151 pipe.append('checkout')
152 if force:
153 pipe.append('-f')
154 pipe.append(commit_hash)
155 result = command.RunPipe([pipe], capture=True, raise_on_error=False)
156 if result.return_code != 0:
157 raise OSError, 'git checkout (%s): %s' % (pipe, result.stderr)
158
159def Clone(git_dir, output_dir):
160 """Checkout the selected commit for this build
161
162 Args:
163 commit_hash: Commit hash to check out
164 """
165 pipe = ['git', 'clone', git_dir, '.']
166 result = command.RunPipe([pipe], capture=True, cwd=output_dir)
167 if result.return_code != 0:
168 raise OSError, 'git clone: %s' % result.stderr
169
170def Fetch(git_dir=None, work_tree=None):
171 """Fetch from the origin repo
172
173 Args:
174 commit_hash: Commit hash to check out
175 """
176 pipe = ['git']
177 if git_dir:
178 pipe.extend(['--git-dir', git_dir])
179 if work_tree:
180 pipe.extend(['--work-tree', work_tree])
181 pipe.append('fetch')
182 result = command.RunPipe([pipe], capture=True)
183 if result.return_code != 0:
184 raise OSError, 'git fetch: %s' % result.stderr
185
Simon Glass0d24de92012-01-14 15:12:45 +0000186def CreatePatches(start, count, series):
187 """Create a series of patches from the top of the current branch.
188
189 The patch files are written to the current directory using
190 git format-patch.
191
192 Args:
193 start: Commit to start from: 0=HEAD, 1=next one, etc.
194 count: number of commits to include
195 Return:
196 Filename of cover letter
197 List of filenames of patch files
198 """
199 if series.get('version'):
200 version = '%s ' % series['version']
201 cmd = ['git', 'format-patch', '-M', '--signoff']
202 if series.get('cover'):
203 cmd.append('--cover-letter')
204 prefix = series.GetPatchPrefix()
205 if prefix:
206 cmd += ['--subject-prefix=%s' % prefix]
207 cmd += ['HEAD~%d..HEAD~%d' % (start + count, start)]
208
209 stdout = command.RunList(cmd)
210 files = stdout.splitlines()
211
212 # We have an extra file if there is a cover letter
213 if series.get('cover'):
214 return files[0], files[1:]
215 else:
216 return None, files
217
218def ApplyPatch(verbose, fname):
219 """Apply a patch with git am to test it
220
221 TODO: Convert these to use command, with stderr option
222
223 Args:
224 fname: filename of patch file to apply
225 """
Simon Glass757f64a2014-04-20 10:50:13 -0600226 col = terminal.Color()
Simon Glass0d24de92012-01-14 15:12:45 +0000227 cmd = ['git', 'am', fname]
228 pipe = subprocess.Popen(cmd, stdout=subprocess.PIPE,
229 stderr=subprocess.PIPE)
230 stdout, stderr = pipe.communicate()
231 re_error = re.compile('^error: patch failed: (.+):(\d+)')
232 for line in stderr.splitlines():
233 if verbose:
234 print line
235 match = re_error.match(line)
236 if match:
Simon Glass757f64a2014-04-20 10:50:13 -0600237 print checkpatch.GetWarningMsg(col, 'warning', match.group(1),
238 int(match.group(2)), 'Patch failed')
Simon Glass0d24de92012-01-14 15:12:45 +0000239 return pipe.returncode == 0, stdout
240
241def ApplyPatches(verbose, args, start_point):
242 """Apply the patches with git am to make sure all is well
243
244 Args:
245 verbose: Print out 'git am' output verbatim
246 args: List of patch files to apply
247 start_point: Number of commits back from HEAD to start applying.
248 Normally this is len(args), but it can be larger if a start
249 offset was given.
250 """
251 error_count = 0
252 col = terminal.Color()
253
254 # Figure out our current position
255 cmd = ['git', 'name-rev', 'HEAD', '--name-only']
256 pipe = subprocess.Popen(cmd, stdout=subprocess.PIPE)
257 stdout, stderr = pipe.communicate()
258 if pipe.returncode:
259 str = 'Could not find current commit name'
260 print col.Color(col.RED, str)
261 print stdout
262 return False
263 old_head = stdout.splitlines()[0]
Simon Glass42519782014-06-11 23:27:09 -0600264 if old_head == 'undefined':
265 str = "Invalid HEAD '%s'" % stdout.strip()
266 print col.Color(col.RED, str)
267 return False
Simon Glass0d24de92012-01-14 15:12:45 +0000268
269 # Checkout the required start point
270 cmd = ['git', 'checkout', 'HEAD~%d' % start_point]
271 pipe = subprocess.Popen(cmd, stdout=subprocess.PIPE,
272 stderr=subprocess.PIPE)
273 stdout, stderr = pipe.communicate()
274 if pipe.returncode:
275 str = 'Could not move to commit before patch series'
276 print col.Color(col.RED, str)
277 print stdout, stderr
278 return False
279
280 # Apply all the patches
281 for fname in args:
282 ok, stdout = ApplyPatch(verbose, fname)
283 if not ok:
284 print col.Color(col.RED, 'git am returned errors for %s: will '
285 'skip this patch' % fname)
286 if verbose:
287 print stdout
288 error_count += 1
289 cmd = ['git', 'am', '--skip']
290 pipe = subprocess.Popen(cmd, stdout=subprocess.PIPE)
291 stdout, stderr = pipe.communicate()
292 if pipe.returncode != 0:
293 print col.Color(col.RED, 'Unable to skip patch! Aborting...')
294 print stdout
295 break
296
297 # Return to our previous position
298 cmd = ['git', 'checkout', old_head]
299 pipe = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
300 stdout, stderr = pipe.communicate()
301 if pipe.returncode:
302 print col.Color(col.RED, 'Could not move back to head commit')
303 print stdout, stderr
304 return error_count == 0
305
Simon Glassa1318f72013-03-26 13:09:42 +0000306def BuildEmailList(in_list, tag=None, alias=None, raise_on_error=True):
Simon Glass0d24de92012-01-14 15:12:45 +0000307 """Build a list of email addresses based on an input list.
308
309 Takes a list of email addresses and aliases, and turns this into a list
310 of only email address, by resolving any aliases that are present.
311
312 If the tag is given, then each email address is prepended with this
313 tag and a space. If the tag starts with a minus sign (indicating a
314 command line parameter) then the email address is quoted.
315
316 Args:
317 in_list: List of aliases/email addresses
318 tag: Text to put before each address
Simon Glassa1318f72013-03-26 13:09:42 +0000319 alias: Alias dictionary
320 raise_on_error: True to raise an error when an alias fails to match,
321 False to just print a message.
Simon Glass0d24de92012-01-14 15:12:45 +0000322
323 Returns:
324 List of email addresses
325
326 >>> alias = {}
327 >>> alias['fred'] = ['f.bloggs@napier.co.nz']
328 >>> alias['john'] = ['j.bloggs@napier.co.nz']
329 >>> alias['mary'] = ['Mary Poppins <m.poppins@cloud.net>']
330 >>> alias['boys'] = ['fred', ' john']
331 >>> alias['all'] = ['fred ', 'john', ' mary ']
332 >>> BuildEmailList(['john', 'mary'], None, alias)
333 ['j.bloggs@napier.co.nz', 'Mary Poppins <m.poppins@cloud.net>']
334 >>> BuildEmailList(['john', 'mary'], '--to', alias)
335 ['--to "j.bloggs@napier.co.nz"', \
336'--to "Mary Poppins <m.poppins@cloud.net>"']
337 >>> BuildEmailList(['john', 'mary'], 'Cc', alias)
338 ['Cc j.bloggs@napier.co.nz', 'Cc Mary Poppins <m.poppins@cloud.net>']
339 """
340 quote = '"' if tag and tag[0] == '-' else ''
341 raw = []
342 for item in in_list:
Simon Glassa1318f72013-03-26 13:09:42 +0000343 raw += LookupEmail(item, alias, raise_on_error=raise_on_error)
Simon Glass0d24de92012-01-14 15:12:45 +0000344 result = []
345 for item in raw:
346 if not item in result:
347 result.append(item)
348 if tag:
349 return ['%s %s%s%s' % (tag, quote, email, quote) for email in result]
350 return result
351
Simon Glassa1318f72013-03-26 13:09:42 +0000352def EmailPatches(series, cover_fname, args, dry_run, raise_on_error, cc_fname,
Doug Anderson6d819922013-03-17 10:31:04 +0000353 self_only=False, alias=None, in_reply_to=None):
Simon Glass0d24de92012-01-14 15:12:45 +0000354 """Email a patch series.
355
356 Args:
357 series: Series object containing destination info
358 cover_fname: filename of cover letter
359 args: list of filenames of patch files
360 dry_run: Just return the command that would be run
Simon Glassa1318f72013-03-26 13:09:42 +0000361 raise_on_error: True to raise an error when an alias fails to match,
362 False to just print a message.
Simon Glass0d24de92012-01-14 15:12:45 +0000363 cc_fname: Filename of Cc file for per-commit Cc
364 self_only: True to just email to yourself as a test
Doug Anderson6d819922013-03-17 10:31:04 +0000365 in_reply_to: If set we'll pass this to git as --in-reply-to.
366 Should be a message ID that this is in reply to.
Simon Glass0d24de92012-01-14 15:12:45 +0000367
368 Returns:
369 Git command that was/would be run
370
Doug Andersona9700482012-11-26 15:21:40 +0000371 # For the duration of this doctest pretend that we ran patman with ./patman
372 >>> _old_argv0 = sys.argv[0]
373 >>> sys.argv[0] = './patman'
374
Simon Glass0d24de92012-01-14 15:12:45 +0000375 >>> alias = {}
376 >>> alias['fred'] = ['f.bloggs@napier.co.nz']
377 >>> alias['john'] = ['j.bloggs@napier.co.nz']
378 >>> alias['mary'] = ['m.poppins@cloud.net']
379 >>> alias['boys'] = ['fred', ' john']
380 >>> alias['all'] = ['fred ', 'john', ' mary ']
381 >>> alias[os.getenv('USER')] = ['this-is-me@me.com']
382 >>> series = series.Series()
383 >>> series.to = ['fred']
384 >>> series.cc = ['mary']
Simon Glassa1318f72013-03-26 13:09:42 +0000385 >>> EmailPatches(series, 'cover', ['p1', 'p2'], True, True, 'cc-fname', \
386 False, alias)
Simon Glass0d24de92012-01-14 15:12:45 +0000387 'git send-email --annotate --to "f.bloggs@napier.co.nz" --cc \
388"m.poppins@cloud.net" --cc-cmd "./patman --cc-cmd cc-fname" cover p1 p2'
Simon Glassa1318f72013-03-26 13:09:42 +0000389 >>> EmailPatches(series, None, ['p1'], True, True, 'cc-fname', False, \
390 alias)
Simon Glass0d24de92012-01-14 15:12:45 +0000391 'git send-email --annotate --to "f.bloggs@napier.co.nz" --cc \
392"m.poppins@cloud.net" --cc-cmd "./patman --cc-cmd cc-fname" p1'
393 >>> series.cc = ['all']
Simon Glassa1318f72013-03-26 13:09:42 +0000394 >>> EmailPatches(series, 'cover', ['p1', 'p2'], True, True, 'cc-fname', \
395 True, alias)
Simon Glass0d24de92012-01-14 15:12:45 +0000396 'git send-email --annotate --to "this-is-me@me.com" --cc-cmd "./patman \
397--cc-cmd cc-fname" cover p1 p2'
Simon Glassa1318f72013-03-26 13:09:42 +0000398 >>> EmailPatches(series, 'cover', ['p1', 'p2'], True, True, 'cc-fname', \
399 False, alias)
Simon Glass0d24de92012-01-14 15:12:45 +0000400 'git send-email --annotate --to "f.bloggs@napier.co.nz" --cc \
401"f.bloggs@napier.co.nz" --cc "j.bloggs@napier.co.nz" --cc \
402"m.poppins@cloud.net" --cc-cmd "./patman --cc-cmd cc-fname" cover p1 p2'
Doug Andersona9700482012-11-26 15:21:40 +0000403
404 # Restore argv[0] since we clobbered it.
405 >>> sys.argv[0] = _old_argv0
Simon Glass0d24de92012-01-14 15:12:45 +0000406 """
Simon Glassa1318f72013-03-26 13:09:42 +0000407 to = BuildEmailList(series.get('to'), '--to', alias, raise_on_error)
Simon Glass0d24de92012-01-14 15:12:45 +0000408 if not to:
Masahiro Yamadaee860c62014-07-18 14:23:20 +0900409 git_config_to = command.Output('git', 'config', 'sendemail.to')
410 if not git_config_to:
411 print ("No recipient.\n"
412 "Please add something like this to a commit\n"
413 "Series-to: Fred Bloggs <f.blogs@napier.co.nz>\n"
414 "Or do something like this\n"
415 "git config sendemail.to u-boot@lists.denx.de")
416 return
Simon Glassa1318f72013-03-26 13:09:42 +0000417 cc = BuildEmailList(series.get('cc'), '--cc', alias, raise_on_error)
Simon Glass0d24de92012-01-14 15:12:45 +0000418 if self_only:
Simon Glassa1318f72013-03-26 13:09:42 +0000419 to = BuildEmailList([os.getenv('USER')], '--to', alias, raise_on_error)
Simon Glass0d24de92012-01-14 15:12:45 +0000420 cc = []
421 cmd = ['git', 'send-email', '--annotate']
Doug Anderson6d819922013-03-17 10:31:04 +0000422 if in_reply_to:
423 cmd.append('--in-reply-to="%s"' % in_reply_to)
424
Simon Glass0d24de92012-01-14 15:12:45 +0000425 cmd += to
426 cmd += cc
427 cmd += ['--cc-cmd', '"%s --cc-cmd %s"' % (sys.argv[0], cc_fname)]
428 if cover_fname:
429 cmd.append(cover_fname)
430 cmd += args
431 str = ' '.join(cmd)
432 if not dry_run:
433 os.system(str)
434 return str
435
436
Simon Glassa1318f72013-03-26 13:09:42 +0000437def LookupEmail(lookup_name, alias=None, raise_on_error=True, level=0):
Simon Glass0d24de92012-01-14 15:12:45 +0000438 """If an email address is an alias, look it up and return the full name
439
440 TODO: Why not just use git's own alias feature?
441
442 Args:
443 lookup_name: Alias or email address to look up
Simon Glassa1318f72013-03-26 13:09:42 +0000444 alias: Dictionary containing aliases (None to use settings default)
445 raise_on_error: True to raise an error when an alias fails to match,
446 False to just print a message.
Simon Glass0d24de92012-01-14 15:12:45 +0000447
448 Returns:
449 tuple:
450 list containing a list of email addresses
451
452 Raises:
453 OSError if a recursive alias reference was found
454 ValueError if an alias was not found
455
456 >>> alias = {}
457 >>> alias['fred'] = ['f.bloggs@napier.co.nz']
458 >>> alias['john'] = ['j.bloggs@napier.co.nz']
459 >>> alias['mary'] = ['m.poppins@cloud.net']
460 >>> alias['boys'] = ['fred', ' john', 'f.bloggs@napier.co.nz']
461 >>> alias['all'] = ['fred ', 'john', ' mary ']
462 >>> alias['loop'] = ['other', 'john', ' mary ']
463 >>> alias['other'] = ['loop', 'john', ' mary ']
464 >>> LookupEmail('mary', alias)
465 ['m.poppins@cloud.net']
466 >>> LookupEmail('arthur.wellesley@howe.ro.uk', alias)
467 ['arthur.wellesley@howe.ro.uk']
468 >>> LookupEmail('boys', alias)
469 ['f.bloggs@napier.co.nz', 'j.bloggs@napier.co.nz']
470 >>> LookupEmail('all', alias)
471 ['f.bloggs@napier.co.nz', 'j.bloggs@napier.co.nz', 'm.poppins@cloud.net']
472 >>> LookupEmail('odd', alias)
473 Traceback (most recent call last):
474 ...
475 ValueError: Alias 'odd' not found
476 >>> LookupEmail('loop', alias)
477 Traceback (most recent call last):
478 ...
479 OSError: Recursive email alias at 'other'
Simon Glassa1318f72013-03-26 13:09:42 +0000480 >>> LookupEmail('odd', alias, raise_on_error=False)
481 \033[1;31mAlias 'odd' not found\033[0m
482 []
483 >>> # In this case the loop part will effectively be ignored.
484 >>> LookupEmail('loop', alias, raise_on_error=False)
485 \033[1;31mRecursive email alias at 'other'\033[0m
486 \033[1;31mRecursive email alias at 'john'\033[0m
487 \033[1;31mRecursive email alias at 'mary'\033[0m
488 ['j.bloggs@napier.co.nz', 'm.poppins@cloud.net']
Simon Glass0d24de92012-01-14 15:12:45 +0000489 """
490 if not alias:
491 alias = settings.alias
492 lookup_name = lookup_name.strip()
493 if '@' in lookup_name: # Perhaps a real email address
494 return [lookup_name]
495
496 lookup_name = lookup_name.lower()
Simon Glassa1318f72013-03-26 13:09:42 +0000497 col = terminal.Color()
Simon Glass0d24de92012-01-14 15:12:45 +0000498
499 out_list = []
Simon Glassa1318f72013-03-26 13:09:42 +0000500 if level > 10:
501 msg = "Recursive email alias at '%s'" % lookup_name
502 if raise_on_error:
503 raise OSError, msg
504 else:
505 print col.Color(col.RED, msg)
506 return out_list
507
Simon Glass0d24de92012-01-14 15:12:45 +0000508 if lookup_name:
509 if not lookup_name in alias:
Simon Glassa1318f72013-03-26 13:09:42 +0000510 msg = "Alias '%s' not found" % lookup_name
511 if raise_on_error:
512 raise ValueError, msg
513 else:
514 print col.Color(col.RED, msg)
515 return out_list
Simon Glass0d24de92012-01-14 15:12:45 +0000516 for item in alias[lookup_name]:
Simon Glassa1318f72013-03-26 13:09:42 +0000517 todo = LookupEmail(item, alias, raise_on_error, level + 1)
Simon Glass0d24de92012-01-14 15:12:45 +0000518 for new_item in todo:
519 if not new_item in out_list:
520 out_list.append(new_item)
521
522 #print "No match for alias '%s'" % lookup_name
523 return out_list
524
525def GetTopLevel():
526 """Return name of top-level directory for this git repo.
527
528 Returns:
529 Full path to git top-level directory
530
531 This test makes sure that we are running tests in the right subdir
532
Doug Andersona9700482012-11-26 15:21:40 +0000533 >>> os.path.realpath(os.path.dirname(__file__)) == \
534 os.path.join(GetTopLevel(), 'tools', 'patman')
Simon Glass0d24de92012-01-14 15:12:45 +0000535 True
536 """
537 return command.OutputOneLine('git', 'rev-parse', '--show-toplevel')
538
539def GetAliasFile():
540 """Gets the name of the git alias file.
541
542 Returns:
543 Filename of git alias file, or None if none
544 """
Simon Glassdc191502012-12-15 10:42:05 +0000545 fname = command.OutputOneLine('git', 'config', 'sendemail.aliasesfile',
546 raise_on_error=False)
Simon Glass0d24de92012-01-14 15:12:45 +0000547 if fname:
548 fname = os.path.join(GetTopLevel(), fname.strip())
549 return fname
550
Vikram Narayanan87d65552012-05-23 09:01:06 +0000551def GetDefaultUserName():
552 """Gets the user.name from .gitconfig file.
553
554 Returns:
555 User name found in .gitconfig file, or None if none
556 """
557 uname = command.OutputOneLine('git', 'config', '--global', 'user.name')
558 return uname
559
560def GetDefaultUserEmail():
561 """Gets the user.email from the global .gitconfig file.
562
563 Returns:
564 User's email found in .gitconfig file, or None if none
565 """
566 uemail = command.OutputOneLine('git', 'config', '--global', 'user.email')
567 return uemail
568
Simon Glass0d24de92012-01-14 15:12:45 +0000569def Setup():
570 """Set up git utils, by reading the alias files."""
Simon Glass0d24de92012-01-14 15:12:45 +0000571 # Check for a git alias file also
572 alias_fname = GetAliasFile()
573 if alias_fname:
574 settings.ReadGitAliases(alias_fname)
Simon Glasse49f14a2014-08-09 15:33:11 -0600575 cmd = LogCmd(None, count=0)
576 use_no_decorate = (command.RunPipe([cmd], raise_on_error=False)
577 .return_code == 0)
Simon Glass0d24de92012-01-14 15:12:45 +0000578
Simon Glass5f6a1c42012-12-15 10:42:07 +0000579def GetHead():
580 """Get the hash of the current HEAD
581
582 Returns:
583 Hash of HEAD
584 """
585 return command.OutputOneLine('git', 'show', '-s', '--pretty=format:%H')
586
Simon Glass0d24de92012-01-14 15:12:45 +0000587if __name__ == "__main__":
588 import doctest
589
590 doctest.testmod()