1*0d24de9dSSimon Glass# Copyright (c) 2011 The Chromium OS Authors. 2*0d24de9dSSimon Glass# 3*0d24de9dSSimon Glass# See file CREDITS for list of people who contributed to this 4*0d24de9dSSimon Glass# project. 5*0d24de9dSSimon Glass# 6*0d24de9dSSimon Glass# This program is free software; you can redistribute it and/or 7*0d24de9dSSimon Glass# modify it under the terms of the GNU General Public License as 8*0d24de9dSSimon Glass# published by the Free Software Foundation; either version 2 of 9*0d24de9dSSimon Glass# the License, or (at your option) any later version. 10*0d24de9dSSimon Glass# 11*0d24de9dSSimon Glass# This program is distributed in the hope that it will be useful, 12*0d24de9dSSimon Glass# but WITHOUT ANY WARRANTY; without even the implied warranty of 13*0d24de9dSSimon Glass# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14*0d24de9dSSimon Glass# GNU General Public License for more details. 15*0d24de9dSSimon Glass# 16*0d24de9dSSimon Glass# You should have received a copy of the GNU General Public License 17*0d24de9dSSimon Glass# along with this program; if not, write to the Free Software 18*0d24de9dSSimon Glass# Foundation, Inc., 59 Temple Place, Suite 330, Boston, 19*0d24de9dSSimon Glass# MA 02111-1307 USA 20*0d24de9dSSimon Glass# 21*0d24de9dSSimon Glass 22*0d24de9dSSimon Glassimport command 23*0d24de9dSSimon Glassimport re 24*0d24de9dSSimon Glassimport os 25*0d24de9dSSimon Glassimport series 26*0d24de9dSSimon Glassimport settings 27*0d24de9dSSimon Glassimport subprocess 28*0d24de9dSSimon Glassimport sys 29*0d24de9dSSimon Glassimport terminal 30*0d24de9dSSimon Glass 31*0d24de9dSSimon Glass 32*0d24de9dSSimon Glassdef CountCommitsToBranch(): 33*0d24de9dSSimon Glass """Returns number of commits between HEAD and the tracking branch. 34*0d24de9dSSimon Glass 35*0d24de9dSSimon Glass This looks back to the tracking branch and works out the number of commits 36*0d24de9dSSimon Glass since then. 37*0d24de9dSSimon Glass 38*0d24de9dSSimon Glass Return: 39*0d24de9dSSimon Glass Number of patches that exist on top of the branch 40*0d24de9dSSimon Glass """ 41*0d24de9dSSimon Glass pipe = [['git', 'log', '--oneline', '@{upstream}..'], 42*0d24de9dSSimon Glass ['wc', '-l']] 43*0d24de9dSSimon Glass stdout = command.RunPipe(pipe, capture=True, oneline=True) 44*0d24de9dSSimon Glass patch_count = int(stdout) 45*0d24de9dSSimon Glass return patch_count 46*0d24de9dSSimon Glass 47*0d24de9dSSimon Glassdef CreatePatches(start, count, series): 48*0d24de9dSSimon Glass """Create a series of patches from the top of the current branch. 49*0d24de9dSSimon Glass 50*0d24de9dSSimon Glass The patch files are written to the current directory using 51*0d24de9dSSimon Glass git format-patch. 52*0d24de9dSSimon Glass 53*0d24de9dSSimon Glass Args: 54*0d24de9dSSimon Glass start: Commit to start from: 0=HEAD, 1=next one, etc. 55*0d24de9dSSimon Glass count: number of commits to include 56*0d24de9dSSimon Glass Return: 57*0d24de9dSSimon Glass Filename of cover letter 58*0d24de9dSSimon Glass List of filenames of patch files 59*0d24de9dSSimon Glass """ 60*0d24de9dSSimon Glass if series.get('version'): 61*0d24de9dSSimon Glass version = '%s ' % series['version'] 62*0d24de9dSSimon Glass cmd = ['git', 'format-patch', '-M', '--signoff'] 63*0d24de9dSSimon Glass if series.get('cover'): 64*0d24de9dSSimon Glass cmd.append('--cover-letter') 65*0d24de9dSSimon Glass prefix = series.GetPatchPrefix() 66*0d24de9dSSimon Glass if prefix: 67*0d24de9dSSimon Glass cmd += ['--subject-prefix=%s' % prefix] 68*0d24de9dSSimon Glass cmd += ['HEAD~%d..HEAD~%d' % (start + count, start)] 69*0d24de9dSSimon Glass 70*0d24de9dSSimon Glass stdout = command.RunList(cmd) 71*0d24de9dSSimon Glass files = stdout.splitlines() 72*0d24de9dSSimon Glass 73*0d24de9dSSimon Glass # We have an extra file if there is a cover letter 74*0d24de9dSSimon Glass if series.get('cover'): 75*0d24de9dSSimon Glass return files[0], files[1:] 76*0d24de9dSSimon Glass else: 77*0d24de9dSSimon Glass return None, files 78*0d24de9dSSimon Glass 79*0d24de9dSSimon Glassdef ApplyPatch(verbose, fname): 80*0d24de9dSSimon Glass """Apply a patch with git am to test it 81*0d24de9dSSimon Glass 82*0d24de9dSSimon Glass TODO: Convert these to use command, with stderr option 83*0d24de9dSSimon Glass 84*0d24de9dSSimon Glass Args: 85*0d24de9dSSimon Glass fname: filename of patch file to apply 86*0d24de9dSSimon Glass """ 87*0d24de9dSSimon Glass cmd = ['git', 'am', fname] 88*0d24de9dSSimon Glass pipe = subprocess.Popen(cmd, stdout=subprocess.PIPE, 89*0d24de9dSSimon Glass stderr=subprocess.PIPE) 90*0d24de9dSSimon Glass stdout, stderr = pipe.communicate() 91*0d24de9dSSimon Glass re_error = re.compile('^error: patch failed: (.+):(\d+)') 92*0d24de9dSSimon Glass for line in stderr.splitlines(): 93*0d24de9dSSimon Glass if verbose: 94*0d24de9dSSimon Glass print line 95*0d24de9dSSimon Glass match = re_error.match(line) 96*0d24de9dSSimon Glass if match: 97*0d24de9dSSimon Glass print GetWarningMsg('warning', match.group(1), int(match.group(2)), 98*0d24de9dSSimon Glass 'Patch failed') 99*0d24de9dSSimon Glass return pipe.returncode == 0, stdout 100*0d24de9dSSimon Glass 101*0d24de9dSSimon Glassdef ApplyPatches(verbose, args, start_point): 102*0d24de9dSSimon Glass """Apply the patches with git am to make sure all is well 103*0d24de9dSSimon Glass 104*0d24de9dSSimon Glass Args: 105*0d24de9dSSimon Glass verbose: Print out 'git am' output verbatim 106*0d24de9dSSimon Glass args: List of patch files to apply 107*0d24de9dSSimon Glass start_point: Number of commits back from HEAD to start applying. 108*0d24de9dSSimon Glass Normally this is len(args), but it can be larger if a start 109*0d24de9dSSimon Glass offset was given. 110*0d24de9dSSimon Glass """ 111*0d24de9dSSimon Glass error_count = 0 112*0d24de9dSSimon Glass col = terminal.Color() 113*0d24de9dSSimon Glass 114*0d24de9dSSimon Glass # Figure out our current position 115*0d24de9dSSimon Glass cmd = ['git', 'name-rev', 'HEAD', '--name-only'] 116*0d24de9dSSimon Glass pipe = subprocess.Popen(cmd, stdout=subprocess.PIPE) 117*0d24de9dSSimon Glass stdout, stderr = pipe.communicate() 118*0d24de9dSSimon Glass if pipe.returncode: 119*0d24de9dSSimon Glass str = 'Could not find current commit name' 120*0d24de9dSSimon Glass print col.Color(col.RED, str) 121*0d24de9dSSimon Glass print stdout 122*0d24de9dSSimon Glass return False 123*0d24de9dSSimon Glass old_head = stdout.splitlines()[0] 124*0d24de9dSSimon Glass 125*0d24de9dSSimon Glass # Checkout the required start point 126*0d24de9dSSimon Glass cmd = ['git', 'checkout', 'HEAD~%d' % start_point] 127*0d24de9dSSimon Glass pipe = subprocess.Popen(cmd, stdout=subprocess.PIPE, 128*0d24de9dSSimon Glass stderr=subprocess.PIPE) 129*0d24de9dSSimon Glass stdout, stderr = pipe.communicate() 130*0d24de9dSSimon Glass if pipe.returncode: 131*0d24de9dSSimon Glass str = 'Could not move to commit before patch series' 132*0d24de9dSSimon Glass print col.Color(col.RED, str) 133*0d24de9dSSimon Glass print stdout, stderr 134*0d24de9dSSimon Glass return False 135*0d24de9dSSimon Glass 136*0d24de9dSSimon Glass # Apply all the patches 137*0d24de9dSSimon Glass for fname in args: 138*0d24de9dSSimon Glass ok, stdout = ApplyPatch(verbose, fname) 139*0d24de9dSSimon Glass if not ok: 140*0d24de9dSSimon Glass print col.Color(col.RED, 'git am returned errors for %s: will ' 141*0d24de9dSSimon Glass 'skip this patch' % fname) 142*0d24de9dSSimon Glass if verbose: 143*0d24de9dSSimon Glass print stdout 144*0d24de9dSSimon Glass error_count += 1 145*0d24de9dSSimon Glass cmd = ['git', 'am', '--skip'] 146*0d24de9dSSimon Glass pipe = subprocess.Popen(cmd, stdout=subprocess.PIPE) 147*0d24de9dSSimon Glass stdout, stderr = pipe.communicate() 148*0d24de9dSSimon Glass if pipe.returncode != 0: 149*0d24de9dSSimon Glass print col.Color(col.RED, 'Unable to skip patch! Aborting...') 150*0d24de9dSSimon Glass print stdout 151*0d24de9dSSimon Glass break 152*0d24de9dSSimon Glass 153*0d24de9dSSimon Glass # Return to our previous position 154*0d24de9dSSimon Glass cmd = ['git', 'checkout', old_head] 155*0d24de9dSSimon Glass pipe = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 156*0d24de9dSSimon Glass stdout, stderr = pipe.communicate() 157*0d24de9dSSimon Glass if pipe.returncode: 158*0d24de9dSSimon Glass print col.Color(col.RED, 'Could not move back to head commit') 159*0d24de9dSSimon Glass print stdout, stderr 160*0d24de9dSSimon Glass return error_count == 0 161*0d24de9dSSimon Glass 162*0d24de9dSSimon Glassdef BuildEmailList(in_list, tag=None, alias=None): 163*0d24de9dSSimon Glass """Build a list of email addresses based on an input list. 164*0d24de9dSSimon Glass 165*0d24de9dSSimon Glass Takes a list of email addresses and aliases, and turns this into a list 166*0d24de9dSSimon Glass of only email address, by resolving any aliases that are present. 167*0d24de9dSSimon Glass 168*0d24de9dSSimon Glass If the tag is given, then each email address is prepended with this 169*0d24de9dSSimon Glass tag and a space. If the tag starts with a minus sign (indicating a 170*0d24de9dSSimon Glass command line parameter) then the email address is quoted. 171*0d24de9dSSimon Glass 172*0d24de9dSSimon Glass Args: 173*0d24de9dSSimon Glass in_list: List of aliases/email addresses 174*0d24de9dSSimon Glass tag: Text to put before each address 175*0d24de9dSSimon Glass 176*0d24de9dSSimon Glass Returns: 177*0d24de9dSSimon Glass List of email addresses 178*0d24de9dSSimon Glass 179*0d24de9dSSimon Glass >>> alias = {} 180*0d24de9dSSimon Glass >>> alias['fred'] = ['f.bloggs@napier.co.nz'] 181*0d24de9dSSimon Glass >>> alias['john'] = ['j.bloggs@napier.co.nz'] 182*0d24de9dSSimon Glass >>> alias['mary'] = ['Mary Poppins <m.poppins@cloud.net>'] 183*0d24de9dSSimon Glass >>> alias['boys'] = ['fred', ' john'] 184*0d24de9dSSimon Glass >>> alias['all'] = ['fred ', 'john', ' mary '] 185*0d24de9dSSimon Glass >>> BuildEmailList(['john', 'mary'], None, alias) 186*0d24de9dSSimon Glass ['j.bloggs@napier.co.nz', 'Mary Poppins <m.poppins@cloud.net>'] 187*0d24de9dSSimon Glass >>> BuildEmailList(['john', 'mary'], '--to', alias) 188*0d24de9dSSimon Glass ['--to "j.bloggs@napier.co.nz"', \ 189*0d24de9dSSimon Glass'--to "Mary Poppins <m.poppins@cloud.net>"'] 190*0d24de9dSSimon Glass >>> BuildEmailList(['john', 'mary'], 'Cc', alias) 191*0d24de9dSSimon Glass ['Cc j.bloggs@napier.co.nz', 'Cc Mary Poppins <m.poppins@cloud.net>'] 192*0d24de9dSSimon Glass """ 193*0d24de9dSSimon Glass quote = '"' if tag and tag[0] == '-' else '' 194*0d24de9dSSimon Glass raw = [] 195*0d24de9dSSimon Glass for item in in_list: 196*0d24de9dSSimon Glass raw += LookupEmail(item, alias) 197*0d24de9dSSimon Glass result = [] 198*0d24de9dSSimon Glass for item in raw: 199*0d24de9dSSimon Glass if not item in result: 200*0d24de9dSSimon Glass result.append(item) 201*0d24de9dSSimon Glass if tag: 202*0d24de9dSSimon Glass return ['%s %s%s%s' % (tag, quote, email, quote) for email in result] 203*0d24de9dSSimon Glass return result 204*0d24de9dSSimon Glass 205*0d24de9dSSimon Glassdef EmailPatches(series, cover_fname, args, dry_run, cc_fname, 206*0d24de9dSSimon Glass self_only=False, alias=None): 207*0d24de9dSSimon Glass """Email a patch series. 208*0d24de9dSSimon Glass 209*0d24de9dSSimon Glass Args: 210*0d24de9dSSimon Glass series: Series object containing destination info 211*0d24de9dSSimon Glass cover_fname: filename of cover letter 212*0d24de9dSSimon Glass args: list of filenames of patch files 213*0d24de9dSSimon Glass dry_run: Just return the command that would be run 214*0d24de9dSSimon Glass cc_fname: Filename of Cc file for per-commit Cc 215*0d24de9dSSimon Glass self_only: True to just email to yourself as a test 216*0d24de9dSSimon Glass 217*0d24de9dSSimon Glass Returns: 218*0d24de9dSSimon Glass Git command that was/would be run 219*0d24de9dSSimon Glass 220*0d24de9dSSimon Glass >>> alias = {} 221*0d24de9dSSimon Glass >>> alias['fred'] = ['f.bloggs@napier.co.nz'] 222*0d24de9dSSimon Glass >>> alias['john'] = ['j.bloggs@napier.co.nz'] 223*0d24de9dSSimon Glass >>> alias['mary'] = ['m.poppins@cloud.net'] 224*0d24de9dSSimon Glass >>> alias['boys'] = ['fred', ' john'] 225*0d24de9dSSimon Glass >>> alias['all'] = ['fred ', 'john', ' mary '] 226*0d24de9dSSimon Glass >>> alias[os.getenv('USER')] = ['this-is-me@me.com'] 227*0d24de9dSSimon Glass >>> series = series.Series() 228*0d24de9dSSimon Glass >>> series.to = ['fred'] 229*0d24de9dSSimon Glass >>> series.cc = ['mary'] 230*0d24de9dSSimon Glass >>> EmailPatches(series, 'cover', ['p1', 'p2'], True, 'cc-fname', False, \ 231*0d24de9dSSimon Glass alias) 232*0d24de9dSSimon Glass 'git send-email --annotate --to "f.bloggs@napier.co.nz" --cc \ 233*0d24de9dSSimon Glass"m.poppins@cloud.net" --cc-cmd "./patman --cc-cmd cc-fname" cover p1 p2' 234*0d24de9dSSimon Glass >>> EmailPatches(series, None, ['p1'], True, 'cc-fname', False, alias) 235*0d24de9dSSimon Glass 'git send-email --annotate --to "f.bloggs@napier.co.nz" --cc \ 236*0d24de9dSSimon Glass"m.poppins@cloud.net" --cc-cmd "./patman --cc-cmd cc-fname" p1' 237*0d24de9dSSimon Glass >>> series.cc = ['all'] 238*0d24de9dSSimon Glass >>> EmailPatches(series, 'cover', ['p1', 'p2'], True, 'cc-fname', True, \ 239*0d24de9dSSimon Glass alias) 240*0d24de9dSSimon Glass 'git send-email --annotate --to "this-is-me@me.com" --cc-cmd "./patman \ 241*0d24de9dSSimon Glass--cc-cmd cc-fname" cover p1 p2' 242*0d24de9dSSimon Glass >>> EmailPatches(series, 'cover', ['p1', 'p2'], True, 'cc-fname', False, \ 243*0d24de9dSSimon Glass alias) 244*0d24de9dSSimon Glass 'git send-email --annotate --to "f.bloggs@napier.co.nz" --cc \ 245*0d24de9dSSimon Glass"f.bloggs@napier.co.nz" --cc "j.bloggs@napier.co.nz" --cc \ 246*0d24de9dSSimon Glass"m.poppins@cloud.net" --cc-cmd "./patman --cc-cmd cc-fname" cover p1 p2' 247*0d24de9dSSimon Glass """ 248*0d24de9dSSimon Glass to = BuildEmailList(series.get('to'), '--to', alias) 249*0d24de9dSSimon Glass if not to: 250*0d24de9dSSimon Glass print ("No recipient, please add something like this to a commit\n" 251*0d24de9dSSimon Glass "Series-to: Fred Bloggs <f.blogs@napier.co.nz>") 252*0d24de9dSSimon Glass return 253*0d24de9dSSimon Glass cc = BuildEmailList(series.get('cc'), '--cc', alias) 254*0d24de9dSSimon Glass if self_only: 255*0d24de9dSSimon Glass to = BuildEmailList([os.getenv('USER')], '--to', alias) 256*0d24de9dSSimon Glass cc = [] 257*0d24de9dSSimon Glass cmd = ['git', 'send-email', '--annotate'] 258*0d24de9dSSimon Glass cmd += to 259*0d24de9dSSimon Glass cmd += cc 260*0d24de9dSSimon Glass cmd += ['--cc-cmd', '"%s --cc-cmd %s"' % (sys.argv[0], cc_fname)] 261*0d24de9dSSimon Glass if cover_fname: 262*0d24de9dSSimon Glass cmd.append(cover_fname) 263*0d24de9dSSimon Glass cmd += args 264*0d24de9dSSimon Glass str = ' '.join(cmd) 265*0d24de9dSSimon Glass if not dry_run: 266*0d24de9dSSimon Glass os.system(str) 267*0d24de9dSSimon Glass return str 268*0d24de9dSSimon Glass 269*0d24de9dSSimon Glass 270*0d24de9dSSimon Glassdef LookupEmail(lookup_name, alias=None, level=0): 271*0d24de9dSSimon Glass """If an email address is an alias, look it up and return the full name 272*0d24de9dSSimon Glass 273*0d24de9dSSimon Glass TODO: Why not just use git's own alias feature? 274*0d24de9dSSimon Glass 275*0d24de9dSSimon Glass Args: 276*0d24de9dSSimon Glass lookup_name: Alias or email address to look up 277*0d24de9dSSimon Glass 278*0d24de9dSSimon Glass Returns: 279*0d24de9dSSimon Glass tuple: 280*0d24de9dSSimon Glass list containing a list of email addresses 281*0d24de9dSSimon Glass 282*0d24de9dSSimon Glass Raises: 283*0d24de9dSSimon Glass OSError if a recursive alias reference was found 284*0d24de9dSSimon Glass ValueError if an alias was not found 285*0d24de9dSSimon Glass 286*0d24de9dSSimon Glass >>> alias = {} 287*0d24de9dSSimon Glass >>> alias['fred'] = ['f.bloggs@napier.co.nz'] 288*0d24de9dSSimon Glass >>> alias['john'] = ['j.bloggs@napier.co.nz'] 289*0d24de9dSSimon Glass >>> alias['mary'] = ['m.poppins@cloud.net'] 290*0d24de9dSSimon Glass >>> alias['boys'] = ['fred', ' john', 'f.bloggs@napier.co.nz'] 291*0d24de9dSSimon Glass >>> alias['all'] = ['fred ', 'john', ' mary '] 292*0d24de9dSSimon Glass >>> alias['loop'] = ['other', 'john', ' mary '] 293*0d24de9dSSimon Glass >>> alias['other'] = ['loop', 'john', ' mary '] 294*0d24de9dSSimon Glass >>> LookupEmail('mary', alias) 295*0d24de9dSSimon Glass ['m.poppins@cloud.net'] 296*0d24de9dSSimon Glass >>> LookupEmail('arthur.wellesley@howe.ro.uk', alias) 297*0d24de9dSSimon Glass ['arthur.wellesley@howe.ro.uk'] 298*0d24de9dSSimon Glass >>> LookupEmail('boys', alias) 299*0d24de9dSSimon Glass ['f.bloggs@napier.co.nz', 'j.bloggs@napier.co.nz'] 300*0d24de9dSSimon Glass >>> LookupEmail('all', alias) 301*0d24de9dSSimon Glass ['f.bloggs@napier.co.nz', 'j.bloggs@napier.co.nz', 'm.poppins@cloud.net'] 302*0d24de9dSSimon Glass >>> LookupEmail('odd', alias) 303*0d24de9dSSimon Glass Traceback (most recent call last): 304*0d24de9dSSimon Glass ... 305*0d24de9dSSimon Glass ValueError: Alias 'odd' not found 306*0d24de9dSSimon Glass >>> LookupEmail('loop', alias) 307*0d24de9dSSimon Glass Traceback (most recent call last): 308*0d24de9dSSimon Glass ... 309*0d24de9dSSimon Glass OSError: Recursive email alias at 'other' 310*0d24de9dSSimon Glass """ 311*0d24de9dSSimon Glass if not alias: 312*0d24de9dSSimon Glass alias = settings.alias 313*0d24de9dSSimon Glass lookup_name = lookup_name.strip() 314*0d24de9dSSimon Glass if '@' in lookup_name: # Perhaps a real email address 315*0d24de9dSSimon Glass return [lookup_name] 316*0d24de9dSSimon Glass 317*0d24de9dSSimon Glass lookup_name = lookup_name.lower() 318*0d24de9dSSimon Glass 319*0d24de9dSSimon Glass if level > 10: 320*0d24de9dSSimon Glass raise OSError, "Recursive email alias at '%s'" % lookup_name 321*0d24de9dSSimon Glass 322*0d24de9dSSimon Glass out_list = [] 323*0d24de9dSSimon Glass if lookup_name: 324*0d24de9dSSimon Glass if not lookup_name in alias: 325*0d24de9dSSimon Glass raise ValueError, "Alias '%s' not found" % lookup_name 326*0d24de9dSSimon Glass for item in alias[lookup_name]: 327*0d24de9dSSimon Glass todo = LookupEmail(item, alias, level + 1) 328*0d24de9dSSimon Glass for new_item in todo: 329*0d24de9dSSimon Glass if not new_item in out_list: 330*0d24de9dSSimon Glass out_list.append(new_item) 331*0d24de9dSSimon Glass 332*0d24de9dSSimon Glass #print "No match for alias '%s'" % lookup_name 333*0d24de9dSSimon Glass return out_list 334*0d24de9dSSimon Glass 335*0d24de9dSSimon Glassdef GetTopLevel(): 336*0d24de9dSSimon Glass """Return name of top-level directory for this git repo. 337*0d24de9dSSimon Glass 338*0d24de9dSSimon Glass Returns: 339*0d24de9dSSimon Glass Full path to git top-level directory 340*0d24de9dSSimon Glass 341*0d24de9dSSimon Glass This test makes sure that we are running tests in the right subdir 342*0d24de9dSSimon Glass 343*0d24de9dSSimon Glass >>> os.path.realpath(os.getcwd()) == \ 344*0d24de9dSSimon Glass os.path.join(GetTopLevel(), 'tools', 'scripts', 'patman') 345*0d24de9dSSimon Glass True 346*0d24de9dSSimon Glass """ 347*0d24de9dSSimon Glass return command.OutputOneLine('git', 'rev-parse', '--show-toplevel') 348*0d24de9dSSimon Glass 349*0d24de9dSSimon Glassdef GetAliasFile(): 350*0d24de9dSSimon Glass """Gets the name of the git alias file. 351*0d24de9dSSimon Glass 352*0d24de9dSSimon Glass Returns: 353*0d24de9dSSimon Glass Filename of git alias file, or None if none 354*0d24de9dSSimon Glass """ 355*0d24de9dSSimon Glass fname = command.OutputOneLine('git', 'config', 'sendemail.aliasesfile') 356*0d24de9dSSimon Glass if fname: 357*0d24de9dSSimon Glass fname = os.path.join(GetTopLevel(), fname.strip()) 358*0d24de9dSSimon Glass return fname 359*0d24de9dSSimon Glass 360*0d24de9dSSimon Glassdef Setup(): 361*0d24de9dSSimon Glass """Set up git utils, by reading the alias files.""" 362*0d24de9dSSimon Glass settings.Setup('') 363*0d24de9dSSimon Glass 364*0d24de9dSSimon Glass # Check for a git alias file also 365*0d24de9dSSimon Glass alias_fname = GetAliasFile() 366*0d24de9dSSimon Glass if alias_fname: 367*0d24de9dSSimon Glass settings.ReadGitAliases(alias_fname) 368*0d24de9dSSimon Glass 369*0d24de9dSSimon Glassif __name__ == "__main__": 370*0d24de9dSSimon Glass import doctest 371*0d24de9dSSimon Glass 372*0d24de9dSSimon Glass doctest.testmod() 373