Chromium Code Reviews| 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 |
| 79 env['GIT_PAGER'] = 'cat' # Magic string that prevents a pager. | |
|
M-A Ruel
2013/07/08 19:10:50
Move the comment on its own line.
Does this work
iannucci
2013/07/08 20:21:56
(Drive-by: Yeah, GIT_PAGER=cat is a magic value)
Daniel Bratell
2013/07/08 22:08:06
It seems to work when I test manually and accordin
| |
| 80 out, code = subprocess2.communicate(['git'] + args, | |
| 81 env=env, | |
| 79 stdout=subprocess2.PIPE) | 82 stdout=subprocess2.PIPE) |
| 80 return code, out[0] | 83 return code, out[0] |
| 81 except ValueError: | 84 except ValueError: |
| 82 # When the subprocess fails, it returns None. That triggers a ValueError | 85 # When the subprocess fails, it returns None. That triggers a ValueError |
| 83 # when trying to unpack the return value into (out, code). | 86 # when trying to unpack the return value into (out, code). |
| 84 return 1, '' | 87 return 1, '' |
| 85 | 88 |
| 86 | 89 |
| 87 def usage(more): | 90 def usage(more): |
| 88 def hook(fn): | 91 def hook(fn): |
| (...skipping 129 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 218 | 221 |
| 219 | 222 |
| 220 def print_stats(similarity, find_copies, args): | 223 def print_stats(similarity, find_copies, args): |
| 221 """Prints statistics about the change to the user.""" | 224 """Prints statistics about the change to the user.""" |
| 222 # --no-ext-diff is broken in some versions of Git, so try to work around | 225 # --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 | 226 # this by overriding the environment (but there is still a problem if the |
| 224 # git config key "diff.external" is used). | 227 # git config key "diff.external" is used). |
| 225 env = os.environ.copy() | 228 env = os.environ.copy() |
| 226 if 'GIT_EXTERNAL_DIFF' in env: | 229 if 'GIT_EXTERNAL_DIFF' in env: |
| 227 del env['GIT_EXTERNAL_DIFF'] | 230 del env['GIT_EXTERNAL_DIFF'] |
| 231 env['GIT_PAGER'] = 'cat' # Magical string that disables pagers. | |
|
M-A Ruel
2013/07/08 19:10:50
Comment on its own line
| |
| 228 | 232 |
| 229 if find_copies: | 233 if find_copies: |
| 230 similarity_options = ['--find-copies-harder', '-l100000', | 234 similarity_options = ['--find-copies-harder', '-l100000', |
| 231 '-C%s' % similarity] | 235 '-C%s' % similarity] |
| 232 else: | 236 else: |
| 233 similarity_options = ['-M%s' % similarity] | 237 similarity_options = ['-M%s' % similarity] |
| 234 | 238 |
| 235 return subprocess2.call( | 239 return subprocess2.call( |
| 236 ['git', '--no-pager', | 240 ['git', |
| 237 'diff', '--no-ext-diff', '--stat'] + similarity_options + args, | 241 'diff', '--no-ext-diff', '--stat'] + similarity_options + args, |
| 238 env=env) | 242 env=env) |
| 239 | 243 |
| 240 | 244 |
| 241 class Settings(object): | 245 class Settings(object): |
| 242 def __init__(self): | 246 def __init__(self): |
| 243 self.default_server = None | 247 self.default_server = None |
| 244 self.cc = None | 248 self.cc = None |
| 245 self.root = None | 249 self.root = None |
| 246 self.is_git_svn = None | 250 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.') | 298 DieWithError('Repo doesn\'t appear to be a git-svn repo.') |
| 295 | 299 |
| 296 # Try to figure out which remote branch we're based on. | 300 # Try to figure out which remote branch we're based on. |
| 297 # Strategy: | 301 # Strategy: |
| 298 # 1) iterate through our branch history and find the svn URL. | 302 # 1) iterate through our branch history and find the svn URL. |
| 299 # 2) find the svn-remote that fetches from the URL. | 303 # 2) find the svn-remote that fetches from the URL. |
| 300 | 304 |
| 301 # regexp matching the git-svn line that contains the URL. | 305 # regexp matching the git-svn line that contains the URL. |
| 302 git_svn_re = re.compile(r'^\s*git-svn-id: (\S+)@', re.MULTILINE) | 306 git_svn_re = re.compile(r'^\s*git-svn-id: (\S+)@', re.MULTILINE) |
| 303 | 307 |
| 308 env = os.environ | |
|
M-A Ruel
2013/07/08 19:10:50
env = os.environ.copy()
Daniel Bratell
2013/07/08 22:08:06
I considered it but it's not really necessary sinc
M-A Ruel
2013/07/08 22:15:13
First rule of maintainability is to not mutate glo
| |
| 309 env['GIT_PAGER'] = 'cat' # Magical string that disables pagers. | |
|
M-A Ruel
2013/07/08 19:10:50
Comment on its own line.
| |
| 310 | |
| 304 # We don't want to go through all of history, so read a line from the | 311 # We don't want to go through all of history, so read a line from the |
| 305 # pipe at a time. | 312 # pipe at a time. |
| 306 # The -100 is an arbitrary limit so we don't search forever. | 313 # The -100 is an arbitrary limit so we don't search forever. |
| 307 cmd = ['git', '--no-pager', 'log', '-100', '--pretty=medium'] | 314 cmd = ['git', 'log', '-100', '--pretty=medium'] |
| 308 proc = subprocess2.Popen(cmd, stdout=subprocess2.PIPE) | 315 proc = subprocess2.Popen(cmd, stdout=subprocess2.PIPE, env=env) |
| 309 url = None | 316 url = None |
| 310 for line in proc.stdout: | 317 for line in proc.stdout: |
| 311 match = git_svn_re.match(line) | 318 match = git_svn_re.match(line) |
| 312 if match: | 319 if match: |
| 313 url = match.group(1) | 320 url = match.group(1) |
| 314 proc.stdout.close() # Cut pipe. | 321 proc.stdout.close() # Cut pipe. |
| 315 break | 322 break |
| 316 | 323 |
| 317 if url: | 324 if url: |
| 318 svn_remote_re = re.compile(r'^svn-remote\.([^.]+)\.url (.*)$') | 325 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]) | 684 RunGit(['config', self._RietveldServer(), self.rietveld_server]) |
| 678 else: | 685 else: |
| 679 RunGit(['config', '--unset', self._IssueSetting()]) | 686 RunGit(['config', '--unset', self._IssueSetting()]) |
| 680 self.SetPatchset(0) | 687 self.SetPatchset(0) |
| 681 self.has_issue = False | 688 self.has_issue = False |
| 682 | 689 |
| 683 def GetChange(self, upstream_branch, author): | 690 def GetChange(self, upstream_branch, author): |
| 684 if not self.GitSanityChecks(upstream_branch): | 691 if not self.GitSanityChecks(upstream_branch): |
| 685 DieWithError('\nGit sanity check failure') | 692 DieWithError('\nGit sanity check failure') |
| 686 | 693 |
| 687 root = RunCommand(['git', '--no-pager', 'rev-parse', '--show-cdup']).strip() | 694 env = os.environ |
|
M-A Ruel
2013/07/08 19:10:50
same
| |
| 695 env['GIT_PAGER'] = 'cat' # Magical string that disables pagers. | |
| 696 | |
| 697 root = RunCommand(['git', 'rev-parse', '--show-cdup'], env=env).strip() | |
| 688 if not root: | 698 if not root: |
| 689 root = '.' | 699 root = '.' |
| 690 absroot = os.path.abspath(root) | 700 absroot = os.path.abspath(root) |
| 691 | 701 |
| 692 # We use the sha1 of HEAD as a name of this change. | 702 # We use the sha1 of HEAD as a name of this change. |
| 693 name = RunCommand(['git', '--no-pager', 'rev-parse', 'HEAD']).strip() | 703 name = RunCommand(['git', 'rev-parse', 'HEAD'], env=env).strip() |
| 694 # Need to pass a relative path for msysgit. | 704 # Need to pass a relative path for msysgit. |
| 695 try: | 705 try: |
| 696 files = scm.GIT.CaptureStatus([root], '.', upstream_branch) | 706 files = scm.GIT.CaptureStatus([root], '.', upstream_branch) |
| 697 except subprocess2.CalledProcessError: | 707 except subprocess2.CalledProcessError: |
| 698 DieWithError( | 708 DieWithError( |
| 699 ('\nFailed to diff against upstream branch %s!\n\n' | 709 ('\nFailed to diff against upstream branch %s!\n\n' |
| 700 'This branch probably doesn\'t exist anymore. To reset the\n' | 710 'This branch probably doesn\'t exist anymore. To reset the\n' |
| 701 'tracking branch, please run\n' | 711 'tracking branch, please run\n' |
| 702 ' git branch --set-upstream %s trunk\n' | 712 ' git branch --set-upstream %s trunk\n' |
| 703 'replacing trunk with origin/master or the relevant branch') % | 713 'replacing trunk with origin/master or the relevant branch') % |
| 704 (upstream_branch, self.GetBranch())) | 714 (upstream_branch, self.GetBranch())) |
| 705 | 715 |
| 706 issue = self.GetIssue() | 716 issue = self.GetIssue() |
| 707 patchset = self.GetPatchset() | 717 patchset = self.GetPatchset() |
| 708 if issue: | 718 if issue: |
| 709 description = self.GetDescription() | 719 description = self.GetDescription() |
| 710 else: | 720 else: |
| 711 # If the change was never uploaded, use the log messages of all commits | 721 # 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 | 722 # up to the branch point, as git cl upload will prefill the description |
| 713 # with these log messages. | 723 # with these log messages. |
| 714 description = RunCommand(['git', '--no-pager', | 724 description = RunCommand(['git', |
| 715 'log', '--pretty=format:%s%n%n%b', | 725 'log', '--pretty=format:%s%n%n%b', |
| 716 '%s...' % (upstream_branch)]).strip() | 726 '%s...' % (upstream_branch)], |
| 727 env=env).strip() | |
| 717 | 728 |
| 718 if not author: | 729 if not author: |
| 719 author = RunGit(['config', 'user.email']).strip() or None | 730 author = RunGit(['config', 'user.email']).strip() or None |
| 720 return presubmit_support.GitChange( | 731 return presubmit_support.GitChange( |
| 721 name, | 732 name, |
| 722 description, | 733 description, |
| 723 absroot, | 734 absroot, |
| 724 files, | 735 files, |
| 725 issue, | 736 issue, |
| 726 patchset, | 737 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 | 1755 # 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 | 1756 # 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. | 1757 # 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. | 1758 # re.sub() should be used but flags=re.MULTILINE is only in python 2.7. |
| 1748 try: | 1759 try: |
| 1749 patch_data = subprocess2.check_output( | 1760 patch_data = subprocess2.check_output( |
| 1750 ['sed', '-e', 's|^--- a/|--- |; s|^+++ b/|+++ |'], stdin=patch_data) | 1761 ['sed', '-e', 's|^--- a/|--- |; s|^+++ b/|+++ |'], stdin=patch_data) |
| 1751 except subprocess2.CalledProcessError: | 1762 except subprocess2.CalledProcessError: |
| 1752 DieWithError('Git patch mungling failed.') | 1763 DieWithError('Git patch mungling failed.') |
| 1753 logging.info(patch_data) | 1764 logging.info(patch_data) |
| 1765 env = os.environ | |
|
M-A Ruel
2013/07/08 19:10:50
same
| |
| 1766 env['GIT_PAGER'] = 'cat' # Magical string that disables pagers. | |
| 1767 | |
| 1754 # We use "git apply" to apply the patch instead of "patch" so that we can | 1768 # We use "git apply" to apply the patch instead of "patch" so that we can |
| 1755 # pick up file adds. | 1769 # pick up file adds. |
| 1756 # The --index flag means: also insert into the index (so we catch adds). | 1770 # The --index flag means: also insert into the index (so we catch adds). |
| 1757 cmd = ['git', '--no-pager', 'apply', '--index', '-p0'] | 1771 cmd = ['git', 'apply', '--index', '-p0'] |
| 1758 if options.reject: | 1772 if options.reject: |
| 1759 cmd.append('--reject') | 1773 cmd.append('--reject') |
| 1760 try: | 1774 try: |
| 1761 subprocess2.check_call(cmd, stdin=patch_data, stdout=subprocess2.VOID) | 1775 subprocess2.check_call(cmd, env=env, |
| 1776 stdin=patch_data, stdout=subprocess2.VOID) | |
| 1762 except subprocess2.CalledProcessError: | 1777 except subprocess2.CalledProcessError: |
| 1763 DieWithError('Failed to apply the patch') | 1778 DieWithError('Failed to apply the patch') |
| 1764 | 1779 |
| 1765 # If we had an issue, commit the current state and register the issue. | 1780 # If we had an issue, commit the current state and register the issue. |
| 1766 if not options.nocommit: | 1781 if not options.nocommit: |
| 1767 RunGit(['commit', '-m', 'patch from issue %s' % issue]) | 1782 RunGit(['commit', '-m', 'patch from issue %s' % issue]) |
| 1768 cl = Changelist() | 1783 cl = Changelist() |
| 1769 cl.SetIssue(issue) | 1784 cl.SetIssue(issue) |
| 1770 cl.SetPatchset(patchset) | 1785 cl.SetPatchset(patchset) |
| 1771 print "Committed patch locally." | 1786 print "Committed patch locally." |
| 1772 else: | 1787 else: |
| 1773 print "Patch applied to index." | 1788 print "Patch applied to index." |
| 1774 return 0 | 1789 return 0 |
| 1775 | 1790 |
| 1776 | 1791 |
| 1777 def CMDrebase(parser, args): | 1792 def CMDrebase(parser, args): |
| 1778 """rebase current branch on top of svn repo""" | 1793 """rebase current branch on top of svn repo""" |
| 1779 # Provide a wrapper for git svn rebase to help avoid accidental | 1794 # Provide a wrapper for git svn rebase to help avoid accidental |
| 1780 # git svn dcommit. | 1795 # git svn dcommit. |
| 1781 # It's the only command that doesn't use parser at all since we just defer | 1796 # It's the only command that doesn't use parser at all since we just defer |
| 1782 # execution to git-svn. | 1797 # execution to git-svn. |
| 1783 return subprocess2.call(['git', '--no-pager', 'svn', 'rebase'] + args) | 1798 env = os.environ |
|
M-A Ruel
2013/07/08 19:10:50
same
| |
| 1799 env['GIT_PAGER'] = 'cat' # Magical string that disables pagers. | |
| 1800 | |
| 1801 return subprocess2.call(['git', 'svn', 'rebase'] + args, env=env) | |
| 1784 | 1802 |
| 1785 | 1803 |
| 1786 def GetTreeStatus(): | 1804 def GetTreeStatus(): |
| 1787 """Fetches the tree status and returns either 'open', 'closed', | 1805 """Fetches the tree status and returns either 'open', 'closed', |
| 1788 'unknown' or 'unset'.""" | 1806 'unknown' or 'unset'.""" |
| 1789 url = settings.GetTreeStatusUrl(error_ok=True) | 1807 url = settings.GetTreeStatusUrl(error_ok=True) |
| 1790 if url: | 1808 if url: |
| 1791 status = urllib2.urlopen(url).read().lower() | 1809 status = urllib2.urlopen(url).read().lower() |
| 1792 if status.find('closed') != -1 or status == '0': | 1810 if status.find('closed') != -1 or status == '0': |
| 1793 return 'closed' | 1811 return 'closed' |
| (...skipping 348 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 2142 GenUsage(parser, 'help') | 2160 GenUsage(parser, 'help') |
| 2143 return CMDhelp(parser, argv) | 2161 return CMDhelp(parser, argv) |
| 2144 | 2162 |
| 2145 | 2163 |
| 2146 if __name__ == '__main__': | 2164 if __name__ == '__main__': |
| 2147 # These affect sys.stdout so do it outside of main() to simplify mocks in | 2165 # These affect sys.stdout so do it outside of main() to simplify mocks in |
| 2148 # unit testing. | 2166 # unit testing. |
| 2149 fix_encoding.fix_encoding() | 2167 fix_encoding.fix_encoding() |
| 2150 colorama.init() | 2168 colorama.init() |
| 2151 sys.exit(main(sys.argv[1:])) | 2169 sys.exit(main(sys.argv[1:])) |
| OLD | NEW |