| 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 |