summaryrefslogtreecommitdiffstats
path: root/project.py
diff options
context:
space:
mode:
authorShawn O. Pearce <sop@google.com>2011-10-03 08:30:24 -0700
committerShawn O. Pearce <sop@google.com>2011-10-03 08:30:24 -0700
commitc325dc35f621fe24d0460bb14547cdb51e00c00b (patch)
tree9154d75a5a5348609fc3b6d637871298c831b407 /project.py
parentf322b9abb4cadc67b991baf6ba1b9f2fbd5d7812 (diff)
downloadgit-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.py136
1 files changed, 74 insertions, 62 deletions
diff --git a/project.py b/project.py
index 5adfe82e..43f4713c 100644
--- a/project.py
+++ b/project.py
@@ -16,10 +16,12 @@ import traceback
16import errno 16import errno
17import filecmp 17import filecmp
18import os 18import os
19import random
19import re 20import re
20import shutil 21import shutil
21import stat 22import stat
22import sys 23import sys
24import time
23import urllib2 25import urllib2
24 26
25from color import Coloring 27from 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')