summaryrefslogtreecommitdiffstats
path: root/project.py
diff options
context:
space:
mode:
Diffstat (limited to 'project.py')
-rw-r--r--project.py152
1 files changed, 89 insertions, 63 deletions
diff --git a/project.py b/project.py
index 992a0c07..5b26b64c 100644
--- a/project.py
+++ b/project.py
@@ -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
254class _Annotation(object): 254class 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
262def _SafeExpandPath(base, subpath, skipfinal=False): 278def _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: