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: |
