| 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 """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 sys | 11 import sys |
| 12 import time | 12 import time |
| 13 | 13 |
| 14 import scm | 14 import scm |
| 15 import gclient_utils | 15 import gclient_utils |
| 16 | 16 |
| 17 | 17 |
| 18 class DiffFilterer(object): | 18 class DiffFilterer(object): |
| 19 """Simple class which tracks which file is being diffed and | 19 """Simple class which tracks which file is being diffed and |
| 20 replaces instances of its file name in the original and | 20 replaces instances of its file name in the original and |
| 21 working copy lines of the svn/git diff output.""" | 21 working copy lines of the svn/git diff output.""" |
| 22 index_string = "Index: " | 22 index_string = "Index: " |
| 23 original_prefix = "--- " | 23 original_prefix = "--- " |
| 24 working_prefix = "+++ " | 24 working_prefix = "+++ " |
| 25 | 25 |
| 26 def __init__(self, relpath, stdout): | 26 def __init__(self, relpath): |
| 27 # Note that we always use '/' as the path separator to be | 27 # Note that we always use '/' as the path separator to be |
| 28 # consistent with svn's cygwin-style output on Windows | 28 # consistent with svn's cygwin-style output on Windows |
| 29 self._relpath = relpath.replace("\\", "/") | 29 self._relpath = relpath.replace("\\", "/") |
| 30 self._current_file = "" | 30 self._current_file = "" |
| 31 self._replacement_file = "" | 31 self._replacement_file = "" |
| 32 self._stdout = stdout | |
| 33 | 32 |
| 34 def SetCurrentFile(self, current_file): | 33 def SetCurrentFile(self, current_file): |
| 35 self._current_file = current_file | 34 self._current_file = current_file |
| 36 # Note that we always use '/' as the path separator to be | 35 # Note that we always use '/' as the path separator to be |
| 37 # consistent with svn's cygwin-style output on Windows | 36 # consistent with svn's cygwin-style output on Windows |
| 38 self._replacement_file = posixpath.join(self._relpath, current_file) | 37 self._replacement_file = posixpath.join(self._relpath, current_file) |
| 39 | 38 |
| 40 def _Replace(self, line): | 39 def _Replace(self, line): |
| 41 return line.replace(self._current_file, self._replacement_file) | 40 return line.replace(self._current_file, self._replacement_file) |
| 42 | 41 |
| 43 def Filter(self, line): | 42 def Filter(self, line): |
| 44 if (line.startswith(self.index_string)): | 43 if (line.startswith(self.index_string)): |
| 45 self.SetCurrentFile(line[len(self.index_string):]) | 44 self.SetCurrentFile(line[len(self.index_string):]) |
| 46 line = self._Replace(line) | 45 line = self._Replace(line) |
| 47 else: | 46 else: |
| 48 if (line.startswith(self.original_prefix) or | 47 if (line.startswith(self.original_prefix) or |
| 49 line.startswith(self.working_prefix)): | 48 line.startswith(self.working_prefix)): |
| 50 line = self._Replace(line) | 49 line = self._Replace(line) |
| 51 self._stdout.write(line + '\n') | 50 print(line) |
| 52 | 51 |
| 53 | 52 |
| 54 ### SCM abstraction layer | 53 ### SCM abstraction layer |
| 55 | 54 |
| 56 # Factory Method for SCM wrapper creation | 55 # Factory Method for SCM wrapper creation |
| 57 | 56 |
| 58 def GetScmName(url): | 57 def GetScmName(url): |
| 59 if url: | 58 if url: |
| 60 url, _ = gclient_utils.SplitUrlRevision(url) | 59 url, _ = gclient_utils.SplitUrlRevision(url) |
| 61 if (url.startswith('git://') or url.startswith('ssh://') or | 60 if (url.startswith('git://') or url.startswith('ssh://') or |
| (...skipping 84 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 146 """Generates a patch file which can be applied to the root of the | 145 """Generates a patch file which can be applied to the root of the |
| 147 repository. | 146 repository. |
| 148 | 147 |
| 149 The patch file is generated from a diff of the merge base of HEAD and | 148 The patch file is generated from a diff of the merge base of HEAD and |
| 150 its upstream branch. | 149 its upstream branch. |
| 151 """ | 150 """ |
| 152 merge_base = self._Capture(['merge-base', 'HEAD', 'origin']) | 151 merge_base = self._Capture(['merge-base', 'HEAD', 'origin']) |
| 153 gclient_utils.CheckCallAndFilter( | 152 gclient_utils.CheckCallAndFilter( |
| 154 ['git', 'diff', merge_base], | 153 ['git', 'diff', merge_base], |
| 155 cwd=self.checkout_path, | 154 cwd=self.checkout_path, |
| 156 filter_fn=DiffFilterer(self.relpath, options.stdout).Filter, | 155 filter_fn=DiffFilterer(self.relpath).Filter) |
| 157 stdout=options.stdout) | |
| 158 | 156 |
| 159 def update(self, options, args, file_list): | 157 def update(self, options, args, file_list): |
| 160 """Runs git to update or transparently checkout the working copy. | 158 """Runs git to update or transparently checkout the working copy. |
| 161 | 159 |
| 162 All updated files will be appended to file_list. | 160 All updated files will be appended to file_list. |
| 163 | 161 |
| 164 Raises: | 162 Raises: |
| 165 Error: if can't get URL for relative path. | 163 Error: if can't get URL for relative path. |
| 166 """ | 164 """ |
| 167 if args: | 165 if args: |
| (...skipping 10 matching lines...) Expand all Loading... |
| 178 revision = str(options.revision) | 176 revision = str(options.revision) |
| 179 if not revision: | 177 if not revision: |
| 180 revision = default_rev | 178 revision = default_rev |
| 181 | 179 |
| 182 rev_str = ' at %s' % revision | 180 rev_str = ' at %s' % revision |
| 183 files = [] | 181 files = [] |
| 184 | 182 |
| 185 printed_path = False | 183 printed_path = False |
| 186 verbose = [] | 184 verbose = [] |
| 187 if options.verbose: | 185 if options.verbose: |
| 188 options.stdout.write("\n_____ %s%s\n" % (self.relpath, rev_str)) | 186 print('\n_____ %s%s' % (self.relpath, rev_str)) |
| 189 verbose = ['--verbose'] | 187 verbose = ['--verbose'] |
| 190 printed_path = True | 188 printed_path = True |
| 191 | 189 |
| 192 if revision.startswith('refs/heads/'): | 190 if revision.startswith('refs/heads/'): |
| 193 rev_type = "branch" | 191 rev_type = "branch" |
| 194 elif revision.startswith('origin/'): | 192 elif revision.startswith('origin/'): |
| 195 # For compatability with old naming, translate 'origin' to 'refs/heads' | 193 # For compatability with old naming, translate 'origin' to 'refs/heads' |
| 196 revision = revision.replace('origin/', 'refs/heads/') | 194 revision = revision.replace('origin/', 'refs/heads/') |
| 197 rev_type = "branch" | 195 rev_type = "branch" |
| 198 else: | 196 else: |
| 199 # hash is also a tag, only make a distinction at checkout | 197 # hash is also a tag, only make a distinction at checkout |
| 200 rev_type = "hash" | 198 rev_type = "hash" |
| 201 | 199 |
| 202 if not os.path.exists(self.checkout_path): | 200 if not os.path.exists(self.checkout_path): |
| 203 self._Clone(revision, url, options) | 201 self._Clone(revision, url, options) |
| 204 files = self._Capture(['ls-files']).split() | 202 files = self._Capture(['ls-files']).split() |
| 205 file_list.extend([os.path.join(self.checkout_path, f) for f in files]) | 203 file_list.extend([os.path.join(self.checkout_path, f) for f in files]) |
| 206 if not verbose: | 204 if not verbose: |
| 207 # Make the output a little prettier. It's nice to have some whitespace | 205 # Make the output a little prettier. It's nice to have some whitespace |
| 208 # between projects when cloning. | 206 # between projects when cloning. |
| 209 options.stdout.write('\n') | 207 print('') |
| 210 return | 208 return |
| 211 | 209 |
| 212 if not os.path.exists(os.path.join(self.checkout_path, '.git')): | 210 if not os.path.exists(os.path.join(self.checkout_path, '.git')): |
| 213 raise gclient_utils.Error('\n____ %s%s\n' | 211 raise gclient_utils.Error('\n____ %s%s\n' |
| 214 '\tPath is not a git repo. No .git dir.\n' | 212 '\tPath is not a git repo. No .git dir.\n' |
| 215 '\tTo resolve:\n' | 213 '\tTo resolve:\n' |
| 216 '\t\trm -rf %s\n' | 214 '\t\trm -rf %s\n' |
| 217 '\tAnd run gclient sync again\n' | 215 '\tAnd run gclient sync again\n' |
| 218 % (self.relpath, rev_str, self.relpath)) | 216 % (self.relpath, rev_str, self.relpath)) |
| 219 | 217 |
| (...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 259 for _ in range(10): | 257 for _ in range(10): |
| 260 try: | 258 try: |
| 261 remote_output = scm.GIT.Capture( | 259 remote_output = scm.GIT.Capture( |
| 262 ['remote'] + verbose + ['update'], | 260 ['remote'] + verbose + ['update'], |
| 263 cwd=self.checkout_path) | 261 cwd=self.checkout_path) |
| 264 break | 262 break |
| 265 except gclient_utils.CheckCallError, e: | 263 except gclient_utils.CheckCallError, e: |
| 266 # Hackish but at that point, git is known to work so just checking for | 264 # Hackish but at that point, git is known to work so just checking for |
| 267 # 502 in stderr should be fine. | 265 # 502 in stderr should be fine. |
| 268 if '502' in e.stderr: | 266 if '502' in e.stderr: |
| 269 options.stdout.write(str(e) + '\n') | 267 print(str(e)) |
| 270 options.stdout.write('Sleeping %.1f seconds and retrying...\n' % | 268 print('Sleeping %.1f seconds and retrying...' % backoff_time) |
| 271 backoff_time) | |
| 272 time.sleep(backoff_time) | 269 time.sleep(backoff_time) |
| 273 backoff_time *= 1.3 | 270 backoff_time *= 1.3 |
| 274 continue | 271 continue |
| 275 raise | 272 raise |
| 276 | 273 |
| 277 if verbose: | 274 if verbose: |
| 278 options.stdout.write(remote_output) | 275 print(remote_output.strip()) |
| 279 | 276 |
| 280 # This is a big hammer, debatable if it should even be here... | 277 # This is a big hammer, debatable if it should even be here... |
| 281 if options.force or options.reset: | 278 if options.force or options.reset: |
| 282 self._Run(['reset', '--hard', 'HEAD'], options) | 279 self._Run(['reset', '--hard', 'HEAD'], options) |
| 283 | 280 |
| 284 if current_type == 'detached': | 281 if current_type == 'detached': |
| 285 # case 0 | 282 # case 0 |
| 286 self._CheckClean(rev_str) | 283 self._CheckClean(rev_str) |
| 287 self._CheckDetachedHead(rev_str, options) | 284 self._CheckDetachedHead(rev_str, options) |
| 288 self._Capture(['checkout', '--quiet', '%s^0' % revision]) | 285 self._Capture(['checkout', '--quiet', '%s^0' % revision]) |
| 289 if not printed_path: | 286 if not printed_path: |
| 290 options.stdout.write('\n_____ %s%s\n' % (self.relpath, rev_str)) | 287 print('\n_____ %s%s' % (self.relpath, rev_str)) |
| 291 elif current_type == 'hash': | 288 elif current_type == 'hash': |
| 292 # case 1 | 289 # case 1 |
| 293 if scm.GIT.IsGitSvn(self.checkout_path) and upstream_branch is not None: | 290 if scm.GIT.IsGitSvn(self.checkout_path) and upstream_branch is not None: |
| 294 # Our git-svn branch (upstream_branch) is our upstream | 291 # Our git-svn branch (upstream_branch) is our upstream |
| 295 self._AttemptRebase(upstream_branch, files, options, | 292 self._AttemptRebase(upstream_branch, files, options, |
| 296 newbase=revision, printed_path=printed_path) | 293 newbase=revision, printed_path=printed_path) |
| 297 printed_path = True | 294 printed_path = True |
| 298 else: | 295 else: |
| 299 # Can't find a merge-base since we don't know our upstream. That makes | 296 # Can't find a merge-base since we don't know our upstream. That makes |
| 300 # this command VERY likely to produce a rebase failure. For now we | 297 # this command VERY likely to produce a rebase failure. For now we |
| 301 # assume origin is our upstream since that's what the old behavior was. | 298 # assume origin is our upstream since that's what the old behavior was. |
| 302 upstream_branch = 'origin' | 299 upstream_branch = 'origin' |
| 303 if options.revision or deps_revision: | 300 if options.revision or deps_revision: |
| 304 upstream_branch = revision | 301 upstream_branch = revision |
| 305 self._AttemptRebase(upstream_branch, files, options, | 302 self._AttemptRebase(upstream_branch, files, options, |
| 306 printed_path=printed_path) | 303 printed_path=printed_path) |
| 307 printed_path = True | 304 printed_path = True |
| 308 elif rev_type == 'hash': | 305 elif rev_type == 'hash': |
| 309 # case 2 | 306 # case 2 |
| 310 self._AttemptRebase(upstream_branch, files, options, | 307 self._AttemptRebase(upstream_branch, files, options, |
| 311 newbase=revision, printed_path=printed_path) | 308 newbase=revision, printed_path=printed_path) |
| 312 printed_path = True | 309 printed_path = True |
| 313 elif revision.replace('heads', 'remotes/origin') != upstream_branch: | 310 elif revision.replace('heads', 'remotes/origin') != upstream_branch: |
| 314 # case 4 | 311 # case 4 |
| 315 new_base = revision.replace('heads', 'remotes/origin') | 312 new_base = revision.replace('heads', 'remotes/origin') |
| 316 if not printed_path: | 313 if not printed_path: |
| 317 options.stdout.write('\n_____ %s%s\n' % (self.relpath, rev_str)) | 314 print('\n_____ %s%s' % (self.relpath, rev_str)) |
| 318 switch_error = ("Switching upstream branch from %s to %s\n" | 315 switch_error = ("Switching upstream branch from %s to %s\n" |
| 319 % (upstream_branch, new_base) + | 316 % (upstream_branch, new_base) + |
| 320 "Please merge or rebase manually:\n" + | 317 "Please merge or rebase manually:\n" + |
| 321 "cd %s; git rebase %s\n" % (self.checkout_path, new_base) + | 318 "cd %s; git rebase %s\n" % (self.checkout_path, new_base) + |
| 322 "OR git checkout -b <some new branch> %s" % new_base) | 319 "OR git checkout -b <some new branch> %s" % new_base) |
| 323 raise gclient_utils.Error(switch_error) | 320 raise gclient_utils.Error(switch_error) |
| 324 else: | 321 else: |
| 325 # case 3 - the default case | 322 # case 3 - the default case |
| 326 files = self._Capture(['diff', upstream_branch, '--name-only']).split() | 323 files = self._Capture(['diff', upstream_branch, '--name-only']).split() |
| 327 if verbose: | 324 if verbose: |
| 328 options.stdout.write('Trying fast-forward merge to branch : %s\n' % | 325 print('Trying fast-forward merge to branch : %s' % upstream_branch) |
| 329 upstream_branch) | |
| 330 try: | 326 try: |
| 331 merge_output = scm.GIT.Capture(['merge', '--ff-only', upstream_branch], | 327 merge_output = scm.GIT.Capture(['merge', '--ff-only', upstream_branch], |
| 332 cwd=self.checkout_path) | 328 cwd=self.checkout_path) |
| 333 except gclient_utils.CheckCallError, e: | 329 except gclient_utils.CheckCallError, e: |
| 334 if re.match('fatal: Not possible to fast-forward, aborting.', e.stderr): | 330 if re.match('fatal: Not possible to fast-forward, aborting.', e.stderr): |
| 335 if not printed_path: | 331 if not printed_path: |
| 336 options.stdout.write('\n_____ %s%s\n' % (self.relpath, rev_str)) | 332 print('\n_____ %s%s' % (self.relpath, rev_str)) |
| 337 printed_path = True | 333 printed_path = True |
| 338 while True: | 334 while True: |
| 339 try: | 335 try: |
| 340 # TODO(maruel): That can't work with --jobs. | 336 # TODO(maruel): That can't work with --jobs. |
| 341 action = str(raw_input("Cannot fast-forward merge, attempt to " | 337 action = str(raw_input("Cannot fast-forward merge, attempt to " |
| 342 "rebase? (y)es / (q)uit / (s)kip : ")) | 338 "rebase? (y)es / (q)uit / (s)kip : ")) |
| 343 except ValueError: | 339 except ValueError: |
| 344 gclient_utils.Error('Invalid Character') | 340 gclient_utils.Error('Invalid Character') |
| 345 continue | 341 continue |
| 346 if re.match(r'yes|y', action, re.I): | 342 if re.match(r'yes|y', action, re.I): |
| 347 self._AttemptRebase(upstream_branch, files, options, | 343 self._AttemptRebase(upstream_branch, files, options, |
| 348 printed_path=printed_path) | 344 printed_path=printed_path) |
| 349 printed_path = True | 345 printed_path = True |
| 350 break | 346 break |
| 351 elif re.match(r'quit|q', action, re.I): | 347 elif re.match(r'quit|q', action, re.I): |
| 352 raise gclient_utils.Error("Can't fast-forward, please merge or " | 348 raise gclient_utils.Error("Can't fast-forward, please merge or " |
| 353 "rebase manually.\n" | 349 "rebase manually.\n" |
| 354 "cd %s && git " % self.checkout_path | 350 "cd %s && git " % self.checkout_path |
| 355 + "rebase %s" % upstream_branch) | 351 + "rebase %s" % upstream_branch) |
| 356 elif re.match(r'skip|s', action, re.I): | 352 elif re.match(r'skip|s', action, re.I): |
| 357 options.stdout.write('Skipping %s\n' % self.relpath) | 353 print('Skipping %s' % self.relpath) |
| 358 return | 354 return |
| 359 else: | 355 else: |
| 360 options.stdout.write('Input not recognized\n') | 356 print('Input not recognized') |
| 361 elif re.match("error: Your local changes to '.*' would be " | 357 elif re.match("error: Your local changes to '.*' would be " |
| 362 "overwritten by merge. Aborting.\nPlease, commit your " | 358 "overwritten by merge. Aborting.\nPlease, commit your " |
| 363 "changes or stash them before you can merge.\n", | 359 "changes or stash them before you can merge.\n", |
| 364 e.stderr): | 360 e.stderr): |
| 365 if not printed_path: | 361 if not printed_path: |
| 366 options.stdout.write('\n_____ %s%s\n' % (self.relpath, rev_str)) | 362 print('\n_____ %s%s' % (self.relpath, rev_str)) |
| 367 printed_path = True | 363 printed_path = True |
| 368 raise gclient_utils.Error(e.stderr) | 364 raise gclient_utils.Error(e.stderr) |
| 369 else: | 365 else: |
| 370 # Some other problem happened with the merge | 366 # Some other problem happened with the merge |
| 371 logging.error("Error during fast-forward merge in %s!" % self.relpath) | 367 logging.error("Error during fast-forward merge in %s!" % self.relpath) |
| 372 options.stdout.write(e.stderr + '\n') | 368 print(e.stderr) |
| 373 raise | 369 raise |
| 374 else: | 370 else: |
| 375 # Fast-forward merge was successful | 371 # Fast-forward merge was successful |
| 376 if not re.match('Already up-to-date.', merge_output) or verbose: | 372 if not re.match('Already up-to-date.', merge_output) or verbose: |
| 377 if not printed_path: | 373 if not printed_path: |
| 378 options.stdout.write('\n_____ %s%s\n' % (self.relpath, rev_str)) | 374 print('\n_____ %s%s' % (self.relpath, rev_str)) |
| 379 printed_path = True | 375 printed_path = True |
| 380 options.stdout.write(merge_output) | 376 print(merge_output.strip()) |
| 381 if not verbose: | 377 if not verbose: |
| 382 # Make the output a little prettier. It's nice to have some | 378 # Make the output a little prettier. It's nice to have some |
| 383 # whitespace between projects when syncing. | 379 # whitespace between projects when syncing. |
| 384 options.stdout.write('\n') | 380 print('') |
| 385 | 381 |
| 386 file_list.extend([os.path.join(self.checkout_path, f) for f in files]) | 382 file_list.extend([os.path.join(self.checkout_path, f) for f in files]) |
| 387 | 383 |
| 388 # If the rebase generated a conflict, abort and ask user to fix | 384 # If the rebase generated a conflict, abort and ask user to fix |
| 389 if self._IsRebasing(): | 385 if self._IsRebasing(): |
| 390 raise gclient_utils.Error('\n____ %s%s\n' | 386 raise gclient_utils.Error('\n____ %s%s\n' |
| 391 '\nConflict while rebasing this branch.\n' | 387 '\nConflict while rebasing this branch.\n' |
| 392 'Fix the conflict and run gclient again.\n' | 388 'Fix the conflict and run gclient again.\n' |
| 393 'See man git-rebase for details.\n' | 389 'See man git-rebase for details.\n' |
| 394 % (self.relpath, rev_str)) | 390 % (self.relpath, rev_str)) |
| 395 | 391 |
| 396 if verbose: | 392 if verbose: |
| 397 options.stdout.write('Checked out revision %s\n' % | 393 print('Checked out revision %s' % self.revinfo(options, (), None)) |
| 398 self.revinfo(options, (), None)) | |
| 399 | 394 |
| 400 def revert(self, options, args, file_list): | 395 def revert(self, options, args, file_list): |
| 401 """Reverts local modifications. | 396 """Reverts local modifications. |
| 402 | 397 |
| 403 All reverted files will be appended to file_list. | 398 All reverted files will be appended to file_list. |
| 404 """ | 399 """ |
| 405 if not os.path.isdir(self.checkout_path): | 400 if not os.path.isdir(self.checkout_path): |
| 406 # revert won't work if the directory doesn't exist. It needs to | 401 # revert won't work if the directory doesn't exist. It needs to |
| 407 # checkout instead. | 402 # checkout instead. |
| 408 options.stdout.write('\n_____ %s is missing, synching instead\n' % | 403 print('\n_____ %s is missing, synching instead' % self.relpath) |
| 409 self.relpath) | |
| 410 # Don't reuse the args. | 404 # Don't reuse the args. |
| 411 return self.update(options, [], file_list) | 405 return self.update(options, [], file_list) |
| 412 | 406 |
| 413 default_rev = "refs/heads/master" | 407 default_rev = "refs/heads/master" |
| 414 _, deps_revision = gclient_utils.SplitUrlRevision(self.url) | 408 _, deps_revision = gclient_utils.SplitUrlRevision(self.url) |
| 415 if not deps_revision: | 409 if not deps_revision: |
| 416 deps_revision = default_rev | 410 deps_revision = default_rev |
| 417 if deps_revision.startswith('refs/heads/'): | 411 if deps_revision.startswith('refs/heads/'): |
| 418 deps_revision = deps_revision.replace('refs/heads/', 'origin/') | 412 deps_revision = deps_revision.replace('refs/heads/', 'origin/') |
| 419 | 413 |
| 420 files = self._Capture(['diff', deps_revision, '--name-only']).split() | 414 files = self._Capture(['diff', deps_revision, '--name-only']).split() |
| 421 self._Run(['reset', '--hard', deps_revision], options) | 415 self._Run(['reset', '--hard', deps_revision], options) |
| 422 file_list.extend([os.path.join(self.checkout_path, f) for f in files]) | 416 file_list.extend([os.path.join(self.checkout_path, f) for f in files]) |
| 423 | 417 |
| 424 def revinfo(self, options, args, file_list): | 418 def revinfo(self, options, args, file_list): |
| 425 """Returns revision""" | 419 """Returns revision""" |
| 426 return self._Capture(['rev-parse', 'HEAD']) | 420 return self._Capture(['rev-parse', 'HEAD']) |
| 427 | 421 |
| 428 def runhooks(self, options, args, file_list): | 422 def runhooks(self, options, args, file_list): |
| 429 self.status(options, args, file_list) | 423 self.status(options, args, file_list) |
| 430 | 424 |
| 431 def status(self, options, args, file_list): | 425 def status(self, options, args, file_list): |
| 432 """Display status information.""" | 426 """Display status information.""" |
| 433 if not os.path.isdir(self.checkout_path): | 427 if not os.path.isdir(self.checkout_path): |
| 434 options.stdout.write( | 428 print(('\n________ couldn\'t run status in %s:\n' |
| 435 ('\n________ couldn\'t run status in %s:\nThe directory ' | 429 'The directory does not exist.') % self.checkout_path) |
| 436 'does not exist.\n') % self.checkout_path) | |
| 437 else: | 430 else: |
| 438 merge_base = self._Capture(['merge-base', 'HEAD', 'origin']) | 431 merge_base = self._Capture(['merge-base', 'HEAD', 'origin']) |
| 439 self._Run(['diff', '--name-status', merge_base], options) | 432 self._Run(['diff', '--name-status', merge_base], options) |
| 440 files = self._Capture(['diff', '--name-only', merge_base]).split() | 433 files = self._Capture(['diff', '--name-only', merge_base]).split() |
| 441 file_list.extend([os.path.join(self.checkout_path, f) for f in files]) | 434 file_list.extend([os.path.join(self.checkout_path, f) for f in files]) |
| 442 | 435 |
| 443 def FullUrlForRelativeUrl(self, url): | 436 def FullUrlForRelativeUrl(self, url): |
| 444 # Strip from last '/' | 437 # Strip from last '/' |
| 445 # Equivalent to unix basename | 438 # Equivalent to unix basename |
| 446 base_url = self.url | 439 base_url = self.url |
| 447 return base_url[:base_url.rfind('/')] + url | 440 return base_url[:base_url.rfind('/')] + url |
| 448 | 441 |
| 449 def _Clone(self, revision, url, options): | 442 def _Clone(self, revision, url, options): |
| 450 """Clone a git repository from the given URL. | 443 """Clone a git repository from the given URL. |
| 451 | 444 |
| 452 Once we've cloned the repo, we checkout a working branch if the specified | 445 Once we've cloned the repo, we checkout a working branch if the specified |
| 453 revision is a branch head. If it is a tag or a specific commit, then we | 446 revision is a branch head. If it is a tag or a specific commit, then we |
| 454 leave HEAD detached as it makes future updates simpler -- in this case the | 447 leave HEAD detached as it makes future updates simpler -- in this case the |
| 455 user should first create a new branch or switch to an existing branch before | 448 user should first create a new branch or switch to an existing branch before |
| 456 making changes in the repo.""" | 449 making changes in the repo.""" |
| 457 if not options.verbose: | 450 if not options.verbose: |
| 458 # git clone doesn't seem to insert a newline properly before printing | 451 # git clone doesn't seem to insert a newline properly before printing |
| 459 # to stdout | 452 # to stdout |
| 460 options.stdout.write('\n') | 453 print('') |
| 461 | 454 |
| 462 clone_cmd = ['clone'] | 455 clone_cmd = ['clone'] |
| 463 if revision.startswith('refs/heads/'): | 456 if revision.startswith('refs/heads/'): |
| 464 clone_cmd.extend(['-b', revision.replace('refs/heads/', '')]) | 457 clone_cmd.extend(['-b', revision.replace('refs/heads/', '')]) |
| 465 detach_head = False | 458 detach_head = False |
| 466 else: | 459 else: |
| 467 clone_cmd.append('--no-checkout') | 460 clone_cmd.append('--no-checkout') |
| 468 detach_head = True | 461 detach_head = True |
| 469 if options.verbose: | 462 if options.verbose: |
| 470 clone_cmd.append('--verbose') | 463 clone_cmd.append('--verbose') |
| 471 clone_cmd.extend([url, self.checkout_path]) | 464 clone_cmd.extend([url, self.checkout_path]) |
| 472 | 465 |
| 473 for _ in range(3): | 466 for _ in range(3): |
| 474 try: | 467 try: |
| 475 self._Run(clone_cmd, options, cwd=self._root_dir) | 468 self._Run(clone_cmd, options, cwd=self._root_dir) |
| 476 break | 469 break |
| 477 except gclient_utils.Error, e: | 470 except gclient_utils.Error, e: |
| 478 # TODO(maruel): Hackish, should be fixed by moving _Run() to | 471 # TODO(maruel): Hackish, should be fixed by moving _Run() to |
| 479 # CheckCall(). | 472 # CheckCall(). |
| 480 # Too bad we don't have access to the actual output. | 473 # Too bad we don't have access to the actual output. |
| 481 # We should check for "transfer closed with NNN bytes remaining to | 474 # We should check for "transfer closed with NNN bytes remaining to |
| 482 # read". In the meantime, just make sure .git exists. | 475 # read". In the meantime, just make sure .git exists. |
| 483 if (e.args[0] == 'git command clone returned 128' and | 476 if (e.args[0] == 'git command clone returned 128' and |
| 484 os.path.exists(os.path.join(self.checkout_path, '.git'))): | 477 os.path.exists(os.path.join(self.checkout_path, '.git'))): |
| 485 options.stdout.write(str(e) + '\n') | 478 print(str(e)) |
| 486 options.stdout.write('Retrying...\n') | 479 print('Retrying...') |
| 487 continue | 480 continue |
| 488 raise e | 481 raise e |
| 489 | 482 |
| 490 if detach_head: | 483 if detach_head: |
| 491 # Squelch git's very verbose detached HEAD warning and use our own | 484 # Squelch git's very verbose detached HEAD warning and use our own |
| 492 self._Capture(['checkout', '--quiet', '%s^0' % revision]) | 485 self._Capture(['checkout', '--quiet', '%s^0' % revision]) |
| 493 options.stdout.write( | 486 print( |
| 494 ('Checked out %s to a detached HEAD. Before making any commits\n' | 487 ('Checked out %s to a detached HEAD. Before making any commits\n' |
| 495 'in this repo, you should use \'git checkout <branch>\' to switch to\n' | 488 'in this repo, you should use \'git checkout <branch>\' to switch to\n' |
| 496 'an existing branch or use \'git checkout origin -b <branch>\' to\n' | 489 'an existing branch or use \'git checkout origin -b <branch>\' to\n' |
| 497 'create a new branch for your work.') % revision) | 490 'create a new branch for your work.') % revision) |
| 498 | 491 |
| 499 def _AttemptRebase(self, upstream, files, options, newbase=None, | 492 def _AttemptRebase(self, upstream, files, options, newbase=None, |
| 500 branch=None, printed_path=False): | 493 branch=None, printed_path=False): |
| 501 """Attempt to rebase onto either upstream or, if specified, newbase.""" | 494 """Attempt to rebase onto either upstream or, if specified, newbase.""" |
| 502 files.extend(self._Capture(['diff', upstream, '--name-only']).split()) | 495 files.extend(self._Capture(['diff', upstream, '--name-only']).split()) |
| 503 revision = upstream | 496 revision = upstream |
| 504 if newbase: | 497 if newbase: |
| 505 revision = newbase | 498 revision = newbase |
| 506 if not printed_path: | 499 if not printed_path: |
| 507 options.stdout.write('\n_____ %s : Attempting rebase onto %s...\n' % ( | 500 print('\n_____ %s : Attempting rebase onto %s...' % ( |
| 508 self.relpath, revision)) | 501 self.relpath, revision)) |
| 509 printed_path = True | 502 printed_path = True |
| 510 else: | 503 else: |
| 511 options.stdout.write('Attempting rebase onto %s...\n' % revision) | 504 print('Attempting rebase onto %s...' % revision) |
| 512 | 505 |
| 513 # Build the rebase command here using the args | 506 # Build the rebase command here using the args |
| 514 # git rebase [options] [--onto <newbase>] <upstream> [<branch>] | 507 # git rebase [options] [--onto <newbase>] <upstream> [<branch>] |
| 515 rebase_cmd = ['rebase'] | 508 rebase_cmd = ['rebase'] |
| 516 if options.verbose: | 509 if options.verbose: |
| 517 rebase_cmd.append('--verbose') | 510 rebase_cmd.append('--verbose') |
| 518 if newbase: | 511 if newbase: |
| 519 rebase_cmd.extend(['--onto', newbase]) | 512 rebase_cmd.extend(['--onto', newbase]) |
| 520 rebase_cmd.append(upstream) | 513 rebase_cmd.append(upstream) |
| 521 if branch: | 514 if branch: |
| (...skipping 14 matching lines...) Expand all Loading... |
| 536 if re.match(r'yes|y', rebase_action, re.I): | 529 if re.match(r'yes|y', rebase_action, re.I): |
| 537 self._Run(['reset', '--hard', 'HEAD'], options) | 530 self._Run(['reset', '--hard', 'HEAD'], options) |
| 538 # Should this be recursive? | 531 # Should this be recursive? |
| 539 rebase_output = scm.GIT.Capture(rebase_cmd, cwd=self.checkout_path) | 532 rebase_output = scm.GIT.Capture(rebase_cmd, cwd=self.checkout_path) |
| 540 break | 533 break |
| 541 elif re.match(r'quit|q', rebase_action, re.I): | 534 elif re.match(r'quit|q', rebase_action, re.I): |
| 542 raise gclient_utils.Error("Please merge or rebase manually\n" | 535 raise gclient_utils.Error("Please merge or rebase manually\n" |
| 543 "cd %s && git " % self.checkout_path | 536 "cd %s && git " % self.checkout_path |
| 544 + "%s" % ' '.join(rebase_cmd)) | 537 + "%s" % ' '.join(rebase_cmd)) |
| 545 elif re.match(r'show|s', rebase_action, re.I): | 538 elif re.match(r'show|s', rebase_action, re.I): |
| 546 options.stdout.write('\n%s\n' % e.stderr.strip()) | 539 print('\n%s' % e.stderr.strip()) |
| 547 continue | 540 continue |
| 548 else: | 541 else: |
| 549 gclient_utils.Error("Input not recognized") | 542 gclient_utils.Error("Input not recognized") |
| 550 continue | 543 continue |
| 551 elif re.search(r'^CONFLICT', e.stdout, re.M): | 544 elif re.search(r'^CONFLICT', e.stdout, re.M): |
| 552 raise gclient_utils.Error("Conflict while rebasing this branch.\n" | 545 raise gclient_utils.Error("Conflict while rebasing this branch.\n" |
| 553 "Fix the conflict and run gclient again.\n" | 546 "Fix the conflict and run gclient again.\n" |
| 554 "See 'man git-rebase' for details.\n") | 547 "See 'man git-rebase' for details.\n") |
| 555 else: | 548 else: |
| 556 options.stdout.write(e.stdout.strip() + '\n') | 549 print(e.stdout.strip()) |
| 557 options.stdout.write('Rebase produced error output:\n%s\n' % | 550 print('Rebase produced error output:\n%s' % e.stderr.strip()) |
| 558 e.stderr.strip()) | |
| 559 raise gclient_utils.Error("Unrecognized error, please merge or rebase " | 551 raise gclient_utils.Error("Unrecognized error, please merge or rebase " |
| 560 "manually.\ncd %s && git " % | 552 "manually.\ncd %s && git " % |
| 561 self.checkout_path | 553 self.checkout_path |
| 562 + "%s" % ' '.join(rebase_cmd)) | 554 + "%s" % ' '.join(rebase_cmd)) |
| 563 | 555 |
| 564 options.stdout.write(rebase_output) | 556 print(rebase_output.strip()) |
| 565 if not options.verbose: | 557 if not options.verbose: |
| 566 # Make the output a little prettier. It's nice to have some | 558 # Make the output a little prettier. It's nice to have some |
| 567 # whitespace between projects when syncing. | 559 # whitespace between projects when syncing. |
| 568 options.stdout.write('\n') | 560 print('') |
| 569 | 561 |
| 570 @staticmethod | 562 @staticmethod |
| 571 def _CheckMinVersion(min_version): | 563 def _CheckMinVersion(min_version): |
| 572 (ok, current_version) = scm.GIT.AssertVersion(min_version) | 564 (ok, current_version) = scm.GIT.AssertVersion(min_version) |
| 573 if not ok: | 565 if not ok: |
| 574 raise gclient_utils.Error('git version %s < minimum required %s' % | 566 raise gclient_utils.Error('git version %s < minimum required %s' % |
| 575 (current_version, min_version)) | 567 (current_version, min_version)) |
| 576 | 568 |
| 577 def _IsRebasing(self): | 569 def _IsRebasing(self): |
| 578 # Check for any of REBASE-i/REBASE-m/REBASE/AM. Unfortunately git doesn't | 570 # Check for any of REBASE-i/REBASE-m/REBASE/AM. Unfortunately git doesn't |
| (...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 617 raise gclient_utils.Error('\n____ %s%s\n' | 609 raise gclient_utils.Error('\n____ %s%s\n' |
| 618 '\tAlready in a conflict, i.e. (no branch).\n' | 610 '\tAlready in a conflict, i.e. (no branch).\n' |
| 619 '\tFix the conflict and run gclient again.\n' | 611 '\tFix the conflict and run gclient again.\n' |
| 620 '\tOr to abort run:\n\t\tgit-rebase --abort\n' | 612 '\tOr to abort run:\n\t\tgit-rebase --abort\n' |
| 621 '\tSee man git-rebase for details.\n' | 613 '\tSee man git-rebase for details.\n' |
| 622 % (self.relpath, rev_str)) | 614 % (self.relpath, rev_str)) |
| 623 # Let's just save off the commit so we can proceed. | 615 # Let's just save off the commit so we can proceed. |
| 624 name = ('saved-by-gclient-' + | 616 name = ('saved-by-gclient-' + |
| 625 self._Capture(['rev-parse', '--short', 'HEAD'])) | 617 self._Capture(['rev-parse', '--short', 'HEAD'])) |
| 626 self._Capture(['branch', name]) | 618 self._Capture(['branch', name]) |
| 627 options.stdout.write( | 619 print('\n_____ found an unreferenced commit and saved it as \'%s\'' % |
| 628 '\n_____ found an unreferenced commit and saved it as \'%s\'\n' % | |
| 629 name) | 620 name) |
| 630 | 621 |
| 631 def _GetCurrentBranch(self): | 622 def _GetCurrentBranch(self): |
| 632 # Returns name of current branch or None for detached HEAD | 623 # Returns name of current branch or None for detached HEAD |
| 633 branch = self._Capture(['rev-parse', '--abbrev-ref=strict', 'HEAD']) | 624 branch = self._Capture(['rev-parse', '--abbrev-ref=strict', 'HEAD']) |
| 634 if branch == 'HEAD': | 625 if branch == 'HEAD': |
| 635 return None | 626 return None |
| 636 return branch | 627 return branch |
| 637 | 628 |
| 638 def _Capture(self, args): | 629 def _Capture(self, args): |
| 639 return gclient_utils.CheckCall( | 630 return gclient_utils.CheckCall( |
| 640 ['git'] + args, cwd=self.checkout_path, print_error=False)[0].strip() | 631 ['git'] + args, cwd=self.checkout_path, print_error=False)[0].strip() |
| 641 | 632 |
| 642 def _Run(self, args, options, **kwargs): | 633 def _Run(self, args, options, **kwargs): |
| 643 kwargs.setdefault('cwd', self.checkout_path) | 634 kwargs.setdefault('cwd', self.checkout_path) |
| 644 gclient_utils.CheckCallAndFilterAndHeader(['git'] + args, | 635 gclient_utils.CheckCallAndFilterAndHeader(['git'] + args, |
| 645 always=options.verbose, stdout=options.stdout, **kwargs) | 636 always=options.verbose, **kwargs) |
| 646 | 637 |
| 647 | 638 |
| 648 class SVNWrapper(SCMWrapper): | 639 class SVNWrapper(SCMWrapper): |
| 649 """ Wrapper for SVN """ | 640 """ Wrapper for SVN """ |
| 650 | 641 |
| 651 def cleanup(self, options, args, file_list): | 642 def cleanup(self, options, args, file_list): |
| 652 """Cleanup working copy.""" | 643 """Cleanup working copy.""" |
| 653 self._Run(['cleanup'] + args, options) | 644 self._Run(['cleanup'] + args, options) |
| 654 | 645 |
| 655 def diff(self, options, args, file_list): | 646 def diff(self, options, args, file_list): |
| (...skipping 17 matching lines...) Expand all Loading... |
| 673 def pack(self, options, args, file_list): | 664 def pack(self, options, args, file_list): |
| 674 """Generates a patch file which can be applied to the root of the | 665 """Generates a patch file which can be applied to the root of the |
| 675 repository.""" | 666 repository.""" |
| 676 if not os.path.isdir(self.checkout_path): | 667 if not os.path.isdir(self.checkout_path): |
| 677 raise gclient_utils.Error('Directory %s is not present.' % | 668 raise gclient_utils.Error('Directory %s is not present.' % |
| 678 self.checkout_path) | 669 self.checkout_path) |
| 679 gclient_utils.CheckCallAndFilter( | 670 gclient_utils.CheckCallAndFilter( |
| 680 ['svn', 'diff', '-x', '--ignore-eol-style'] + args, | 671 ['svn', 'diff', '-x', '--ignore-eol-style'] + args, |
| 681 cwd=self.checkout_path, | 672 cwd=self.checkout_path, |
| 682 print_stdout=False, | 673 print_stdout=False, |
| 683 filter_fn=DiffFilterer(self.relpath, options.stdout).Filter, | 674 filter_fn=DiffFilterer(self.relpath).Filter) |
| 684 stdout=options.stdout) | |
| 685 | 675 |
| 686 def update(self, options, args, file_list): | 676 def update(self, options, args, file_list): |
| 687 """Runs svn to update or transparently checkout the working copy. | 677 """Runs svn to update or transparently checkout the working copy. |
| 688 | 678 |
| 689 All updated files will be appended to file_list. | 679 All updated files will be appended to file_list. |
| 690 | 680 |
| 691 Raises: | 681 Raises: |
| 692 Error: if can't get URL for relative path. | 682 Error: if can't get URL for relative path. |
| 693 """ | 683 """ |
| 694 # Only update if git is not controlling the directory. | 684 # Only update if git is not controlling the directory. |
| 695 git_path = os.path.join(self.checkout_path, '.git') | 685 git_path = os.path.join(self.checkout_path, '.git') |
| 696 if os.path.exists(git_path): | 686 if os.path.exists(git_path): |
| 697 options.stdout.write('________ found .git directory; skipping %s\n' % | 687 print('________ found .git directory; skipping %s' % self.relpath) |
| 698 self.relpath) | |
| 699 return | 688 return |
| 700 | 689 |
| 701 if args: | 690 if args: |
| 702 raise gclient_utils.Error("Unsupported argument(s): %s" % ",".join(args)) | 691 raise gclient_utils.Error("Unsupported argument(s): %s" % ",".join(args)) |
| 703 | 692 |
| 704 # revision is the revision to match. It is None if no revision is specified, | 693 # revision is the revision to match. It is None if no revision is specified, |
| 705 # i.e. the 'deps ain't pinned'. | 694 # i.e. the 'deps ain't pinned'. |
| 706 url, revision = gclient_utils.SplitUrlRevision(self.url) | 695 url, revision = gclient_utils.SplitUrlRevision(self.url) |
| 707 # Keep the original unpinned url for reference in case the repo is switched. | 696 # Keep the original unpinned url for reference in case the repo is switched. |
| 708 base_url = url | 697 base_url = url |
| (...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 750 # The repository url changed, need to switch. | 739 # The repository url changed, need to switch. |
| 751 try: | 740 try: |
| 752 to_info = scm.SVN.CaptureInfo(url) | 741 to_info = scm.SVN.CaptureInfo(url) |
| 753 except gclient_utils.Error: | 742 except gclient_utils.Error: |
| 754 # The url is invalid or the server is not accessible, it's safer to bail | 743 # The url is invalid or the server is not accessible, it's safer to bail |
| 755 # out right now. | 744 # out right now. |
| 756 raise gclient_utils.Error('This url is unreachable: %s' % url) | 745 raise gclient_utils.Error('This url is unreachable: %s' % url) |
| 757 can_switch = ((from_info['Repository Root'] != to_info['Repository Root']) | 746 can_switch = ((from_info['Repository Root'] != to_info['Repository Root']) |
| 758 and (from_info['UUID'] == to_info['UUID'])) | 747 and (from_info['UUID'] == to_info['UUID'])) |
| 759 if can_switch: | 748 if can_switch: |
| 760 options.stdout.write('\n_____ relocating %s to a new checkout\n' % | 749 print('\n_____ relocating %s to a new checkout' % self.relpath) |
| 761 self.relpath) | |
| 762 # We have different roots, so check if we can switch --relocate. | 750 # We have different roots, so check if we can switch --relocate. |
| 763 # Subversion only permits this if the repository UUIDs match. | 751 # Subversion only permits this if the repository UUIDs match. |
| 764 # Perform the switch --relocate, then rewrite the from_url | 752 # Perform the switch --relocate, then rewrite the from_url |
| 765 # to reflect where we "are now." (This is the same way that | 753 # to reflect where we "are now." (This is the same way that |
| 766 # Subversion itself handles the metadata when switch --relocate | 754 # Subversion itself handles the metadata when switch --relocate |
| 767 # is used.) This makes the checks below for whether we | 755 # is used.) This makes the checks below for whether we |
| 768 # can update to a revision or have to switch to a different | 756 # can update to a revision or have to switch to a different |
| 769 # branch work as expected. | 757 # branch work as expected. |
| 770 # TODO(maruel): TEST ME ! | 758 # TODO(maruel): TEST ME ! |
| 771 command = ['switch', '--relocate', | 759 command = ['switch', '--relocate', |
| 772 from_info['Repository Root'], | 760 from_info['Repository Root'], |
| 773 to_info['Repository Root'], | 761 to_info['Repository Root'], |
| 774 self.relpath] | 762 self.relpath] |
| 775 self._Run(command, options, cwd=self._root_dir) | 763 self._Run(command, options, cwd=self._root_dir) |
| 776 from_info['URL'] = from_info['URL'].replace( | 764 from_info['URL'] = from_info['URL'].replace( |
| 777 from_info['Repository Root'], | 765 from_info['Repository Root'], |
| 778 to_info['Repository Root']) | 766 to_info['Repository Root']) |
| 779 else: | 767 else: |
| 780 if not options.force and not options.reset: | 768 if not options.force and not options.reset: |
| 781 # Look for local modifications but ignore unversioned files. | 769 # Look for local modifications but ignore unversioned files. |
| 782 for status in scm.SVN.CaptureStatus(self.checkout_path): | 770 for status in scm.SVN.CaptureStatus(self.checkout_path): |
| 783 if status[0] != '?': | 771 if status[0] != '?': |
| 784 raise gclient_utils.Error( | 772 raise gclient_utils.Error( |
| 785 ('Can\'t switch the checkout to %s; UUID don\'t match and ' | 773 ('Can\'t switch the checkout to %s; UUID don\'t match and ' |
| 786 'there is local changes in %s. Delete the directory and ' | 774 'there is local changes in %s. Delete the directory and ' |
| 787 'try again.') % (url, self.checkout_path)) | 775 'try again.') % (url, self.checkout_path)) |
| 788 # Ok delete it. | 776 # Ok delete it. |
| 789 options.stdout.write('\n_____ switching %s to a new checkout\n' % | 777 print('\n_____ switching %s to a new checkout' % self.relpath) |
| 790 self.relpath) | |
| 791 gclient_utils.RemoveDirectory(self.checkout_path) | 778 gclient_utils.RemoveDirectory(self.checkout_path) |
| 792 # We need to checkout. | 779 # We need to checkout. |
| 793 command = ['checkout', url, self.checkout_path] | 780 command = ['checkout', url, self.checkout_path] |
| 794 command = self._AddAdditionalUpdateFlags(command, options, revision) | 781 command = self._AddAdditionalUpdateFlags(command, options, revision) |
| 795 self._RunAndGetFileList(command, options, file_list, self._root_dir) | 782 self._RunAndGetFileList(command, options, file_list, self._root_dir) |
| 796 return | 783 return |
| 797 | 784 |
| 798 # If the provided url has a revision number that matches the revision | 785 # If the provided url has a revision number that matches the revision |
| 799 # number of the existing directory, then we don't need to bother updating. | 786 # number of the existing directory, then we don't need to bother updating. |
| 800 if not options.force and str(from_info['Revision']) == revision: | 787 if not options.force and str(from_info['Revision']) == revision: |
| 801 if options.verbose or not forced_revision: | 788 if options.verbose or not forced_revision: |
| 802 options.stdout.write('\n_____ %s%s\n' % (self.relpath, rev_str)) | 789 print('\n_____ %s%s' % (self.relpath, rev_str)) |
| 803 return | 790 return |
| 804 | 791 |
| 805 command = ['update', self.checkout_path] | 792 command = ['update', self.checkout_path] |
| 806 command = self._AddAdditionalUpdateFlags(command, options, revision) | 793 command = self._AddAdditionalUpdateFlags(command, options, revision) |
| 807 self._RunAndGetFileList(command, options, file_list, self._root_dir) | 794 self._RunAndGetFileList(command, options, file_list, self._root_dir) |
| 808 | 795 |
| 809 def updatesingle(self, options, args, file_list): | 796 def updatesingle(self, options, args, file_list): |
| 810 filename = args.pop() | 797 filename = args.pop() |
| 811 if scm.SVN.AssertVersion("1.5")[0]: | 798 if scm.SVN.AssertVersion("1.5")[0]: |
| 812 if not os.path.exists(os.path.join(self.checkout_path, '.svn')): | 799 if not os.path.exists(os.path.join(self.checkout_path, '.svn')): |
| (...skipping 23 matching lines...) Expand all Loading... |
| 836 | 823 |
| 837 def revert(self, options, args, file_list): | 824 def revert(self, options, args, file_list): |
| 838 """Reverts local modifications. Subversion specific. | 825 """Reverts local modifications. Subversion specific. |
| 839 | 826 |
| 840 All reverted files will be appended to file_list, even if Subversion | 827 All reverted files will be appended to file_list, even if Subversion |
| 841 doesn't know about them. | 828 doesn't know about them. |
| 842 """ | 829 """ |
| 843 if not os.path.isdir(self.checkout_path): | 830 if not os.path.isdir(self.checkout_path): |
| 844 # svn revert won't work if the directory doesn't exist. It needs to | 831 # svn revert won't work if the directory doesn't exist. It needs to |
| 845 # checkout instead. | 832 # checkout instead. |
| 846 options.stdout.write('\n_____ %s is missing, synching instead\n' % | 833 print('\n_____ %s is missing, synching instead' % self.relpath) |
| 847 self.relpath) | |
| 848 # Don't reuse the args. | 834 # Don't reuse the args. |
| 849 return self.update(options, [], file_list) | 835 return self.update(options, [], file_list) |
| 850 | 836 |
| 851 # Do a flush of sys.stdout every 10 secs or so otherwise it may never be | |
| 852 # flushed fast enough for buildbot. | |
| 853 last_flushed_at = time.time() | |
| 854 sys.stdout.flush() | |
| 855 | |
| 856 for file_status in scm.SVN.CaptureStatus(self.checkout_path): | 837 for file_status in scm.SVN.CaptureStatus(self.checkout_path): |
| 857 file_path = os.path.join(self.checkout_path, file_status[1]) | 838 file_path = os.path.join(self.checkout_path, file_status[1]) |
| 858 if file_status[0][0] == 'X': | 839 if file_status[0][0] == 'X': |
| 859 # Ignore externals. | 840 # Ignore externals. |
| 860 logging.info('Ignoring external %s' % file_path) | 841 logging.info('Ignoring external %s' % file_path) |
| 861 continue | 842 continue |
| 862 | 843 |
| 863 if logging.getLogger().isEnabledFor(logging.INFO): | 844 if logging.getLogger().isEnabledFor(logging.INFO): |
| 864 logging.info('%s%s' % (file[0], file[1])) | 845 logging.info('%s%s' % (file[0], file[1])) |
| 865 else: | 846 else: |
| 866 options.stdout.write(file_path + '\n') | 847 print(file_path) |
| 867 # Flush at least 10 seconds between line writes. We wait at least 10 | |
| 868 # seconds to avoid overloading the reader that called us with output, | |
| 869 # which can slow busy readers down. | |
| 870 if (time.time() - last_flushed_at) > 10: | |
| 871 last_flushed_at = time.time() | |
| 872 sys.stdout.flush() | |
| 873 | 848 |
| 874 if file_status[0].isspace(): | 849 if file_status[0].isspace(): |
| 875 logging.error('No idea what is the status of %s.\n' | 850 logging.error('No idea what is the status of %s.\n' |
| 876 'You just found a bug in gclient, please ping ' | 851 'You just found a bug in gclient, please ping ' |
| 877 'maruel@chromium.org ASAP!' % file_path) | 852 'maruel@chromium.org ASAP!' % file_path) |
| 878 # svn revert is really stupid. It fails on inconsistent line-endings, | 853 # svn revert is really stupid. It fails on inconsistent line-endings, |
| 879 # on switched directories, etc. So take no chance and delete everything! | 854 # on switched directories, etc. So take no chance and delete everything! |
| 880 try: | 855 try: |
| 881 if not os.path.exists(file_path): | 856 if not os.path.exists(file_path): |
| 882 pass | 857 pass |
| (...skipping 27 matching lines...) Expand all Loading... |
| 910 return None | 885 return None |
| 911 | 886 |
| 912 def runhooks(self, options, args, file_list): | 887 def runhooks(self, options, args, file_list): |
| 913 self.status(options, args, file_list) | 888 self.status(options, args, file_list) |
| 914 | 889 |
| 915 def status(self, options, args, file_list): | 890 def status(self, options, args, file_list): |
| 916 """Display status information.""" | 891 """Display status information.""" |
| 917 command = ['status'] + args | 892 command = ['status'] + args |
| 918 if not os.path.isdir(self.checkout_path): | 893 if not os.path.isdir(self.checkout_path): |
| 919 # svn status won't work if the directory doesn't exist. | 894 # svn status won't work if the directory doesn't exist. |
| 920 options.stdout.write( | 895 print(('\n________ couldn\'t run \'%s\' in \'%s\':\n' |
| 921 ('\n________ couldn\'t run \'%s\' in \'%s\':\nThe directory ' | 896 'The directory does not exist.') % |
| 922 'does not exist.') % (' '.join(command), self.checkout_path)) | 897 (' '.join(command), self.checkout_path)) |
| 923 # There's no file list to retrieve. | 898 # There's no file list to retrieve. |
| 924 else: | 899 else: |
| 925 self._RunAndGetFileList(command, options, file_list) | 900 self._RunAndGetFileList(command, options, file_list) |
| 926 | 901 |
| 927 def FullUrlForRelativeUrl(self, url): | 902 def FullUrlForRelativeUrl(self, url): |
| 928 # Find the forth '/' and strip from there. A bit hackish. | 903 # Find the forth '/' and strip from there. A bit hackish. |
| 929 return '/'.join(self.url.split('/')[:4]) + url | 904 return '/'.join(self.url.split('/')[:4]) + url |
| 930 | 905 |
| 931 def _Run(self, args, options, **kwargs): | 906 def _Run(self, args, options, **kwargs): |
| 932 """Runs a commands that goes to stdout.""" | 907 """Runs a commands that goes to stdout.""" |
| 933 kwargs.setdefault('cwd', self.checkout_path) | 908 kwargs.setdefault('cwd', self.checkout_path) |
| 934 gclient_utils.CheckCallAndFilterAndHeader(['svn'] + args, | 909 gclient_utils.CheckCallAndFilterAndHeader(['svn'] + args, |
| 935 always=options.verbose, stdout=options.stdout, **kwargs) | 910 always=options.verbose, **kwargs) |
| 936 | 911 |
| 937 def _RunAndGetFileList(self, args, options, file_list, cwd=None): | 912 def _RunAndGetFileList(self, args, options, file_list, cwd=None): |
| 938 """Runs a commands that goes to stdout and grabs the file listed.""" | 913 """Runs a commands that goes to stdout and grabs the file listed.""" |
| 939 cwd = cwd or self.checkout_path | 914 cwd = cwd or self.checkout_path |
| 940 scm.SVN.RunAndGetFileList(options.verbose, args, cwd=cwd, | 915 scm.SVN.RunAndGetFileList(options.verbose, args, cwd=cwd, |
| 941 file_list=file_list, stdout=options.stdout) | 916 file_list=file_list) |
| 942 | 917 |
| 943 @staticmethod | 918 @staticmethod |
| 944 def _AddAdditionalUpdateFlags(command, options, revision): | 919 def _AddAdditionalUpdateFlags(command, options, revision): |
| 945 """Add additional flags to command depending on what options are set. | 920 """Add additional flags to command depending on what options are set. |
| 946 command should be a list of strings that represents an svn command. | 921 command should be a list of strings that represents an svn command. |
| 947 | 922 |
| 948 This method returns a new list to be used as a command.""" | 923 This method returns a new list to be used as a command.""" |
| 949 new_command = command[:] | 924 new_command = command[:] |
| 950 if revision: | 925 if revision: |
| 951 new_command.extend(['--revision', str(revision).strip()]) | 926 new_command.extend(['--revision', str(revision).strip()]) |
| 952 # --force was added to 'svn update' in svn 1.5. | 927 # --force was added to 'svn update' in svn 1.5. |
| 953 if ((options.force or options.manually_grab_svn_rev) and | 928 if ((options.force or options.manually_grab_svn_rev) and |
| 954 scm.SVN.AssertVersion("1.5")[0]): | 929 scm.SVN.AssertVersion("1.5")[0]): |
| 955 new_command.append('--force') | 930 new_command.append('--force') |
| 956 return new_command | 931 return new_command |
| OLD | NEW |