| OLD | NEW |
| 1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
| 2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. | 2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 3 # Use of this source code is governed by a BSD-style license that can be | 3 # Use of this source code is governed by a BSD-style license that can be |
| 4 # found in the LICENSE file. | 4 # found in the LICENSE file. |
| 5 | 5 |
| 6 # Copyright (C) 2008 Evan Martin <martine@danga.com> | 6 # Copyright (C) 2008 Evan Martin <martine@danga.com> |
| 7 | 7 |
| 8 """A git-command for integrating reviews on Rietveld.""" | 8 """A git-command for integrating reviews on Rietveld.""" |
| 9 | 9 |
| 10 import difflib | 10 import difflib |
| (...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 62 logging.debug('Failed running %s', args) | 62 logging.debug('Failed running %s', args) |
| 63 if not error_ok: | 63 if not error_ok: |
| 64 DieWithError( | 64 DieWithError( |
| 65 'Command "%s" failed.\n%s' % ( | 65 'Command "%s" failed.\n%s' % ( |
| 66 ' '.join(args), error_message or e.stdout or '')) | 66 ' '.join(args), error_message or e.stdout or '')) |
| 67 return e.stdout | 67 return e.stdout |
| 68 | 68 |
| 69 | 69 |
| 70 def RunGit(args, **kwargs): | 70 def RunGit(args, **kwargs): |
| 71 """Returns stdout.""" | 71 """Returns stdout.""" |
| 72 return RunCommand(['git', '--no-pager'] + args, **kwargs) | 72 return RunCommand(['git'] + args, **kwargs) |
| 73 | 73 |
| 74 | 74 |
| 75 def RunGitWithCode(args): | 75 def RunGitWithCode(args): |
| 76 """Returns return code and stdout.""" | 76 """Returns return code and stdout.""" |
| 77 try: | 77 try: |
| 78 out, code = subprocess2.communicate(['git', '--no-pager'] + args, | 78 env = os.environ.copy() |
| 79 # 'cat' is a magical git string that disables pagers on all platforms. |
| 80 env['GIT_PAGER'] = 'cat' |
| 81 out, code = subprocess2.communicate(['git'] + args, |
| 82 env=env, |
| 79 stdout=subprocess2.PIPE) | 83 stdout=subprocess2.PIPE) |
| 80 return code, out[0] | 84 return code, out[0] |
| 81 except ValueError: | 85 except ValueError: |
| 82 # When the subprocess fails, it returns None. That triggers a ValueError | 86 # When the subprocess fails, it returns None. That triggers a ValueError |
| 83 # when trying to unpack the return value into (out, code). | 87 # when trying to unpack the return value into (out, code). |
| 84 return 1, '' | 88 return 1, '' |
| 85 | 89 |
| 86 | 90 |
| 87 def usage(more): | 91 def usage(more): |
| 88 def hook(fn): | 92 def hook(fn): |
| (...skipping 129 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 218 | 222 |
| 219 | 223 |
| 220 def print_stats(similarity, find_copies, args): | 224 def print_stats(similarity, find_copies, args): |
| 221 """Prints statistics about the change to the user.""" | 225 """Prints statistics about the change to the user.""" |
| 222 # --no-ext-diff is broken in some versions of Git, so try to work around | 226 # --no-ext-diff is broken in some versions of Git, so try to work around |
| 223 # this by overriding the environment (but there is still a problem if the | 227 # this by overriding the environment (but there is still a problem if the |
| 224 # git config key "diff.external" is used). | 228 # git config key "diff.external" is used). |
| 225 env = os.environ.copy() | 229 env = os.environ.copy() |
| 226 if 'GIT_EXTERNAL_DIFF' in env: | 230 if 'GIT_EXTERNAL_DIFF' in env: |
| 227 del env['GIT_EXTERNAL_DIFF'] | 231 del env['GIT_EXTERNAL_DIFF'] |
| 232 # 'cat' is a magical git string that disables pagers on all platforms. |
| 233 env['GIT_PAGER'] = 'cat' |
| 228 | 234 |
| 229 if find_copies: | 235 if find_copies: |
| 230 similarity_options = ['--find-copies-harder', '-l100000', | 236 similarity_options = ['--find-copies-harder', '-l100000', |
| 231 '-C%s' % similarity] | 237 '-C%s' % similarity] |
| 232 else: | 238 else: |
| 233 similarity_options = ['-M%s' % similarity] | 239 similarity_options = ['-M%s' % similarity] |
| 234 | 240 |
| 235 return subprocess2.call( | 241 return subprocess2.call( |
| 236 ['git', '--no-pager', | 242 ['git', |
| 237 'diff', '--no-ext-diff', '--stat'] + similarity_options + args, | 243 'diff', '--no-ext-diff', '--stat'] + similarity_options + args, |
| 238 env=env) | 244 env=env) |
| 239 | 245 |
| 240 | 246 |
| 241 class Settings(object): | 247 class Settings(object): |
| 242 def __init__(self): | 248 def __init__(self): |
| 243 self.default_server = None | 249 self.default_server = None |
| 244 self.cc = None | 250 self.cc = None |
| 245 self.root = None | 251 self.root = None |
| 246 self.is_git_svn = None | 252 self.is_git_svn = None |
| (...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 294 DieWithError('Repo doesn\'t appear to be a git-svn repo.') | 300 DieWithError('Repo doesn\'t appear to be a git-svn repo.') |
| 295 | 301 |
| 296 # Try to figure out which remote branch we're based on. | 302 # Try to figure out which remote branch we're based on. |
| 297 # Strategy: | 303 # Strategy: |
| 298 # 1) iterate through our branch history and find the svn URL. | 304 # 1) iterate through our branch history and find the svn URL. |
| 299 # 2) find the svn-remote that fetches from the URL. | 305 # 2) find the svn-remote that fetches from the URL. |
| 300 | 306 |
| 301 # regexp matching the git-svn line that contains the URL. | 307 # regexp matching the git-svn line that contains the URL. |
| 302 git_svn_re = re.compile(r'^\s*git-svn-id: (\S+)@', re.MULTILINE) | 308 git_svn_re = re.compile(r'^\s*git-svn-id: (\S+)@', re.MULTILINE) |
| 303 | 309 |
| 310 env = os.environ.copy() |
| 311 # 'cat' is a magical git string that disables pagers on all platforms. |
| 312 env['GIT_PAGER'] = 'cat' |
| 313 |
| 304 # We don't want to go through all of history, so read a line from the | 314 # We don't want to go through all of history, so read a line from the |
| 305 # pipe at a time. | 315 # pipe at a time. |
| 306 # The -100 is an arbitrary limit so we don't search forever. | 316 # The -100 is an arbitrary limit so we don't search forever. |
| 307 cmd = ['git', '--no-pager', 'log', '-100', '--pretty=medium'] | 317 cmd = ['git', 'log', '-100', '--pretty=medium'] |
| 308 proc = subprocess2.Popen(cmd, stdout=subprocess2.PIPE) | 318 proc = subprocess2.Popen(cmd, stdout=subprocess2.PIPE, env=env) |
| 309 url = None | 319 url = None |
| 310 for line in proc.stdout: | 320 for line in proc.stdout: |
| 311 match = git_svn_re.match(line) | 321 match = git_svn_re.match(line) |
| 312 if match: | 322 if match: |
| 313 url = match.group(1) | 323 url = match.group(1) |
| 314 proc.stdout.close() # Cut pipe. | 324 proc.stdout.close() # Cut pipe. |
| 315 break | 325 break |
| 316 | 326 |
| 317 if url: | 327 if url: |
| 318 svn_remote_re = re.compile(r'^svn-remote\.([^.]+)\.url (.*)$') | 328 svn_remote_re = re.compile(r'^svn-remote\.([^.]+)\.url (.*)$') |
| (...skipping 358 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 677 RunGit(['config', self._RietveldServer(), self.rietveld_server]) | 687 RunGit(['config', self._RietveldServer(), self.rietveld_server]) |
| 678 else: | 688 else: |
| 679 RunGit(['config', '--unset', self._IssueSetting()]) | 689 RunGit(['config', '--unset', self._IssueSetting()]) |
| 680 self.SetPatchset(0) | 690 self.SetPatchset(0) |
| 681 self.has_issue = False | 691 self.has_issue = False |
| 682 | 692 |
| 683 def GetChange(self, upstream_branch, author): | 693 def GetChange(self, upstream_branch, author): |
| 684 if not self.GitSanityChecks(upstream_branch): | 694 if not self.GitSanityChecks(upstream_branch): |
| 685 DieWithError('\nGit sanity check failure') | 695 DieWithError('\nGit sanity check failure') |
| 686 | 696 |
| 687 root = RunCommand(['git', '--no-pager', 'rev-parse', '--show-cdup']).strip() | 697 env = os.environ.copy() |
| 698 # 'cat' is a magical git string that disables pagers on all platforms. |
| 699 env['GIT_PAGER'] = 'cat' |
| 700 |
| 701 root = RunCommand(['git', 'rev-parse', '--show-cdup'], env=env).strip() |
| 688 if not root: | 702 if not root: |
| 689 root = '.' | 703 root = '.' |
| 690 absroot = os.path.abspath(root) | 704 absroot = os.path.abspath(root) |
| 691 | 705 |
| 692 # We use the sha1 of HEAD as a name of this change. | 706 # We use the sha1 of HEAD as a name of this change. |
| 693 name = RunCommand(['git', '--no-pager', 'rev-parse', 'HEAD']).strip() | 707 name = RunCommand(['git', 'rev-parse', 'HEAD'], env=env).strip() |
| 694 # Need to pass a relative path for msysgit. | 708 # Need to pass a relative path for msysgit. |
| 695 try: | 709 try: |
| 696 files = scm.GIT.CaptureStatus([root], '.', upstream_branch) | 710 files = scm.GIT.CaptureStatus([root], '.', upstream_branch) |
| 697 except subprocess2.CalledProcessError: | 711 except subprocess2.CalledProcessError: |
| 698 DieWithError( | 712 DieWithError( |
| 699 ('\nFailed to diff against upstream branch %s!\n\n' | 713 ('\nFailed to diff against upstream branch %s!\n\n' |
| 700 'This branch probably doesn\'t exist anymore. To reset the\n' | 714 'This branch probably doesn\'t exist anymore. To reset the\n' |
| 701 'tracking branch, please run\n' | 715 'tracking branch, please run\n' |
| 702 ' git branch --set-upstream %s trunk\n' | 716 ' git branch --set-upstream %s trunk\n' |
| 703 'replacing trunk with origin/master or the relevant branch') % | 717 'replacing trunk with origin/master or the relevant branch') % |
| 704 (upstream_branch, self.GetBranch())) | 718 (upstream_branch, self.GetBranch())) |
| 705 | 719 |
| 706 issue = self.GetIssue() | 720 issue = self.GetIssue() |
| 707 patchset = self.GetPatchset() | 721 patchset = self.GetPatchset() |
| 708 if issue: | 722 if issue: |
| 709 description = self.GetDescription() | 723 description = self.GetDescription() |
| 710 else: | 724 else: |
| 711 # If the change was never uploaded, use the log messages of all commits | 725 # If the change was never uploaded, use the log messages of all commits |
| 712 # up to the branch point, as git cl upload will prefill the description | 726 # up to the branch point, as git cl upload will prefill the description |
| 713 # with these log messages. | 727 # with these log messages. |
| 714 description = RunCommand(['git', '--no-pager', | 728 description = RunCommand(['git', |
| 715 'log', '--pretty=format:%s%n%n%b', | 729 'log', '--pretty=format:%s%n%n%b', |
| 716 '%s...' % (upstream_branch)]).strip() | 730 '%s...' % (upstream_branch)], |
| 731 env=env).strip() |
| 717 | 732 |
| 718 if not author: | 733 if not author: |
| 719 author = RunGit(['config', 'user.email']).strip() or None | 734 author = RunGit(['config', 'user.email']).strip() or None |
| 720 return presubmit_support.GitChange( | 735 return presubmit_support.GitChange( |
| 721 name, | 736 name, |
| 722 description, | 737 description, |
| 723 absroot, | 738 absroot, |
| 724 files, | 739 files, |
| 725 issue, | 740 issue, |
| 726 patchset, | 741 patchset, |
| (...skipping 1017 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1744 # Git patches have a/ at the beginning of source paths. We strip that out | 1759 # Git patches have a/ at the beginning of source paths. We strip that out |
| 1745 # with a sed script rather than the -p flag to patch so we can feed either | 1760 # with a sed script rather than the -p flag to patch so we can feed either |
| 1746 # Git or svn-style patches into the same apply command. | 1761 # Git or svn-style patches into the same apply command. |
| 1747 # re.sub() should be used but flags=re.MULTILINE is only in python 2.7. | 1762 # re.sub() should be used but flags=re.MULTILINE is only in python 2.7. |
| 1748 try: | 1763 try: |
| 1749 patch_data = subprocess2.check_output( | 1764 patch_data = subprocess2.check_output( |
| 1750 ['sed', '-e', 's|^--- a/|--- |; s|^+++ b/|+++ |'], stdin=patch_data) | 1765 ['sed', '-e', 's|^--- a/|--- |; s|^+++ b/|+++ |'], stdin=patch_data) |
| 1751 except subprocess2.CalledProcessError: | 1766 except subprocess2.CalledProcessError: |
| 1752 DieWithError('Git patch mungling failed.') | 1767 DieWithError('Git patch mungling failed.') |
| 1753 logging.info(patch_data) | 1768 logging.info(patch_data) |
| 1769 env = os.environ.copy() |
| 1770 # 'cat' is a magical git string that disables pagers on all platforms. |
| 1771 env['GIT_PAGER'] = 'cat' |
| 1772 |
| 1754 # We use "git apply" to apply the patch instead of "patch" so that we can | 1773 # We use "git apply" to apply the patch instead of "patch" so that we can |
| 1755 # pick up file adds. | 1774 # pick up file adds. |
| 1756 # The --index flag means: also insert into the index (so we catch adds). | 1775 # The --index flag means: also insert into the index (so we catch adds). |
| 1757 cmd = ['git', '--no-pager', 'apply', '--index', '-p0'] | 1776 cmd = ['git', 'apply', '--index', '-p0'] |
| 1758 if options.reject: | 1777 if options.reject: |
| 1759 cmd.append('--reject') | 1778 cmd.append('--reject') |
| 1760 try: | 1779 try: |
| 1761 subprocess2.check_call(cmd, stdin=patch_data, stdout=subprocess2.VOID) | 1780 subprocess2.check_call(cmd, env=env, |
| 1781 stdin=patch_data, stdout=subprocess2.VOID) |
| 1762 except subprocess2.CalledProcessError: | 1782 except subprocess2.CalledProcessError: |
| 1763 DieWithError('Failed to apply the patch') | 1783 DieWithError('Failed to apply the patch') |
| 1764 | 1784 |
| 1765 # If we had an issue, commit the current state and register the issue. | 1785 # If we had an issue, commit the current state and register the issue. |
| 1766 if not options.nocommit: | 1786 if not options.nocommit: |
| 1767 RunGit(['commit', '-m', 'patch from issue %s' % issue]) | 1787 RunGit(['commit', '-m', 'patch from issue %s' % issue]) |
| 1768 cl = Changelist() | 1788 cl = Changelist() |
| 1769 cl.SetIssue(issue) | 1789 cl.SetIssue(issue) |
| 1770 cl.SetPatchset(patchset) | 1790 cl.SetPatchset(patchset) |
| 1771 print "Committed patch locally." | 1791 print "Committed patch locally." |
| 1772 else: | 1792 else: |
| 1773 print "Patch applied to index." | 1793 print "Patch applied to index." |
| 1774 return 0 | 1794 return 0 |
| 1775 | 1795 |
| 1776 | 1796 |
| 1777 def CMDrebase(parser, args): | 1797 def CMDrebase(parser, args): |
| 1778 """rebase current branch on top of svn repo""" | 1798 """rebase current branch on top of svn repo""" |
| 1779 # Provide a wrapper for git svn rebase to help avoid accidental | 1799 # Provide a wrapper for git svn rebase to help avoid accidental |
| 1780 # git svn dcommit. | 1800 # git svn dcommit. |
| 1781 # It's the only command that doesn't use parser at all since we just defer | 1801 # It's the only command that doesn't use parser at all since we just defer |
| 1782 # execution to git-svn. | 1802 # execution to git-svn. |
| 1783 return subprocess2.call(['git', '--no-pager', 'svn', 'rebase'] + args) | 1803 env = os.environ.copy() |
| 1804 # 'cat' is a magical git string that disables pagers on all platforms. |
| 1805 env['GIT_PAGER'] = 'cat' |
| 1806 |
| 1807 return subprocess2.call(['git', 'svn', 'rebase'] + args, env=env) |
| 1784 | 1808 |
| 1785 | 1809 |
| 1786 def GetTreeStatus(): | 1810 def GetTreeStatus(): |
| 1787 """Fetches the tree status and returns either 'open', 'closed', | 1811 """Fetches the tree status and returns either 'open', 'closed', |
| 1788 'unknown' or 'unset'.""" | 1812 'unknown' or 'unset'.""" |
| 1789 url = settings.GetTreeStatusUrl(error_ok=True) | 1813 url = settings.GetTreeStatusUrl(error_ok=True) |
| 1790 if url: | 1814 if url: |
| 1791 status = urllib2.urlopen(url).read().lower() | 1815 status = urllib2.urlopen(url).read().lower() |
| 1792 if status.find('closed') != -1 or status == '0': | 1816 if status.find('closed') != -1 or status == '0': |
| 1793 return 'closed' | 1817 return 'closed' |
| (...skipping 348 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2142 GenUsage(parser, 'help') | 2166 GenUsage(parser, 'help') |
| 2143 return CMDhelp(parser, argv) | 2167 return CMDhelp(parser, argv) |
| 2144 | 2168 |
| 2145 | 2169 |
| 2146 if __name__ == '__main__': | 2170 if __name__ == '__main__': |
| 2147 # These affect sys.stdout so do it outside of main() to simplify mocks in | 2171 # These affect sys.stdout so do it outside of main() to simplify mocks in |
| 2148 # unit testing. | 2172 # unit testing. |
| 2149 fix_encoding.fix_encoding() | 2173 fix_encoding.fix_encoding() |
| 2150 colorama.init() | 2174 colorama.init() |
| 2151 sys.exit(main(sys.argv[1:])) | 2175 sys.exit(main(sys.argv[1:])) |
| OLD | NEW |