OLD | NEW |
1 # Copyright (c) 2009 The Chromium Authors. All rights reserved. | 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 | 2 # Use of this source code is governed by a BSD-style license that can be |
3 # found in the LICENSE file. | 3 # found in the LICENSE file. |
4 | 4 |
5 """Gclient-specific SCM-specific operations.""" | 5 """Gclient-specific SCM-specific operations.""" |
6 | 6 |
7 import logging | 7 import logging |
8 import os | 8 import os |
9 import posixpath | 9 import posixpath |
10 import re | 10 import re |
(...skipping 92 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
103 if not command in commands: | 103 if not command in commands: |
104 raise gclient_utils.Error('Unknown command %s' % command) | 104 raise gclient_utils.Error('Unknown command %s' % command) |
105 | 105 |
106 if not command in dir(self): | 106 if not command in dir(self): |
107 raise gclient_utils.Error('Command %s not implemented in %s wrapper' % ( | 107 raise gclient_utils.Error('Command %s not implemented in %s wrapper' % ( |
108 command, self.scm_name)) | 108 command, self.scm_name)) |
109 | 109 |
110 return getattr(self, command)(options, args, file_list) | 110 return getattr(self, command)(options, args, file_list) |
111 | 111 |
112 | 112 |
113 class GitWrapper(SCMWrapper, scm.GIT): | 113 class GitWrapper(SCMWrapper): |
114 """Wrapper for Git""" | 114 """Wrapper for Git""" |
115 | 115 |
116 def cleanup(self, options, args, file_list): | 116 def cleanup(self, options, args, file_list): |
117 """'Cleanup' the repo. | 117 """'Cleanup' the repo. |
118 | 118 |
119 There's no real git equivalent for the svn cleanup command, do a no-op. | 119 There's no real git equivalent for the svn cleanup command, do a no-op. |
120 """ | 120 """ |
121 __pychecker__ = 'unusednames=options,args,file_list' | 121 __pychecker__ = 'unusednames=options,args,file_list' |
122 | 122 |
123 def diff(self, options, args, file_list): | 123 def diff(self, options, args, file_list): |
(...skipping 15 matching lines...) Expand all Loading... |
139 self._Run(['checkout-index', '-a', '--prefix=%s/' % export_path], | 139 self._Run(['checkout-index', '-a', '--prefix=%s/' % export_path], |
140 redirect_stdout=False) | 140 redirect_stdout=False) |
141 | 141 |
142 def pack(self, options, args, file_list): | 142 def pack(self, options, args, file_list): |
143 """Generates a patch file which can be applied to the root of the | 143 """Generates a patch file which can be applied to the root of the |
144 repository. | 144 repository. |
145 | 145 |
146 The patch file is generated from a diff of the merge base of HEAD and | 146 The patch file is generated from a diff of the merge base of HEAD and |
147 its upstream branch. | 147 its upstream branch. |
148 """ | 148 """ |
149 __pychecker__ = 'unusednames=options,file_list' | 149 __pychecker__ = 'unusednames=options,args,file_list' |
150 path = os.path.join(self._root_dir, self.relpath) | 150 path = os.path.join(self._root_dir, self.relpath) |
151 merge_base = self._Run(['merge-base', 'HEAD', 'origin']) | 151 merge_base = self._Run(['merge-base', 'HEAD', 'origin']) |
152 command = ['diff', merge_base] | 152 command = ['diff', merge_base] |
153 filterer = DiffFilterer(self.relpath) | 153 filterer = DiffFilterer(self.relpath) |
154 self.RunAndFilterOutput(command, path, False, False, filterer.Filter) | 154 scm.GIT.RunAndFilterOutput(command, path, False, False, filterer.Filter) |
155 | 155 |
156 def update(self, options, args, file_list): | 156 def update(self, options, args, file_list): |
157 """Runs git to update or transparently checkout the working copy. | 157 """Runs git to update or transparently checkout the working copy. |
158 | 158 |
159 All updated files will be appended to file_list. | 159 All updated files will be appended to file_list. |
160 | 160 |
161 Raises: | 161 Raises: |
162 Error: if can't get URL for relative path. | 162 Error: if can't get URL for relative path. |
163 """ | 163 """ |
164 | 164 |
(...skipping 24 matching lines...) Expand all Loading... |
189 if revision.startswith('refs/heads/'): | 189 if revision.startswith('refs/heads/'): |
190 rev_type = "branch" | 190 rev_type = "branch" |
191 elif revision.startswith('origin/'): | 191 elif revision.startswith('origin/'): |
192 # For compatability with old naming, translate 'origin' to 'refs/heads' | 192 # For compatability with old naming, translate 'origin' to 'refs/heads' |
193 revision = revision.replace('origin/', 'refs/heads/') | 193 revision = revision.replace('origin/', 'refs/heads/') |
194 rev_type = "branch" | 194 rev_type = "branch" |
195 else: | 195 else: |
196 # hash is also a tag, only make a distinction at checkout | 196 # hash is also a tag, only make a distinction at checkout |
197 rev_type = "hash" | 197 rev_type = "hash" |
198 | 198 |
199 | |
200 if not os.path.exists(self.checkout_path): | 199 if not os.path.exists(self.checkout_path): |
201 self._Clone(rev_type, revision, url, options.verbose) | 200 self._Clone(rev_type, revision, url, options.verbose) |
202 files = self._Run(['ls-files']).split() | 201 files = self._Run(['ls-files']).split() |
203 file_list.extend([os.path.join(self.checkout_path, f) for f in files]) | 202 file_list.extend([os.path.join(self.checkout_path, f) for f in files]) |
204 if not verbose: | 203 if not verbose: |
205 # Make the output a little prettier. It's nice to have some whitespace | 204 # Make the output a little prettier. It's nice to have some whitespace |
206 # between projects when cloning. | 205 # between projects when cloning. |
207 print "" | 206 print "" |
208 return | 207 return |
209 | 208 |
(...skipping 24 matching lines...) Expand all Loading... |
234 # - rebase those changes on top of the hash | 233 # - rebase those changes on top of the hash |
235 # 3) current branch based on a remote with or without changes, no switch | 234 # 3) current branch based on a remote with or without changes, no switch |
236 # - see if we can FF, if not, prompt the user for rebase, merge, or stop | 235 # - see if we can FF, if not, prompt the user for rebase, merge, or stop |
237 # 4) current branch based on a remote, switches to a new remote | 236 # 4) current branch based on a remote, switches to a new remote |
238 # - exit | 237 # - exit |
239 | 238 |
240 # GetUpstream returns something like 'refs/remotes/origin/master' for a | 239 # GetUpstream returns something like 'refs/remotes/origin/master' for a |
241 # tracking branch | 240 # tracking branch |
242 # or 'master' if not a tracking branch (it's based on a specific rev/hash) | 241 # or 'master' if not a tracking branch (it's based on a specific rev/hash) |
243 # or it returns None if it couldn't find an upstream | 242 # or it returns None if it couldn't find an upstream |
244 upstream_branch = self.GetUpstream(self.checkout_path) | 243 upstream_branch = scm.GIT.GetUpstream(self.checkout_path) |
245 if not upstream_branch or not upstream_branch.startswith('refs/remotes'): | 244 if not upstream_branch or not upstream_branch.startswith('refs/remotes'): |
246 current_type = "hash" | 245 current_type = "hash" |
247 logging.debug("Current branch is based off a specific rev and is not " | 246 logging.debug("Current branch is based off a specific rev and is not " |
248 "tracking an upstream.") | 247 "tracking an upstream.") |
249 elif upstream_branch.startswith('refs/remotes'): | 248 elif upstream_branch.startswith('refs/remotes'): |
250 current_type = "branch" | 249 current_type = "branch" |
251 else: | 250 else: |
252 raise gclient_utils.Error('Invalid Upstream') | 251 raise gclient_utils.Error('Invalid Upstream') |
253 | 252 |
254 # Update the remotes first so we have all the refs. | 253 # Update the remotes first so we have all the refs. |
255 for i in range(3): | 254 for _ in range(3): |
256 try: | 255 try: |
257 remote_output, remote_err = self.Capture( | 256 remote_output, remote_err = scm.GIT.Capture( |
258 ['remote'] + verbose + ['update'], | 257 ['remote'] + verbose + ['update'], |
259 self.checkout_path, | 258 self.checkout_path, |
260 print_error=False) | 259 print_error=False) |
261 break | 260 break |
262 except gclient_utils.CheckCallError, e: | 261 except gclient_utils.CheckCallError, e: |
263 # Hackish but at that point, git is known to work so just checking for | 262 # Hackish but at that point, git is known to work so just checking for |
264 # 502 in stderr should be fine. | 263 # 502 in stderr should be fine. |
265 if '502' in e.stderr: | 264 if '502' in e.stderr: |
266 print str(e) | 265 print str(e) |
267 print "Retrying..." | 266 print "Retrying..." |
268 continue | 267 continue |
269 raise e | 268 raise e |
270 | 269 |
271 if verbose: | 270 if verbose: |
272 print remote_output.strip() | 271 print remote_output.strip() |
273 # git remote update prints to stderr when used with --verbose | 272 # git remote update prints to stderr when used with --verbose |
274 print remote_err.strip() | 273 print remote_err.strip() |
275 | 274 |
276 # This is a big hammer, debatable if it should even be here... | 275 # This is a big hammer, debatable if it should even be here... |
277 if options.force or options.reset: | 276 if options.force or options.reset: |
278 self._Run(['reset', '--hard', 'HEAD'], redirect_stdout=False) | 277 self._Run(['reset', '--hard', 'HEAD'], redirect_stdout=False) |
279 | 278 |
280 if current_type is 'hash': | 279 if current_type == 'hash': |
281 # case 1 | 280 # case 1 |
282 if self.IsGitSvn(self.checkout_path) and upstream_branch is not None: | 281 if scm.GIT.IsGitSvn(self.checkout_path) and upstream_branch is not None: |
283 # Our git-svn branch (upstream_branch) is our upstream | 282 # Our git-svn branch (upstream_branch) is our upstream |
284 self._AttemptRebase(upstream_branch, files, verbose=options.verbose, | 283 self._AttemptRebase(upstream_branch, files, verbose=options.verbose, |
285 newbase=revision, printed_path=printed_path) | 284 newbase=revision, printed_path=printed_path) |
286 printed_path = True | 285 printed_path = True |
287 else: | 286 else: |
288 # Can't find a merge-base since we don't know our upstream. That makes | 287 # Can't find a merge-base since we don't know our upstream. That makes |
289 # this command VERY likely to produce a rebase failure. For now we | 288 # this command VERY likely to produce a rebase failure. For now we |
290 # assume origin is our upstream since that's what the old behavior was. | 289 # assume origin is our upstream since that's what the old behavior was. |
291 upstream_branch = 'origin' | 290 upstream_branch = 'origin' |
292 if options.revision: | 291 if options.revision: |
293 upstream_branch = revision | 292 upstream_branch = revision |
294 self._AttemptRebase(upstream_branch, files=files, | 293 self._AttemptRebase(upstream_branch, files=files, |
295 verbose=options.verbose, printed_path=printed_path) | 294 verbose=options.verbose, printed_path=printed_path) |
296 printed_path = True | 295 printed_path = True |
297 elif rev_type is 'hash': | 296 elif rev_type == 'hash': |
298 # case 2 | 297 # case 2 |
299 self._AttemptRebase(upstream_branch, files, verbose=options.verbose, | 298 self._AttemptRebase(upstream_branch, files, verbose=options.verbose, |
300 newbase=revision, printed_path=printed_path) | 299 newbase=revision, printed_path=printed_path) |
301 printed_path = True | 300 printed_path = True |
302 elif revision.replace('heads', 'remotes/origin') != upstream_branch: | 301 elif revision.replace('heads', 'remotes/origin') != upstream_branch: |
303 # case 4 | 302 # case 4 |
304 new_base = revision.replace('heads', 'remotes/origin') | 303 new_base = revision.replace('heads', 'remotes/origin') |
305 if not printed_path: | 304 if not printed_path: |
306 print("\n_____ %s%s" % (self.relpath, rev_str)) | 305 print("\n_____ %s%s" % (self.relpath, rev_str)) |
307 switch_error = ("Switching upstream branch from %s to %s\n" | 306 switch_error = ("Switching upstream branch from %s to %s\n" |
308 % (upstream_branch, new_base) + | 307 % (upstream_branch, new_base) + |
309 "Please merge or rebase manually:\n" + | 308 "Please merge or rebase manually:\n" + |
310 "cd %s; git rebase %s\n" % (self.checkout_path, new_base) + | 309 "cd %s; git rebase %s\n" % (self.checkout_path, new_base) + |
311 "OR git checkout -b <some new branch> %s" % new_base) | 310 "OR git checkout -b <some new branch> %s" % new_base) |
312 raise gclient_utils.Error(switch_error) | 311 raise gclient_utils.Error(switch_error) |
313 else: | 312 else: |
314 # case 3 - the default case | 313 # case 3 - the default case |
315 files = self._Run(['diff', upstream_branch, '--name-only']).split() | 314 files = self._Run(['diff', upstream_branch, '--name-only']).split() |
316 if verbose: | 315 if verbose: |
317 print "Trying fast-forward merge to branch : %s" % upstream_branch | 316 print "Trying fast-forward merge to branch : %s" % upstream_branch |
318 try: | 317 try: |
319 merge_output, merge_err = self.Capture(['merge', '--ff-only', | 318 merge_output, merge_err = scm.GIT.Capture(['merge', '--ff-only', |
320 upstream_branch], | 319 upstream_branch], |
321 self.checkout_path, | 320 self.checkout_path, |
322 print_error=False) | 321 print_error=False) |
323 except gclient_utils.CheckCallError, e: | 322 except gclient_utils.CheckCallError, e: |
324 if re.match('fatal: Not possible to fast-forward, aborting.', e.stderr): | 323 if re.match('fatal: Not possible to fast-forward, aborting.', e.stderr): |
325 if not printed_path: | 324 if not printed_path: |
326 print("\n_____ %s%s" % (self.relpath, rev_str)) | 325 print("\n_____ %s%s" % (self.relpath, rev_str)) |
327 printed_path = True | 326 printed_path = True |
328 while True: | 327 while True: |
329 try: | 328 try: |
330 action = str(raw_input("Cannot fast-forward merge, attempt to " | 329 action = str(raw_input("Cannot fast-forward merge, attempt to " |
331 "rebase? (y)es / (q)uit / (s)kip : ")) | 330 "rebase? (y)es / (q)uit / (s)kip : ")) |
332 except ValueError: | 331 except ValueError: |
(...skipping 107 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
440 if not verbose: | 439 if not verbose: |
441 # git clone doesn't seem to insert a newline properly before printing | 440 # git clone doesn't seem to insert a newline properly before printing |
442 # to stdout | 441 # to stdout |
443 print "" | 442 print "" |
444 | 443 |
445 clone_cmd = ['clone'] | 444 clone_cmd = ['clone'] |
446 if verbose: | 445 if verbose: |
447 clone_cmd.append('--verbose') | 446 clone_cmd.append('--verbose') |
448 clone_cmd.extend([url, self.checkout_path]) | 447 clone_cmd.extend([url, self.checkout_path]) |
449 | 448 |
450 for i in range(3): | 449 for _ in range(3): |
451 try: | 450 try: |
452 self._Run(clone_cmd, cwd=self._root_dir, redirect_stdout=False) | 451 self._Run(clone_cmd, cwd=self._root_dir, redirect_stdout=False) |
453 break | 452 break |
454 except gclient_utils.Error, e: | 453 except gclient_utils.Error, e: |
455 # TODO(maruel): Hackish, should be fixed by moving _Run() to | 454 # TODO(maruel): Hackish, should be fixed by moving _Run() to |
456 # CheckCall(). | 455 # CheckCall(). |
457 # Too bad we don't have access to the actual output. | 456 # Too bad we don't have access to the actual output. |
458 # We should check for "transfer closed with NNN bytes remaining to | 457 # We should check for "transfer closed with NNN bytes remaining to |
459 # read". In the meantime, just make sure .git exists. | 458 # read". In the meantime, just make sure .git exists. |
460 if (e.args[0] == 'git command clone returned 128' and | 459 if (e.args[0] == 'git command clone returned 128' and |
461 os.path.exists(os.path.join(self.checkout_path, '.git'))): | 460 os.path.exists(os.path.join(self.checkout_path, '.git'))): |
462 print str(e) | 461 print str(e) |
463 print "Retrying..." | 462 print "Retrying..." |
464 continue | 463 continue |
465 raise e | 464 raise e |
466 | 465 |
467 if rev_type is "branch": | 466 if rev_type == "branch": |
468 short_rev = revision.replace('refs/heads/', '') | 467 short_rev = revision.replace('refs/heads/', '') |
469 new_branch = revision.replace('heads', 'remotes/origin') | 468 new_branch = revision.replace('heads', 'remotes/origin') |
470 elif revision.startswith('refs/tags/'): | 469 elif revision.startswith('refs/tags/'): |
471 short_rev = revision.replace('refs/tags/', '') | 470 short_rev = revision.replace('refs/tags/', '') |
472 new_branch = revision | 471 new_branch = revision |
473 else: | 472 else: |
474 # revision is a specific sha1 hash | 473 # revision is a specific sha1 hash |
475 short_rev = revision | 474 short_rev = revision |
476 new_branch = revision | 475 new_branch = revision |
477 | 476 |
(...skipping 21 matching lines...) Expand all Loading... |
499 rebase_cmd = ['rebase'] | 498 rebase_cmd = ['rebase'] |
500 if verbose: | 499 if verbose: |
501 rebase_cmd.append('--verbose') | 500 rebase_cmd.append('--verbose') |
502 if newbase: | 501 if newbase: |
503 rebase_cmd.extend(['--onto', newbase]) | 502 rebase_cmd.extend(['--onto', newbase]) |
504 rebase_cmd.append(upstream) | 503 rebase_cmd.append(upstream) |
505 if branch: | 504 if branch: |
506 rebase_cmd.append(branch) | 505 rebase_cmd.append(branch) |
507 | 506 |
508 try: | 507 try: |
509 rebase_output, rebase_err = self.Capture(rebase_cmd, self.checkout_path, | 508 rebase_output, rebase_err = scm.GIT.Capture(rebase_cmd, |
510 print_error=False) | 509 self.checkout_path, |
| 510 print_error=False) |
511 except gclient_utils.CheckCallError, e: | 511 except gclient_utils.CheckCallError, e: |
512 if re.match(r'cannot rebase: you have unstaged changes', e.stderr) or \ | 512 if re.match(r'cannot rebase: you have unstaged changes', e.stderr) or \ |
513 re.match(r'cannot rebase: your index contains uncommitted changes', | 513 re.match(r'cannot rebase: your index contains uncommitted changes', |
514 e.stderr): | 514 e.stderr): |
515 while True: | 515 while True: |
516 rebase_action = str(raw_input("Cannot rebase because of unstaged " | 516 rebase_action = str(raw_input("Cannot rebase because of unstaged " |
517 "changes.\n'git reset --hard HEAD' ?\n" | 517 "changes.\n'git reset --hard HEAD' ?\n" |
518 "WARNING: destroys any uncommitted " | 518 "WARNING: destroys any uncommitted " |
519 "work in your current branch!" | 519 "work in your current branch!" |
520 " (y)es / (q)uit / (s)how : ")) | 520 " (y)es / (q)uit / (s)how : ")) |
521 if re.match(r'yes|y', rebase_action, re.I): | 521 if re.match(r'yes|y', rebase_action, re.I): |
522 self._Run(['reset', '--hard', 'HEAD'], redirect_stdout=False) | 522 self._Run(['reset', '--hard', 'HEAD'], redirect_stdout=False) |
523 # Should this be recursive? | 523 # Should this be recursive? |
524 rebase_output, rebase_err = self.Capture(rebase_cmd, | 524 rebase_output, rebase_err = scm.GIT.Capture(rebase_cmd, |
525 self.checkout_path) | 525 self.checkout_path) |
526 break | 526 break |
527 elif re.match(r'quit|q', rebase_action, re.I): | 527 elif re.match(r'quit|q', rebase_action, re.I): |
528 raise gclient_utils.Error("Please merge or rebase manually\n" | 528 raise gclient_utils.Error("Please merge or rebase manually\n" |
529 "cd %s && git " % self.checkout_path | 529 "cd %s && git " % self.checkout_path |
530 + "%s" % ' '.join(rebase_cmd)) | 530 + "%s" % ' '.join(rebase_cmd)) |
531 elif re.match(r'show|s', rebase_action, re.I): | 531 elif re.match(r'show|s', rebase_action, re.I): |
532 print "\n%s" % e.stderr.strip() | 532 print "\n%s" % e.stderr.strip() |
533 continue | 533 continue |
534 else: | 534 else: |
535 gclient_utils.Error("Input not recognized") | 535 gclient_utils.Error("Input not recognized") |
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
571 | 571 |
572 def _Run(self, args, cwd=None, redirect_stdout=True): | 572 def _Run(self, args, cwd=None, redirect_stdout=True): |
573 # TODO(maruel): Merge with Capture or better gclient_utils.CheckCall(). | 573 # TODO(maruel): Merge with Capture or better gclient_utils.CheckCall(). |
574 if cwd is None: | 574 if cwd is None: |
575 cwd = self.checkout_path | 575 cwd = self.checkout_path |
576 stdout = None | 576 stdout = None |
577 if redirect_stdout: | 577 if redirect_stdout: |
578 stdout = subprocess.PIPE | 578 stdout = subprocess.PIPE |
579 if cwd == None: | 579 if cwd == None: |
580 cwd = self.checkout_path | 580 cwd = self.checkout_path |
581 cmd = [self.COMMAND] | 581 cmd = [scm.GIT.COMMAND] |
582 cmd.extend(args) | 582 cmd.extend(args) |
583 logging.debug(cmd) | 583 logging.debug(cmd) |
584 try: | 584 try: |
585 sp = subprocess.Popen(cmd, cwd=cwd, stdout=stdout) | 585 sp = subprocess.Popen(cmd, cwd=cwd, stdout=stdout) |
586 output = sp.communicate()[0] | 586 output = sp.communicate()[0] |
587 except OSError: | 587 except OSError: |
588 raise gclient_utils.Error("git command '%s' failed to run." % | 588 raise gclient_utils.Error("git command '%s' failed to run." % |
589 ' '.join(cmd) + "\nCheck that you have git installed.") | 589 ' '.join(cmd) + "\nCheck that you have git installed.") |
590 if sp.returncode: | 590 if sp.returncode: |
591 raise gclient_utils.Error('git command %s returned %d' % | 591 raise gclient_utils.Error('git command %s returned %d' % |
592 (args[0], sp.returncode)) | 592 (args[0], sp.returncode)) |
593 if output is not None: | 593 if output is not None: |
594 return output.strip() | 594 return output.strip() |
595 | 595 |
596 | 596 |
597 class SVNWrapper(SCMWrapper, scm.SVN): | 597 class SVNWrapper(SCMWrapper): |
598 """ Wrapper for SVN """ | 598 """ Wrapper for SVN """ |
599 | 599 |
600 def cleanup(self, options, args, file_list): | 600 def cleanup(self, options, args, file_list): |
601 """Cleanup working copy.""" | 601 """Cleanup working copy.""" |
602 __pychecker__ = 'unusednames=file_list,options' | 602 __pychecker__ = 'unusednames=file_list,options' |
603 command = ['cleanup'] | 603 command = ['cleanup'] |
604 command.extend(args) | 604 command.extend(args) |
605 self.Run(command, os.path.join(self._root_dir, self.relpath)) | 605 scm.SVN.Run(command, os.path.join(self._root_dir, self.relpath)) |
606 | 606 |
607 def diff(self, options, args, file_list): | 607 def diff(self, options, args, file_list): |
608 # NOTE: This function does not currently modify file_list. | 608 # NOTE: This function does not currently modify file_list. |
609 __pychecker__ = 'unusednames=file_list,options' | 609 __pychecker__ = 'unusednames=file_list,options' |
610 command = ['diff'] | 610 command = ['diff'] |
611 command.extend(args) | 611 command.extend(args) |
612 self.Run(command, os.path.join(self._root_dir, self.relpath)) | 612 scm.SVN.Run(command, os.path.join(self._root_dir, self.relpath)) |
613 | 613 |
614 def export(self, options, args, file_list): | 614 def export(self, options, args, file_list): |
615 """Export a clean directory tree into the given path.""" | 615 """Export a clean directory tree into the given path.""" |
616 __pychecker__ = 'unusednames=file_list,options' | 616 __pychecker__ = 'unusednames=file_list,options' |
617 assert len(args) == 1 | 617 assert len(args) == 1 |
618 export_path = os.path.abspath(os.path.join(args[0], self.relpath)) | 618 export_path = os.path.abspath(os.path.join(args[0], self.relpath)) |
619 try: | 619 try: |
620 os.makedirs(export_path) | 620 os.makedirs(export_path) |
621 except OSError: | 621 except OSError: |
622 pass | 622 pass |
623 assert os.path.exists(export_path) | 623 assert os.path.exists(export_path) |
624 command = ['export', '--force', '.'] | 624 command = ['export', '--force', '.'] |
625 command.append(export_path) | 625 command.append(export_path) |
626 self.Run(command, os.path.join(self._root_dir, self.relpath)) | 626 scm.SVN.Run(command, os.path.join(self._root_dir, self.relpath)) |
627 | 627 |
628 def pack(self, options, args, file_list): | 628 def pack(self, options, args, file_list): |
629 """Generates a patch file which can be applied to the root of the | 629 """Generates a patch file which can be applied to the root of the |
630 repository.""" | 630 repository.""" |
631 __pychecker__ = 'unusednames=file_list,options' | 631 __pychecker__ = 'unusednames=file_list,options' |
632 path = os.path.join(self._root_dir, self.relpath) | 632 path = os.path.join(self._root_dir, self.relpath) |
633 command = ['diff'] | 633 command = ['diff'] |
634 command.extend(args) | 634 command.extend(args) |
635 | 635 |
636 filterer = DiffFilterer(self.relpath) | 636 filterer = DiffFilterer(self.relpath) |
637 self.RunAndFilterOutput(command, path, False, False, filterer.Filter) | 637 scm.SVN.RunAndFilterOutput(command, path, False, False, filterer.Filter) |
638 | 638 |
639 def update(self, options, args, file_list): | 639 def update(self, options, args, file_list): |
640 """Runs svn to update or transparently checkout the working copy. | 640 """Runs svn to update or transparently checkout the working copy. |
641 | 641 |
642 All updated files will be appended to file_list. | 642 All updated files will be appended to file_list. |
643 | 643 |
644 Raises: | 644 Raises: |
645 Error: if can't get URL for relative path. | 645 Error: if can't get URL for relative path. |
646 """ | 646 """ |
647 # Only update if git is not controlling the directory. | 647 # Only update if git is not controlling the directory. |
(...skipping 16 matching lines...) Expand all Loading... |
664 if revision: | 664 if revision: |
665 forced_revision = True | 665 forced_revision = True |
666 url = '%s@%s' % (url, revision) | 666 url = '%s@%s' % (url, revision) |
667 rev_str = ' at %s' % revision | 667 rev_str = ' at %s' % revision |
668 | 668 |
669 if not os.path.exists(checkout_path): | 669 if not os.path.exists(checkout_path): |
670 # We need to checkout. | 670 # We need to checkout. |
671 command = ['checkout', url, checkout_path] | 671 command = ['checkout', url, checkout_path] |
672 if revision: | 672 if revision: |
673 command.extend(['--revision', str(revision)]) | 673 command.extend(['--revision', str(revision)]) |
674 self.RunAndGetFileList(options, command, self._root_dir, file_list) | 674 scm.SVN.RunAndGetFileList(options, command, self._root_dir, file_list) |
675 return | 675 return |
676 | 676 |
677 # Get the existing scm url and the revision number of the current checkout. | 677 # Get the existing scm url and the revision number of the current checkout. |
678 from_info = self.CaptureInfo(os.path.join(checkout_path, '.'), '.') | 678 from_info = scm.SVN.CaptureInfo(os.path.join(checkout_path, '.'), '.') |
679 if not from_info: | 679 if not from_info: |
680 raise gclient_utils.Error("Can't update/checkout %r if an unversioned " | 680 raise gclient_utils.Error("Can't update/checkout %r if an unversioned " |
681 "directory is present. Delete the directory " | 681 "directory is present. Delete the directory " |
682 "and try again." % | 682 "and try again." % |
683 checkout_path) | 683 checkout_path) |
684 | 684 |
685 if options.manually_grab_svn_rev: | 685 if options.manually_grab_svn_rev: |
686 # Retrieve the current HEAD version because svn is slow at null updates. | 686 # Retrieve the current HEAD version because svn is slow at null updates. |
687 if not revision: | 687 if not revision: |
688 from_info_live = self.CaptureInfo(from_info['URL'], '.') | 688 from_info_live = scm.SVN.CaptureInfo(from_info['URL'], '.') |
689 revision = str(from_info_live['Revision']) | 689 revision = str(from_info_live['Revision']) |
690 rev_str = ' at %s' % revision | 690 rev_str = ' at %s' % revision |
691 | 691 |
692 if from_info['URL'] != base_url: | 692 if from_info['URL'] != base_url: |
693 to_info = self.CaptureInfo(url, '.') | 693 to_info = scm.SVN.CaptureInfo(url, '.') |
694 if not to_info.get('Repository Root') or not to_info.get('UUID'): | 694 if not to_info.get('Repository Root') or not to_info.get('UUID'): |
695 # The url is invalid or the server is not accessible, it's safer to bail | 695 # The url is invalid or the server is not accessible, it's safer to bail |
696 # out right now. | 696 # out right now. |
697 raise gclient_utils.Error('This url is unreachable: %s' % url) | 697 raise gclient_utils.Error('This url is unreachable: %s' % url) |
698 can_switch = ((from_info['Repository Root'] != to_info['Repository Root']) | 698 can_switch = ((from_info['Repository Root'] != to_info['Repository Root']) |
699 and (from_info['UUID'] == to_info['UUID'])) | 699 and (from_info['UUID'] == to_info['UUID'])) |
700 if can_switch: | 700 if can_switch: |
701 print("\n_____ relocating %s to a new checkout" % self.relpath) | 701 print("\n_____ relocating %s to a new checkout" % self.relpath) |
702 # We have different roots, so check if we can switch --relocate. | 702 # We have different roots, so check if we can switch --relocate. |
703 # Subversion only permits this if the repository UUIDs match. | 703 # Subversion only permits this if the repository UUIDs match. |
704 # Perform the switch --relocate, then rewrite the from_url | 704 # Perform the switch --relocate, then rewrite the from_url |
705 # to reflect where we "are now." (This is the same way that | 705 # to reflect where we "are now." (This is the same way that |
706 # Subversion itself handles the metadata when switch --relocate | 706 # Subversion itself handles the metadata when switch --relocate |
707 # is used.) This makes the checks below for whether we | 707 # is used.) This makes the checks below for whether we |
708 # can update to a revision or have to switch to a different | 708 # can update to a revision or have to switch to a different |
709 # branch work as expected. | 709 # branch work as expected. |
710 # TODO(maruel): TEST ME ! | 710 # TODO(maruel): TEST ME ! |
711 command = ["switch", "--relocate", | 711 command = ["switch", "--relocate", |
712 from_info['Repository Root'], | 712 from_info['Repository Root'], |
713 to_info['Repository Root'], | 713 to_info['Repository Root'], |
714 self.relpath] | 714 self.relpath] |
715 self.Run(command, self._root_dir) | 715 scm.SVN.Run(command, self._root_dir) |
716 from_info['URL'] = from_info['URL'].replace( | 716 from_info['URL'] = from_info['URL'].replace( |
717 from_info['Repository Root'], | 717 from_info['Repository Root'], |
718 to_info['Repository Root']) | 718 to_info['Repository Root']) |
719 else: | 719 else: |
720 if self.CaptureStatus(checkout_path): | 720 if scm.SVN.CaptureStatus(checkout_path): |
721 raise gclient_utils.Error("Can't switch the checkout to %s; UUID " | 721 raise gclient_utils.Error("Can't switch the checkout to %s; UUID " |
722 "don't match and there is local changes " | 722 "don't match and there is local changes " |
723 "in %s. Delete the directory and " | 723 "in %s. Delete the directory and " |
724 "try again." % (url, checkout_path)) | 724 "try again." % (url, checkout_path)) |
725 # Ok delete it. | 725 # Ok delete it. |
726 print("\n_____ switching %s to a new checkout" % self.relpath) | 726 print("\n_____ switching %s to a new checkout" % self.relpath) |
727 gclient_utils.RemoveDirectory(checkout_path) | 727 gclient_utils.RemoveDirectory(checkout_path) |
728 # We need to checkout. | 728 # We need to checkout. |
729 command = ['checkout', url, checkout_path] | 729 command = ['checkout', url, checkout_path] |
730 if revision: | 730 if revision: |
731 command.extend(['--revision', str(revision)]) | 731 command.extend(['--revision', str(revision)]) |
732 self.RunAndGetFileList(options, command, self._root_dir, file_list) | 732 scm.SVN.RunAndGetFileList(options, command, self._root_dir, file_list) |
733 return | 733 return |
734 | 734 |
735 | 735 |
736 # If the provided url has a revision number that matches the revision | 736 # If the provided url has a revision number that matches the revision |
737 # number of the existing directory, then we don't need to bother updating. | 737 # number of the existing directory, then we don't need to bother updating. |
738 if not options.force and str(from_info['Revision']) == revision: | 738 if not options.force and str(from_info['Revision']) == revision: |
739 if options.verbose or not forced_revision: | 739 if options.verbose or not forced_revision: |
740 print("\n_____ %s%s" % (self.relpath, rev_str)) | 740 print("\n_____ %s%s" % (self.relpath, rev_str)) |
741 return | 741 return |
742 | 742 |
743 command = ["update", checkout_path] | 743 command = ["update", checkout_path] |
744 if revision: | 744 if revision: |
745 command.extend(['--revision', str(revision)]) | 745 command.extend(['--revision', str(revision)]) |
746 self.RunAndGetFileList(options, command, self._root_dir, file_list) | 746 scm.SVN.RunAndGetFileList(options, command, self._root_dir, file_list) |
747 | 747 |
748 def revert(self, options, args, file_list): | 748 def revert(self, options, args, file_list): |
749 """Reverts local modifications. Subversion specific. | 749 """Reverts local modifications. Subversion specific. |
750 | 750 |
751 All reverted files will be appended to file_list, even if Subversion | 751 All reverted files will be appended to file_list, even if Subversion |
752 doesn't know about them. | 752 doesn't know about them. |
753 """ | 753 """ |
754 __pychecker__ = 'unusednames=args' | 754 __pychecker__ = 'unusednames=args' |
755 path = os.path.join(self._root_dir, self.relpath) | 755 path = os.path.join(self._root_dir, self.relpath) |
756 if not os.path.isdir(path): | 756 if not os.path.isdir(path): |
757 # svn revert won't work if the directory doesn't exist. It needs to | 757 # svn revert won't work if the directory doesn't exist. It needs to |
758 # checkout instead. | 758 # checkout instead. |
759 print("\n_____ %s is missing, synching instead" % self.relpath) | 759 print("\n_____ %s is missing, synching instead" % self.relpath) |
760 # Don't reuse the args. | 760 # Don't reuse the args. |
761 return self.update(options, [], file_list) | 761 return self.update(options, [], file_list) |
762 | 762 |
763 for file_status in self.CaptureStatus(path): | 763 for file_status in scm.SVN.CaptureStatus(path): |
764 file_path = os.path.join(path, file_status[1]) | 764 file_path = os.path.join(path, file_status[1]) |
765 if file_status[0][0] == 'X': | 765 if file_status[0][0] == 'X': |
766 # Ignore externals. | 766 # Ignore externals. |
767 logging.info('Ignoring external %s' % file_path) | 767 logging.info('Ignoring external %s' % file_path) |
768 continue | 768 continue |
769 | 769 |
770 if logging.getLogger().isEnabledFor(logging.INFO): | 770 if logging.getLogger().isEnabledFor(logging.INFO): |
771 logging.info('%s%s' % (file[0], file[1])) | 771 logging.info('%s%s' % (file[0], file[1])) |
772 else: | 772 else: |
773 print(file_path) | 773 print(file_path) |
(...skipping 14 matching lines...) Expand all Loading... |
788 gclient_utils.RemoveDirectory(file_path) | 788 gclient_utils.RemoveDirectory(file_path) |
789 else: | 789 else: |
790 logging.error('no idea what is %s.\nYou just found a bug in gclient' | 790 logging.error('no idea what is %s.\nYou just found a bug in gclient' |
791 ', please ping maruel@chromium.org ASAP!' % file_path) | 791 ', please ping maruel@chromium.org ASAP!' % file_path) |
792 except EnvironmentError: | 792 except EnvironmentError: |
793 logging.error('Failed to remove %s.' % file_path) | 793 logging.error('Failed to remove %s.' % file_path) |
794 | 794 |
795 try: | 795 try: |
796 # svn revert is so broken we don't even use it. Using | 796 # svn revert is so broken we don't even use it. Using |
797 # "svn up --revision BASE" achieve the same effect. | 797 # "svn up --revision BASE" achieve the same effect. |
798 self.RunAndGetFileList(options, ['update', '--revision', 'BASE'], path, | 798 scm.SVN.RunAndGetFileList(options, ['update', '--revision', 'BASE'], path, |
799 file_list) | 799 file_list) |
800 except OSError, e: | 800 except OSError, e: |
801 # Maybe the directory disapeared meanwhile. We don't want it to throw an | 801 # Maybe the directory disapeared meanwhile. We don't want it to throw an |
802 # exception. | 802 # exception. |
803 logging.error('Failed to update:\n%s' % str(e)) | 803 logging.error('Failed to update:\n%s' % str(e)) |
804 | 804 |
805 def revinfo(self, options, args, file_list): | 805 def revinfo(self, options, args, file_list): |
806 """Display revision""" | 806 """Display revision""" |
807 __pychecker__ = 'unusednames=args,file_list,options' | 807 __pychecker__ = 'unusednames=args,file_list,options' |
808 return self.CaptureHeadRevision(self.url) | 808 return scm.SVN.CaptureHeadRevision(self.url) |
809 | 809 |
810 def runhooks(self, options, args, file_list): | 810 def runhooks(self, options, args, file_list): |
811 self.status(options, args, file_list) | 811 self.status(options, args, file_list) |
812 | 812 |
813 def status(self, options, args, file_list): | 813 def status(self, options, args, file_list): |
814 """Display status information.""" | 814 """Display status information.""" |
815 path = os.path.join(self._root_dir, self.relpath) | 815 path = os.path.join(self._root_dir, self.relpath) |
816 command = ['status'] | 816 command = ['status'] |
817 command.extend(args) | 817 command.extend(args) |
818 if not os.path.isdir(path): | 818 if not os.path.isdir(path): |
819 # svn status won't work if the directory doesn't exist. | 819 # svn status won't work if the directory doesn't exist. |
820 print("\n________ couldn't run \'%s\' in \'%s\':\nThe directory " | 820 print("\n________ couldn't run \'%s\' in \'%s\':\nThe directory " |
821 "does not exist." | 821 "does not exist." |
822 % (' '.join(command), path)) | 822 % (' '.join(command), path)) |
823 # There's no file list to retrieve. | 823 # There's no file list to retrieve. |
824 else: | 824 else: |
825 self.RunAndGetFileList(options, command, path, file_list) | 825 scm.SVN.RunAndGetFileList(options, command, path, file_list) |
826 | 826 |
827 def FullUrlForRelativeUrl(self, url): | 827 def FullUrlForRelativeUrl(self, url): |
828 # Find the forth '/' and strip from there. A bit hackish. | 828 # Find the forth '/' and strip from there. A bit hackish. |
829 return '/'.join(self.url.split('/')[:4]) + url | 829 return '/'.join(self.url.split('/')[:4]) + url |
OLD | NEW |