diff options
author | Shawn O. Pearce <sop@google.com> | 2011-10-03 08:30:24 -0700 |
---|---|---|
committer | Shawn O. Pearce <sop@google.com> | 2011-10-03 08:30:24 -0700 |
commit | c325dc35f621fe24d0460bb14547cdb51e00c00b (patch) | |
tree | 9154d75a5a5348609fc3b6d637871298c831b407 /project.py | |
parent | f322b9abb4cadc67b991baf6ba1b9f2fbd5d7812 (diff) | |
download | git-repo-c325dc35f621fe24d0460bb14547cdb51e00c00b.tar.gz |
sync: Fetch after applying bundle and retry after errorsv1.7.7.1
After a $GIT_URL/clone.bundle has been applied to the new local
repository, perform an incremental fetch using `git fetch` to ensure
the local repository is up-to-date. This allows the hosting server
to offer stale /clone.bundle files to bootstrap a new client.
If a single git fetch fails, it may succeed again after a short
delay. Transient failures are typical in environments where the
remote Git server happens to have limits on how many requests it
can serve at once (the anonymous git daemon, or an HTTP server).
Wait a randomized delay between 30 and 45 seconds and retry the
failed project once. This delay gives the site time to recover
from a transient traffic spike, and the randomization makes it less
likely that a spike occurs again from all of the same clients.
Change-Id: I97fb0fcb33630fb78ac1a21d1a4a3e2268ab60c0
Signed-off-by: Shawn O. Pearce <sop@google.com>
Diffstat (limited to 'project.py')
-rw-r--r-- | project.py | 136 |
1 files changed, 74 insertions, 62 deletions
@@ -16,10 +16,12 @@ import traceback | |||
16 | import errno | 16 | import errno |
17 | import filecmp | 17 | import filecmp |
18 | import os | 18 | import os |
19 | import random | ||
19 | import re | 20 | import re |
20 | import shutil | 21 | import shutil |
21 | import stat | 22 | import stat |
22 | import sys | 23 | import sys |
24 | import time | ||
23 | import urllib2 | 25 | import urllib2 |
24 | 26 | ||
25 | from color import Coloring | 27 | from color import Coloring |
@@ -894,9 +896,25 @@ class Project(object): | |||
894 | is_new = not self.Exists | 896 | is_new = not self.Exists |
895 | if is_new: | 897 | if is_new: |
896 | self._InitGitDir() | 898 | self._InitGitDir() |
897 | |||
898 | self._InitRemote() | 899 | self._InitRemote() |
899 | if not self._RemoteFetch(initial=is_new, quiet=quiet): | 900 | |
901 | if is_new: | ||
902 | alt = os.path.join(self.gitdir, 'objects/info/alternates') | ||
903 | try: | ||
904 | fd = open(alt, 'rb') | ||
905 | try: | ||
906 | alt_dir = fd.readline().rstrip() | ||
907 | finally: | ||
908 | fd.close() | ||
909 | except IOError: | ||
910 | alt_dir = None | ||
911 | else: | ||
912 | alt_dir = None | ||
913 | |||
914 | if alt_dir is None and self._ApplyCloneBundle(initial=is_new, quiet=quiet): | ||
915 | is_new = False | ||
916 | |||
917 | if not self._RemoteFetch(initial=is_new, quiet=quiet, alt_dir=alt_dir): | ||
900 | return False | 918 | return False |
901 | 919 | ||
902 | #Check that the requested ref was found after fetch | 920 | #Check that the requested ref was found after fetch |
@@ -1307,7 +1325,8 @@ class Project(object): | |||
1307 | 1325 | ||
1308 | def _RemoteFetch(self, name=None, tag=None, | 1326 | def _RemoteFetch(self, name=None, tag=None, |
1309 | initial=False, | 1327 | initial=False, |
1310 | quiet=False): | 1328 | quiet=False, |
1329 | alt_dir=None): | ||
1311 | if not name: | 1330 | if not name: |
1312 | name = self.remote.name | 1331 | name = self.remote.name |
1313 | 1332 | ||
@@ -1316,29 +1335,9 @@ class Project(object): | |||
1316 | if remote.PreConnectFetch(): | 1335 | if remote.PreConnectFetch(): |
1317 | ssh_proxy = True | 1336 | ssh_proxy = True |
1318 | 1337 | ||
1319 | bundle_dst = os.path.join(self.gitdir, 'clone.bundle') | ||
1320 | bundle_tmp = os.path.join(self.gitdir, 'clone.bundle.tmp') | ||
1321 | use_bundle = False | ||
1322 | if os.path.exists(bundle_dst) or os.path.exists(bundle_tmp): | ||
1323 | use_bundle = True | ||
1324 | |||
1325 | if initial: | 1338 | if initial: |
1326 | alt = os.path.join(self.gitdir, 'objects/info/alternates') | 1339 | if alt_dir and 'objects' == os.path.basename(alt_dir): |
1327 | try: | 1340 | ref_dir = os.path.dirname(alt_dir) |
1328 | fd = open(alt, 'rb') | ||
1329 | try: | ||
1330 | ref_dir = fd.readline() | ||
1331 | if ref_dir and ref_dir.endswith('\n'): | ||
1332 | ref_dir = ref_dir[:-1] | ||
1333 | finally: | ||
1334 | fd.close() | ||
1335 | except IOError, e: | ||
1336 | ref_dir = None | ||
1337 | |||
1338 | if ref_dir and 'objects' == os.path.basename(ref_dir): | ||
1339 | if use_bundle: | ||
1340 | use_bundle = False | ||
1341 | ref_dir = os.path.dirname(ref_dir) | ||
1342 | packed_refs = os.path.join(self.gitdir, 'packed-refs') | 1341 | packed_refs = os.path.join(self.gitdir, 'packed-refs') |
1343 | remote = self.GetRemote(name) | 1342 | remote = self.GetRemote(name) |
1344 | 1343 | ||
@@ -1374,10 +1373,8 @@ class Project(object): | |||
1374 | old_packed += line | 1373 | old_packed += line |
1375 | 1374 | ||
1376 | _lwrite(packed_refs, tmp_packed) | 1375 | _lwrite(packed_refs, tmp_packed) |
1377 | |||
1378 | else: | 1376 | else: |
1379 | ref_dir = None | 1377 | alt_dir = None |
1380 | use_bundle = True | ||
1381 | 1378 | ||
1382 | cmd = ['fetch'] | 1379 | cmd = ['fetch'] |
1383 | 1380 | ||
@@ -1386,59 +1383,74 @@ class Project(object): | |||
1386 | depth = self.manifest.manifestProject.config.GetString('repo.depth') | 1383 | depth = self.manifest.manifestProject.config.GetString('repo.depth') |
1387 | if depth and initial: | 1384 | if depth and initial: |
1388 | cmd.append('--depth=%s' % depth) | 1385 | cmd.append('--depth=%s' % depth) |
1389 | use_bundle = False | ||
1390 | 1386 | ||
1391 | if quiet: | 1387 | if quiet: |
1392 | cmd.append('--quiet') | 1388 | cmd.append('--quiet') |
1393 | if not self.worktree: | 1389 | if not self.worktree: |
1394 | cmd.append('--update-head-ok') | 1390 | cmd.append('--update-head-ok') |
1395 | 1391 | cmd.append(name) | |
1396 | if use_bundle and not os.path.exists(bundle_dst): | 1392 | if tag is not None: |
1397 | bundle_url = remote.url + '/clone.bundle' | 1393 | cmd.append('tag') |
1398 | bundle_url = GitConfig.ForUser().UrlInsteadOf(bundle_url) | 1394 | cmd.append(tag) |
1399 | if GetSchemeFromUrl(bundle_url) in ('http', 'https'): | 1395 | |
1400 | use_bundle = self._FetchBundle( | 1396 | ok = False |
1401 | bundle_url, | 1397 | for i in range(2): |
1402 | bundle_tmp, | 1398 | if GitCommand(self, cmd, bare=True, ssh_proxy=ssh_proxy).Wait() == 0: |
1403 | bundle_dst, | 1399 | ok = True |
1404 | quiet=quiet) | 1400 | break |
1405 | else: | 1401 | time.sleep(random.randint(30, 45)) |
1406 | use_bundle = False | ||
1407 | |||
1408 | if use_bundle: | ||
1409 | if not quiet: | ||
1410 | cmd.append('--quiet') | ||
1411 | cmd.append(bundle_dst) | ||
1412 | for f in remote.fetch: | ||
1413 | cmd.append(str(f)) | ||
1414 | cmd.append('refs/tags/*:refs/tags/*') | ||
1415 | else: | ||
1416 | cmd.append(name) | ||
1417 | if tag is not None: | ||
1418 | cmd.append('tag') | ||
1419 | cmd.append(tag) | ||
1420 | |||
1421 | ok = GitCommand(self, | ||
1422 | cmd, | ||
1423 | bare = True, | ||
1424 | ssh_proxy = ssh_proxy).Wait() == 0 | ||
1425 | 1402 | ||
1426 | if initial: | 1403 | if initial: |
1427 | if ref_dir: | 1404 | if alt_dir: |
1428 | if old_packed != '': | 1405 | if old_packed != '': |
1429 | _lwrite(packed_refs, old_packed) | 1406 | _lwrite(packed_refs, old_packed) |
1430 | else: | 1407 | else: |
1431 | os.remove(packed_refs) | 1408 | os.remove(packed_refs) |
1432 | self.bare_git.pack_refs('--all', '--prune') | 1409 | self.bare_git.pack_refs('--all', '--prune') |
1410 | return ok | ||
1411 | |||
1412 | def _ApplyCloneBundle(self, initial=False, quiet=False): | ||
1413 | if initial and self.manifest.manifestProject.config.GetString('repo.depth'): | ||
1414 | return False | ||
1415 | |||
1416 | remote = self.GetRemote(self.remote.name) | ||
1417 | bundle_url = remote.url + '/clone.bundle' | ||
1418 | bundle_url = GitConfig.ForUser().UrlInsteadOf(bundle_url) | ||
1419 | if GetSchemeFromUrl(bundle_url) not in ('http', 'https'): | ||
1420 | return False | ||
1421 | |||
1422 | bundle_dst = os.path.join(self.gitdir, 'clone.bundle') | ||
1423 | bundle_tmp = os.path.join(self.gitdir, 'clone.bundle.tmp') | ||
1433 | 1424 | ||
1425 | exist_dst = os.path.exists(bundle_dst) | ||
1426 | exist_tmp = os.path.exists(bundle_tmp) | ||
1427 | |||
1428 | if not initial and not exist_dst and not exist_tmp: | ||
1429 | return False | ||
1430 | |||
1431 | if not exist_dst: | ||
1432 | exist_dst = self._FetchBundle(bundle_url, bundle_tmp, bundle_dst, quiet) | ||
1433 | if not exist_dst: | ||
1434 | return False | ||
1435 | |||
1436 | cmd = ['fetch'] | ||
1437 | if quiet: | ||
1438 | cmd.append('--quiet') | ||
1439 | if not self.worktree: | ||
1440 | cmd.append('--update-head-ok') | ||
1441 | cmd.append(bundle_dst) | ||
1442 | for f in remote.fetch: | ||
1443 | cmd.append(str(f)) | ||
1444 | cmd.append('refs/tags/*:refs/tags/*') | ||
1445 | |||
1446 | ok = GitCommand(self, cmd, bare=True).Wait() == 0 | ||
1434 | if os.path.exists(bundle_dst): | 1447 | if os.path.exists(bundle_dst): |
1435 | os.remove(bundle_dst) | 1448 | os.remove(bundle_dst) |
1436 | if os.path.exists(bundle_tmp): | 1449 | if os.path.exists(bundle_tmp): |
1437 | os.remove(bundle_tmp) | 1450 | os.remove(bundle_tmp) |
1438 | |||
1439 | return ok | 1451 | return ok |
1440 | 1452 | ||
1441 | def _FetchBundle(self, srcUrl, tmpPath, dstPath, quiet=False): | 1453 | def _FetchBundle(self, srcUrl, tmpPath, dstPath, quiet): |
1442 | keep = True | 1454 | keep = True |
1443 | done = False | 1455 | done = False |
1444 | dest = open(tmpPath, 'a+b') | 1456 | dest = open(tmpPath, 'a+b') |