summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--manifest_xml.py14
-rw-r--r--project.py9
-rw-r--r--subcmds/abandon.py71
-rw-r--r--subcmds/status.py12
-rw-r--r--subcmds/sync.py22
5 files changed, 88 insertions, 40 deletions
diff --git a/manifest_xml.py b/manifest_xml.py
index 9c882af6..0859e1fb 100644
--- a/manifest_xml.py
+++ b/manifest_xml.py
@@ -40,8 +40,18 @@ LOCAL_MANIFEST_NAME = 'local_manifest.xml'
40LOCAL_MANIFESTS_DIR_NAME = 'local_manifests' 40LOCAL_MANIFESTS_DIR_NAME = 'local_manifests'
41 41
42# urljoin gets confused if the scheme is not known. 42# urljoin gets confused if the scheme is not known.
43urllib.parse.uses_relative.extend(['ssh', 'git', 'persistent-https', 'rpc']) 43urllib.parse.uses_relative.extend([
44urllib.parse.uses_netloc.extend(['ssh', 'git', 'persistent-https', 'rpc']) 44 'ssh',
45 'git',
46 'persistent-https',
47 'sso',
48 'rpc'])
49urllib.parse.uses_netloc.extend([
50 'ssh',
51 'git',
52 'persistent-https',
53 'sso',
54 'rpc'])
45 55
46class _Default(object): 56class _Default(object):
47 """Project defaults within the manifest.""" 57 """Project defaults within the manifest."""
diff --git a/project.py b/project.py
index cdb61c21..633ae073 100644
--- a/project.py
+++ b/project.py
@@ -911,11 +911,13 @@ class Project(object):
911 else: 911 else:
912 return False 912 return False
913 913
914 def PrintWorkTreeStatus(self, output_redir=None): 914 def PrintWorkTreeStatus(self, output_redir=None, quiet=False):
915 """Prints the status of the repository to stdout. 915 """Prints the status of the repository to stdout.
916 916
917 Args: 917 Args:
918 output: If specified, redirect the output to this object. 918 output: If specified, redirect the output to this object.
919 quiet: If True then only print the project name. Do not print
920 the modified files, branch name, etc.
919 """ 921 """
920 if not os.path.isdir(self.worktree): 922 if not os.path.isdir(self.worktree):
921 if output_redir is None: 923 if output_redir is None:
@@ -941,6 +943,10 @@ class Project(object):
941 out.redirect(output_redir) 943 out.redirect(output_redir)
942 out.project('project %-40s', self.relpath + '/ ') 944 out.project('project %-40s', self.relpath + '/ ')
943 945
946 if quiet:
947 out.nl()
948 return 'DIRTY'
949
944 branch = self.CurrentBranch 950 branch = self.CurrentBranch
945 if branch is None: 951 if branch is None:
946 out.nobranch('(*** NO BRANCH ***)') 952 out.nobranch('(*** NO BRANCH ***)')
@@ -2394,6 +2400,7 @@ class Project(object):
2394 src = os.path.realpath(os.path.join(srcdir, name)) 2400 src = os.path.realpath(os.path.join(srcdir, name))
2395 # Fail if the links are pointing to the wrong place 2401 # Fail if the links are pointing to the wrong place
2396 if src != dst: 2402 if src != dst:
2403 _error('%s is different in %s vs %s', name, destdir, srcdir)
2397 raise GitError('--force-sync not enabled; cannot overwrite a local ' 2404 raise GitError('--force-sync not enabled; cannot overwrite a local '
2398 'work tree. If you\'re comfortable with the ' 2405 'work tree. If you\'re comfortable with the '
2399 'possibility of losing the work tree\'s git metadata,' 2406 'possibility of losing the work tree\'s git metadata,'
diff --git a/subcmds/abandon.py b/subcmds/abandon.py
index b94ccdd3..6f78da74 100644
--- a/subcmds/abandon.py
+++ b/subcmds/abandon.py
@@ -16,6 +16,7 @@
16from __future__ import print_function 16from __future__ import print_function
17import sys 17import sys
18from command import Command 18from command import Command
19from collections import defaultdict
19from git_command import git 20from git_command import git
20from progress import Progress 21from progress import Progress
21 22
@@ -23,49 +24,75 @@ class Abandon(Command):
23 common = True 24 common = True
24 helpSummary = "Permanently abandon a development branch" 25 helpSummary = "Permanently abandon a development branch"
25 helpUsage = """ 26 helpUsage = """
26%prog <branchname> [<project>...] 27%prog [--all | <branchname>] [<project>...]
27 28
28This subcommand permanently abandons a development branch by 29This subcommand permanently abandons a development branch by
29deleting it (and all its history) from your local repository. 30deleting it (and all its history) from your local repository.
30 31
31It is equivalent to "git branch -D <branchname>". 32It is equivalent to "git branch -D <branchname>".
32""" 33"""
34 def _Options(self, p):
35 p.add_option('--all',
36 dest='all', action='store_true',
37 help='delete all branches in all projects')
33 38
34 def Execute(self, opt, args): 39 def Execute(self, opt, args):
35 if not args: 40 if not opt.all and not args:
36 self.Usage() 41 self.Usage()
37 42
38 nb = args[0] 43 if not opt.all:
39 if not git.check_ref_format('heads/%s' % nb): 44 nb = args[0]
40 print("error: '%s' is not a valid name" % nb, file=sys.stderr) 45 if not git.check_ref_format('heads/%s' % nb):
41 sys.exit(1) 46 print("error: '%s' is not a valid name" % nb, file=sys.stderr)
47 sys.exit(1)
48 else:
49 args.insert(0,None)
50 nb = "'All local branches'"
42 51
43 nb = args[0] 52 err = defaultdict(list)
44 err = [] 53 success = defaultdict(list)
45 success = []
46 all_projects = self.GetProjects(args[1:]) 54 all_projects = self.GetProjects(args[1:])
47 55
48 pm = Progress('Abandon %s' % nb, len(all_projects)) 56 pm = Progress('Abandon %s' % nb, len(all_projects))
49 for project in all_projects: 57 for project in all_projects:
50 pm.update() 58 pm.update()
51 59
52 status = project.AbandonBranch(nb) 60 if opt.all:
53 if status is not None: 61 branches = project.GetBranches().keys()
54 if status: 62 else:
55 success.append(project) 63 branches = [nb]
56 else: 64
57 err.append(project) 65 for name in branches:
66 status = project.AbandonBranch(name)
67 if status is not None:
68 if status:
69 success[name].append(project)
70 else:
71 err[name].append(project)
58 pm.end() 72 pm.end()
59 73
74 width = 25
75 for name in branches:
76 if width < len(name):
77 width = len(name)
78
60 if err: 79 if err:
61 for p in err: 80 for br in err.keys():
62 print("error: %s/: cannot abandon %s" % (p.relpath, nb), 81 err_msg = "error: cannot abandon %s" %br
63 file=sys.stderr) 82 print(err_msg, file=sys.stderr)
83 for proj in err[br]:
84 print(' '*len(err_msg) + " | %s" % p.relpath, file=sys.stderr)
64 sys.exit(1) 85 sys.exit(1)
65 elif not success: 86 elif not success:
66 print('error: no project has branch %s' % nb, file=sys.stderr) 87 print('error: no project has local branch(es) : %s' % nb,
88 file=sys.stderr)
67 sys.exit(1) 89 sys.exit(1)
68 else: 90 else:
69 print('Abandoned in %d project(s):\n %s' 91 print('Abandoned branches:', file=sys.stderr)
70 % (len(success), '\n '.join(p.relpath for p in success)), 92 for br in success.keys():
71 file=sys.stderr) 93 if len(all_projects) > 1 and len(all_projects) == len(success[br]):
94 result = "all project"
95 else:
96 result = "%s" % (
97 ('\n'+' '*width + '| ').join(p.relpath for p in success[br]))
98 print("%s%s| %s\n" % (br,' '*(width-len(br)), result),file=sys.stderr)
diff --git a/subcmds/status.py b/subcmds/status.py
index 38c229b1..60e26ff4 100644
--- a/subcmds/status.py
+++ b/subcmds/status.py
@@ -89,8 +89,10 @@ the following meanings:
89 p.add_option('-o', '--orphans', 89 p.add_option('-o', '--orphans',
90 dest='orphans', action='store_true', 90 dest='orphans', action='store_true',
91 help="include objects in working directory outside of repo projects") 91 help="include objects in working directory outside of repo projects")
92 p.add_option('-q', '--quiet', action='store_true',
93 help="only print the name of modified projects")
92 94
93 def _StatusHelper(self, project, clean_counter, sem): 95 def _StatusHelper(self, project, clean_counter, sem, quiet):
94 """Obtains the status for a specific project. 96 """Obtains the status for a specific project.
95 97
96 Obtains the status for a project, redirecting the output to 98 Obtains the status for a project, redirecting the output to
@@ -104,7 +106,7 @@ the following meanings:
104 output: Where to output the status. 106 output: Where to output the status.
105 """ 107 """
106 try: 108 try:
107 state = project.PrintWorkTreeStatus() 109 state = project.PrintWorkTreeStatus(quiet=quiet)
108 if state == 'CLEAN': 110 if state == 'CLEAN':
109 next(clean_counter) 111 next(clean_counter)
110 finally: 112 finally:
@@ -132,7 +134,7 @@ the following meanings:
132 134
133 if opt.jobs == 1: 135 if opt.jobs == 1:
134 for project in all_projects: 136 for project in all_projects:
135 state = project.PrintWorkTreeStatus() 137 state = project.PrintWorkTreeStatus(quiet=opt.quiet)
136 if state == 'CLEAN': 138 if state == 'CLEAN':
137 next(counter) 139 next(counter)
138 else: 140 else:
@@ -142,13 +144,13 @@ the following meanings:
142 sem.acquire() 144 sem.acquire()
143 145
144 t = _threading.Thread(target=self._StatusHelper, 146 t = _threading.Thread(target=self._StatusHelper,
145 args=(project, counter, sem)) 147 args=(project, counter, sem, opt.quiet))
146 threads.append(t) 148 threads.append(t)
147 t.daemon = True 149 t.daemon = True
148 t.start() 150 t.start()
149 for t in threads: 151 for t in threads:
150 t.join() 152 t.join()
151 if len(all_projects) == next(counter): 153 if not opt.quiet and len(all_projects) == next(counter):
152 print('nothing to commit (working directory clean)') 154 print('nothing to commit (working directory clean)')
153 155
154 if opt.orphans: 156 if opt.orphans:
diff --git a/subcmds/sync.py b/subcmds/sync.py
index 7ba9ebfc..bbb166c0 100644
--- a/subcmds/sync.py
+++ b/subcmds/sync.py
@@ -255,7 +255,7 @@ later is required to fix a server side protocol bug.
255 dest='repo_upgraded', action='store_true', 255 dest='repo_upgraded', action='store_true',
256 help=SUPPRESS_HELP) 256 help=SUPPRESS_HELP)
257 257
258 def _FetchProjectList(self, opt, projects, *args, **kwargs): 258 def _FetchProjectList(self, opt, projects, sem, *args, **kwargs):
259 """Main function of the fetch threads when jobs are > 1. 259 """Main function of the fetch threads when jobs are > 1.
260 260
261 Delegates most of the work to _FetchHelper. 261 Delegates most of the work to _FetchHelper.
@@ -263,15 +263,20 @@ later is required to fix a server side protocol bug.
263 Args: 263 Args:
264 opt: Program options returned from optparse. See _Options(). 264 opt: Program options returned from optparse. See _Options().
265 projects: Projects to fetch. 265 projects: Projects to fetch.
266 sem: We'll release() this semaphore when we exit so that another thread
267 can be started up.
266 *args, **kwargs: Remaining arguments to pass to _FetchHelper. See the 268 *args, **kwargs: Remaining arguments to pass to _FetchHelper. See the
267 _FetchHelper docstring for details. 269 _FetchHelper docstring for details.
268 """ 270 """
269 for project in projects: 271 try:
270 success = self._FetchHelper(opt, project, *args, **kwargs) 272 for project in projects:
271 if not success and not opt.force_broken: 273 success = self._FetchHelper(opt, project, *args, **kwargs)
272 break 274 if not success and not opt.force_broken:
275 break
276 finally:
277 sem.release()
273 278
274 def _FetchHelper(self, opt, project, lock, fetched, pm, sem, err_event): 279 def _FetchHelper(self, opt, project, lock, fetched, pm, err_event):
275 """Fetch git objects for a single project. 280 """Fetch git objects for a single project.
276 281
277 Args: 282 Args:
@@ -283,8 +288,6 @@ later is required to fix a server side protocol bug.
283 (with our lock held). 288 (with our lock held).
284 pm: Instance of a Project object. We will call pm.update() (with our 289 pm: Instance of a Project object. We will call pm.update() (with our
285 lock held). 290 lock held).
286 sem: We'll release() this semaphore when we exit so that another thread
287 can be started up.
288 err_event: We'll set this event in the case of an error (after printing 291 err_event: We'll set this event in the case of an error (after printing
289 out info about the error). 292 out info about the error).
290 293
@@ -340,7 +343,6 @@ later is required to fix a server side protocol bug.
340 finally: 343 finally:
341 if did_lock: 344 if did_lock:
342 lock.release() 345 lock.release()
343 sem.release()
344 346
345 return success 347 return success
346 348
@@ -365,10 +367,10 @@ later is required to fix a server side protocol bug.
365 sem.acquire() 367 sem.acquire()
366 kwargs = dict(opt=opt, 368 kwargs = dict(opt=opt,
367 projects=project_list, 369 projects=project_list,
370 sem=sem,
368 lock=lock, 371 lock=lock,
369 fetched=fetched, 372 fetched=fetched,
370 pm=pm, 373 pm=pm,
371 sem=sem,
372 err_event=err_event) 374 err_event=err_event)
373 if self.jobs > 1: 375 if self.jobs > 1:
374 t = _threading.Thread(target = self._FetchProjectList, 376 t = _threading.Thread(target = self._FetchProjectList,