| OLD | NEW |
| 1 #!/usr/bin/python | 1 #!/usr/bin/python |
| 2 # git-cl -- a git-command for integrating reviews on Rietveld | 2 # git-cl -- a git-command for integrating reviews on Rietveld |
| 3 # Copyright (C) 2008 Evan Martin <martine@danga.com> | 3 # Copyright (C) 2008 Evan Martin <martine@danga.com> |
| 4 | 4 |
| 5 import getpass | 5 import getpass |
| 6 import optparse | 6 import optparse |
| 7 import os | 7 import os |
| 8 import re | 8 import re |
| 9 import readline | |
| 10 import subprocess | 9 import subprocess |
| 11 import sys | 10 import sys |
| 12 import tempfile | 11 import tempfile |
| 13 import textwrap | 12 import textwrap |
| 14 import upload | 13 import upload |
| 15 import urllib2 | 14 import urllib2 |
| 16 | 15 |
| 16 try: |
| 17 import readline |
| 18 except ImportError: |
| 19 pass |
| 20 |
| 17 DEFAULT_SERVER = 'codereview.appspot.com' | 21 DEFAULT_SERVER = 'codereview.appspot.com' |
| 18 | 22 |
| 19 def DieWithError(message): | 23 def DieWithError(message): |
| 20 print >>sys.stderr, message | 24 print >>sys.stderr, message |
| 21 sys.exit(1) | 25 sys.exit(1) |
| 22 | 26 |
| 23 | 27 |
| 24 def RunGit(args, error_ok=False, error_message=None, exit_code=False): | 28 def RunGit(args, error_ok=False, error_message=None, exit_code=False, |
| 29 redirect_stdout=True): |
| 25 cmd = ['git'] + args | 30 cmd = ['git'] + args |
| 26 # Useful for debugging: | 31 # Useful for debugging: |
| 27 # print >>sys.stderr, ' '.join(cmd) | 32 # print >>sys.stderr, ' '.join(cmd) |
| 28 proc = subprocess.Popen(cmd, stdout=subprocess.PIPE) | 33 if redirect_stdout: |
| 34 stdout = subprocess.PIPE |
| 35 else: |
| 36 stdout = None |
| 37 proc = subprocess.Popen(cmd, stdout=stdout) |
| 29 output = proc.communicate()[0] | 38 output = proc.communicate()[0] |
| 30 if exit_code: | 39 if exit_code: |
| 31 return proc.returncode | 40 return proc.returncode |
| 32 if not error_ok and proc.returncode != 0: | 41 if not error_ok and proc.returncode != 0: |
| 33 DieWithError('Command "%s" failed.\n' % (' '.join(cmd)) + | 42 DieWithError('Command "%s" failed.\n' % (' '.join(cmd)) + |
| 34 (error_message or output)) | 43 (error_message or output)) |
| 35 return output | 44 return output |
| 36 | 45 |
| 37 | 46 |
| 38 class Settings: | 47 class Settings: |
| (...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 79 # 2) iterate through our branch history and match up the URLs. | 88 # 2) iterate through our branch history and match up the URLs. |
| 80 | 89 |
| 81 # regexp matching the git-svn line that contains the URL. | 90 # regexp matching the git-svn line that contains the URL. |
| 82 git_svn_re = re.compile(r'^\s*git-svn-id: (\S+)@', re.MULTILINE) | 91 git_svn_re = re.compile(r'^\s*git-svn-id: (\S+)@', re.MULTILINE) |
| 83 | 92 |
| 84 # Get the refname and svn url for all refs/remotes/*. | 93 # Get the refname and svn url for all refs/remotes/*. |
| 85 remotes = RunGit(['for-each-ref', '--format=%(refname)', | 94 remotes = RunGit(['for-each-ref', '--format=%(refname)', |
| 86 'refs/remotes']).splitlines() | 95 'refs/remotes']).splitlines() |
| 87 svn_refs = {} | 96 svn_refs = {} |
| 88 for ref in remotes: | 97 for ref in remotes: |
| 89 # git-svn remote refs are generally directly in the refs/remotes/dir, | |
| 90 # not a subdirectory (like refs/remotes/origin/master). | |
| 91 if '/' in ref[len('refs/remotes/'):]: | |
| 92 continue | |
| 93 match = git_svn_re.search(RunGit(['cat-file', '-p', ref])) | 98 match = git_svn_re.search(RunGit(['cat-file', '-p', ref])) |
| 94 if match: | 99 if match: |
| 95 svn_refs[match.group(1)] = ref | 100 svn_refs[match.group(1)] = ref |
| 96 | 101 |
| 97 if len(svn_refs) == 1: | 102 if len(svn_refs) == 1: |
| 98 # Only one svn branch exists -- seems like a good candidate. | 103 # Only one svn branch exists -- seems like a good candidate. |
| 99 self.svn_branch = svn_refs.values()[0] | 104 self.svn_branch = svn_refs.values()[0] |
| 100 elif len(svn_refs) > 1: | 105 elif len(svn_refs) > 1: |
| 101 # We have more than one remote branch available. We don't | 106 # We have more than one remote branch available. We don't |
| 102 # want to go through all of history, so read a line from the | 107 # want to go through all of history, so read a line from the |
| (...skipping 267 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 370 | 375 |
| 371 file = tempfile.NamedTemporaryFile() | 376 file = tempfile.NamedTemporaryFile() |
| 372 filename = file.name | 377 filename = file.name |
| 373 file.write(starting_text) | 378 file.write(starting_text) |
| 374 file.flush() | 379 file.flush() |
| 375 | 380 |
| 376 ret = subprocess.call(editor + ' ' + filename, shell=True) | 381 ret = subprocess.call(editor + ' ' + filename, shell=True) |
| 377 if ret != 0: | 382 if ret != 0: |
| 378 return | 383 return |
| 379 | 384 |
| 380 text = open(filename).read() | 385 file.flush() |
| 386 file.seek(0) |
| 387 text = file.read() |
| 381 file.close() | 388 file.close() |
| 382 stripcomment_re = re.compile(r'^#.*$', re.MULTILINE) | 389 stripcomment_re = re.compile(r'^#.*$', re.MULTILINE) |
| 383 return stripcomment_re.sub('', text).strip() | 390 return stripcomment_re.sub('', text).strip() |
| 384 | 391 |
| 385 | 392 |
| 386 def CmdUpload(args): | 393 def CmdUpload(args): |
| 387 parser = optparse.OptionParser( | 394 parser = optparse.OptionParser( |
| 388 usage='git cl upload [options] [args to "git diff"]') | 395 usage='git cl upload [options] [args to "git diff"]') |
| 389 parser.add_option('-m', dest='message', help='message for patch') | 396 parser.add_option('-m', dest='message', help='message for patch') |
| 390 parser.add_option('-r', '--reviewers', | 397 parser.add_option('-r', '--reviewers', |
| (...skipping 123 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 514 | 521 |
| 515 # We want to squash all this branch's commits into one commit with the | 522 # We want to squash all this branch's commits into one commit with the |
| 516 # proper description. | 523 # proper description. |
| 517 # We do this by doing a "merge --squash" into a new commit branch, then | 524 # We do this by doing a "merge --squash" into a new commit branch, then |
| 518 # dcommitting that. | 525 # dcommitting that. |
| 519 MERGE_BRANCH = 'git-cl-commit' | 526 MERGE_BRANCH = 'git-cl-commit' |
| 520 # Delete the merge branch if it already exists. | 527 # Delete the merge branch if it already exists. |
| 521 if RunGit(['show-ref', '--quiet', '--verify', 'refs/heads/' + MERGE_BRANCH], | 528 if RunGit(['show-ref', '--quiet', '--verify', 'refs/heads/' + MERGE_BRANCH], |
| 522 exit_code=True) == 0: | 529 exit_code=True) == 0: |
| 523 RunGit(['branch', '-D', MERGE_BRANCH]) | 530 RunGit(['branch', '-D', MERGE_BRANCH]) |
| 531 |
| 532 # We might be in a directory that's present in this branch but not in the |
| 533 # trunk. Move up to the top of the tree so that git commands that expect a |
| 534 # valid CWD won't fail after we check out the merge branch. |
| 535 rel_base_path = RunGit(['rev-parse', '--show-cdup']).strip() |
| 536 if rel_base_path: |
| 537 os.chdir(rel_base_path) |
| 538 |
| 524 # Stuff our change into the merge branch. | 539 # Stuff our change into the merge branch. |
| 525 RunGit(['checkout', '-q', '-b', MERGE_BRANCH, base_branch]) | 540 RunGit(['checkout', '-q', '-b', MERGE_BRANCH, base_branch]) |
| 526 RunGit(['merge', '--squash', cl.GetBranchRef()]) | 541 RunGit(['merge', '--squash', cl.GetBranchRef()]) |
| 527 RunGit(['commit', '-m', description]) | 542 RunGit(['commit', '-m', description]) |
| 528 # dcommit the merge branch. | 543 # dcommit the merge branch. |
| 529 output = RunGit(['svn', 'dcommit']) | 544 output = RunGit(['svn', 'dcommit']) |
| 530 # And then swap back to the original branch and clean up. | 545 # And then swap back to the original branch and clean up. |
| 531 RunGit(['checkout', '-q', cl.GetBranch()]) | 546 RunGit(['checkout', '-q', cl.GetBranch()]) |
| 532 RunGit(['branch', '-D', MERGE_BRANCH]) | 547 RunGit(['branch', '-D', MERGE_BRANCH]) |
| 533 if output.find("Committed r") != -1: | 548 if output.find("Committed r") != -1: |
| (...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 611 RunGit(['commit', '-m', 'patch from issue %s' % issue]) | 626 RunGit(['commit', '-m', 'patch from issue %s' % issue]) |
| 612 cl = Changelist() | 627 cl = Changelist() |
| 613 cl.SetIssue(issue) | 628 cl.SetIssue(issue) |
| 614 print "Committed patch." | 629 print "Committed patch." |
| 615 else: | 630 else: |
| 616 print "Patch applied to index." | 631 print "Patch applied to index." |
| 617 | 632 |
| 618 def CmdRebase(args): | 633 def CmdRebase(args): |
| 619 # Provide a wrapper for git svn rebase to help avoid accidental | 634 # Provide a wrapper for git svn rebase to help avoid accidental |
| 620 # git svn dcommit. | 635 # git svn dcommit. |
| 621 RunGit(['svn', 'rebase']) | 636 RunGit(['svn', 'rebase'], redirect_stdout=False) |
| 622 | 637 |
| 623 def GetTreeStatus(): | 638 def GetTreeStatus(): |
| 624 """Fetches the tree status and returns either 'open', 'closed', | 639 """Fetches the tree status and returns either 'open', 'closed', |
| 625 'unknown' or 'unset'.""" | 640 'unknown' or 'unset'.""" |
| 626 url = settings.GetTreeStatusUrl(error_ok=True) | 641 url = settings.GetTreeStatusUrl(error_ok=True) |
| 627 if url: | 642 if url: |
| 628 status = urllib2.urlopen(url).read().lower() | 643 status = urllib2.urlopen(url).read().lower() |
| 629 if status.find('closed') != -1: | 644 if status.find('closed') != -1: |
| 630 return 'closed' | 645 return 'closed' |
| 631 elif status.find('open') != -1: | 646 elif status.find('open') != -1: |
| (...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 673 command = argv[1] | 688 command = argv[1] |
| 674 for name, _, func in COMMANDS: | 689 for name, _, func in COMMANDS: |
| 675 if name == command: | 690 if name == command: |
| 676 return func(argv[2:]) | 691 return func(argv[2:]) |
| 677 print 'unknown command: %s' % command | 692 print 'unknown command: %s' % command |
| 678 Usage(argv[0]) | 693 Usage(argv[0]) |
| 679 | 694 |
| 680 | 695 |
| 681 if __name__ == '__main__': | 696 if __name__ == '__main__': |
| 682 sys.exit(main(sys.argv)) | 697 sys.exit(main(sys.argv)) |
| OLD | NEW |