diff options
-rw-r--r-- | git_config.py | 12 | ||||
-rw-r--r-- | git_refs.py | 11 | ||||
-rw-r--r-- | progress.py | 12 | ||||
-rw-r--r-- | project.py | 25 | ||||
-rw-r--r-- | subcmds/download.py | 5 | ||||
-rw-r--r-- | subcmds/init.py | 2 | ||||
-rw-r--r-- | subcmds/stage.py | 4 | ||||
-rw-r--r-- | subcmds/start.py | 10 | ||||
-rw-r--r-- | subcmds/sync.py | 10 | ||||
-rw-r--r-- | subcmds/upload.py | 20 |
10 files changed, 76 insertions, 35 deletions
diff --git a/git_config.py b/git_config.py index e00f6be2..8c247394 100644 --- a/git_config.py +++ b/git_config.py | |||
@@ -50,16 +50,24 @@ else: | |||
50 | from git_command import GitCommand | 50 | from git_command import GitCommand |
51 | from git_command import ssh_sock | 51 | from git_command import ssh_sock |
52 | from git_command import terminate_ssh_clients | 52 | from git_command import terminate_ssh_clients |
53 | from git_refs import R_CHANGES, R_HEADS, R_TAGS | ||
53 | 54 | ||
54 | R_HEADS = 'refs/heads/' | ||
55 | R_TAGS = 'refs/tags/' | ||
56 | ID_RE = re.compile(r'^[0-9a-f]{40}$') | 55 | ID_RE = re.compile(r'^[0-9a-f]{40}$') |
57 | 56 | ||
58 | REVIEW_CACHE = dict() | 57 | REVIEW_CACHE = dict() |
59 | 58 | ||
59 | def IsChange(rev): | ||
60 | return rev.startswith(R_CHANGES) | ||
61 | |||
60 | def IsId(rev): | 62 | def IsId(rev): |
61 | return ID_RE.match(rev) | 63 | return ID_RE.match(rev) |
62 | 64 | ||
65 | def IsTag(rev): | ||
66 | return rev.startswith(R_TAGS) | ||
67 | |||
68 | def IsImmutable(rev): | ||
69 | return IsChange(rev) or IsId(rev) or IsTag(rev) | ||
70 | |||
63 | def _key(name): | 71 | def _key(name): |
64 | parts = name.split('.') | 72 | parts = name.split('.') |
65 | if len(parts) < 2: | 73 | if len(parts) < 2: |
diff --git a/git_refs.py b/git_refs.py index 3c266061..58c838a6 100644 --- a/git_refs.py +++ b/git_refs.py | |||
@@ -16,11 +16,12 @@ | |||
16 | import os | 16 | import os |
17 | from trace import Trace | 17 | from trace import Trace |
18 | 18 | ||
19 | HEAD = 'HEAD' | 19 | HEAD = 'HEAD' |
20 | R_HEADS = 'refs/heads/' | 20 | R_CHANGES = 'refs/changes/' |
21 | R_TAGS = 'refs/tags/' | 21 | R_HEADS = 'refs/heads/' |
22 | R_PUB = 'refs/published/' | 22 | R_TAGS = 'refs/tags/' |
23 | R_M = 'refs/remotes/m/' | 23 | R_PUB = 'refs/published/' |
24 | R_M = 'refs/remotes/m/' | ||
24 | 25 | ||
25 | 26 | ||
26 | class GitRefs(object): | 27 | class GitRefs(object): |
diff --git a/progress.py b/progress.py index d948654f..0dd5d1a8 100644 --- a/progress.py +++ b/progress.py | |||
@@ -21,7 +21,8 @@ from trace import IsTrace | |||
21 | _NOT_TTY = not os.isatty(2) | 21 | _NOT_TTY = not os.isatty(2) |
22 | 22 | ||
23 | class Progress(object): | 23 | class Progress(object): |
24 | def __init__(self, title, total=0, units=''): | 24 | def __init__(self, title, total=0, units='', print_newline=False, |
25 | always_print_percentage=False): | ||
25 | self._title = title | 26 | self._title = title |
26 | self._total = total | 27 | self._total = total |
27 | self._done = 0 | 28 | self._done = 0 |
@@ -29,6 +30,8 @@ class Progress(object): | |||
29 | self._start = time() | 30 | self._start = time() |
30 | self._show = False | 31 | self._show = False |
31 | self._units = units | 32 | self._units = units |
33 | self._print_newline = print_newline | ||
34 | self._always_print_percentage = always_print_percentage | ||
32 | 35 | ||
33 | def update(self, inc=1): | 36 | def update(self, inc=1): |
34 | self._done += inc | 37 | self._done += inc |
@@ -50,13 +53,14 @@ class Progress(object): | |||
50 | else: | 53 | else: |
51 | p = (100 * self._done) / self._total | 54 | p = (100 * self._done) / self._total |
52 | 55 | ||
53 | if self._lastp != p: | 56 | if self._lastp != p or self._always_print_percentage: |
54 | self._lastp = p | 57 | self._lastp = p |
55 | sys.stderr.write('\r%s: %3d%% (%d%s/%d%s) ' % ( | 58 | sys.stderr.write('\r%s: %3d%% (%d%s/%d%s)%s' % ( |
56 | self._title, | 59 | self._title, |
57 | p, | 60 | p, |
58 | self._done, self._units, | 61 | self._done, self._units, |
59 | self._total, self._units)) | 62 | self._total, self._units, |
63 | "\n" if self._print_newline else "")) | ||
60 | sys.stderr.flush() | 64 | sys.stderr.flush() |
61 | 65 | ||
62 | def end(self): | 66 | def end(self): |
@@ -177,11 +177,15 @@ class ReviewableBranch(object): | |||
177 | def UploadForReview(self, people, | 177 | def UploadForReview(self, people, |
178 | auto_topic=False, | 178 | auto_topic=False, |
179 | draft=False, | 179 | draft=False, |
180 | private=False, | ||
181 | wip=False, | ||
180 | dest_branch=None): | 182 | dest_branch=None): |
181 | self.project.UploadForReview(self.name, | 183 | self.project.UploadForReview(self.name, |
182 | people, | 184 | people, |
183 | auto_topic=auto_topic, | 185 | auto_topic=auto_topic, |
184 | draft=draft, | 186 | draft=draft, |
187 | private=private, | ||
188 | wip=wip, | ||
185 | dest_branch=dest_branch) | 189 | dest_branch=dest_branch) |
186 | 190 | ||
187 | def GetPublishedRefs(self): | 191 | def GetPublishedRefs(self): |
@@ -1108,6 +1112,8 @@ class Project(object): | |||
1108 | people=([], []), | 1112 | people=([], []), |
1109 | auto_topic=False, | 1113 | auto_topic=False, |
1110 | draft=False, | 1114 | draft=False, |
1115 | private=False, | ||
1116 | wip=False, | ||
1111 | dest_branch=None): | 1117 | dest_branch=None): |
1112 | """Uploads the named branch for code review. | 1118 | """Uploads the named branch for code review. |
1113 | """ | 1119 | """ |
@@ -1159,9 +1165,14 @@ class Project(object): | |||
1159 | dest_branch) | 1165 | dest_branch) |
1160 | if auto_topic: | 1166 | if auto_topic: |
1161 | ref_spec = ref_spec + '/' + branch.name | 1167 | ref_spec = ref_spec + '/' + branch.name |
1168 | |||
1162 | if not url.startswith('ssh://'): | 1169 | if not url.startswith('ssh://'): |
1163 | rp = ['r=%s' % p for p in people[0]] + \ | 1170 | rp = ['r=%s' % p for p in people[0]] + \ |
1164 | ['cc=%s' % p for p in people[1]] | 1171 | ['cc=%s' % p for p in people[1]] |
1172 | if private: | ||
1173 | rp = rp + ['private'] | ||
1174 | if wip: | ||
1175 | rp = rp + ['wip'] | ||
1165 | if rp: | 1176 | if rp: |
1166 | ref_spec = ref_spec + '%' + ','.join(rp) | 1177 | ref_spec = ref_spec + '%' + ','.join(rp) |
1167 | cmd.append(ref_spec) | 1178 | cmd.append(ref_spec) |
@@ -1275,7 +1286,7 @@ class Project(object): | |||
1275 | 1286 | ||
1276 | need_to_fetch = not (optimized_fetch and | 1287 | need_to_fetch = not (optimized_fetch and |
1277 | (ID_RE.match(self.revisionExpr) and | 1288 | (ID_RE.match(self.revisionExpr) and |
1278 | self._CheckForSha1())) | 1289 | self._CheckForImmutableRevision())) |
1279 | if (need_to_fetch and | 1290 | if (need_to_fetch and |
1280 | not self._RemoteFetch(initial=is_new, quiet=quiet, alt_dir=alt_dir, | 1291 | not self._RemoteFetch(initial=is_new, quiet=quiet, alt_dir=alt_dir, |
1281 | current_branch_only=current_branch_only, | 1292 | current_branch_only=current_branch_only, |
@@ -1885,7 +1896,7 @@ class Project(object): | |||
1885 | 1896 | ||
1886 | 1897 | ||
1887 | # Direct Git Commands ## | 1898 | # Direct Git Commands ## |
1888 | def _CheckForSha1(self): | 1899 | def _CheckForImmutableRevision(self): |
1889 | try: | 1900 | try: |
1890 | # if revision (sha or tag) is not present then following function | 1901 | # if revision (sha or tag) is not present then following function |
1891 | # throws an error. | 1902 | # throws an error. |
@@ -1939,7 +1950,9 @@ class Project(object): | |||
1939 | tag_name = self.revisionExpr[len(R_TAGS):] | 1950 | tag_name = self.revisionExpr[len(R_TAGS):] |
1940 | 1951 | ||
1941 | if is_sha1 or tag_name is not None: | 1952 | if is_sha1 or tag_name is not None: |
1942 | if self._CheckForSha1(): | 1953 | if self._CheckForImmutableRevision(): |
1954 | print('Skipped fetching project %s (already have persistent ref)' | ||
1955 | % self.name) | ||
1943 | return True | 1956 | return True |
1944 | if is_sha1 and not depth: | 1957 | if is_sha1 and not depth: |
1945 | # When syncing a specific commit and --depth is not set: | 1958 | # When syncing a specific commit and --depth is not set: |
@@ -2095,7 +2108,7 @@ class Project(object): | |||
2095 | # We just synced the upstream given branch; verify we | 2108 | # We just synced the upstream given branch; verify we |
2096 | # got what we wanted, else trigger a second run of all | 2109 | # got what we wanted, else trigger a second run of all |
2097 | # refs. | 2110 | # refs. |
2098 | if not self._CheckForSha1(): | 2111 | if not self._CheckForImmutableRevision(): |
2099 | if current_branch_only and depth: | 2112 | if current_branch_only and depth: |
2100 | # Sync the current branch only with depth set to None | 2113 | # Sync the current branch only with depth set to None |
2101 | return self._RemoteFetch(name=name, | 2114 | return self._RemoteFetch(name=name, |
@@ -2961,14 +2974,14 @@ class MetaProject(Project): | |||
2961 | self.revisionExpr = base | 2974 | self.revisionExpr = base |
2962 | self.revisionId = None | 2975 | self.revisionId = None |
2963 | 2976 | ||
2964 | def MetaBranchSwitch(self): | 2977 | def MetaBranchSwitch(self, submodules=False): |
2965 | """ Prepare MetaProject for manifest branch switch | 2978 | """ Prepare MetaProject for manifest branch switch |
2966 | """ | 2979 | """ |
2967 | 2980 | ||
2968 | # detach and delete manifest branch, allowing a new | 2981 | # detach and delete manifest branch, allowing a new |
2969 | # branch to take over | 2982 | # branch to take over |
2970 | syncbuf = SyncBuffer(self.config, detach_head=True) | 2983 | syncbuf = SyncBuffer(self.config, detach_head=True) |
2971 | self.Sync_LocalHalf(syncbuf) | 2984 | self.Sync_LocalHalf(syncbuf, submodules=submodules) |
2972 | syncbuf.Finish() | 2985 | syncbuf.Finish() |
2973 | 2986 | ||
2974 | return GitCommand(self, | 2987 | return GitCommand(self, |
diff --git a/subcmds/download.py b/subcmds/download.py index a029462e..e1010aa2 100644 --- a/subcmds/download.py +++ b/subcmds/download.py | |||
@@ -26,11 +26,12 @@ class Download(Command): | |||
26 | common = True | 26 | common = True |
27 | helpSummary = "Download and checkout a change" | 27 | helpSummary = "Download and checkout a change" |
28 | helpUsage = """ | 28 | helpUsage = """ |
29 | %prog {project change[/patchset]}... | 29 | %prog {[project] change[/patchset]}... |
30 | """ | 30 | """ |
31 | helpDescription = """ | 31 | helpDescription = """ |
32 | The '%prog' command downloads a change from the review system and | 32 | The '%prog' command downloads a change from the review system and |
33 | makes it available in your project's local working directory. | 33 | makes it available in your project's local working directory. |
34 | If no project is specified try to use current directory as a project. | ||
34 | """ | 35 | """ |
35 | 36 | ||
36 | def _Options(self, p): | 37 | def _Options(self, p): |
@@ -55,7 +56,7 @@ makes it available in your project's local working directory. | |||
55 | m = CHANGE_RE.match(a) | 56 | m = CHANGE_RE.match(a) |
56 | if m: | 57 | if m: |
57 | if not project: | 58 | if not project: |
58 | self.Usage() | 59 | project = self.GetProjects(".")[0] |
59 | chg_id = int(m.group(1)) | 60 | chg_id = int(m.group(1)) |
60 | if m.group(2): | 61 | if m.group(2): |
61 | ps_id = int(m.group(2)) | 62 | ps_id = int(m.group(2)) |
diff --git a/subcmds/init.py b/subcmds/init.py index e6470916..eeddca06 100644 --- a/subcmds/init.py +++ b/subcmds/init.py | |||
@@ -256,7 +256,7 @@ to update the working directory files. | |||
256 | sys.exit(1) | 256 | sys.exit(1) |
257 | 257 | ||
258 | if opt.manifest_branch: | 258 | if opt.manifest_branch: |
259 | m.MetaBranchSwitch() | 259 | m.MetaBranchSwitch(submodules=opt.submodules) |
260 | 260 | ||
261 | syncbuf = SyncBuffer(m.config) | 261 | syncbuf = SyncBuffer(m.config) |
262 | m.Sync_LocalHalf(syncbuf, submodules=opt.submodules) | 262 | m.Sync_LocalHalf(syncbuf, submodules=opt.submodules) |
diff --git a/subcmds/stage.py b/subcmds/stage.py index 28849764..9d354268 100644 --- a/subcmds/stage.py +++ b/subcmds/stage.py | |||
@@ -60,8 +60,8 @@ The '%prog' command stages files to prepare the next commit. | |||
60 | out.nl() | 60 | out.nl() |
61 | 61 | ||
62 | for i in range(len(all_projects)): | 62 | for i in range(len(all_projects)): |
63 | p = all_projects[i] | 63 | project = all_projects[i] |
64 | out.write('%3d: %s', i + 1, p.relpath + '/') | 64 | out.write('%3d: %s', i + 1, project.relpath + '/') |
65 | out.nl() | 65 | out.nl() |
66 | out.nl() | 66 | out.nl() |
67 | 67 | ||
diff --git a/subcmds/start.py b/subcmds/start.py index 290b6897..c3ec303e 100644 --- a/subcmds/start.py +++ b/subcmds/start.py | |||
@@ -18,7 +18,7 @@ import os | |||
18 | import sys | 18 | import sys |
19 | 19 | ||
20 | from command import Command | 20 | from command import Command |
21 | from git_config import IsId | 21 | from git_config import IsImmutable |
22 | from git_command import git | 22 | from git_command import git |
23 | import gitc_utils | 23 | import gitc_utils |
24 | from progress import Progress | 24 | from progress import Progress |
@@ -96,11 +96,11 @@ revision specified in the manifest. | |||
96 | project.Sync_LocalHalf(sync_buf) | 96 | project.Sync_LocalHalf(sync_buf) |
97 | project.revisionId = gitc_project.old_revision | 97 | project.revisionId = gitc_project.old_revision |
98 | 98 | ||
99 | # If the current revision is a specific SHA1 then we can't push back | 99 | # If the current revision is immutable, such as a SHA1, a tag or |
100 | # to it; so substitute with dest_branch if defined, or with manifest | 100 | # a change, then we can't push back to it. Substitute with |
101 | # default revision instead. | 101 | # dest_branch, if defined; or with manifest default revision instead. |
102 | branch_merge = '' | 102 | branch_merge = '' |
103 | if IsId(project.revisionExpr): | 103 | if IsImmutable(project.revisionExpr): |
104 | if project.dest_branch: | 104 | if project.dest_branch: |
105 | branch_merge = project.dest_branch | 105 | branch_merge = project.dest_branch |
106 | else: | 106 | else: |
diff --git a/subcmds/sync.py b/subcmds/sync.py index 797fc403..b88c596d 100644 --- a/subcmds/sync.py +++ b/subcmds/sync.py | |||
@@ -356,7 +356,9 @@ later is required to fix a server side protocol bug. | |||
356 | def _Fetch(self, projects, opt): | 356 | def _Fetch(self, projects, opt): |
357 | fetched = set() | 357 | fetched = set() |
358 | lock = _threading.Lock() | 358 | lock = _threading.Lock() |
359 | pm = Progress('Fetching projects', len(projects)) | 359 | pm = Progress('Fetching projects', len(projects), |
360 | print_newline=not(opt.quiet), | ||
361 | always_print_percentage=opt.quiet) | ||
360 | 362 | ||
361 | objdir_project_map = dict() | 363 | objdir_project_map = dict() |
362 | for project in projects: | 364 | for project in projects: |
@@ -393,7 +395,7 @@ later is required to fix a server side protocol bug. | |||
393 | t.join() | 395 | t.join() |
394 | 396 | ||
395 | # If we saw an error, exit with code 1 so that other scripts can check. | 397 | # If we saw an error, exit with code 1 so that other scripts can check. |
396 | if err_event.isSet(): | 398 | if err_event.isSet() and not opt.force_broken: |
397 | print('\nerror: Exited sync due to fetch errors', file=sys.stderr) | 399 | print('\nerror: Exited sync due to fetch errors', file=sys.stderr) |
398 | sys.exit(1) | 400 | sys.exit(1) |
399 | 401 | ||
@@ -779,8 +781,8 @@ later is required to fix a server side protocol bug. | |||
779 | # generate a new args list to represent the opened projects. | 781 | # generate a new args list to represent the opened projects. |
780 | # TODO: make this more reliable -- if there's a project name/path overlap, | 782 | # TODO: make this more reliable -- if there's a project name/path overlap, |
781 | # this may choose the wrong project. | 783 | # this may choose the wrong project. |
782 | args = [os.path.relpath(self.manifest.paths[p].worktree, os.getcwd()) | 784 | args = [os.path.relpath(self.manifest.paths[path].worktree, os.getcwd()) |
783 | for p in opened_projects] | 785 | for path in opened_projects] |
784 | if not args: | 786 | if not args: |
785 | return | 787 | return |
786 | all_projects = self.GetProjects(args, | 788 | all_projects = self.GetProjects(args, |
diff --git a/subcmds/upload.py b/subcmds/upload.py index 1172dadc..61b18bc2 100644 --- a/subcmds/upload.py +++ b/subcmds/upload.py | |||
@@ -154,6 +154,12 @@ Gerrit Code Review: http://code.google.com/p/gerrit/ | |||
154 | p.add_option('-d', '--draft', | 154 | p.add_option('-d', '--draft', |
155 | action='store_true', dest='draft', default=False, | 155 | action='store_true', dest='draft', default=False, |
156 | help='If specified, upload as a draft.') | 156 | help='If specified, upload as a draft.') |
157 | p.add_option('-p', '--private', | ||
158 | action='store_true', dest='private', default=False, | ||
159 | help='If specified, upload as a private change.') | ||
160 | p.add_option('-w', '--wip', | ||
161 | action='store_true', dest='wip', default=False, | ||
162 | help='If specified, upload as a work-in-progress change.') | ||
157 | p.add_option('-D', '--destination', '--dest', | 163 | p.add_option('-D', '--destination', '--dest', |
158 | type='string', action='store', dest='dest_branch', | 164 | type='string', action='store', dest='dest_branch', |
159 | metavar='BRANCH', | 165 | metavar='BRANCH', |
@@ -198,7 +204,8 @@ Gerrit Code Review: http://code.google.com/p/gerrit/ | |||
198 | commit_list = branch.commits | 204 | commit_list = branch.commits |
199 | 205 | ||
200 | destination = opt.dest_branch or project.dest_branch or project.revisionExpr | 206 | destination = opt.dest_branch or project.dest_branch or project.revisionExpr |
201 | print('Upload project %s/ to remote branch %s:' % (project.relpath, destination)) | 207 | print('Upload project %s/ to remote branch %s%s:' % |
208 | (project.relpath, destination, ' (draft)' if opt.draft else '')) | ||
202 | print(' branch %s (%2d commit%s, %s):' % ( | 209 | print(' branch %s (%2d commit%s, %s):' % ( |
203 | name, | 210 | name, |
204 | len(commit_list), | 211 | len(commit_list), |
@@ -377,7 +384,12 @@ Gerrit Code Review: http://code.google.com/p/gerrit/ | |||
377 | branch.uploaded = False | 384 | branch.uploaded = False |
378 | continue | 385 | continue |
379 | 386 | ||
380 | branch.UploadForReview(people, auto_topic=opt.auto_topic, draft=opt.draft, dest_branch=destination) | 387 | branch.UploadForReview(people, |
388 | auto_topic=opt.auto_topic, | ||
389 | draft=opt.draft, | ||
390 | private=opt.private, | ||
391 | wip=opt.wip, | ||
392 | dest_branch=destination) | ||
381 | branch.uploaded = True | 393 | branch.uploaded = True |
382 | except UploadError as e: | 394 | except UploadError as e: |
383 | branch.error = e | 395 | branch.error = e |
@@ -463,8 +475,8 @@ Gerrit Code Review: http://code.google.com/p/gerrit/ | |||
463 | self.manifest.topdir, | 475 | self.manifest.topdir, |
464 | self.manifest.manifestProject.GetRemote('origin').url, | 476 | self.manifest.manifestProject.GetRemote('origin').url, |
465 | abort_if_user_denies=True) | 477 | abort_if_user_denies=True) |
466 | pending_proj_names = [project.name for (project, avail) in pending] | 478 | pending_proj_names = [project.name for (project, available) in pending] |
467 | pending_worktrees = [project.worktree for (project, avail) in pending] | 479 | pending_worktrees = [project.worktree for (project, available) in pending] |
468 | try: | 480 | try: |
469 | hook.Run(opt.allow_all_hooks, project_list=pending_proj_names, | 481 | hook.Run(opt.allow_all_hooks, project_list=pending_proj_names, |
470 | worktree_list=pending_worktrees) | 482 | worktree_list=pending_worktrees) |