summaryrefslogtreecommitdiffstats
path: root/project.py
diff options
context:
space:
mode:
Diffstat (limited to 'project.py')
-rw-r--r--project.py85
1 files changed, 32 insertions, 53 deletions
diff --git a/project.py b/project.py
index a117f4df..5d8f61e1 100644
--- a/project.py
+++ b/project.py
@@ -13,7 +13,6 @@
13# limitations under the License. 13# limitations under the License.
14 14
15from __future__ import print_function 15from __future__ import print_function
16import contextlib
17import errno 16import errno
18import filecmp 17import filecmp
19import glob 18import glob
@@ -31,7 +30,7 @@ import traceback
31 30
32from color import Coloring 31from color import Coloring
33from git_command import GitCommand, git_require 32from git_command import GitCommand, git_require
34from git_config import GitConfig, IsId, GetSchemeFromUrl, ID_RE 33from git_config import GitConfig, IsId, GetSchemeFromUrl, GetUrlCookieFile, ID_RE
35from error import GitError, HookError, UploadError, DownloadError 34from error import GitError, HookError, UploadError, DownloadError
36from error import ManifestInvalidRevisionError 35from error import ManifestInvalidRevisionError
37from error import NoManifestException 36from 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
66def _warn(fmt, *args):
67 msg = fmt % args
68 print('warn: %s' % msg, file=sys.stderr)
69
67def not_rev(r): 70def 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.