| OLD | NEW |
| 1 # Copyright (c) 2010 The Chromium Authors. All rights reserved. | 1 # Copyright (c) 2010 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 """SCM-specific utility classes.""" | 5 """SCM-specific utility classes.""" |
| 6 | 6 |
| 7 import cStringIO | 7 import cStringIO |
| 8 import glob | 8 import glob |
| 9 import logging | 9 import logging |
| 10 import os | 10 import os |
| 11 import re | 11 import re |
| 12 import shutil | |
| 13 import subprocess | |
| 14 import sys | 12 import sys |
| 15 import tempfile | 13 import tempfile |
| 16 import time | 14 import time |
| 17 from xml.etree import ElementTree | 15 from xml.etree import ElementTree |
| 18 | 16 |
| 19 import gclient_utils | 17 import gclient_utils |
| 20 import subprocess2 | 18 import subprocess2 |
| 21 | 19 |
| 22 | 20 |
| 23 def ValidateEmail(email): | 21 def ValidateEmail(email): |
| (...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 83 stdout=subprocess2.VOID, | 81 stdout=subprocess2.VOID, |
| 84 cwd=root) | 82 cwd=root) |
| 85 return 'git' | 83 return 'git' |
| 86 except (OSError, subprocess2.CalledProcessError): | 84 except (OSError, subprocess2.CalledProcessError): |
| 87 return None | 85 return None |
| 88 | 86 |
| 89 | 87 |
| 90 class GIT(object): | 88 class GIT(object): |
| 91 @staticmethod | 89 @staticmethod |
| 92 def Capture(args, **kwargs): | 90 def Capture(args, **kwargs): |
| 93 return gclient_utils.CheckCall(['git'] + args, print_error=False, | 91 return subprocess2.check_output( |
| 94 **kwargs)[0] | 92 ['git'] + args, stderr=subprocess2.VOID, **kwargs) |
| 95 | 93 |
| 96 @staticmethod | 94 @staticmethod |
| 97 def CaptureStatus(files, upstream_branch=None): | 95 def CaptureStatus(files, upstream_branch=None): |
| 98 """Returns git status. | 96 """Returns git status. |
| 99 | 97 |
| 100 @files can be a string (one file) or a list of files. | 98 @files can be a string (one file) or a list of files. |
| 101 | 99 |
| 102 Returns an array of (status, file) tuples.""" | 100 Returns an array of (status, file) tuples.""" |
| 103 if upstream_branch is None: | 101 if upstream_branch is None: |
| 104 upstream_branch = GIT.GetUpstreamBranch(os.getcwd()) | 102 upstream_branch = GIT.GetUpstreamBranch(os.getcwd()) |
| (...skipping 21 matching lines...) Expand all Loading... |
| 126 results.append(('%s ' % m.group(1)[0], m.group(2))) | 124 results.append(('%s ' % m.group(1)[0], m.group(2))) |
| 127 return results | 125 return results |
| 128 | 126 |
| 129 @staticmethod | 127 @staticmethod |
| 130 def GetEmail(cwd): | 128 def GetEmail(cwd): |
| 131 """Retrieves the user email address if known.""" | 129 """Retrieves the user email address if known.""" |
| 132 # We could want to look at the svn cred when it has a svn remote but it | 130 # We could want to look at the svn cred when it has a svn remote but it |
| 133 # should be fine for now, users should simply configure their git settings. | 131 # should be fine for now, users should simply configure their git settings. |
| 134 try: | 132 try: |
| 135 return GIT.Capture(['config', 'user.email'], cwd=cwd).strip() | 133 return GIT.Capture(['config', 'user.email'], cwd=cwd).strip() |
| 136 except gclient_utils.CheckCallError: | 134 except subprocess2.CalledProcessError: |
| 137 return '' | 135 return '' |
| 138 | 136 |
| 139 @staticmethod | 137 @staticmethod |
| 140 def ShortBranchName(branch): | 138 def ShortBranchName(branch): |
| 141 """Converts a name like 'refs/heads/foo' to just 'foo'.""" | 139 """Converts a name like 'refs/heads/foo' to just 'foo'.""" |
| 142 return branch.replace('refs/heads/', '') | 140 return branch.replace('refs/heads/', '') |
| 143 | 141 |
| 144 @staticmethod | 142 @staticmethod |
| 145 def GetBranchRef(cwd): | 143 def GetBranchRef(cwd): |
| 146 """Returns the full branch reference, e.g. 'refs/heads/master'.""" | 144 """Returns the full branch reference, e.g. 'refs/heads/master'.""" |
| 147 return GIT.Capture(['symbolic-ref', 'HEAD'], cwd=cwd).strip() | 145 return GIT.Capture(['symbolic-ref', 'HEAD'], cwd=cwd).strip() |
| 148 | 146 |
| 149 @staticmethod | 147 @staticmethod |
| 150 def GetBranch(cwd): | 148 def GetBranch(cwd): |
| 151 """Returns the short branch name, e.g. 'master'.""" | 149 """Returns the short branch name, e.g. 'master'.""" |
| 152 return GIT.ShortBranchName(GIT.GetBranchRef(cwd)) | 150 return GIT.ShortBranchName(GIT.GetBranchRef(cwd)) |
| 153 | 151 |
| 154 @staticmethod | 152 @staticmethod |
| 155 def IsGitSvn(cwd): | 153 def IsGitSvn(cwd): |
| 156 """Returns true if this repo looks like it's using git-svn.""" | 154 """Returns true if this repo looks like it's using git-svn.""" |
| 157 # If you have any "svn-remote.*" config keys, we think you're using svn. | 155 # If you have any "svn-remote.*" config keys, we think you're using svn. |
| 158 try: | 156 try: |
| 159 GIT.Capture(['config', '--get-regexp', r'^svn-remote\.'], cwd=cwd) | 157 GIT.Capture(['config', '--get-regexp', r'^svn-remote\.'], cwd=cwd) |
| 160 return True | 158 return True |
| 161 except gclient_utils.CheckCallError: | 159 except subprocess2.CalledProcessError: |
| 162 return False | 160 return False |
| 163 | 161 |
| 164 @staticmethod | 162 @staticmethod |
| 165 def MatchSvnGlob(url, base_url, glob_spec, allow_wildcards): | 163 def MatchSvnGlob(url, base_url, glob_spec, allow_wildcards): |
| 166 """Return the corresponding git ref if |base_url| together with |glob_spec| | 164 """Return the corresponding git ref if |base_url| together with |glob_spec| |
| 167 matches the full |url|. | 165 matches the full |url|. |
| 168 | 166 |
| 169 If |allow_wildcards| is true, |glob_spec| can contain wildcards (see below). | 167 If |allow_wildcards| is true, |glob_spec| can contain wildcards (see below). |
| 170 """ | 168 """ |
| 171 fetch_suburl, as_ref = glob_spec.split(':') | 169 fetch_suburl, as_ref = glob_spec.split(':') |
| (...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 211 # 1) iterate through our branch history and find the svn URL. | 209 # 1) iterate through our branch history and find the svn URL. |
| 212 # 2) find the svn-remote that fetches from the URL. | 210 # 2) find the svn-remote that fetches from the URL. |
| 213 | 211 |
| 214 # regexp matching the git-svn line that contains the URL. | 212 # regexp matching the git-svn line that contains the URL. |
| 215 git_svn_re = re.compile(r'^\s*git-svn-id: (\S+)@', re.MULTILINE) | 213 git_svn_re = re.compile(r'^\s*git-svn-id: (\S+)@', re.MULTILINE) |
| 216 | 214 |
| 217 # We don't want to go through all of history, so read a line from the | 215 # We don't want to go through all of history, so read a line from the |
| 218 # pipe at a time. | 216 # pipe at a time. |
| 219 # The -100 is an arbitrary limit so we don't search forever. | 217 # The -100 is an arbitrary limit so we don't search forever. |
| 220 cmd = ['git', 'log', '-100', '--pretty=medium'] | 218 cmd = ['git', 'log', '-100', '--pretty=medium'] |
| 221 proc = gclient_utils.Popen(cmd, stdout=subprocess.PIPE) | 219 proc = subprocess2.Popen(cmd, stdout=subprocess2.PIPE) |
| 222 url = None | 220 url = None |
| 223 for line in proc.stdout: | 221 for line in proc.stdout: |
| 224 match = git_svn_re.match(line) | 222 match = git_svn_re.match(line) |
| 225 if match: | 223 if match: |
| 226 url = match.group(1) | 224 url = match.group(1) |
| 227 proc.stdout.close() # Cut pipe. | 225 proc.stdout.close() # Cut pipe. |
| 228 break | 226 break |
| 229 | 227 |
| 230 if url: | 228 if url: |
| 231 svn_remote_re = re.compile(r'^svn-remote\.([^.]+)\.url (.*)$') | 229 svn_remote_re = re.compile(r'^svn-remote\.([^.]+)\.url (.*)$') |
| 232 remotes = GIT.Capture(['config', '--get-regexp', | 230 remotes = GIT.Capture(['config', '--get-regexp', |
| 233 r'^svn-remote\..*\.url'], cwd=cwd).splitlines() | 231 r'^svn-remote\..*\.url'], cwd=cwd).splitlines() |
| 234 for remote in remotes: | 232 for remote in remotes: |
| 235 match = svn_remote_re.match(remote) | 233 match = svn_remote_re.match(remote) |
| 236 if match: | 234 if match: |
| 237 remote = match.group(1) | 235 remote = match.group(1) |
| 238 base_url = match.group(2) | 236 base_url = match.group(2) |
| 239 try: | 237 try: |
| 240 fetch_spec = GIT.Capture( | 238 fetch_spec = GIT.Capture( |
| 241 ['config', 'svn-remote.%s.fetch' % remote], | 239 ['config', 'svn-remote.%s.fetch' % remote], |
| 242 cwd=cwd).strip() | 240 cwd=cwd).strip() |
| 243 branch = GIT.MatchSvnGlob(url, base_url, fetch_spec, False) | 241 branch = GIT.MatchSvnGlob(url, base_url, fetch_spec, False) |
| 244 except gclient_utils.CheckCallError: | 242 except subprocess2.CalledProcessError: |
| 245 branch = None | 243 branch = None |
| 246 if branch: | 244 if branch: |
| 247 return branch | 245 return branch |
| 248 try: | 246 try: |
| 249 branch_spec = GIT.Capture( | 247 branch_spec = GIT.Capture( |
| 250 ['config', 'svn-remote.%s.branches' % remote], | 248 ['config', 'svn-remote.%s.branches' % remote], |
| 251 cwd=cwd).strip() | 249 cwd=cwd).strip() |
| 252 branch = GIT.MatchSvnGlob(url, base_url, branch_spec, True) | 250 branch = GIT.MatchSvnGlob(url, base_url, branch_spec, True) |
| 253 except gclient_utils.CheckCallError: | 251 except subprocess2.CalledProcessError: |
| 254 branch = None | 252 branch = None |
| 255 if branch: | 253 if branch: |
| 256 return branch | 254 return branch |
| 257 try: | 255 try: |
| 258 tag_spec = GIT.Capture( | 256 tag_spec = GIT.Capture( |
| 259 ['config', 'svn-remote.%s.tags' % remote], | 257 ['config', 'svn-remote.%s.tags' % remote], |
| 260 cwd=cwd).strip() | 258 cwd=cwd).strip() |
| 261 branch = GIT.MatchSvnGlob(url, base_url, tag_spec, True) | 259 branch = GIT.MatchSvnGlob(url, base_url, tag_spec, True) |
| 262 except gclient_utils.CheckCallError: | 260 except subprocess2.CalledProcessError: |
| 263 branch = None | 261 branch = None |
| 264 if branch: | 262 if branch: |
| 265 return branch | 263 return branch |
| 266 | 264 |
| 267 @staticmethod | 265 @staticmethod |
| 268 def FetchUpstreamTuple(cwd): | 266 def FetchUpstreamTuple(cwd): |
| 269 """Returns a tuple containg remote and remote ref, | 267 """Returns a tuple containg remote and remote ref, |
| 270 e.g. 'origin', 'refs/heads/master' | 268 e.g. 'origin', 'refs/heads/master' |
| 271 Tries to be intelligent and understand git-svn. | 269 Tries to be intelligent and understand git-svn. |
| 272 """ | 270 """ |
| 273 remote = '.' | 271 remote = '.' |
| 274 branch = GIT.GetBranch(cwd) | 272 branch = GIT.GetBranch(cwd) |
| 275 try: | 273 try: |
| 276 upstream_branch = GIT.Capture( | 274 upstream_branch = GIT.Capture( |
| 277 ['config', 'branch.%s.merge' % branch], cwd=cwd).strip() | 275 ['config', 'branch.%s.merge' % branch], cwd=cwd).strip() |
| 278 except (gclient_utils.Error, subprocess2.CalledProcessError): | 276 except subprocess2.CalledProcessError: |
| 279 upstream_branch = None | 277 upstream_branch = None |
| 280 if upstream_branch: | 278 if upstream_branch: |
| 281 try: | 279 try: |
| 282 remote = GIT.Capture( | 280 remote = GIT.Capture( |
| 283 ['config', 'branch.%s.remote' % branch], cwd=cwd).strip() | 281 ['config', 'branch.%s.remote' % branch], cwd=cwd).strip() |
| 284 except (gclient_utils.Error, subprocess2.CalledProcessError): | 282 except subprocess2.CalledProcessError: |
| 285 pass | 283 pass |
| 286 else: | 284 else: |
| 287 try: | 285 try: |
| 288 upstream_branch = GIT.Capture( | 286 upstream_branch = GIT.Capture( |
| 289 ['config', 'rietveld.upstream-branch'], cwd=cwd).strip() | 287 ['config', 'rietveld.upstream-branch'], cwd=cwd).strip() |
| 290 except (gclient_utils.Error, subprocess2.CalledProcessError): | 288 except subprocess2.CalledProcessError: |
| 291 upstream_branch = None | 289 upstream_branch = None |
| 292 if upstream_branch: | 290 if upstream_branch: |
| 293 try: | 291 try: |
| 294 remote = GIT.Capture( | 292 remote = GIT.Capture( |
| 295 ['config', 'rietveld.upstream-remote'], cwd=cwd).strip() | 293 ['config', 'rietveld.upstream-remote'], cwd=cwd).strip() |
| 296 except (gclient_utils.Error, subprocess2.CalledProcessError): | 294 except subprocess2.CalledProcessError: |
| 297 pass | 295 pass |
| 298 else: | 296 else: |
| 299 # Fall back on trying a git-svn upstream branch. | 297 # Fall back on trying a git-svn upstream branch. |
| 300 if GIT.IsGitSvn(cwd): | 298 if GIT.IsGitSvn(cwd): |
| 301 upstream_branch = GIT.GetSVNBranch(cwd) | 299 upstream_branch = GIT.GetSVNBranch(cwd) |
| 302 else: | 300 else: |
| 303 # Else, try to guess the origin remote. | 301 # Else, try to guess the origin remote. |
| 304 remote_branches = GIT.Capture(['branch', '-r'], cwd=cwd).split() | 302 remote_branches = GIT.Capture(['branch', '-r'], cwd=cwd).split() |
| 305 if 'origin/master' in remote_branches: | 303 if 'origin/master' in remote_branches: |
| 306 # Fall back on origin/master if it exits. | 304 # Fall back on origin/master if it exits. |
| (...skipping 83 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 390 return (True, current_version) | 388 return (True, current_version) |
| 391 | 389 |
| 392 | 390 |
| 393 class SVN(object): | 391 class SVN(object): |
| 394 current_version = None | 392 current_version = None |
| 395 | 393 |
| 396 @staticmethod | 394 @staticmethod |
| 397 def Capture(args, **kwargs): | 395 def Capture(args, **kwargs): |
| 398 """Always redirect stderr. | 396 """Always redirect stderr. |
| 399 | 397 |
| 400 Throws an exception if non-0 is returned.""" | 398 Throws an exception if non-0 is returned. |
| 401 return gclient_utils.CheckCall(['svn'] + args, print_error=False, | 399 """ |
| 402 **kwargs)[0] | 400 return subprocess2.check_output(['svn'] + args, **kwargs) |
| 403 | 401 |
| 404 @staticmethod | 402 @staticmethod |
| 405 def RunAndGetFileList(verbose, args, cwd, file_list, stdout=None): | 403 def RunAndGetFileList(verbose, args, cwd, file_list, stdout=None): |
| 406 """Runs svn checkout, update, or status, output to stdout. | 404 """Runs svn checkout, update, or status, output to stdout. |
| 407 | 405 |
| 408 The first item in args must be either "checkout", "update", or "status". | 406 The first item in args must be either "checkout", "update", or "status". |
| 409 | 407 |
| 410 svn's stdout is parsed to collect a list of files checked out or updated. | 408 svn's stdout is parsed to collect a list of files checked out or updated. |
| 411 These files are appended to file_list. svn's stdout is also printed to | 409 These files are appended to file_list. svn's stdout is also printed to |
| 412 sys.stdout as in Run. | 410 sys.stdout as in Run. |
| (...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 457 if line.startswith('svn: '): | 455 if line.startswith('svn: '): |
| 458 failure.append(line) | 456 failure.append(line) |
| 459 | 457 |
| 460 try: | 458 try: |
| 461 gclient_utils.CheckCallAndFilterAndHeader( | 459 gclient_utils.CheckCallAndFilterAndHeader( |
| 462 ['svn'] + args, | 460 ['svn'] + args, |
| 463 cwd=cwd, | 461 cwd=cwd, |
| 464 always=verbose, | 462 always=verbose, |
| 465 filter_fn=CaptureMatchingLines, | 463 filter_fn=CaptureMatchingLines, |
| 466 stdout=stdout) | 464 stdout=stdout) |
| 467 except (gclient_utils.Error, subprocess2.CalledProcessError): | 465 except subprocess2.CalledProcessError: |
| 468 def IsKnownFailure(): | 466 def IsKnownFailure(): |
| 469 for x in failure: | 467 for x in failure: |
| 470 if (x.startswith('svn: OPTIONS of') or | 468 if (x.startswith('svn: OPTIONS of') or |
| 471 x.startswith('svn: PROPFIND of') or | 469 x.startswith('svn: PROPFIND of') or |
| 472 x.startswith('svn: REPORT of') or | 470 x.startswith('svn: REPORT of') or |
| 473 x.startswith('svn: Unknown hostname') or | 471 x.startswith('svn: Unknown hostname') or |
| 474 x.startswith('svn: Server sent unexpected return value')): | 472 x.startswith('svn: Server sent unexpected return value')): |
| 475 return True | 473 return True |
| 476 return False | 474 return False |
| 477 | 475 |
| (...skipping 180 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 658 filename: The file to check | 656 filename: The file to check |
| 659 property_name: The name of the SVN property, e.g. "svn:mime-type" | 657 property_name: The name of the SVN property, e.g. "svn:mime-type" |
| 660 | 658 |
| 661 Returns: | 659 Returns: |
| 662 The value of the property, which will be the empty string if the property | 660 The value of the property, which will be the empty string if the property |
| 663 is not set on the file. If the file is not under version control, the | 661 is not set on the file. If the file is not under version control, the |
| 664 empty string is also returned. | 662 empty string is also returned. |
| 665 """ | 663 """ |
| 666 try: | 664 try: |
| 667 return SVN.Capture(['propget', property_name, filename]) | 665 return SVN.Capture(['propget', property_name, filename]) |
| 668 except (gclient_utils.Error, subprocess2.CalledProcessError): | 666 except subprocess2.CalledProcessError: |
| 669 return '' | 667 return '' |
| 670 | 668 |
| 671 @staticmethod | 669 @staticmethod |
| 672 def DiffItem(filename, full_move=False, revision=None): | 670 def DiffItem(filename, full_move=False, revision=None): |
| 673 """Diffs a single file. | 671 """Diffs a single file. |
| 674 | 672 |
| 675 Should be simple, eh? No it isn't. | 673 Should be simple, eh? No it isn't. |
| 676 Be sure to be in the appropriate directory before calling to have the | 674 Be sure to be in the appropriate directory before calling to have the |
| 677 expected relative path. | 675 expected relative path. |
| 678 full_move means that move or copy operations should completely recreate the | 676 full_move means that move or copy operations should completely recreate the |
| 679 files, usually in the prospect to apply the patch for a try job.""" | 677 files, usually in the prospect to apply the patch for a try job.""" |
| 680 # If the user specified a custom diff command in their svn config file, | 678 # If the user specified a custom diff command in their svn config file, |
| 681 # then it'll be used when we do svn diff, which we don't want to happen | 679 # then it'll be used when we do svn diff, which we don't want to happen |
| 682 # since we want the unified diff. Using --diff-cmd=diff doesn't always | 680 # since we want the unified diff. Using --diff-cmd=diff doesn't always |
| 683 # work, since they can have another diff executable in their path that | 681 # work, since they can have another diff executable in their path that |
| 684 # gives different line endings. So we use a bogus temp directory as the | 682 # gives different line endings. So we use a bogus temp directory as the |
| 685 # config directory, which gets around these problems. | 683 # config directory, which gets around these problems. |
| 686 bogus_dir = tempfile.mkdtemp() | 684 bogus_dir = tempfile.mkdtemp() |
| 687 try: | 685 try: |
| 688 # Use "svn info" output instead of os.path.isdir because the latter fails | 686 # Use "svn info" output instead of os.path.isdir because the latter fails |
| 689 # when the file is deleted. | 687 # when the file is deleted. |
| 690 return SVN._DiffItemInternal(filename, SVN.CaptureInfo(filename), | 688 return SVN._DiffItemInternal(filename, SVN.CaptureInfo(filename), |
| 691 bogus_dir, | 689 bogus_dir, |
| 692 full_move=full_move, revision=revision) | 690 full_move=full_move, revision=revision) |
| 693 finally: | 691 finally: |
| 694 shutil.rmtree(bogus_dir) | 692 gclient_utils.RemoveDirectory(bogus_dir) |
| 695 | 693 |
| 696 @staticmethod | 694 @staticmethod |
| 697 def _DiffItemInternal(filename, info, bogus_dir, full_move=False, | 695 def _DiffItemInternal(filename, info, bogus_dir, full_move=False, |
| 698 revision=None): | 696 revision=None): |
| 699 """Grabs the diff data.""" | 697 """Grabs the diff data.""" |
| 700 command = ["diff", "--config-dir", bogus_dir, filename] | 698 command = ["diff", "--config-dir", bogus_dir, filename] |
| 701 if revision: | 699 if revision: |
| 702 command.extend(['--revision', revision]) | 700 command.extend(['--revision', revision]) |
| 703 data = None | 701 data = None |
| 704 if SVN.IsMovedInfo(info): | 702 if SVN.IsMovedInfo(info): |
| (...skipping 29 matching lines...) Expand all Loading... |
| 734 data = SVN.Capture(command) | 732 data = SVN.Capture(command) |
| 735 if not data: | 733 if not data: |
| 736 # We put in an empty Index entry so upload.py knows about them. | 734 # We put in an empty Index entry so upload.py knows about them. |
| 737 data = "Index: %s\n" % filename.replace(os.sep, '/') | 735 data = "Index: %s\n" % filename.replace(os.sep, '/') |
| 738 # Otherwise silently ignore directories. | 736 # Otherwise silently ignore directories. |
| 739 else: | 737 else: |
| 740 if info.get("Node Kind") != "directory": | 738 if info.get("Node Kind") != "directory": |
| 741 # Normal simple case. | 739 # Normal simple case. |
| 742 try: | 740 try: |
| 743 data = SVN.Capture(command) | 741 data = SVN.Capture(command) |
| 744 except gclient_utils.CheckCallError: | 742 except subprocess2.CalledProcessError: |
| 745 if revision: | 743 if revision: |
| 746 data = GenFakeDiff(filename) | 744 data = GenFakeDiff(filename) |
| 747 else: | 745 else: |
| 748 raise | 746 raise |
| 749 # Otherwise silently ignore directories. | 747 # Otherwise silently ignore directories. |
| 750 return data | 748 return data |
| 751 | 749 |
| 752 @staticmethod | 750 @staticmethod |
| 753 def GenerateDiff(filenames, root=None, full_move=False, revision=None): | 751 def GenerateDiff(filenames, root=None, full_move=False, revision=None): |
| 754 """Returns a string containing the diff for the given file list. | 752 """Returns a string containing the diff for the given file list. |
| (...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 798 if SVN.IsMovedInfo(info): | 796 if SVN.IsMovedInfo(info): |
| 799 # for now, the most common case is a head copy, | 797 # for now, the most common case is a head copy, |
| 800 # so let's just encode that as a straight up cp. | 798 # so let's just encode that as a straight up cp. |
| 801 srcurl = info.get('Copied From URL') | 799 srcurl = info.get('Copied From URL') |
| 802 root = info.get('Repository Root') | 800 root = info.get('Repository Root') |
| 803 rev = int(info.get('Copied From Rev')) | 801 rev = int(info.get('Copied From Rev')) |
| 804 assert srcurl.startswith(root) | 802 assert srcurl.startswith(root) |
| 805 src = srcurl[len(root)+1:] | 803 src = srcurl[len(root)+1:] |
| 806 try: | 804 try: |
| 807 srcinfo = SVN.CaptureInfo(srcurl) | 805 srcinfo = SVN.CaptureInfo(srcurl) |
| 808 except gclient_utils.CheckCallError, e: | 806 except subprocess2.CalledProcessError, e: |
| 809 if not 'Not a valid URL' in e.stderr: | 807 if not 'Not a valid URL' in e.stderr: |
| 810 raise | 808 raise |
| 811 # Assume the file was deleted. No idea how to figure out at which | 809 # Assume the file was deleted. No idea how to figure out at which |
| 812 # revision the file was deleted. | 810 # revision the file was deleted. |
| 813 srcinfo = {'Revision': rev} | 811 srcinfo = {'Revision': rev} |
| 814 if (srcinfo.get('Revision') != rev and | 812 if (srcinfo.get('Revision') != rev and |
| 815 SVN.Capture(['diff', '-r', '%d:head' % rev, srcurl])): | 813 SVN.Capture(['diff', '-r', '%d:head' % rev, srcurl])): |
| 816 metaheaders.append("#$ svn cp -r %d %s %s " | 814 metaheaders.append("#$ svn cp -r %d %s %s " |
| 817 "### WARNING: note non-trunk copy\n" % | 815 "### WARNING: note non-trunk copy\n" % |
| 818 (rev, src, filename)) | 816 (rev, src, filename)) |
| (...skipping 13 matching lines...) Expand all Loading... |
| 832 # Use StringIO since it can be messy when diffing a directory move with | 830 # Use StringIO since it can be messy when diffing a directory move with |
| 833 # full_move=True. | 831 # full_move=True. |
| 834 buf = cStringIO.StringIO() | 832 buf = cStringIO.StringIO() |
| 835 for d in filter(None, diffs): | 833 for d in filter(None, diffs): |
| 836 buf.write(d) | 834 buf.write(d) |
| 837 result = buf.getvalue() | 835 result = buf.getvalue() |
| 838 buf.close() | 836 buf.close() |
| 839 return result | 837 return result |
| 840 finally: | 838 finally: |
| 841 os.chdir(previous_cwd) | 839 os.chdir(previous_cwd) |
| 842 shutil.rmtree(bogus_dir) | 840 gclient_utils.RemoveDirectory(bogus_dir) |
| 843 | 841 |
| 844 @staticmethod | 842 @staticmethod |
| 845 def GetEmail(repo_root): | 843 def GetEmail(repo_root): |
| 846 """Retrieves the svn account which we assume is an email address.""" | 844 """Retrieves the svn account which we assume is an email address.""" |
| 847 try: | 845 try: |
| 848 infos = SVN.CaptureInfo(repo_root) | 846 infos = SVN.CaptureInfo(repo_root) |
| 849 except (gclient_utils.Error, subprocess2.CalledProcessError): | 847 except subprocess2.CalledProcessError: |
| 850 return None | 848 return None |
| 851 | 849 |
| 852 # Should check for uuid but it is incorrectly saved for https creds. | 850 # Should check for uuid but it is incorrectly saved for https creds. |
| 853 root = infos['Repository Root'] | 851 root = infos['Repository Root'] |
| 854 realm = root.rsplit('/', 1)[0] | 852 realm = root.rsplit('/', 1)[0] |
| 855 uuid = infos['UUID'] | 853 uuid = infos['UUID'] |
| 856 if root.startswith('https') or not uuid: | 854 if root.startswith('https') or not uuid: |
| 857 regexp = re.compile(r'<%s:\d+>.*' % realm) | 855 regexp = re.compile(r'<%s:\d+>.*' % realm) |
| 858 else: | 856 else: |
| 859 regexp = re.compile(r'<%s:\d+> %s' % (realm, uuid)) | 857 regexp = re.compile(r'<%s:\d+> %s' % (realm, uuid)) |
| (...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 901 def GetCheckoutRoot(directory): | 899 def GetCheckoutRoot(directory): |
| 902 """Returns the top level directory of the current repository. | 900 """Returns the top level directory of the current repository. |
| 903 | 901 |
| 904 The directory is returned as an absolute path. | 902 The directory is returned as an absolute path. |
| 905 """ | 903 """ |
| 906 directory = os.path.abspath(directory) | 904 directory = os.path.abspath(directory) |
| 907 try: | 905 try: |
| 908 info = SVN.CaptureInfo(directory) | 906 info = SVN.CaptureInfo(directory) |
| 909 cur_dir_repo_root = info['Repository Root'] | 907 cur_dir_repo_root = info['Repository Root'] |
| 910 url = info['URL'] | 908 url = info['URL'] |
| 911 except (gclient_utils.Error, subprocess2.CalledProcessError): | 909 except subprocess2.CalledProcessError: |
| 912 return None | 910 return None |
| 913 while True: | 911 while True: |
| 914 parent = os.path.dirname(directory) | 912 parent = os.path.dirname(directory) |
| 915 try: | 913 try: |
| 916 info = SVN.CaptureInfo(parent) | 914 info = SVN.CaptureInfo(parent) |
| 917 if (info['Repository Root'] != cur_dir_repo_root or | 915 if (info['Repository Root'] != cur_dir_repo_root or |
| 918 info['URL'] != os.path.dirname(url)): | 916 info['URL'] != os.path.dirname(url)): |
| 919 break | 917 break |
| 920 url = info['URL'] | 918 url = info['URL'] |
| 921 except (gclient_utils.Error, subprocess2.CalledProcessError): | 919 except subprocess2.CalledProcessError: |
| 922 break | 920 break |
| 923 directory = parent | 921 directory = parent |
| 924 return GetCasedPath(directory) | 922 return GetCasedPath(directory) |
| 925 | 923 |
| 926 @staticmethod | 924 @staticmethod |
| 927 def AssertVersion(min_version): | 925 def AssertVersion(min_version): |
| 928 """Asserts svn's version is at least min_version.""" | 926 """Asserts svn's version is at least min_version.""" |
| 929 def only_int(val): | 927 def only_int(val): |
| 930 if val.isdigit(): | 928 if val.isdigit(): |
| 931 return int(val) | 929 return int(val) |
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 966 if os.path.exists(file_path): | 964 if os.path.exists(file_path): |
| 967 # svn revert is really stupid. It fails on inconsistent line-endings, | 965 # svn revert is really stupid. It fails on inconsistent line-endings, |
| 968 # on switched directories, etc. So take no chance and delete everything! | 966 # on switched directories, etc. So take no chance and delete everything! |
| 969 # In theory, it wouldn't be necessary for property-only change but then | 967 # In theory, it wouldn't be necessary for property-only change but then |
| 970 # it'd have to look for switched directories, etc so it's not worth | 968 # it'd have to look for switched directories, etc so it's not worth |
| 971 # optimizing this use case. | 969 # optimizing this use case. |
| 972 if os.path.isfile(file_path) or os.path.islink(file_path): | 970 if os.path.isfile(file_path) or os.path.islink(file_path): |
| 973 logging.info('os.remove(%s)' % file_path) | 971 logging.info('os.remove(%s)' % file_path) |
| 974 os.remove(file_path) | 972 os.remove(file_path) |
| 975 elif os.path.isdir(file_path): | 973 elif os.path.isdir(file_path): |
| 976 logging.info('gclient_utils.RemoveDirectory(%s)' % file_path) | 974 logging.info('RemoveDirectory(%s)' % file_path) |
| 977 gclient_utils.RemoveDirectory(file_path) | 975 gclient_utils.RemoveDirectory(file_path) |
| 978 else: | 976 else: |
| 979 logging.critical( | 977 logging.critical( |
| 980 ('No idea what is %s.\nYou just found a bug in gclient' | 978 ('No idea what is %s.\nYou just found a bug in gclient' |
| 981 ', please ping maruel@chromium.org ASAP!') % file_path) | 979 ', please ping maruel@chromium.org ASAP!') % file_path) |
| 982 | 980 |
| 983 if (file_status[0][0] in ('D', 'A', '!') or | 981 if (file_status[0][0] in ('D', 'A', '!') or |
| 984 not file_status[0][1:].isspace()): | 982 not file_status[0][1:].isspace()): |
| 985 # Added, deleted file requires manual intervention and require calling | 983 # Added, deleted file requires manual intervention and require calling |
| 986 # revert, like for properties. | 984 # revert, like for properties. |
| 987 try: | 985 try: |
| 988 SVN.Capture(['revert', file_status[1]], cwd=repo_root) | 986 SVN.Capture(['revert', file_status[1]], cwd=repo_root) |
| 989 except gclient_utils.CheckCallError: | 987 except subprocess2.CalledProcessError: |
| 990 if not os.path.exists(file_path): | 988 if not os.path.exists(file_path): |
| 991 continue | 989 continue |
| 992 raise | 990 raise |
| OLD | NEW |