Skip to content

Commit

Permalink
BaseTools: add script to configure local git options
Browse files Browse the repository at this point in the history
Patch contribution and review is greatly simplified by following the
steps described in "Laszlo's unkempt guide":
https://github.com/tianocore/tianocore.github.io/wiki/Laszlo's-unkempt-git-guide-for-edk2-contributors-and-maintainers
but there are a lot of tedious manual steps in there, so here is a
python script that configures all options I am aware of
*for the repository the script is executed from*.

Signed-off-by: Leif Lindholm <leif.lindholm@linaro.org>
Acked-by: Laszlo Ersek <lersek@redhat.com>
Reviewed-by: Bob Feng <bob.c.feng@intel.com>
  • Loading branch information
Leif Lindholm authored and BobCF committed Jun 12, 2019
1 parent 5b3e695 commit 4eb0acb
Showing 1 changed file with 204 additions and 0 deletions.
204 changes: 204 additions & 0 deletions BaseTools/Scripts/SetupGit.py
@@ -0,0 +1,204 @@
## @file
# Set up the git configuration for contributing to TianoCore projects
#
# Copyright (c) 2019, Linaro Ltd. All rights reserved.<BR>
#
# SPDX-License-Identifier: BSD-2-Clause-Patent
#

from __future__ import print_function
import argparse
import os.path
import re
import sys

try:
import git
except ImportError:
print('Unable to load gitpython module - please install and try again.')
sys.exit(1)

try:
# Try Python 2 'ConfigParser' module first since helpful lib2to3 will
# otherwise automagically load it with the name 'configparser'
import ConfigParser
except ImportError:
# Otherwise, try loading the Python 3 'configparser' under an alias
try:
import configparser as ConfigParser
except ImportError:
print("Unable to load configparser/ConfigParser module - please install and try again!")
sys.exit(1)


# Assumptions: Script is in edk2/BaseTools/Scripts,
# templates in edk2/BaseTools/Conf
CONFDIR = os.path.join(os.path.dirname(os.path.dirname(os.path.realpath(__file__))),
'Conf')

UPSTREAMS = [
{'name': 'edk2',
'repo': 'https://github.com/tianocore/edk2.git',
'list': 'devel@edk2.groups.io'},
{'name': 'edk2-platforms',
'repo': 'https://github.com/tianocore/edk2-platforms.git',
'list': 'devel@edk2.groups.io', 'prefix': 'edk2-platforms'},
{'name': 'edk2-non-osi',
'repo': 'https://github.com/tianocore/edk2-non-osi.git',
'list': 'devel@edk2.groups.io', 'prefix': 'edk2-non-osi'}
]

# The minimum version required for all of the below options to work
MIN_GIT_VERSION = (1, 9, 0)

# Set of options to be set identically for all repositories
OPTIONS = [
{'section': 'am', 'option': 'keepcr', 'value': True},
{'section': 'am', 'option': 'signoff', 'value': True},
{'section': 'cherry-pick', 'option': 'signoff', 'value': True},
{'section': 'color', 'option': 'diff', 'value': True},
{'section': 'color', 'option': 'grep', 'value': 'auto'},
{'section': 'commit', 'option': 'signoff', 'value': True},
{'section': 'core', 'option': 'abbrev', 'value': 12},
{'section': 'core', 'option': 'attributesFile',
'value': os.path.join(CONFDIR, 'gitattributes').replace('\\', '/')},
{'section': 'core', 'option': 'whitespace', 'value': 'cr-at-eol'},
{'section': 'diff', 'option': 'algorithm', 'value': 'patience'},
{'section': 'diff', 'option': 'orderFile',
'value': os.path.join(CONFDIR, 'diff.order').replace('\\', '/')},
{'section': 'diff', 'option': 'renames', 'value': 'copies'},
{'section': 'diff', 'option': 'statGraphWidth', 'value': '20'},
{'section': 'diff "ini"', 'option': 'xfuncname',
'value': '^\\\\[[A-Za-z0-9_., ]+]'},
{'section': 'format', 'option': 'coverLetter', 'value': True},
{'section': 'format', 'option': 'numbered', 'value': True},
{'section': 'format', 'option': 'signoff', 'value': False},
{'section': 'notes', 'option': 'rewriteRef', 'value': 'refs/notes/commits'},
{'section': 'sendemail', 'option': 'chainreplyto', 'value': False},
{'section': 'sendemail', 'option': 'thread', 'value': True},
]


