4
Copyright (c) 2016 Red Hat, Inc. <http://www.redhat.com>
5
This file is part of GlusterFS.
7
This file is licensed to you under your choice of the GNU Lesser
8
General Public License, version 3 or any later version (LGPLv3 or
9
later), or the GNU General Public License, version 2 (GPLv2), in all
10
cases as published by the Free Software Foundation.
15
This script helps in visualizing backported and missed commits between two
16
different branches, tags or commit ranges. In the list of missed commits,
17
it will help you identify patches which are posted for reviews on gerrit server.
20
$ ./extras/git-branch-diff.py --help
21
usage: git-branch-diff.py [-h] [-s SOURCE] -t TARGET [-a AUTHOR] [-p PATH]
24
git wrapper to diff local or remote branches/tags/commit-ranges
27
-h, --help show this help message and exit
28
-s SOURCE, --source SOURCE
29
source pattern, it could be a branch, tag or a commit
31
-t TARGET, --target TARGET
32
target pattern, it could be a branch, tag or a commit
34
-a AUTHOR, --author AUTHOR
35
default: git config name/email, to provide multiple
36
specify comma separated values
37
-p PATH, --path PATH show source and target diff w.r.t given path, to
38
provide multiple specify space in between them
39
-o OPTIONS, --options OPTIONS
40
add other git options such as --after=<>, --before=<>
44
$ ./extras/git-branch-diff.py -t origin/release-3.8
46
$ ./extras/git-branch-diff.py -s local_branch -t origin/release-3.7
48
$ ./extras/git-branch-diff.py -s 4517bf8..e66add8 -t origin/release-3.7
49
$ ./extras/git-branch-diff.py -s HEAD..c4efd39 -t origin/release-3.7
51
$ ./extras/git-branch-diff.py -t v3.7.11 --author="author@redhat.com"
52
$ ./extras/git-branch-diff.py -t v3.7.11 --author="authorX, authorY, authorZ"
54
$ ./extras/git-branch-diff.py -t origin/release-3.8 --path="xlators/"
55
$ ./extras/git-branch-diff.py -t origin/release-3.8 --path="./xlators ./rpc"
57
$ ./extras/git-branch-diff.py -t origin/release-3.6 --author="*"
58
$ ./extras/git-branch-diff.py -t origin/release-3.6 --author="All"
59
$ ./extras/git-branch-diff.py -t origin/release-3.6 --author="Null"
61
$ ./extras/git-branch-diff.py -t v3.7.11 --options="--after=2015-03-01 \
65
While backporting commit to another branch only subject of the patch may
66
remain unchanged, all others such as commit message, commit Id, change Id,
67
bug Id, may be changed. This script works by taking commit subject as the
68
key value for comparing two git branches, which can be local or remote.
70
Note: This script may ignore commits which have altered their commit subjects
71
while backporting patches. Also this script doesn't have any intelligence to
72
detect squashed commits.
75
Prasanna Kumar Kalever <prasanna.kalever@redhat.com>
78
from __future__ import print_function
89
self.tick = u'\033[1;32m[ \u2714 ]\033[0m'
90
self.cross = u'\033[1;31m[ \u2716 ]\033[0m'
91
self.green_set = u'\033[1;34m'
92
self.yello_set = u'\033[4;33m'
93
self.color_unset = '\033[0m'
97
" replace default values with actual values from command args"
98
self.g_author = self.argsdict['author']
99
self.s_pattern = self.argsdict['source']
100
self.t_pattern = self.argsdict['target']
101
self.r_path = self.argsdict['path']
102
self.options = ' '.join(self.argsdict['options'])
104
self.gerrit_server = "http://review.gluster.org"
106
def check_dir_exist (self, os_path):
107
" checks whether given path exist"
108
path_list = os_path.split()
109
for path in path_list:
110
if not os.path.exists(path):
111
raise argparse.ArgumentTypeError("'%s' path %s is not valid"
115
def check_pattern_exist (self):
116
" defend to check given branch[s] exit"
117
status_sbr, op = commands.getstatusoutput('git log ' +
119
status_tbr, op = commands.getstatusoutput('git log ' +
122
print("Error: --source=" + self.s_pattern + " doesn't exit\n")
123
self.parser.print_help()
125
elif status_tbr != 0:
126
print("Error: --target=" + self.t_pattern + " doesn't exit\n")
127
self.parser.print_help()
130
def check_author_exist (self):
131
" defend to check given author exist, format in case of multiple"
132
contrib_list = ['', '*', 'all', 'All', 'ALL', 'null', 'Null', 'NULL']
133
if self.g_author in contrib_list:
136
ide_list = self.g_author.split(',')
138
cmd4 = 'git log ' + self.s_pattern + ' --author=' + ide
139
c_list = subprocess.check_output(cmd4, shell = True)
141
print("Error: --author=%s doesn't exit" %self.g_author)
142
print("see '%s --help'" %__file__)
144
if len(ide_list) > 1:
145
self.g_author = "\|".join(ide_list)
147
def connected_to_gerrit (self):
148
"check if gerrit server is reachable"
150
r = requests.get(self.gerrit_server, timeout=3)
152
except requests.Timeout as err:
154
print("Warning: failed to get list of open review commits on " \
156
"hint: Request timed out! gerrit server could possibly " \
159
except requests.RequestException as err:
160
" handle other errors"
161
print("Warning: failed to get list of open review commits on " \
163
"hint: check with internet connection ...\n")
166
def parse_cmd_args (self):
167
" command line parser"
168
author = subprocess.check_output('git config user.email',
169
shell = True).rstrip('\n')
170
source = "remotes/origin/master"
171
options = [' --pretty=format:"%h %s" ']
172
path = subprocess.check_output('git rev-parse --show-toplevel',
173
shell = True).rstrip('\n')
174
self.parser = argparse.ArgumentParser(description = 'git wrapper to '
175
'diff local or remote branches/'
176
'tags/commit-ranges')
177
self.parser.add_argument('-s',
179
help = 'source pattern, it could be a branch,'
180
' tag or a commit range',
183
self.parser.add_argument('-t',
185
help = 'target pattern, it could be a branch,'
186
' tag or a commit range',
189
self.parser.add_argument('-a',
191
help = 'default: git config name/email, '
192
'to provide multiple specify comma'
196
self.parser.add_argument('-p',
198
type = self.check_dir_exist,
199
help = 'show source and target diff w.r.t '
200
'given path, to provide multiple '
201
'specify space in between them',
204
self.parser.add_argument('-o',
206
help = 'add other git options such as '
207
'--after=<>, --before=<> etc. '
212
self.argsdict = vars(self.parser.parse_args())
214
def print_output (self):
215
" display the result list"
216
print("\n------------------------------------------------------------\n")
217
print(self.tick + " Successfully Backported changes:")
218
print(' {' + 'from: ' + self.s_pattern + \
219
' to: '+ self.t_pattern + '}\n')
220
for key, value in self.s_dict.items():
221
if value in self.t_dict.itervalues():
222
print("[%s%s%s] %s" %(self.yello_set,
226
print("\n------------------------------------------------------------\n")
227
print(self.cross + " Missing patches in " + self.t_pattern + ':\n')
228
if self.connected_to_gerrit():
229
cmd3 = "git review -r origin -l"
230
review_list = subprocess.check_output(cmd3, shell = True).split('\n')
234
for key, value in self.s_dict.items():
235
if value not in self.t_dict.itervalues():
236
if any(value in s for s in review_list):
237
print("[%s%s%s] %s %s(under review)%s" %(self.yello_set,
244
print("[%s%s%s] %s" %(self.yello_set,
248
print("\n------------------------------------------------------------\n")
251
self.check_pattern_exist()
252
self.check_author_exist()
254
" actual git commands"
255
cmd1 = 'git log' + self.options + ' ' + self.s_pattern + \
256
' --author=\'' + self.g_author + '\' ' + self.r_path
258
" could be backported by anybody so --author doesn't apply here"
259
cmd2 = 'git log' + self.options + ' ' + self.t_pattern + \
262
s_list = subprocess.check_output(cmd1, shell = True).split('\n')
263
t_list = subprocess.check_output(cmd2, shell = True)
266
print("No commits in the target: %s" %self.t_pattern)
267
print("see '%s --help'" %__file__)
270
t_list = t_list.split('\n')
276
self.s_dict.update(dict([item.split(' ', 1)]))
278
self.t_dict.update(dict([item.split(' ', 1)]))
283
if __name__ == '__main__':
284
run = GitBranchDiff()