OLD | NEW |
| 1 # Copyright (c) 2009 The Chromium Authors. All rights reserved. |
| 2 # Use of this source code is governed by a BSD-style license that can be |
| 3 # found in the LICENSE file. |
| 4 |
| 5 import os |
| 6 import re |
1 import subprocess | 7 import subprocess |
2 import sys | 8 import sys |
3 import re | |
4 import os | |
5 import webbrowser | 9 import webbrowser |
6 | 10 |
| 11 |
7 def deltree(root): | 12 def deltree(root): |
8 """ | 13 """Removes a given directory.""" |
9 Removes a given directory | |
10 """ | |
11 if (not os.path.exists(root)): | 14 if (not os.path.exists(root)): |
12 return | 15 return |
13 | 16 |
14 if sys.platform == 'win32': | 17 if sys.platform == 'win32': |
15 os.system('rmdir /S /Q ' + root.replace('/','\\')) | 18 os.system('rmdir /S /Q ' + root.replace('/','\\')) |
16 else: | 19 else: |
17 for name in os.listdir(root): | 20 for name in os.listdir(root): |
18 path = os.path.join(root, name) | 21 path = os.path.join(root, name) |
19 if os.path.isdir(path): | 22 if os.path.isdir(path): |
20 deltree(path) | 23 deltree(path) |
21 else: | 24 else: |
22 os.unlink(path) | 25 os.unlink(path) |
23 os.rmdir(root) | 26 os.rmdir(root) |
24 | 27 |
| 28 |
25 def clobberDir(dir): | 29 def clobberDir(dir): |
26 """ | 30 """Removes a given directory.""" |
27 Removes a given directory | 31 |
28 """ | |
29 | |
30 if (os.path.exists(dir)): | 32 if (os.path.exists(dir)): |
31 print dir + " directory found, deleting" | 33 print dir + " directory found, deleting" |
32 #The following line was removed due to access controls in Windows | 34 # The following line was removed due to access controls in Windows |
33 #which make os.unlink(path) calls impossible. | 35 # which make os.unlink(path) calls impossible. |
34 #deltree(dir) | 36 # deltree(dir) |
35 os.system('rmdir /S /Q ' + dir.replace('/','\\')) | 37 os.system('rmdir /S /Q ' + dir.replace('/','\\')) |
| 38 |
36 | 39 |
37 def gclUpload(revision, author): | 40 def gclUpload(revision, author): |
38 command = "gcl upload " + str(revision) + " --send_mail --no_try --no_presubmi
t --reviewers=" + author | 41 command = ("gcl upload " + str(revision) + |
| 42 " --send_mail --no_try --no_presubmit --reviewers=" + author) |
39 os.system(command) | 43 os.system(command) |
40 # subprocess.Popen(command, | 44 # subprocess.Popen(command, |
41 # shell=True, | 45 # shell=True, |
42 # stdout=None, | 46 # stdout=None, |
43 # stderr=subprocess.PIPE) | 47 # stderr=subprocess.PIPE) |
44 # stderr=subprocess.PIPE).stdout.readlines() | 48 # stderr=subprocess.PIPE).stdout.readlines() |
45 # for line in svn_info: | 49 # for line in svn_info: |
46 # match = re.search(r"Issue created. URL: (http://.+)", line) | 50 # match = re.search(r"Issue created. URL: (http://.+)", line) |
47 # if match: | 51 # if match: |
48 # » return match.group(1) | 52 # return match.group(1) |
49 | 53 return None |
50 return None | 54 |
51 | 55 |
52 def getAuthor(url, revision): | 56 def getAuthor(url, revision): |
53 command = 'svn info ' + url + "@"+str(revision) | 57 command = 'svn info ' + url + "@"+str(revision) |
54 svn_info = subprocess.Popen(command, | 58 svn_info = subprocess.Popen(command, |
55 shell=True, | 59 shell=True, |
56 stdout=subprocess.PIPE, | 60 stdout=subprocess.PIPE, |
57 stderr=subprocess.PIPE).stdout.readlines() | 61 stderr=subprocess.PIPE).stdout.readlines() |
58 for line in svn_info: | 62 for line in svn_info: |
59 match = re.search(r"Last Changed Author: (.+)", line) | 63 match = re.search(r"Last Changed Author: (.+)", line) |
60 if match: | 64 if match: |
61 » return match.group(1) | 65 return match.group(1) |
62 | |
63 return None | 66 return None |
64 | 67 |
65 | 68 |
66 def getRevisionLog(url, revision): | 69 def getRevisionLog(url, revision): |
67 """ | 70 """Takes an svn url and gets the associated revision. """ |
68 Takes an svn url and gets the associated revision. | |
69 """ | |
70 command = 'svn log ' + url + " -r"+str(revision) | 71 command = 'svn log ' + url + " -r"+str(revision) |
71 svn_info = subprocess.Popen(command, | 72 svn_info = subprocess.Popen(command, |
72 shell=True, | 73 shell=True, |
73 stdout=subprocess.PIPE, | 74 stdout=subprocess.PIPE, |
74 stderr=subprocess.PIPE).stdout.readlines() | 75 stderr=subprocess.PIPE).stdout.readlines() |
75 rtn= "" | 76 rtn= "" |
76 pos = 0 | 77 pos = 0 |
77 for line in svn_info: | 78 for line in svn_info: |
78 if (pos > 2): | 79 if (pos > 2): |
79 rtn += line.replace('-','').replace('\r','') | 80 rtn += line.replace('-','').replace('\r','') |
80 else: | 81 else: |
81 pos = pos + 1 | 82 pos = pos + 1 |
82 | 83 |
83 return rtn | 84 return rtn |
84 | 85 |
| 86 |
85 def checkoutRevision(url, revision, branch_url): | 87 def checkoutRevision(url, revision, branch_url): |
86 paths = getBestMergePaths(url, revision) | 88 paths = getBestMergePaths(url, revision) |
87 | |
88 deltree('./src') | 89 deltree('./src') |
89 | |
90 if not os.path.exists('./src'): | 90 if not os.path.exists('./src'): |
91 command = 'svn checkout -N ' + branch_url | 91 command = 'svn checkout -N ' + branch_url |
92 print command | 92 print command |
93 os.system(command) | 93 os.system(command) |
94 | 94 |
95 #This line is extremely important due to the way svn behaves in the set-depths | 95 # This line is extremely important due to the way svn behaves in the |
96 #action. If parents aren't handled before children, the child directories get | 96 # set-depths action. If parents aren't handled before children, the child |
97 #clobbered and the merge step fails. | 97 # directories get clobbered and the merge step fails. |
98 paths.sort() | 98 paths.sort() |
99 | |
100 for path in paths: | 99 for path in paths: |
101 subpaths = path.split('/') | 100 subpaths = path.split('/') |
102 subpaths.pop(0) | 101 subpaths.pop(0) |
103 base = './src' | 102 base = './src' |
104 for subpath in subpaths: | 103 for subpath in subpaths: |
105 base += '/' + subpath | 104 base += '/' + subpath |
106 if not os.path.exists(base): | 105 if not os.path.exists(base): |
107 command = ('svn update --depth empty ' + base) | 106 command = ('svn update --depth empty ' + base) |
108 print command | 107 print command |
109 os.system(command) | 108 os.system(command) |
110 else: | 109 else: |
111 print "Found " + base | 110 print "Found " + base |
112 | 111 |
113 files = getFilesInRevision(url, revision) | 112 for file in getFilesInRevision(url, revision): |
114 | 113 # Prevent the tool from clobbering the src directory. |
115 for file in files: | |
116 #Prevent the tool from clobbering the src directory | |
117 if (file == ""): | 114 if (file == ""): |
118 continue | 115 continue |
119 command = ('svn up ./src' + file) | 116 command = ('svn up ./src' + file) |
120 print command | 117 print command |
121 os.system(command) | 118 os.system(command) |
122 | 119 |
123 #def mergeRevision(url, revision): | |
124 # command = 'svn merge -r ' + str(revision-1) + ":" + str(revision) + " " + url | |
125 # print command | |
126 # os.system(command) | |
127 | 120 |
128 def mergeRevision(url, revision, ignoreAncestry=False): | 121 def mergeRevision(url, revision, ignoreAncestry=False): |
129 paths = getBestMergePaths(url, revision) | 122 paths = getBestMergePaths(url, revision) |
130 for path in paths: | 123 for path in paths: |
131 command = ('svn merge -N -r ' + str(revision-1) + ":" + str(revision) + " ") | 124 command = ('svn merge -N -r ' + str(revision-1) + ":" + str(revision) + " ") |
132 if (ignoreAncestry): | 125 if (ignoreAncestry): |
133 command = command + " --ignore-ancestry " | 126 command = command + " --ignore-ancestry " |
134 command = command + url + path + " ./src" + path | 127 command = command + url + path + " ./src" + path |
135 | |
136 print command | 128 print command |
137 os.system(command) | 129 os.system(command) |
138 | 130 |
| 131 |
139 def revertRevision(url, revision): | 132 def revertRevision(url, revision): |
140 paths = getBestMergePaths(url, revision) | 133 paths = getBestMergePaths(url, revision) |
141 for path in paths: | 134 for path in paths: |
142 command = ('svn merge -N -r ' + str(revision) + ":" + str(revision-1) + " "
+ | 135 command = ('svn merge -N -r ' + str(revision) + ":" + str(revision-1) + |
143 url + path + " ./src" + path) | 136 " " + url + path + " ./src" + path) |
144 print command | 137 print command |
145 os.system(command) | 138 os.system(command) |
146 | 139 |
| 140 |
147 def getBestMergePaths(url, revision): | 141 def getBestMergePaths(url, revision): |
148 """ | 142 """Takes an svn url and gets the associated revision.""" |
149 Takes an svn url and gets the associated revision. | |
150 """ | |
151 command = 'svn log ' + url + " -r "+str(revision) + " -v" | 143 command = 'svn log ' + url + " -r "+str(revision) + " -v" |
152 svn_info = subprocess.Popen(command, | 144 svn_info = subprocess.Popen(command, |
153 shell=True, | 145 shell=True, |
154 stdout=subprocess.PIPE, | 146 stdout=subprocess.PIPE, |
155 stderr=subprocess.PIPE).stdout.readlines() | 147 stderr=subprocess.PIPE).stdout.readlines() |
156 map = dict() | 148 map = {} |
157 for line in svn_info: | 149 for line in svn_info: |
158 #match = re.search(r"[\n\r ]+[MADUC][\n\r ]+/.*/src(.*)/.+", line) | |
159 #match = re.search(r"[\n\r ]+[MADUC][\n\r ]+/(?:trunk|branches/\d+)/src(.*)/
.+", line) | |
160 match = re.search(r"[\n\r ]+[MADUC][\n\r ]+/(?:trunk|branches/\d+)/src([^ ]*
)/[^ ]+", line) | 150 match = re.search(r"[\n\r ]+[MADUC][\n\r ]+/(?:trunk|branches/\d+)/src([^ ]*
)/[^ ]+", line) |
161 | |
162 if match: | 151 if match: |
163 map[match.group(1)] = match.group(1) | 152 map[match.group(1)] = match.group(1) |
164 | 153 |
165 return map.keys() | 154 return map.keys() |
166 | 155 |
| 156 |
167 def getFilesInRevision(url, revision): | 157 def getFilesInRevision(url, revision): |
168 """ | 158 """Takes an svn url and gets the associated revision.""" |
169 Takes an svn url and gets the associated revision. | |
170 """ | |
171 command = 'svn log ' + url + " -r "+str(revision) + " -v" | 159 command = 'svn log ' + url + " -r "+str(revision) + " -v" |
172 svn_info = subprocess.Popen(command, | 160 svn_info = subprocess.Popen(command, |
173 shell=True, | 161 shell=True, |
174 stdout=subprocess.PIPE, | 162 stdout=subprocess.PIPE, |
175 stderr=subprocess.PIPE).stdout.readlines() | 163 stderr=subprocess.PIPE).stdout.readlines() |
176 map = dict() | 164 map = {} |
177 for line in svn_info: | 165 for line in svn_info: |
178 match = re.search(r"[\n\r ]+[MADUC][\n\r ]+/(?:trunk|branches/\d+)/src([^ ]*
)/([^ ]+)", line) | 166 match = re.search(r"[\n\r ]+[MADUC][\n\r ]+/(?:trunk|branches/\d+)/src([^ ]*
)/([^ ]+)", line) |
179 | |
180 if match: | 167 if match: |
181 map[match.group(1) + "/" + match.group(2)] = match.group(1) + "/" + match.
group(2) | 168 map[match.group(1) + "/" + match.group(2)] = match.group(1) + "/" + match.
group(2) |
182 | 169 |
183 return map.keys() | 170 return map.keys() |
184 | 171 |
| 172 |
185 def getBestMergePath(url, revision): | 173 def getBestMergePath(url, revision): |
186 """ | 174 """Takes an svn url and gets the associated revision.""" |
187 Takes an svn url and gets the associated revision. | |
188 """ | |
189 command = 'svn log ' + url + " -r "+str(revision) + " -v" | 175 command = 'svn log ' + url + " -r "+str(revision) + " -v" |
190 svn_info = subprocess.Popen(command, | 176 svn_info = subprocess.Popen(command, |
191 shell=True, | 177 shell=True, |
192 stdout=subprocess.PIPE, | 178 stdout=subprocess.PIPE, |
193 stderr=subprocess.PIPE).stdout.readlines() | 179 stderr=subprocess.PIPE).stdout.readlines() |
194 best_path = None | 180 best_path = None |
195 | |
196 for line in svn_info: | 181 for line in svn_info: |
197 match = re.search(r"[\n\r ]+[MADUC][\n\r ]+/.*/src(.*)/.+", line) | 182 match = re.search(r"[\n\r ]+[MADUC][\n\r ]+/.*/src(.*)/.+", line) |
198 if match: | 183 if match: |
199 if (best_path == None): | 184 if (best_path == None): |
200 best_path = match.group(1) | 185 best_path = match.group(1) |
201 else: | 186 else: |
202 best_path = leastPath(match.group(1),best_path) | 187 best_path = leastPath(match.group(1),best_path) |
203 # print best_path | |
204 | 188 |
205 return best_path | 189 return best_path |
206 | 190 |
| 191 |
207 def leastPath(a, b): | 192 def leastPath(a, b): |
208 if (not a) or (a == ""): | 193 if (not a) or (a == ""): |
209 return "" | 194 return "" |
210 if (b == ""): | 195 if (b == ""): |
211 return "" | 196 return "" |
212 if (not b): | 197 if (not b): |
213 return a | 198 return a |
214 | 199 |
215 a_list = a.lstrip("/").split("/") | 200 a_list = a.lstrip("/").split("/") |
216 b_list = b.lstrip("/").split("/") | 201 b_list = b.lstrip("/").split("/") |
217 | |
218 last_match = "" | 202 last_match = "" |
219 while((len(a_list) != 0) and (len(b_list) != 0)): | 203 while((len(a_list) != 0) and (len(b_list) != 0)): |
220 a_value = a_list.pop(0) | 204 a_value = a_list.pop(0) |
221 b_value = b_list.pop(0) | 205 b_value = b_list.pop(0) |
222 if (a_value == b_value): | 206 if (a_value == b_value): |
223 last_match = last_match + "/" + a_value | 207 last_match = last_match + "/" + a_value |
224 else: | 208 else: |
225 break | 209 break |
226 | 210 |
227 return last_match | 211 return last_match |
228 | 212 |
| 213 |
229 def prompt(question): | 214 def prompt(question): |
230 p = None | 215 p = None |
231 | |
232 while not p: | 216 while not p: |
233 print question + " [y|n]:" | 217 print question + " [y|n]:" |
234 p = sys.stdin.readline() | 218 p = sys.stdin.readline() |
235 if p.lower().startswith('n'): | 219 if p.lower().startswith('n'): |
236 return False | 220 return False |
237 elif p.lower().startswith('y'): | 221 elif p.lower().startswith('y'): |
238 return True | 222 return True |
239 else: | 223 else: |
240 p = None | 224 p = None |
| 225 |
241 | 226 |
242 def main(argv=None): | 227 def main(argv=None): |
243 BASE_URL = "svn://chrome-svn/chrome" | 228 BASE_URL = "svn://chrome-svn/chrome" |
244 TRUNK_URL = BASE_URL + "/trunk/src" | 229 TRUNK_URL = BASE_URL + "/trunk/src" |
245 BRANCH_URL = None | 230 BRANCH_URL = None |
246 | 231 |
247 if (len(sys.argv) == 1): | 232 if (len(sys.argv) == 1): |
248 print "WARNING: Please use this tool in an empty directory (or at least one" | 233 print "WARNING: Please use this tool in an empty directory (or at least one" |
249 print "that you don't mind clobbering." | 234 print "that you don't mind clobbering." |
250 print "REQUIRES: SVN 1.5+" | 235 print "REQUIRES: SVN 1.5+" |
251 print "NOTE: NO NEED TO CHECKOUT ANYTHING IN ADVANCE OF USING THIS TOOL." | 236 print "NOTE: NO NEED TO CHECKOUT ANYTHING IN ADVANCE OF USING THIS TOOL." |
252 print "\nValid parameters:" | 237 print "\nValid parameters:" |
253 print "\n[Merge from trunk to branch]" | 238 print "\n[Merge from trunk to branch]" |
254 print "<revision> --merge <branch_num>" | 239 print "<revision> --merge <branch_num>" |
255 print "Example " + sys.argv[0] + " 12345 --merge 187" | 240 print "Example " + sys.argv[0] + " 12345 --merge 187" |
256 print "\n[Merge from trunk to branch, ignoring revision history]" | 241 print "\n[Merge from trunk to branch, ignoring revision history]" |
257 print "<revision> --mplus <branch_num>" | 242 print "<revision> --mplus <branch_num>" |
258 print "Example " + sys.argv[0] + " 12345 --mplus 187" | 243 print "Example " + sys.argv[0] + " 12345 --mplus 187" |
259 print "\n[Revert from trunk]" | 244 print "\n[Revert from trunk]" |
260 print " <revision> --revert" | 245 print " <revision> --revert" |
261 print "Example " + sys.argv[0] + " 12345 --revert" | 246 print "Example " + sys.argv[0] + " 12345 --revert" |
262 print "\n[Revert from branch]" | 247 print "\n[Revert from branch]" |
263 print " <revision> --revert <branch_num>" | 248 print " <revision> --revert <branch_num>" |
264 print "Example " + sys.argv[0] + " 12345 --revert 187" | 249 print "Example " + sys.argv[0] + " 12345 --revert 187" |
265 sys.exit(0) | 250 sys.exit(0) |
266 | 251 |
267 revision = int(sys.argv[1]) | 252 revision = int(sys.argv[1]) |
268 if ((len(sys.argv) >= 4) and (sys.argv[2] in ['--revert','-r'])): | 253 if ((len(sys.argv) >= 4) and (sys.argv[2] in ['--revert','-r'])): |
269 BRANCH_URL = BASE_URL + "/branches/" + sys.argv[3] + "/src" | 254 BRANCH_URL = BASE_URL + "/branches/" + sys.argv[3] + "/src" |
270 url = BRANCH_URL | 255 url = BRANCH_URL |
271 else: | 256 else: |
272 url = TRUNK_URL | 257 url = TRUNK_URL |
273 action = "Merge" | 258 action = "Merge" |
274 | |
275 command = 'svn log ' + url + " -r "+str(revision) + " -v" | 259 command = 'svn log ' + url + " -r "+str(revision) + " -v" |
276 os.system(command) | 260 os.system(command) |
277 | |
278 if not prompt("Is this the correct revision?"): | 261 if not prompt("Is this the correct revision?"): |
279 sys.exit(0) | 262 sys.exit(0) |
280 | 263 |
281 if (len(sys.argv) > 1): | 264 if (len(sys.argv) > 1): |
282 if sys.argv[2] in ['--merge','-m']: | 265 if sys.argv[2] in ['--merge','-m']: |
283 if (len(sys.argv) != 4): | 266 if (len(sys.argv) != 4): |
284 print "Please specify the branch # you want (i.e. 182) after --merge" | 267 print "Please specify the branch # you want (i.e. 182) after --merge" |
285 sys.exit(0) | 268 sys.exit(0) |
286 | 269 |
287 branch_url = "svn://chrome-svn/chrome/branches/" + sys.argv[3] + "/src" | 270 branch_url = "svn://chrome-svn/chrome/branches/" + sys.argv[3] + "/src" |
288 checkoutRevision(url, revision, branch_url) | 271 checkoutRevision(url, revision, branch_url) |
289 mergeRevision(url, revision) | 272 mergeRevision(url, revision) |
290 elif sys.argv[2] in ['--mplus','-p']: | 273 elif sys.argv[2] in ['--mplus','-p']: |
291 if (len(sys.argv) != 4): | 274 if (len(sys.argv) != 4): |
292 print "Please specify the branch # you want (i.e. 182) after --merge" | 275 print "Please specify the branch # you want (i.e. 182) after --merge" |
293 sys.exit(0) | 276 sys.exit(0) |
294 branch_url = "svn://chrome-svn/chrome/branches/" + sys.argv[3] + "/src" | 277 branch_url = "svn://chrome-svn/chrome/branches/" + sys.argv[3] + "/src" |
295 checkoutRevision(url, revision, branch_url) | 278 checkoutRevision(url, revision, branch_url) |
296 mergeRevision(url, revision, True) | 279 mergeRevision(url, revision, True) |
297 elif sys.argv[2] in ['--revert','-r']: | 280 elif sys.argv[2] in ['--revert','-r']: |
298 if (len(sys.argv) == 4): | 281 if (len(sys.argv) == 4): |
299 url = "svn://chrome-svn/chrome/branches/" + sys.argv[3] + "/src" | 282 url = "svn://chrome-svn/chrome/branches/" + sys.argv[3] + "/src" |
300 checkoutRevision(url, revision, url) | 283 checkoutRevision(url, revision, url) |
301 revertRevision(url, revision) | 284 revertRevision(url, revision) |
302 action = "Revert" | 285 action = "Revert" |
303 else: | 286 else: |
304 print "Unknown parameter " + sys.argv[2] | 287 print "Unknown parameter " + sys.argv[2] |
305 sys.exit(0) | 288 sys.exit(0) |
306 | 289 |
307 os.chdir('./src') | 290 os.chdir('./src') |
308 | 291 # Check the base url so we actually find the author who made the change. |
309 #Check the base url so we actually find the author who made the change | |
310 author = getAuthor(BASE_URL, revision) | 292 author = getAuthor(BASE_URL, revision) |
311 | |
312 filename = str(revision)+".txt" | 293 filename = str(revision)+".txt" |
313 out = open(filename,"w") | 294 out = open(filename,"w") |
314 out.write(action +" " + str(revision) + " - ") | 295 out.write(action +" " + str(revision) + " - ") |
315 out.write(getRevisionLog(url, revision)) | 296 out.write(getRevisionLog(url, revision)) |
316 if (author): | 297 if (author): |
317 out.write("TBR=" + author) | 298 out.write("TBR=" + author) |
318 out.close() | 299 out.close() |
319 | |
320 os.system('gcl change ' + str(revision) + " " + filename) | 300 os.system('gcl change ' + str(revision) + " " + filename) |
321 os.unlink(filename) | 301 os.unlink(filename) |
322 print author | 302 print author |
323 print revision | 303 print revision |
324 print "gcl upload " + str(revision) + " --send_mail --no_try --no_presubmit --
reviewers=" + author | 304 print "gcl upload " + str(revision) + " --send_mail --no_try --no_presubmit --
reviewers=" + author |
325 print "gcl commit " + str(revision) + " --no_presubmit --force" | 305 print "gcl commit " + str(revision) + " --no_presubmit --force" |
326 print "gcl delete " + str(revision) | 306 print "gcl delete " + str(revision) |
327 | 307 |
328 if prompt("Would you like to upload?"): | 308 if prompt("Would you like to upload?"): |
329 gclUpload(revision, author) | 309 gclUpload(revision, author) |
330 else: | 310 else: |
331 print "Deleting the changelist." | 311 print "Deleting the changelist." |
332 os.system("gcl delete " + str(revision)) | 312 os.system("gcl delete " + str(revision)) |
333 sys.exit(0) | 313 sys.exit(0) |
334 | 314 |
335 if prompt("Would you like to commit?"): | 315 if prompt("Would you like to commit?"): |
336 os.system("gcl commit " + str(revision) + " --no_presubmit --force") | 316 os.system("gcl commit " + str(revision) + " --no_presubmit --force") |
337 else: | 317 else: |
338 sys.exit(0) | 318 sys.exit(0) |
339 | 319 |
| 320 |
340 if __name__ == "__main__": | 321 if __name__ == "__main__": |
341 sys.exit(main()) | 322 sys.exit(main()) |
OLD | NEW |