def locate_repo():
"""Opens a Repo object for the current tree, searching upwards in the directory hierarchy."""
try:
repo = git.Repo(path='.', search_parent_directories=True)
except (git.InvalidGitRepositoryError, git.NoSuchPathError):
print("It doesn't look like we're inside a git repository - aborting.")
sys.exit(2)
return repo


def fuzzy_match_repo_url(one, other):
"""Compares two repository URLs, ignoring protocol and optional trailing '.git'."""
oneresult = re.match(r'.*://(?P<oneresult>.*?)(\.git)*$', one)
otherresult = re.match(r'.*://(?P<otherresult>.*?)(\.git)*$', other)

if oneresult and otherresult:
onestring = oneresult.group('oneresult')
otherstring = otherresult.group('otherresult')
if onestring == otherstring:
return True

return False


def get_upstream(url):
"""Extracts the dict for the current repo origin."""
for upstream in UPSTREAMS:
if fuzzy_match_repo_url(upstream['repo'], url):
return upstream
print("Unknown upstream '%s' - aborting!" % url)
sys.exit(3)


def check_versions():
"""Checks versions of dependencies."""
version = git.cmd.Git().version_info

if version < MIN_GIT_VERSION:
print('Need git version %d.%d or later!' % (version[0], version[1]))
sys.exit(4)


def write_config_value(repo, section, option, data):
"""."""
with repo.config_writer(config_level='repository') as configwriter:
configwriter.set_value(section, option, data)


if __name__ == '__main__':
check_versions()

PARSER = argparse.ArgumentParser(
description='Sets up a git repository according to TianoCore rules.')
PARSER.add_argument('-c', '--check',
help='check current config only, printing what would be changed',
action='store_true',
required=False)
PARSER.add_argument('-f', '--force',
help='overwrite existing settings conflicting with program defaults',
action='store_true',
required=False)
PARSER.add_argument('-v', '--verbose',
help='enable more detailed output',
action='store_true',
required=False)
ARGS = PARSER.parse_args()

REPO = locate_repo()
if REPO.bare:
print('Bare repo - please check out an upstream one!')
sys.exit(6)

URL = REPO.remotes.origin.url

UPSTREAM = get_upstream(URL)
if not UPSTREAM:
print("Upstream '%s' unknown, aborting!" % URL)
sys.exit(7)

# Set a list email address if our upstream wants it
if 'list' in UPSTREAM:
OPTIONS.append({'section': 'sendemail', 'option': 'to',
'value': UPSTREAM['list']})
# Append a subject prefix entry to OPTIONS if our upstream wants it
if 'prefix' in UPSTREAM:
OPTIONS.append({'section': 'format', 'option': 'subjectPrefix',
'value': "PATCH " + UPSTREAM['prefix']})

CONFIG = REPO.config_reader(config_level='repository')

for entry in OPTIONS:
exists = False
try:
# Make sure to read boolean/int settings as real type rather than strings
if isinstance(entry['value'], bool):
value = CONFIG.getboolean(entry['section'], entry['option'])
elif isinstance(entry['value'], int):
value = CONFIG.getint(entry['section'], entry['option'])
else:
value = CONFIG.get(entry['section'], entry['option'])

exists = True
# Don't bail out from options not already being set
except (ConfigParser.NoSectionError, ConfigParser.NoOptionError):
pass

if exists:
if value == entry['value']:
if ARGS.verbose:
print("%s.%s already set (to '%s')" % (entry['section'],
entry['option'], value))
else:
if ARGS.force:
write_config_value(REPO, entry['section'], entry['option'], entry['value'])
else:
print("Not overwriting existing %s.%s value:" % (entry['section'],
entry['option']))
print(" '%s' != '%s'" % (value, entry['value']))
print(" add '-f' to command line to force overwriting existing settings")
else:
print("%s.%s => '%s'" % (entry['section'], entry['option'], entry['value']))
if not ARGS.check:
write_config_value(REPO, entry['section'], entry['option'], entry['value'])

0 comments on commit 4eb0acb

Please sign in to comment.