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 |