summaryrefslogtreecommitdiffstats
path: root/subcmds/sync.py
diff options
context:
space:
mode:
authorDan Willemsen <dwillemsen@google.com>2016-09-01 16:26:02 -0700
committerDan Willemsen <dwillemsen@google.com>2016-09-20 17:16:12 -0700
commit4350791e0d652721015cc94509233c833dad8812 (patch)
treed28ada6016b8c95898a05bc328267a038566129b /subcmds/sync.py
parent628456833a9be237c7373254fbe0771b2e802a88 (diff)
downloadgit-repo-4350791e0d652721015cc94509233c833dad8812.tar.gz
On project cleanup, don't remove nested projects
When there are nested projects in a manifest, like on AOSP right now: <project path="build" name="platform/build" /> <project path="build/blueprint" name="platform/build/blueprint" /> <project path="build/kati" name="platform/build/kati" /> <project path="build/soong" name="platform/build/soong" /> And the top "build" project is removed (or renamed to remove the nesting), repo just wipes away everything under build/ and re-creates the projects that are still there. But it only checks to see if the build/ project is dirty, so if there are dirty files in a nested project, they'll just be blown away, and a fresh worktree checked out. Instead, behave similarly to how `git clean -dxf` behaves and preserve any subdirectories that have git repositories in them. This isn't as strict as git -- it does not check to see if the '.git' entry is a readable gitdir, just whether an entry named '.git' exists. If it encounters any errors removing files, we'll print them all out to stderr and tell the user that we were unable to clean up the obsolete project, that they should clean it up manually, then sync again. Change-Id: I2f6a7dd205a8e0b7590ca5369e9b0ba21d5a6f77
Diffstat (limited to 'subcmds/sync.py')
-rw-r--r--subcmds/sync.py71
1 files changed, 57 insertions, 14 deletions
diff --git a/subcmds/sync.py b/subcmds/sync.py
index ecf2ffc0..cc0b17e9 100644
--- a/subcmds/sync.py
+++ b/subcmds/sync.py
@@ -457,6 +457,59 @@ later is required to fix a server side protocol bug.
457 else: 457 else:
458 self.manifest._Unload() 458 self.manifest._Unload()
459 459
460 def _DeleteProject(self, path):
461 print('Deleting obsolete path %s' % path, file=sys.stderr)
462
463 # Delete the .git directory first, so we're less likely to have a partially
464 # working git repository around. There shouldn't be any git projects here,
465 # so rmtree works.
466 try:
467 shutil.rmtree(os.path.join(path, '.git'))
468 except OSError:
469 print('Failed to remove %s' % os.path.join(path, '.git'), file=sys.stderr)
470 print('error: Failed to delete obsolete path %s' % path, file=sys.stderr)
471 print(' remove manually, then run sync again', file=sys.stderr)
472 return -1
473
474 # Delete everything under the worktree, except for directories that contain
475 # another git project
476 dirs_to_remove = []
477 failed = False
478 for root, dirs, files in os.walk(path):
479 for f in files:
480 try:
481 os.remove(os.path.join(root, f))
482 except OSError:
483 print('Failed to remove %s' % os.path.join(root, f), file=sys.stderr)
484 failed = True
485 dirs[:] = [d for d in dirs
486 if not os.path.lexists(os.path.join(root, d, '.git'))]
487 dirs_to_remove += [os.path.join(root, d) for d in dirs
488 if os.path.join(root, d) not in dirs_to_remove]
489 for d in reversed(dirs_to_remove):
490 if len(os.listdir(d)) == 0:
491 try:
492 os.rmdir(d)
493 except OSError:
494 print('Failed to remove %s' % os.path.join(root, d), file=sys.stderr)
495 failed = True
496 continue
497 if failed:
498 print('error: Failed to delete obsolete path %s' % path, file=sys.stderr)
499 print(' remove manually, then run sync again', file=sys.stderr)
500 return -1
501
502 # Try deleting parent dirs if they are empty
503 project_dir = path
504 while project_dir != self.manifest.topdir:
505 if len(os.listdir(project_dir)) == 0:
506 os.rmdir(project_dir)
507 else:
508 break
509 project_dir = os.path.dirname(project_dir)
510
511 return 0
512
460 def UpdateProjectList(self): 513 def UpdateProjectList(self):
461 new_project_paths = [] 514 new_project_paths = []
462 for project in self.GetProjects(None, missing_ok=True): 515 for project in self.GetProjects(None, missing_ok=True):
@@ -477,8 +530,8 @@ later is required to fix a server side protocol bug.
477 continue 530 continue
478 if path not in new_project_paths: 531 if path not in new_project_paths:
479 # If the path has already been deleted, we don't need to do it 532 # If the path has already been deleted, we don't need to do it
480 if os.path.exists(self.manifest.topdir + '/' + path): 533 gitdir = os.path.join(self.manifest.topdir, path, '.git')
481 gitdir = os.path.join(self.manifest.topdir, path, '.git') 534 if os.path.exists(gitdir):
482 project = Project( 535 project = Project(
483 manifest = self.manifest, 536 manifest = self.manifest,
484 name = path, 537 name = path,
@@ -497,18 +550,8 @@ later is required to fix a server side protocol bug.
497 print(' commit changes, then run sync again', 550 print(' commit changes, then run sync again',
498 file=sys.stderr) 551 file=sys.stderr)
499 return -1 552 return -1
500 else: 553 elif self._DeleteProject(project.worktree):
501 print('Deleting obsolete path %s' % project.worktree, 554 return -1
502 file=sys.stderr)
503 shutil.rmtree(project.worktree)
504 # Try deleting parent subdirs if they are empty
505 project_dir = os.path.dirname(project.worktree)
506 while project_dir != self.manifest.topdir:
507 try:
508 os.rmdir(project_dir)
509 except OSError:
510 break
511 project_dir = os.path.dirname(project_dir)
512 555
513 new_project_paths.sort() 556 new_project_paths.sort()
514 fd = open(file_path, 'w') 557 fd = open(file_path, 'w')