diff options
Diffstat (limited to 'project.py')
-rw-r--r-- | project.py | 85 |
1 files changed, 32 insertions, 53 deletions
@@ -13,7 +13,6 @@ | |||
13 | # limitations under the License. | 13 | # limitations under the License. |
14 | 14 | ||
15 | from __future__ import print_function | 15 | from __future__ import print_function |
16 | import contextlib | ||
17 | import errno | 16 | import errno |
18 | import filecmp | 17 | import filecmp |
19 | import glob | 18 | import glob |
@@ -31,7 +30,7 @@ import traceback | |||
31 | 30 | ||
32 | from color import Coloring | 31 | from color import Coloring |
33 | from git_command import GitCommand, git_require | 32 | from git_command import GitCommand, git_require |
34 | from git_config import GitConfig, IsId, GetSchemeFromUrl, ID_RE | 33 | from git_config import GitConfig, IsId, GetSchemeFromUrl, GetUrlCookieFile, ID_RE |
35 | from error import GitError, HookError, UploadError, DownloadError | 34 | from error import GitError, HookError, UploadError, DownloadError |
36 | from error import ManifestInvalidRevisionError | 35 | from error import ManifestInvalidRevisionError |
37 | from error import NoManifestException | 36 | from error import NoManifestException |
@@ -64,6 +63,10 @@ def _error(fmt, *args): | |||
64 | msg = fmt % args | 63 | msg = fmt % args |
65 | print('error: %s' % msg, file=sys.stderr) | 64 | print('error: %s' % msg, file=sys.stderr) |
66 | 65 | ||
66 | def _warn(fmt, *args): | ||
67 | msg = fmt % args | ||
68 | print('warn: %s' % msg, file=sys.stderr) | ||
69 | |||
67 | def not_rev(r): | 70 | def not_rev(r): |
68 | return '^' + r | 71 | return '^' + r |
69 | 72 | ||
@@ -569,7 +572,8 @@ class Project(object): | |||
569 | parent=None, | 572 | parent=None, |
570 | is_derived=False, | 573 | is_derived=False, |
571 | dest_branch=None, | 574 | dest_branch=None, |
572 | optimized_fetch=False): | 575 | optimized_fetch=False, |
576 | old_revision=None): | ||
573 | """Init a Project object. | 577 | """Init a Project object. |
574 | 578 | ||
575 | Args: | 579 | Args: |
@@ -593,6 +597,7 @@ class Project(object): | |||
593 | dest_branch: The branch to which to push changes for review by default. | 597 | dest_branch: The branch to which to push changes for review by default. |
594 | optimized_fetch: If True, when a project is set to a sha1 revision, only | 598 | optimized_fetch: If True, when a project is set to a sha1 revision, only |
595 | fetch from the remote if the sha1 is not present locally. | 599 | fetch from the remote if the sha1 is not present locally. |
600 | old_revision: saved git commit id for open GITC projects. | ||
596 | """ | 601 | """ |
597 | self.manifest = manifest | 602 | self.manifest = manifest |
598 | self.name = name | 603 | self.name = name |
@@ -640,6 +645,7 @@ class Project(object): | |||
640 | self.bare_ref = GitRefs(gitdir) | 645 | self.bare_ref = GitRefs(gitdir) |
641 | self.bare_objdir = self._GitGetByExec(self, bare=True, gitdir=objdir) | 646 | self.bare_objdir = self._GitGetByExec(self, bare=True, gitdir=objdir) |
642 | self.dest_branch = dest_branch | 647 | self.dest_branch = dest_branch |
648 | self.old_revision = old_revision | ||
643 | 649 | ||
644 | # This will be filled in if a project is later identified to be the | 650 | # This will be filled in if a project is later identified to be the |
645 | # project containing repo hooks. | 651 | # project containing repo hooks. |
@@ -1093,8 +1099,7 @@ class Project(object): | |||
1093 | tar.extractall(path=path) | 1099 | tar.extractall(path=path) |
1094 | return True | 1100 | return True |
1095 | except (IOError, tarfile.TarError) as e: | 1101 | except (IOError, tarfile.TarError) as e: |
1096 | print("error: Cannot extract archive %s: " | 1102 | _error("Cannot extract archive %s: %s", tarpath, str(e)) |
1097 | "%s" % (tarpath, str(e)), file=sys.stderr) | ||
1098 | return False | 1103 | return False |
1099 | 1104 | ||
1100 | def Sync_NetworkHalf(self, | 1105 | def Sync_NetworkHalf(self, |
@@ -1111,8 +1116,7 @@ class Project(object): | |||
1111 | """ | 1116 | """ |
1112 | if archive and not isinstance(self, MetaProject): | 1117 | if archive and not isinstance(self, MetaProject): |
1113 | if self.remote.url.startswith(('http://', 'https://')): | 1118 | if self.remote.url.startswith(('http://', 'https://')): |
1114 | print("error: %s: Cannot fetch archives from http/https " | 1119 | _error("%s: Cannot fetch archives from http/https remotes.", self.name) |
1115 | "remotes." % self.name, file=sys.stderr) | ||
1116 | return False | 1120 | return False |
1117 | 1121 | ||
1118 | name = self.relpath.replace('\\', '/') | 1122 | name = self.relpath.replace('\\', '/') |
@@ -1123,7 +1127,7 @@ class Project(object): | |||
1123 | try: | 1127 | try: |
1124 | self._FetchArchive(tarpath, cwd=topdir) | 1128 | self._FetchArchive(tarpath, cwd=topdir) |
1125 | except GitError as e: | 1129 | except GitError as e: |
1126 | print('error: %s' % str(e), file=sys.stderr) | 1130 | _error('%s', e) |
1127 | return False | 1131 | return False |
1128 | 1132 | ||
1129 | # From now on, we only need absolute tarpath | 1133 | # From now on, we only need absolute tarpath |
@@ -1134,8 +1138,7 @@ class Project(object): | |||
1134 | try: | 1138 | try: |
1135 | os.remove(tarpath) | 1139 | os.remove(tarpath) |
1136 | except OSError as e: | 1140 | except OSError as e: |
1137 | print("warn: Cannot remove archive %s: " | 1141 | _warn("Cannot remove archive %s: %s", tarpath, str(e)) |
1138 | "%s" % (tarpath, str(e)), file=sys.stderr) | ||
1139 | self._CopyAndLinkFiles() | 1142 | self._CopyAndLinkFiles() |
1140 | return True | 1143 | return True |
1141 | if is_new is None: | 1144 | if is_new is None: |
@@ -1195,6 +1198,8 @@ class Project(object): | |||
1195 | self._InitHooks() | 1198 | self._InitHooks() |
1196 | 1199 | ||
1197 | def _CopyAndLinkFiles(self): | 1200 | def _CopyAndLinkFiles(self): |
1201 | if self.manifest.isGitcClient: | ||
1202 | return | ||
1198 | for copyfile in self.copyfiles: | 1203 | for copyfile in self.copyfiles: |
1199 | copyfile._Copy() | 1204 | copyfile._Copy() |
1200 | for linkfile in self.linkfiles: | 1205 | for linkfile in self.linkfiles: |
@@ -1270,6 +1275,8 @@ class Project(object): | |||
1270 | # Except if the head needs to be detached | 1275 | # Except if the head needs to be detached |
1271 | # | 1276 | # |
1272 | if not syncbuf.detach_head: | 1277 | if not syncbuf.detach_head: |
1278 | # The copy/linkfile config may have changed. | ||
1279 | self._CopyAndLinkFiles() | ||
1273 | return | 1280 | return |
1274 | else: | 1281 | else: |
1275 | lost = self._revlist(not_rev(revid), HEAD) | 1282 | lost = self._revlist(not_rev(revid), HEAD) |
@@ -1287,6 +1294,8 @@ class Project(object): | |||
1287 | if head == revid: | 1294 | if head == revid: |
1288 | # No changes; don't do anything further. | 1295 | # No changes; don't do anything further. |
1289 | # | 1296 | # |
1297 | # The copy/linkfile config may have changed. | ||
1298 | self._CopyAndLinkFiles() | ||
1290 | return | 1299 | return |
1291 | 1300 | ||
1292 | branch = self.GetBranch(branch) | 1301 | branch = self.GetBranch(branch) |
@@ -1425,9 +1434,11 @@ class Project(object): | |||
1425 | 1434 | ||
1426 | ## Branch Management ## | 1435 | ## Branch Management ## |
1427 | 1436 | ||
1428 | def StartBranch(self, name): | 1437 | def StartBranch(self, name, branch_merge=''): |
1429 | """Create a new branch off the manifest's revision. | 1438 | """Create a new branch off the manifest's revision. |
1430 | """ | 1439 | """ |
1440 | if not branch_merge: | ||
1441 | branch_merge = self.revisionExpr | ||
1431 | head = self.work_git.GetHead() | 1442 | head = self.work_git.GetHead() |
1432 | if head == (R_HEADS + name): | 1443 | if head == (R_HEADS + name): |
1433 | return True | 1444 | return True |
@@ -1441,9 +1452,9 @@ class Project(object): | |||
1441 | 1452 | ||
1442 | branch = self.GetBranch(name) | 1453 | branch = self.GetBranch(name) |
1443 | branch.remote = self.GetRemote(self.remote.name) | 1454 | branch.remote = self.GetRemote(self.remote.name) |
1444 | branch.merge = self.revisionExpr | 1455 | branch.merge = branch_merge |
1445 | if not branch.merge.startswith('refs/') and not ID_RE.match(self.revisionExpr): | 1456 | if not branch.merge.startswith('refs/') and not ID_RE.match(branch_merge): |
1446 | branch.merge = R_HEADS + self.revisionExpr | 1457 | branch.merge = R_HEADS + branch_merge |
1447 | revid = self.GetRevisionId(all_refs) | 1458 | revid = self.GetRevisionId(all_refs) |
1448 | 1459 | ||
1449 | if head.startswith(R_HEADS): | 1460 | if head.startswith(R_HEADS): |
@@ -1451,7 +1462,6 @@ class Project(object): | |||
1451 | head = all_refs[head] | 1462 | head = all_refs[head] |
1452 | except KeyError: | 1463 | except KeyError: |
1453 | head = None | 1464 | head = None |
1454 | |||
1455 | if revid and head and revid == head: | 1465 | if revid and head and revid == head: |
1456 | ref = os.path.join(self.gitdir, R_HEADS + name) | 1466 | ref = os.path.join(self.gitdir, R_HEADS + name) |
1457 | try: | 1467 | try: |
@@ -2030,7 +2040,7 @@ class Project(object): | |||
2030 | os.remove(tmpPath) | 2040 | os.remove(tmpPath) |
2031 | if 'http_proxy' in os.environ and 'darwin' == sys.platform: | 2041 | if 'http_proxy' in os.environ and 'darwin' == sys.platform: |
2032 | cmd += ['--proxy', os.environ['http_proxy']] | 2042 | cmd += ['--proxy', os.environ['http_proxy']] |
2033 | with self._GetBundleCookieFile(srcUrl, quiet) as cookiefile: | 2043 | with GetUrlCookieFile(srcUrl, quiet) as (cookiefile, proxy): |
2034 | if cookiefile: | 2044 | if cookiefile: |
2035 | cmd += ['--cookie', cookiefile, '--cookie-jar', cookiefile] | 2045 | cmd += ['--cookie', cookiefile, '--cookie-jar', cookiefile] |
2036 | if srcUrl.startswith('persistent-'): | 2046 | if srcUrl.startswith('persistent-'): |
@@ -2078,40 +2088,6 @@ class Project(object): | |||
2078 | except OSError: | 2088 | except OSError: |
2079 | return False | 2089 | return False |
2080 | 2090 | ||
2081 | @contextlib.contextmanager | ||
2082 | def _GetBundleCookieFile(self, url, quiet): | ||
2083 | if url.startswith('persistent-'): | ||
2084 | try: | ||
2085 | p = subprocess.Popen( | ||
2086 | ['git-remote-persistent-https', '-print_config', url], | ||
2087 | stdin=subprocess.PIPE, stdout=subprocess.PIPE, | ||
2088 | stderr=subprocess.PIPE) | ||
2089 | try: | ||
2090 | prefix = 'http.cookiefile=' | ||
2091 | cookiefile = None | ||
2092 | for line in p.stdout: | ||
2093 | line = line.strip() | ||
2094 | if line.startswith(prefix): | ||
2095 | cookiefile = line[len(prefix):] | ||
2096 | break | ||
2097 | # Leave subprocess open, as cookie file may be transient. | ||
2098 | if cookiefile: | ||
2099 | yield cookiefile | ||
2100 | return | ||
2101 | finally: | ||
2102 | p.stdin.close() | ||
2103 | if p.wait(): | ||
2104 | err_msg = p.stderr.read() | ||
2105 | if ' -print_config' in err_msg: | ||
2106 | pass # Persistent proxy doesn't support -print_config. | ||
2107 | elif not quiet: | ||
2108 | print(err_msg, file=sys.stderr) | ||
2109 | except OSError as e: | ||
2110 | if e.errno == errno.ENOENT: | ||
2111 | pass # No persistent proxy. | ||
2112 | raise | ||
2113 | yield GitConfig.ForUser().GetString('http.cookiefile') | ||
2114 | |||
2115 | def _Checkout(self, rev, quiet=False): | 2091 | def _Checkout(self, rev, quiet=False): |
2116 | cmd = ['checkout'] | 2092 | cmd = ['checkout'] |
2117 | if quiet: | 2093 | if quiet: |
@@ -2182,8 +2158,8 @@ class Project(object): | |||
2182 | try: | 2158 | try: |
2183 | self._CheckDirReference(self.objdir, self.gitdir, share_refs=False) | 2159 | self._CheckDirReference(self.objdir, self.gitdir, share_refs=False) |
2184 | except GitError as e: | 2160 | except GitError as e: |
2185 | print("Retrying clone after deleting %s" % force_sync, file=sys.stderr) | ||
2186 | if force_sync: | 2161 | if force_sync: |
2162 | print("Retrying clone after deleting %s" % self.gitdir, file=sys.stderr) | ||
2187 | try: | 2163 | try: |
2188 | shutil.rmtree(os.path.realpath(self.gitdir)) | 2164 | shutil.rmtree(os.path.realpath(self.gitdir)) |
2189 | if self.worktree and os.path.exists( | 2165 | if self.worktree and os.path.exists( |
@@ -2261,7 +2237,7 @@ class Project(object): | |||
2261 | if filecmp.cmp(stock_hook, dst, shallow=False): | 2237 | if filecmp.cmp(stock_hook, dst, shallow=False): |
2262 | os.remove(dst) | 2238 | os.remove(dst) |
2263 | else: | 2239 | else: |
2264 | _error("%s: Not replacing %s hook", self.relpath, name) | 2240 | _warn("%s: Not replacing locally modified %s hook", self.relpath, name) |
2265 | continue | 2241 | continue |
2266 | try: | 2242 | try: |
2267 | os.symlink(os.path.relpath(stock_hook, os.path.dirname(dst)), dst) | 2243 | os.symlink(os.path.relpath(stock_hook, os.path.dirname(dst)), dst) |
@@ -2320,7 +2296,10 @@ class Project(object): | |||
2320 | # Fail if the links are pointing to the wrong place | 2296 | # Fail if the links are pointing to the wrong place |
2321 | if src != dst: | 2297 | if src != dst: |
2322 | raise GitError('--force-sync not enabled; cannot overwrite a local ' | 2298 | raise GitError('--force-sync not enabled; cannot overwrite a local ' |
2323 | 'work tree') | 2299 | 'work tree. If you\'re comfortable with the ' |
2300 | 'possibility of losing the work tree\'s git metadata,' | ||
2301 | ' use `repo sync --force-sync {0}` to ' | ||
2302 | 'proceed.'.format(self.relpath)) | ||
2324 | 2303 | ||
2325 | def _ReferenceGitDir(self, gitdir, dotgit, share_refs, copy_all): | 2304 | def _ReferenceGitDir(self, gitdir, dotgit, share_refs, copy_all): |
2326 | """Update |dotgit| to reference |gitdir|, using symlinks where possible. | 2305 | """Update |dotgit| to reference |gitdir|, using symlinks where possible. |