]> git.proxmox.com Git - mirror_edk2.git/blob - BaseTools/Scripts/SetupGit.py
BaseTools: add script to configure local git options
[mirror_edk2.git] / BaseTools / Scripts / SetupGit.py
1 ## @file
2 # Set up the git configuration for contributing to TianoCore projects
3 #
4 # Copyright (c) 2019, Linaro Ltd. All rights reserved.<BR>
5 #
6 # SPDX-License-Identifier: BSD-2-Clause-Patent
7 #
8
9 from __future__ import print_function
10 import argparse
11 import os.path
12 import re
13 import sys
14
15 try:
16 import git
17 except ImportError:
18 print('Unable to load gitpython module - please install and try again.')
19 sys.exit(1)
20
21 try:
22 # Try Python 2 'ConfigParser' module first since helpful lib2to3 will
23 # otherwise automagically load it with the name 'configparser'
24 import ConfigParser
25 except ImportError:
26 # Otherwise, try loading the Python 3 'configparser' under an alias
27 try:
28 import configparser as ConfigParser
29 except ImportError:
30 print("Unable to load configparser/ConfigParser module - please install and try again!")
31 sys.exit(1)
32
33
34 # Assumptions: Script is in edk2/BaseTools/Scripts,
35 # templates in edk2/BaseTools/Conf
36 CONFDIR = os.path.join(os.path.dirname(os.path.dirname(os.path.realpath(__file__))),
37 'Conf')
38
39 UPSTREAMS = [
40 {'name': 'edk2',
41 'repo': 'https://github.com/tianocore/edk2.git',
42 'list': 'devel@edk2.groups.io'},
43 {'name': 'edk2-platforms',
44 'repo': 'https://github.com/tianocore/edk2-platforms.git',
45 'list': 'devel@edk2.groups.io', 'prefix': 'edk2-platforms'},
46 {'name': 'edk2-non-osi',
47 'repo': 'https://github.com/tianocore/edk2-non-osi.git',
48 'list': 'devel@edk2.groups.io', 'prefix': 'edk2-non-osi'}
49 ]
50
51 # The minimum version required for all of the below options to work
52 MIN_GIT_VERSION = (1, 9, 0)
53
54 # Set of options to be set identically for all repositories
55 OPTIONS = [
56 {'section': 'am', 'option': 'keepcr', 'value': True},
57 {'section': 'am', 'option': 'signoff', 'value': True},
58 {'section': 'cherry-pick', 'option': 'signoff', 'value': True},
59 {'section': 'color', 'option': 'diff', 'value': True},
60 {'section': 'color', 'option': 'grep', 'value': 'auto'},
61 {'section': 'commit', 'option': 'signoff', 'value': True},
62 {'section': 'core', 'option': 'abbrev', 'value': 12},
63 {'section': 'core', 'option': 'attributesFile',
64 'value': os.path.join(CONFDIR, 'gitattributes').replace('\\', '/')},
65 {'section': 'core', 'option': 'whitespace', 'value': 'cr-at-eol'},
66 {'section': 'diff', 'option': 'algorithm', 'value': 'patience'},
67 {'section': 'diff', 'option': 'orderFile',
68 'value': os.path.join(CONFDIR, 'diff.order').replace('\\', '/')},
69 {'section': 'diff', 'option': 'renames', 'value': 'copies'},
70 {'section': 'diff', 'option': 'statGraphWidth', 'value': '20'},
71 {'section': 'diff "ini"', 'option': 'xfuncname',
72 'value': '^\\\\[[A-Za-z0-9_., ]+]'},
73 {'section': 'format', 'option': 'coverLetter', 'value': True},
74 {'section': 'format', 'option': 'numbered', 'value': True},
75 {'section': 'format', 'option': 'signoff', 'value': False},
76 {'section': 'notes', 'option': 'rewriteRef', 'value': 'refs/notes/commits'},
77 {'section': 'sendemail', 'option': 'chainreplyto', 'value': False},
78 {'section': 'sendemail', 'option': 'thread', 'value': True},
79 ]
80
81
82 def locate_repo():
83 """Opens a Repo object for the current tree, searching upwards in the directory hierarchy."""
84 try:
85 repo = git.Repo(path='.', search_parent_directories=True)
86 except (git.InvalidGitRepositoryError, git.NoSuchPathError):
87 print("It doesn't look like we're inside a git repository - aborting.")
88 sys.exit(2)
89 return repo
90
91
92 def fuzzy_match_repo_url(one, other):
93 """Compares two repository URLs, ignoring protocol and optional trailing '.git'."""
94 oneresult = re.match(r'.*://(?P<oneresult>.*?)(\.git)*$', one)
95 otherresult = re.match(r'.*://(?P<otherresult>.*?)(\.git)*$', other)
96
97 if oneresult and otherresult:
98 onestring = oneresult.group('oneresult')
99 otherstring = otherresult.group('otherresult')
100 if onestring == otherstring:
101 return True
102
103 return False
104
105
106 def get_upstream(url):
107 """Extracts the dict for the current repo origin."""
108 for upstream in UPSTREAMS:
109 if fuzzy_match_repo_url(upstream['repo'], url):
110 return upstream
111 print("Unknown upstream '%s' - aborting!" % url)
112 sys.exit(3)
113
114
115 def check_versions():
116 """Checks versions of dependencies."""
117 version = git.cmd.Git().version_info
118
119 if version < MIN_GIT_VERSION:
120 print('Need git version %d.%d or later!' % (version[0], version[1]))
121 sys.exit(4)
122
123
124 def write_config_value(repo, section, option, data):
125 """."""
126 with repo.config_writer(config_level='repository') as configwriter:
127 configwriter.set_value(section, option, data)
128
129
130 if __name__ == '__main__':
131 check_versions()
132
133 PARSER = argparse.ArgumentParser(
134 description='Sets up a git repository according to TianoCore rules.')
135 PARSER.add_argument('-c', '--check',
136 help='check current config only, printing what would be changed',
137 action='store_true',
138 required=False)
139 PARSER.add_argument('-f', '--force',
140 help='overwrite existing settings conflicting with program defaults',
141 action='store_true',
142 required=False)
143 PARSER.add_argument('-v', '--verbose',
144 help='enable more detailed output',
145 action='store_true',
146 required=False)
147 ARGS = PARSER.parse_args()
148
149 REPO = locate_repo()
150 if REPO.bare:
151 print('Bare repo - please check out an upstream one!')
152 sys.exit(6)
153
154 URL = REPO.remotes.origin.url
155
156 UPSTREAM = get_upstream(URL)
157 if not UPSTREAM:
158 print("Upstream '%s' unknown, aborting!" % URL)
159 sys.exit(7)
160
161 # Set a list email address if our upstream wants it
162 if 'list' in UPSTREAM:
163 OPTIONS.append({'section': 'sendemail', 'option': 'to',
164 'value': UPSTREAM['list']})
165 # Append a subject prefix entry to OPTIONS if our upstream wants it
166 if 'prefix' in UPSTREAM:
167 OPTIONS.append({'section': 'format', 'option': 'subjectPrefix',
168 'value': "PATCH " + UPSTREAM['prefix']})
169
170 CONFIG = REPO.config_reader(config_level='repository')
171
172 for entry in OPTIONS:
173 exists = False
174 try:
175 # Make sure to read boolean/int settings as real type rather than strings
176 if isinstance(entry['value'], bool):
177 value = CONFIG.getboolean(entry['section'], entry['option'])
178 elif isinstance(entry['value'], int):
179 value = CONFIG.getint(entry['section'], entry['option'])
180 else:
181 value = CONFIG.get(entry['section'], entry['option'])
182
183 exists = True
184 # Don't bail out from options not already being set
185 except (ConfigParser.NoSectionError, ConfigParser.NoOptionError):
186 pass
187
188 if exists:
189 if value == entry['value']:
190 if ARGS.verbose:
191 print("%s.%s already set (to '%s')" % (entry['section'],
192 entry['option'], value))
193 else:
194 if ARGS.force:
195 write_config_value(REPO, entry['section'], entry['option'], entry['value'])
196 else:
197 print("Not overwriting existing %s.%s value:" % (entry['section'],
198 entry['option']))
199 print(" '%s' != '%s'" % (value, entry['value']))
200 print(" add '-f' to command line to force overwriting existing settings")
201 else:
202 print("%s.%s => '%s'" % (entry['section'], entry['option'], entry['value']))
203 if not ARGS.check:
204 write_config_value(REPO, entry['section'], entry['option'], entry['value'])