OLD | NEW |
1 # Copyright (c) 2009 The Chromium Authors. All rights reserved. | 1 # Copyright (c) 2009 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 """Gclient-specific SCM-specific operations.""" | 5 """Gclient-specific SCM-specific operations.""" |
6 | 6 |
7 import logging | 7 import logging |
8 import os | 8 import os |
9 import posixpath | 9 import posixpath |
10 import re | 10 import re |
11 import subprocess | 11 import subprocess |
12 | 12 |
13 import scm | 13 import scm |
14 import gclient_utils | 14 import gclient_utils |
15 | 15 |
16 | 16 |
17 class DiffFilterer(object): | 17 class DiffFilterer(object): |
18 """Simple class which tracks which file is being diffed and | 18 """Simple class which tracks which file is being diffed and |
19 replaces instances of its file name in the original and | 19 replaces instances of its file name in the original and |
20 working copy lines of the svn diff output.""" | 20 working copy lines of the svn/git diff output.""" |
21 index_string = "Index: " | 21 index_string = "Index: " |
22 original_prefix = "--- " | 22 original_prefix = "--- " |
23 working_prefix = "+++ " | 23 working_prefix = "+++ " |
24 | 24 |
25 def __init__(self, relpath): | 25 def __init__(self, relpath): |
26 # Note that we always use '/' as the path separator to be | 26 # Note that we always use '/' as the path separator to be |
27 # consistent with svn's cygwin-style output on Windows | 27 # consistent with svn's cygwin-style output on Windows |
28 self._relpath = relpath.replace("\\", "/") | 28 self._relpath = relpath.replace("\\", "/") |
29 self._current_file = "" | 29 self._current_file = "" |
30 self._replacement_file = "" | 30 self._replacement_file = "" |
(...skipping 17 matching lines...) Expand all Loading... |
48 self.ReplaceAndPrint(line) | 48 self.ReplaceAndPrint(line) |
49 else: | 49 else: |
50 print line | 50 print line |
51 | 51 |
52 | 52 |
53 ### SCM abstraction layer | 53 ### SCM abstraction layer |
54 | 54 |
55 # Factory Method for SCM wrapper creation | 55 # Factory Method for SCM wrapper creation |
56 | 56 |
57 def CreateSCM(url=None, root_dir=None, relpath=None, scm_name='svn'): | 57 def CreateSCM(url=None, root_dir=None, relpath=None, scm_name='svn'): |
58 # TODO(maruel): Deduce the SCM from the url. | |
59 scm_map = { | 58 scm_map = { |
60 'svn' : SVNWrapper, | 59 'svn' : SVNWrapper, |
61 'git' : GitWrapper, | 60 'git' : GitWrapper, |
62 } | 61 } |
63 | 62 |
64 orig_url = url | 63 orig_url = url |
65 | 64 |
66 if url: | 65 if url: |
67 url, _ = gclient_utils.SplitUrlRevision(url) | 66 url, _ = gclient_utils.SplitUrlRevision(url) |
68 if url.startswith('git:') or url.startswith('ssh:') or url.endswith('.git'): | 67 if url.startswith('git:') or url.startswith('ssh:') or url.endswith('.git'): |
69 scm_name = 'git' | 68 scm_name = 'git' |
70 | 69 |
71 if not scm_name in scm_map: | 70 if not scm_name in scm_map: |
72 raise gclient_utils.Error('Unsupported scm %s' % scm_name) | 71 raise gclient_utils.Error('Unsupported scm %s' % scm_name) |
73 return scm_map[scm_name](orig_url, root_dir, relpath, scm_name) | 72 return scm_map[scm_name](orig_url, root_dir, relpath, scm_name) |
74 | 73 |
75 | 74 |
76 # SCMWrapper base class | 75 # SCMWrapper base class |
77 | 76 |
78 class SCMWrapper(object): | 77 class SCMWrapper(object): |
79 """Add necessary glue between all the supported SCM. | 78 """Add necessary glue between all the supported SCM. |
80 | 79 |
81 This is the abstraction layer to bind to different SCM. Since currently only | 80 This is the abstraction layer to bind to different SCM. |
82 subversion is supported, a lot of subersionism remains. This can be sorted out | 81 """ |
83 once another SCM is supported.""" | |
84 def __init__(self, url=None, root_dir=None, relpath=None, | 82 def __init__(self, url=None, root_dir=None, relpath=None, |
85 scm_name='svn'): | 83 scm_name='svn'): |
86 self.scm_name = scm_name | 84 self.scm_name = scm_name |
87 self.url = url | 85 self.url = url |
88 self._root_dir = root_dir | 86 self._root_dir = root_dir |
89 if self._root_dir: | 87 if self._root_dir: |
90 self._root_dir = self._root_dir.replace('/', os.sep) | 88 self._root_dir = self._root_dir.replace('/', os.sep) |
91 self.relpath = relpath | 89 self.relpath = relpath |
92 if self.relpath: | 90 if self.relpath: |
93 self.relpath = self.relpath.replace('/', os.sep) | 91 self.relpath = self.relpath.replace('/', os.sep) |
(...skipping 27 matching lines...) Expand all Loading... |
121 self._Run(['prune'], redirect_stdout=False) | 119 self._Run(['prune'], redirect_stdout=False) |
122 self._Run(['fsck'], redirect_stdout=False) | 120 self._Run(['fsck'], redirect_stdout=False) |
123 self._Run(['gc'], redirect_stdout=False) | 121 self._Run(['gc'], redirect_stdout=False) |
124 | 122 |
125 def diff(self, options, args, file_list): | 123 def diff(self, options, args, file_list): |
126 __pychecker__ = 'unusednames=args,file_list,options' | 124 __pychecker__ = 'unusednames=args,file_list,options' |
127 merge_base = self._Run(['merge-base', 'HEAD', 'origin']) | 125 merge_base = self._Run(['merge-base', 'HEAD', 'origin']) |
128 self._Run(['diff', merge_base], redirect_stdout=False) | 126 self._Run(['diff', merge_base], redirect_stdout=False) |
129 | 127 |
130 def export(self, options, args, file_list): | 128 def export(self, options, args, file_list): |
| 129 """Export a clean directory tree into the given path. |
| 130 |
| 131 Exports into the specified directory, creating the path if it does |
| 132 already exist. |
| 133 """ |
131 __pychecker__ = 'unusednames=file_list,options' | 134 __pychecker__ = 'unusednames=file_list,options' |
132 assert len(args) == 1 | 135 assert len(args) == 1 |
133 export_path = os.path.abspath(os.path.join(args[0], self.relpath)) | 136 export_path = os.path.abspath(os.path.join(args[0], self.relpath)) |
134 if not os.path.exists(export_path): | 137 if not os.path.exists(export_path): |
135 os.makedirs(export_path) | 138 os.makedirs(export_path) |
136 self._Run(['checkout-index', '-a', '--prefix=%s/' % export_path], | 139 self._Run(['checkout-index', '-a', '--prefix=%s/' % export_path], |
137 redirect_stdout=False) | 140 redirect_stdout=False) |
138 | 141 |
139 def pack(self, options, args, file_list): | 142 def pack(self, options, args, file_list): |
140 """Generates a patch file which can be applied to the root of the | 143 """Generates a patch file which can be applied to the root of the |
141 repository.""" | 144 repository. |
| 145 |
| 146 The patch file is generated from a diff of the merge base of HEAD and |
| 147 its upstream branch. |
| 148 """ |
142 __pychecker__ = 'unusednames=file_list,options' | 149 __pychecker__ = 'unusednames=file_list,options' |
143 path = os.path.join(self._root_dir, self.relpath) | 150 path = os.path.join(self._root_dir, self.relpath) |
144 merge_base = self._Run(['merge-base', 'HEAD', 'origin']) | 151 merge_base = self._Run(['merge-base', 'HEAD', 'origin']) |
145 command = ['diff', merge_base] | 152 command = ['diff', merge_base] |
146 filterer = DiffFilterer(self.relpath) | 153 filterer = DiffFilterer(self.relpath) |
147 self.RunAndFilterOutput(command, path, False, False, filterer.Filter) | 154 self.RunAndFilterOutput(command, path, False, False, filterer.Filter) |
148 | 155 |
149 def update(self, options, args, file_list): | 156 def update(self, options, args, file_list): |
150 """Runs git to update or transparently checkout the working copy. | 157 """Runs git to update or transparently checkout the working copy. |
151 | 158 |
(...skipping 159 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
311 self.Run(command, os.path.join(self._root_dir, self.relpath)) | 318 self.Run(command, os.path.join(self._root_dir, self.relpath)) |
312 | 319 |
313 def diff(self, options, args, file_list): | 320 def diff(self, options, args, file_list): |
314 # NOTE: This function does not currently modify file_list. | 321 # NOTE: This function does not currently modify file_list. |
315 __pychecker__ = 'unusednames=file_list,options' | 322 __pychecker__ = 'unusednames=file_list,options' |
316 command = ['diff'] | 323 command = ['diff'] |
317 command.extend(args) | 324 command.extend(args) |
318 self.Run(command, os.path.join(self._root_dir, self.relpath)) | 325 self.Run(command, os.path.join(self._root_dir, self.relpath)) |
319 | 326 |
320 def export(self, options, args, file_list): | 327 def export(self, options, args, file_list): |
| 328 """Export a clean directory tree into the given path.""" |
321 __pychecker__ = 'unusednames=file_list,options' | 329 __pychecker__ = 'unusednames=file_list,options' |
322 assert len(args) == 1 | 330 assert len(args) == 1 |
323 export_path = os.path.abspath(os.path.join(args[0], self.relpath)) | 331 export_path = os.path.abspath(os.path.join(args[0], self.relpath)) |
324 try: | 332 try: |
325 os.makedirs(export_path) | 333 os.makedirs(export_path) |
326 except OSError: | 334 except OSError: |
327 pass | 335 pass |
328 assert os.path.exists(export_path) | 336 assert os.path.exists(export_path) |
329 command = ['export', '--force', '.'] | 337 command = ['export', '--force', '.'] |
330 command.append(export_path) | 338 command.append(export_path) |
331 self.Run(command, os.path.join(self._root_dir, self.relpath)) | 339 self.Run(command, os.path.join(self._root_dir, self.relpath)) |
332 | 340 |
333 def pack(self, options, args, file_list): | 341 def pack(self, options, args, file_list): |
334 """Generates a patch file which can be applied to the root of the | 342 """Generates a patch file which can be applied to the root of the |
335 repository.""" | 343 repository.""" |
336 __pychecker__ = 'unusednames=file_list,options' | 344 __pychecker__ = 'unusednames=file_list,options' |
337 path = os.path.join(self._root_dir, self.relpath) | 345 path = os.path.join(self._root_dir, self.relpath) |
338 command = ['diff'] | 346 command = ['diff'] |
339 command.extend(args) | 347 command.extend(args) |
340 | 348 |
341 filterer = DiffFilterer(self.relpath) | 349 filterer = DiffFilterer(self.relpath) |
342 self.RunAndFilterOutput(command, path, False, False, filterer.Filter) | 350 self.RunAndFilterOutput(command, path, False, False, filterer.Filter) |
343 | 351 |
344 def update(self, options, args, file_list): | 352 def update(self, options, args, file_list): |
345 """Runs SCM to update or transparently checkout the working copy. | 353 """Runs svn to update or transparently checkout the working copy. |
346 | 354 |
347 All updated files will be appended to file_list. | 355 All updated files will be appended to file_list. |
348 | 356 |
349 Raises: | 357 Raises: |
350 Error: if can't get URL for relative path. | 358 Error: if can't get URL for relative path. |
351 """ | 359 """ |
352 # Only update if git is not controlling the directory. | 360 # Only update if git is not controlling the directory. |
353 checkout_path = os.path.join(self._root_dir, self.relpath) | 361 checkout_path = os.path.join(self._root_dir, self.relpath) |
354 git_path = os.path.join(self._root_dir, self.relpath, '.git') | 362 git_path = os.path.join(self._root_dir, self.relpath, '.git') |
355 if os.path.exists(git_path): | 363 if os.path.exists(git_path): |
(...skipping 165 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
521 command = ['status'] | 529 command = ['status'] |
522 command.extend(args) | 530 command.extend(args) |
523 if not os.path.isdir(path): | 531 if not os.path.isdir(path): |
524 # svn status won't work if the directory doesn't exist. | 532 # svn status won't work if the directory doesn't exist. |
525 print("\n________ couldn't run \'%s\' in \'%s\':\nThe directory " | 533 print("\n________ couldn't run \'%s\' in \'%s\':\nThe directory " |
526 "does not exist." | 534 "does not exist." |
527 % (' '.join(command), path)) | 535 % (' '.join(command), path)) |
528 # There's no file list to retrieve. | 536 # There's no file list to retrieve. |
529 else: | 537 else: |
530 self.RunAndGetFileList(options, command, path, file_list) | 538 self.RunAndGetFileList(options, command, path, file_list) |
OLD | NEW |