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 |