diff options
Diffstat (limited to 'project.py')
-rw-r--r-- | project.py | 152 |
1 files changed, 89 insertions, 63 deletions
@@ -251,13 +251,29 @@ class DiffColoring(Coloring): | |||
251 | self.fail = self.printer('fail', fg='red') | 251 | self.fail = self.printer('fail', fg='red') |
252 | 252 | ||
253 | 253 | ||
254 | class _Annotation(object): | 254 | class Annotation(object): |
255 | 255 | ||
256 | def __init__(self, name, value, keep): | 256 | def __init__(self, name, value, keep): |
257 | self.name = name | 257 | self.name = name |
258 | self.value = value | 258 | self.value = value |
259 | self.keep = keep | 259 | self.keep = keep |
260 | 260 | ||
261 | def __eq__(self, other): | ||
262 | if not isinstance(other, Annotation): | ||
263 | return False | ||
264 | return self.__dict__ == other.__dict__ | ||
265 | |||
266 | def __lt__(self, other): | ||
267 | # This exists just so that lists of Annotation objects can be sorted, for | ||
268 | # use in comparisons. | ||
269 | if not isinstance(other, Annotation): | ||
270 | raise ValueError('comparison is not between two Annotation objects') | ||
271 | if self.name == other.name: | ||
272 | if self.value == other.value: | ||
273 | return self.keep < other.keep | ||
274 | return self.value < other.value | ||
275 | return self.name < other.name | ||
276 | |||
261 | 277 | ||
262 | def _SafeExpandPath(base, subpath, skipfinal=False): | 278 | def _SafeExpandPath(base, subpath, skipfinal=False): |
263 | """Make sure |subpath| is completely safe under |base|. | 279 | """Make sure |subpath| is completely safe under |base|. |
@@ -503,21 +519,8 @@ class Project(object): | |||
503 | self.client = self.manifest = manifest | 519 | self.client = self.manifest = manifest |
504 | self.name = name | 520 | self.name = name |
505 | self.remote = remote | 521 | self.remote = remote |
506 | self.gitdir = gitdir.replace('\\', '/') | 522 | self.UpdatePaths(relpath, worktree, gitdir, objdir) |
507 | self.objdir = objdir.replace('\\', '/') | 523 | self.SetRevision(revisionExpr, revisionId=revisionId) |
508 | if worktree: | ||
509 | self.worktree = os.path.normpath(worktree).replace('\\', '/') | ||
510 | else: | ||
511 | self.worktree = None | ||
512 | self.relpath = relpath | ||
513 | self.revisionExpr = revisionExpr | ||
514 | |||
515 | if revisionId is None \ | ||
516 | and revisionExpr \ | ||
517 | and IsId(revisionExpr): | ||
518 | self.revisionId = revisionExpr | ||
519 | else: | ||
520 | self.revisionId = revisionId | ||
521 | 524 | ||
522 | self.rebase = rebase | 525 | self.rebase = rebase |
523 | self.groups = groups | 526 | self.groups = groups |
@@ -540,16 +543,6 @@ class Project(object): | |||
540 | self.copyfiles = [] | 543 | self.copyfiles = [] |
541 | self.linkfiles = [] | 544 | self.linkfiles = [] |
542 | self.annotations = [] | 545 | self.annotations = [] |
543 | self.config = GitConfig.ForRepository(gitdir=self.gitdir, | ||
544 | defaults=self.client.globalConfig) | ||
545 | |||
546 | if self.worktree: | ||
547 | self.work_git = self._GitGetByExec(self, bare=False, gitdir=gitdir) | ||
548 | else: | ||
549 | self.work_git = None | ||
550 | self.bare_git = self._GitGetByExec(self, bare=True, gitdir=gitdir) | ||
551 | self.bare_ref = GitRefs(gitdir) | ||
552 | self.bare_objdir = self._GitGetByExec(self, bare=True, gitdir=objdir) | ||
553 | self.dest_branch = dest_branch | 546 | self.dest_branch = dest_branch |
554 | self.old_revision = old_revision | 547 | self.old_revision = old_revision |
555 | 548 | ||
@@ -557,6 +550,35 @@ class Project(object): | |||
557 | # project containing repo hooks. | 550 | # project containing repo hooks. |
558 | self.enabled_repo_hooks = [] | 551 | self.enabled_repo_hooks = [] |
559 | 552 | ||
553 | def SetRevision(self, revisionExpr, revisionId=None): | ||
554 | """Set revisionId based on revision expression and id""" | ||
555 | self.revisionExpr = revisionExpr | ||
556 | if revisionId is None and revisionExpr and IsId(revisionExpr): | ||
557 | self.revisionId = self.revisionExpr | ||
558 | else: | ||
559 | self.revisionId = revisionId | ||
560 | |||
561 | def UpdatePaths(self, relpath, worktree, gitdir, objdir): | ||
562 | """Update paths used by this project""" | ||
563 | self.gitdir = gitdir.replace('\\', '/') | ||
564 | self.objdir = objdir.replace('\\', '/') | ||
565 | if worktree: | ||
566 | self.worktree = os.path.normpath(worktree).replace('\\', '/') | ||
567 | else: | ||
568 | self.worktree = None | ||
569 | self.relpath = relpath | ||
570 | |||
571 | self.config = GitConfig.ForRepository(gitdir=self.gitdir, | ||
572 | defaults=self.manifest.globalConfig) | ||
573 | |||
574 | if self.worktree: | ||
575 | self.work_git = self._GitGetByExec(self, bare=False, gitdir=self.gitdir) | ||
576 | else: | ||
577 | self.work_git = None | ||
578 | self.bare_git = self._GitGetByExec(self, bare=True, gitdir=self.gitdir) | ||
579 | self.bare_ref = GitRefs(self.gitdir) | ||
580 | self.bare_objdir = self._GitGetByExec(self, bare=True, gitdir=self.objdir) | ||
581 | |||
560 | @property | 582 | @property |
561 | def Derived(self): | 583 | def Derived(self): |
562 | return self.is_derived | 584 | return self.is_derived |
@@ -1041,15 +1063,16 @@ class Project(object): | |||
1041 | verbose=False, | 1063 | verbose=False, |
1042 | output_redir=None, | 1064 | output_redir=None, |
1043 | is_new=None, | 1065 | is_new=None, |
1044 | current_branch_only=False, | 1066 | current_branch_only=None, |
1045 | force_sync=False, | 1067 | force_sync=False, |
1046 | clone_bundle=True, | 1068 | clone_bundle=True, |
1047 | tags=True, | 1069 | tags=None, |
1048 | archive=False, | 1070 | archive=False, |
1049 | optimized_fetch=False, | 1071 | optimized_fetch=False, |
1050 | retry_fetches=0, | 1072 | retry_fetches=0, |
1051 | prune=False, | 1073 | prune=False, |
1052 | submodules=False, | 1074 | submodules=False, |
1075 | ssh_proxy=None, | ||
1053 | clone_filter=None, | 1076 | clone_filter=None, |
1054 | partial_clone_exclude=set()): | 1077 | partial_clone_exclude=set()): |
1055 | """Perform only the network IO portion of the sync process. | 1078 | """Perform only the network IO portion of the sync process. |
@@ -1116,7 +1139,7 @@ class Project(object): | |||
1116 | and self._ApplyCloneBundle(initial=is_new, quiet=quiet, verbose=verbose)): | 1139 | and self._ApplyCloneBundle(initial=is_new, quiet=quiet, verbose=verbose)): |
1117 | is_new = False | 1140 | is_new = False |
1118 | 1141 | ||
1119 | if not current_branch_only: | 1142 | if current_branch_only is None: |
1120 | if self.sync_c: | 1143 | if self.sync_c: |
1121 | current_branch_only = True | 1144 | current_branch_only = True |
1122 | elif not self.manifest._loaded: | 1145 | elif not self.manifest._loaded: |
@@ -1125,8 +1148,8 @@ class Project(object): | |||
1125 | elif self.manifest.default.sync_c: | 1148 | elif self.manifest.default.sync_c: |
1126 | current_branch_only = True | 1149 | current_branch_only = True |
1127 | 1150 | ||
1128 | if not self.sync_tags: | 1151 | if tags is None: |
1129 | tags = False | 1152 | tags = self.sync_tags |
1130 | 1153 | ||
1131 | if self.clone_depth: | 1154 | if self.clone_depth: |
1132 | depth = self.clone_depth | 1155 | depth = self.clone_depth |
@@ -1143,6 +1166,7 @@ class Project(object): | |||
1143 | alt_dir=alt_dir, current_branch_only=current_branch_only, | 1166 | alt_dir=alt_dir, current_branch_only=current_branch_only, |
1144 | tags=tags, prune=prune, depth=depth, | 1167 | tags=tags, prune=prune, depth=depth, |
1145 | submodules=submodules, force_sync=force_sync, | 1168 | submodules=submodules, force_sync=force_sync, |
1169 | ssh_proxy=ssh_proxy, | ||
1146 | clone_filter=clone_filter, retry_fetches=retry_fetches): | 1170 | clone_filter=clone_filter, retry_fetches=retry_fetches): |
1147 | return False | 1171 | return False |
1148 | 1172 | ||
@@ -1164,10 +1188,8 @@ class Project(object): | |||
1164 | self._InitMRef() | 1188 | self._InitMRef() |
1165 | else: | 1189 | else: |
1166 | self._InitMirrorHead() | 1190 | self._InitMirrorHead() |
1167 | try: | 1191 | platform_utils.remove(os.path.join(self.gitdir, 'FETCH_HEAD'), |
1168 | platform_utils.remove(os.path.join(self.gitdir, 'FETCH_HEAD')) | 1192 | missing_ok=True) |
1169 | except OSError: | ||
1170 | pass | ||
1171 | return True | 1193 | return True |
1172 | 1194 | ||
1173 | def PostRepoUpgrade(self): | 1195 | def PostRepoUpgrade(self): |
@@ -1214,6 +1236,9 @@ class Project(object): | |||
1214 | (self.revisionExpr, self.name)) | 1236 | (self.revisionExpr, self.name)) |
1215 | 1237 | ||
1216 | def SetRevisionId(self, revisionId): | 1238 | def SetRevisionId(self, revisionId): |
1239 | if self.revisionExpr: | ||
1240 | self.upstream = self.revisionExpr | ||
1241 | |||
1217 | self.revisionId = revisionId | 1242 | self.revisionId = revisionId |
1218 | 1243 | ||
1219 | def Sync_LocalHalf(self, syncbuf, force_sync=False, submodules=False): | 1244 | def Sync_LocalHalf(self, syncbuf, force_sync=False, submodules=False): |
@@ -1443,7 +1468,7 @@ class Project(object): | |||
1443 | self.linkfiles.append(_LinkFile(self.worktree, src, topdir, dest)) | 1468 | self.linkfiles.append(_LinkFile(self.worktree, src, topdir, dest)) |
1444 | 1469 | ||
1445 | def AddAnnotation(self, name, value, keep): | 1470 | def AddAnnotation(self, name, value, keep): |
1446 | self.annotations.append(_Annotation(name, value, keep)) | 1471 | self.annotations.append(Annotation(name, value, keep)) |
1447 | 1472 | ||
1448 | def DownloadPatchSet(self, change_id, patch_id): | 1473 | def DownloadPatchSet(self, change_id, patch_id): |
1449 | """Download a single patch set of a single change to FETCH_HEAD. | 1474 | """Download a single patch set of a single change to FETCH_HEAD. |
@@ -1962,6 +1987,11 @@ class Project(object): | |||
1962 | # throws an error. | 1987 | # throws an error. |
1963 | self.bare_git.rev_list('-1', '--missing=allow-any', | 1988 | self.bare_git.rev_list('-1', '--missing=allow-any', |
1964 | '%s^0' % self.revisionExpr, '--') | 1989 | '%s^0' % self.revisionExpr, '--') |
1990 | if self.upstream: | ||
1991 | rev = self.GetRemote(self.remote.name).ToLocal(self.upstream) | ||
1992 | self.bare_git.rev_list('-1', '--missing=allow-any', | ||
1993 | '%s^0' % rev, '--') | ||
1994 | self.bare_git.merge_base('--is-ancestor', self.revisionExpr, rev) | ||
1965 | return True | 1995 | return True |
1966 | except GitError: | 1996 | except GitError: |
1967 | # There is no such persistent revision. We have to fetch it. | 1997 | # There is no such persistent revision. We have to fetch it. |
@@ -1991,6 +2021,7 @@ class Project(object): | |||
1991 | prune=False, | 2021 | prune=False, |
1992 | depth=None, | 2022 | depth=None, |
1993 | submodules=False, | 2023 | submodules=False, |
2024 | ssh_proxy=None, | ||
1994 | force_sync=False, | 2025 | force_sync=False, |
1995 | clone_filter=None, | 2026 | clone_filter=None, |
1996 | retry_fetches=2, | 2027 | retry_fetches=2, |
@@ -2038,16 +2069,14 @@ class Project(object): | |||
2038 | if not name: | 2069 | if not name: |
2039 | name = self.remote.name | 2070 | name = self.remote.name |
2040 | 2071 | ||
2041 | ssh_proxy = False | ||
2042 | remote = self.GetRemote(name) | 2072 | remote = self.GetRemote(name) |
2043 | if remote.PreConnectFetch(): | 2073 | if not remote.PreConnectFetch(ssh_proxy): |
2044 | ssh_proxy = True | 2074 | ssh_proxy = None |
2045 | 2075 | ||
2046 | if initial: | 2076 | if initial: |
2047 | if alt_dir and 'objects' == os.path.basename(alt_dir): | 2077 | if alt_dir and 'objects' == os.path.basename(alt_dir): |
2048 | ref_dir = os.path.dirname(alt_dir) | 2078 | ref_dir = os.path.dirname(alt_dir) |
2049 | packed_refs = os.path.join(self.gitdir, 'packed-refs') | 2079 | packed_refs = os.path.join(self.gitdir, 'packed-refs') |
2050 | remote = self.GetRemote(name) | ||
2051 | 2080 | ||
2052 | all_refs = self.bare_ref.all | 2081 | all_refs = self.bare_ref.all |
2053 | ids = set(all_refs.values()) | 2082 | ids = set(all_refs.values()) |
@@ -2134,6 +2163,8 @@ class Project(object): | |||
2134 | # Shallow checkout of a specific commit, fetch from that commit and not | 2163 | # Shallow checkout of a specific commit, fetch from that commit and not |
2135 | # the heads only as the commit might be deeper in the history. | 2164 | # the heads only as the commit might be deeper in the history. |
2136 | spec.append(branch) | 2165 | spec.append(branch) |
2166 | if self.upstream: | ||
2167 | spec.append(self.upstream) | ||
2137 | else: | 2168 | else: |
2138 | if is_sha1: | 2169 | if is_sha1: |
2139 | branch = self.upstream | 2170 | branch = self.upstream |
@@ -2191,7 +2222,7 @@ class Project(object): | |||
2191 | ret = prunecmd.Wait() | 2222 | ret = prunecmd.Wait() |
2192 | if ret: | 2223 | if ret: |
2193 | break | 2224 | break |
2194 | output_redir.write('retrying fetch after pruning remote branches') | 2225 | print('retrying fetch after pruning remote branches', file=output_redir) |
2195 | # Continue right away so we don't sleep as we shouldn't need to. | 2226 | # Continue right away so we don't sleep as we shouldn't need to. |
2196 | continue | 2227 | continue |
2197 | elif current_branch_only and is_sha1 and ret == 128: | 2228 | elif current_branch_only and is_sha1 and ret == 128: |
@@ -2204,10 +2235,11 @@ class Project(object): | |||
2204 | break | 2235 | break |
2205 | 2236 | ||
2206 | # Figure out how long to sleep before the next attempt, if there is one. | 2237 | # Figure out how long to sleep before the next attempt, if there is one. |
2207 | if not verbose: | 2238 | if not verbose and gitcmd.stdout: |
2208 | output_redir.write('\n%s:\n%s' % (self.name, gitcmd.stdout)) | 2239 | print('\n%s:\n%s' % (self.name, gitcmd.stdout), end='', file=output_redir) |
2209 | if try_n < retry_fetches - 1: | 2240 | if try_n < retry_fetches - 1: |
2210 | output_redir.write('sleeping %s seconds before retrying' % retry_cur_sleep) | 2241 | print('%s: sleeping %s seconds before retrying' % (self.name, retry_cur_sleep), |
2242 | file=output_redir) | ||
2211 | time.sleep(retry_cur_sleep) | 2243 | time.sleep(retry_cur_sleep) |
2212 | retry_cur_sleep = min(retry_exp_factor * retry_cur_sleep, | 2244 | retry_cur_sleep = min(retry_exp_factor * retry_cur_sleep, |
2213 | MAXIMUM_RETRY_SLEEP_SEC) | 2245 | MAXIMUM_RETRY_SLEEP_SEC) |
@@ -2233,7 +2265,7 @@ class Project(object): | |||
2233 | name=name, quiet=quiet, verbose=verbose, output_redir=output_redir, | 2265 | name=name, quiet=quiet, verbose=verbose, output_redir=output_redir, |
2234 | current_branch_only=current_branch_only and depth, | 2266 | current_branch_only=current_branch_only and depth, |
2235 | initial=False, alt_dir=alt_dir, | 2267 | initial=False, alt_dir=alt_dir, |
2236 | depth=None, clone_filter=clone_filter) | 2268 | depth=None, ssh_proxy=ssh_proxy, clone_filter=clone_filter) |
2237 | 2269 | ||
2238 | return ok | 2270 | return ok |
2239 | 2271 | ||
@@ -2279,15 +2311,12 @@ class Project(object): | |||
2279 | cmd.append('+refs/tags/*:refs/tags/*') | 2311 | cmd.append('+refs/tags/*:refs/tags/*') |
2280 | 2312 | ||
2281 | ok = GitCommand(self, cmd, bare=True).Wait() == 0 | 2313 | ok = GitCommand(self, cmd, bare=True).Wait() == 0 |
2282 | if os.path.exists(bundle_dst): | 2314 | platform_utils.remove(bundle_dst, missing_ok=True) |
2283 | platform_utils.remove(bundle_dst) | 2315 | platform_utils.remove(bundle_tmp, missing_ok=True) |
2284 | if os.path.exists(bundle_tmp): | ||
2285 | platform_utils.remove(bundle_tmp) | ||
2286 | return ok | 2316 | return ok |
2287 | 2317 | ||
2288 | def _FetchBundle(self, srcUrl, tmpPath, dstPath, quiet, verbose): | 2318 | def _FetchBundle(self, srcUrl, tmpPath, dstPath, quiet, verbose): |
2289 | if os.path.exists(dstPath): | 2319 | platform_utils.remove(dstPath, missing_ok=True) |
2290 | platform_utils.remove(dstPath) | ||
2291 | 2320 | ||
2292 | cmd = ['curl', '--fail', '--output', tmpPath, '--netrc', '--location'] | 2321 | cmd = ['curl', '--fail', '--output', tmpPath, '--netrc', '--location'] |
2293 | if quiet: | 2322 | if quiet: |
@@ -2438,14 +2467,6 @@ class Project(object): | |||
2438 | self.bare_objdir.init() | 2467 | self.bare_objdir.init() |
2439 | 2468 | ||
2440 | if self.use_git_worktrees: | 2469 | if self.use_git_worktrees: |
2441 | # Set up the m/ space to point to the worktree-specific ref space. | ||
2442 | # We'll update the worktree-specific ref space on each checkout. | ||
2443 | if self.manifest.branch: | ||
2444 | self.bare_git.symbolic_ref( | ||
2445 | '-m', 'redirecting to worktree scope', | ||
2446 | R_M + self.manifest.branch, | ||
2447 | R_WORKTREE_M + self.manifest.branch) | ||
2448 | |||
2449 | # Enable per-worktree config file support if possible. This is more a | 2470 | # Enable per-worktree config file support if possible. This is more a |
2450 | # nice-to-have feature for users rather than a hard requirement. | 2471 | # nice-to-have feature for users rather than a hard requirement. |
2451 | if git_require((2, 20, 0)): | 2472 | if git_require((2, 20, 0)): |
@@ -2582,6 +2603,14 @@ class Project(object): | |||
2582 | def _InitMRef(self): | 2603 | def _InitMRef(self): |
2583 | if self.manifest.branch: | 2604 | if self.manifest.branch: |
2584 | if self.use_git_worktrees: | 2605 | if self.use_git_worktrees: |
2606 | # Set up the m/ space to point to the worktree-specific ref space. | ||
2607 | # We'll update the worktree-specific ref space on each checkout. | ||
2608 | ref = R_M + self.manifest.branch | ||
2609 | if not self.bare_ref.symref(ref): | ||
2610 | self.bare_git.symbolic_ref( | ||
2611 | '-m', 'redirecting to worktree scope', | ||
2612 | ref, R_WORKTREE_M + self.manifest.branch) | ||
2613 | |||
2585 | # We can't update this ref with git worktrees until it exists. | 2614 | # We can't update this ref with git worktrees until it exists. |
2586 | # We'll wait until the initial checkout to set it. | 2615 | # We'll wait until the initial checkout to set it. |
2587 | if not os.path.exists(self.worktree): | 2616 | if not os.path.exists(self.worktree): |
@@ -2711,10 +2740,7 @@ class Project(object): | |||
2711 | # If the source file doesn't exist, ensure the destination | 2740 | # If the source file doesn't exist, ensure the destination |
2712 | # file doesn't either. | 2741 | # file doesn't either. |
2713 | if name in symlink_files and not os.path.lexists(src): | 2742 | if name in symlink_files and not os.path.lexists(src): |
2714 | try: | 2743 | platform_utils.remove(dst, missing_ok=True) |
2715 | platform_utils.remove(dst) | ||
2716 | except OSError: | ||
2717 | pass | ||
2718 | 2744 | ||
2719 | except OSError as e: | 2745 | except OSError as e: |
2720 | if e.errno == errno.EPERM: | 2746 | if e.errno == errno.EPERM: |