Compare commits

...

9 Commits

Author SHA1 Message Date
e02ac0af2e sync: --no-clone-bundle disables the clone bundle support
Change-Id: Ia9ed7da8451b273c1be620c3dd0dcad777b29096
2012-03-14 15:38:28 -07:00
898e12a2d9 Permit - in URL schemes for special URLs
Clients might be using their own special git-remote-* helper that
has a hypen in its name. Permit - in the scheme part of the URL
when trying to decide if it is an SSH URL and assume it is *not*
SSH if the URL matches "foo-bar://" style.

Change-Id: I7ba2d810a614f6e605a441d5972902c4a14e73fd
2012-03-14 15:28:22 -07:00
ae0a36c9a5 Add support for Apache Digest authentication for repo init.
repo tool supports only Basic authentication for now. For those
who want to use this tool to manage their own projects, in case
the administrator has configured the Apache server with Digest
authentication method, users will fail to be authenticated when
they run the command 'repo init'.
Add the digest authentication password manager to the handler
list will fix this issue.

Since Git HTTP protocol will require the user be authenticated
for fetch operation first before pushing commits to the remote,
it is unlikely for the administrator to implement anonymous
read (aka pull) access and write access (aka push) for
authenticated user. Both read and write have to be authenticated.
Be aware that the user may have to add an extra line in his
~/.netrc file:
-------------------
account example.com
-------------------
where 'example.com' is the realm for Apache Digest authentication.

Change-Id: I76eb27b205554426d9ce1965deaaf727b87916cd
Signed-off-by: Xiaodong Xu <stid.smth@gmail.com>
2012-03-14 15:01:34 -07:00
76abcc1d1e repo status to print project name on clean gits
repo status just prints "# on branch oprofile" if you have branched
in clean status. This doesn't really tell which branch is meant.

Instead we can use the same syntax with modified gits which will
give us detailed information.

Change-Id: I55fe5154d278e10a814281dd2ba501ec6e956730
2012-03-12 12:25:40 -07:00
d315382572 Add 'rebase="false"' attribute to the <project/> XML.
This new attribute can prevent 'repo sync' from automatically rebasing.

I hit a situation in where one of the git repositories I was tracking
was actually an external repository that I wanted to pull commits
into and merge myself. (NOT rebase, since that would lose the merge
history.) In this case, I'm not using 'repo upload', I'm manually
managing the merges to and from this repository.

Everything was going great until I typed 'repo sync' and it rebased
my manually-merged tree. Hence the option to skip it.

Change-Id: I965e0dd1acb87f4a56752ebedc7e2de1c502dbf8
2012-03-12 12:24:22 -07:00
43bda84362 Avoid missing content-length header in project.py
Occassionally, the content-length may be missing when using urlib
in python 2.6 and 2.7.  This change assumes the value of the header is
0 if it doesn't exist

Change-Id: Iaf1c8a796bc667823d4d7c30f9b617644b271d00
2012-03-12 12:13:15 -07:00
9b017dab46 Update SUBMITTING_PATCHES
The review server is now at gerrit-review.googlesource.com.

Change-Id: I4be67fdb1876eb2e2af4420ac63557596b9e233b
2012-02-28 18:54:33 -08:00
e9dc3b3368 sync: Add manifest_name parameter
This parameter changes the manifest used by 'repo sync' for only
this execution. It should be useful for developers wishing to get
the repo temporarily into a known state, without clobbering their
existing manifest.

Tested by shifting Chrome OS between minilayout and full, and
between several release-builder-generated manifests.

Change-Id: I14194b665195b0e78f368d9ec8b8a83227af2627
2012-01-26 12:32:36 -05:00
c9571423f8 upload: Support uploading to Gerrit over https://
If SSH is not available, Gerrit returns NOT_AVAILABLE to the /ssh_info
query made by repo upload. In this case fallback to the /p/$PROJECT URL
that Gerrit also exports and use that for uploads.

Change-Id: I1e3e39ab709ecc0a692614a41a42446426f39c08
2012-01-11 16:18:40 -08:00
8 changed files with 127 additions and 102 deletions

View File

@ -5,7 +5,7 @@ Short Version:
- Make sure all code is under the Apache License, 2.0.
- Publish your changes for review:
git push ssh://review.source.android.com:29418/tools/repo.git HEAD:refs/for/master
git push https://gerrit-review.googlesource.com/git-repo HEAD:refs/for/maint
Long Version:
@ -55,24 +55,23 @@ Do not email your patches to anyone.
Instead, login to the Gerrit Code Review tool at:
https://review.source.android.com/
https://gerrit-review.googlesource.com/
Ensure you have completed one of the necessary contributor
agreements, providing documentation to the project maintainers that
they have right to redistribute your work under the Apache License:
https://review.source.android.com/#settings,agreements
https://gerrit-review.googlesource.com/#/settings/agreements
Ensure you have registered one or more SSH public keys, so you can
push your commits directly over SSH:
Ensure you have obtained an HTTP password to authenticate:
https://review.source.android.com/#settings,ssh-keys
https://gerrit-review.googlesource.com/new-password
Push your patches over SSH to the review server, possibly through
Push your patches over HTTPS to the review server, possibly through
a remembered remote to make this easier in the future:
git config remote.review.url ssh://review.source.android.com:29418/tools/repo.git
git config remote.review.push HEAD:refs/for/master
git config remote.review.url https://gerrit-review.googlesource.com/git-repo
git config remote.review.push HEAD:refs/for/maint
git push review

View File

@ -488,7 +488,7 @@ def close_ssh():
_master_keys_lock = None
URI_SCP = re.compile(r'^([^@:]*@?[^:/]{1,}):')
URI_ALL = re.compile(r'^([a-z][a-z+]*)://([^@/]*@?[^/]*)/')
URI_ALL = re.compile(r'^([a-z][a-z+-]*)://([^@/]*@?[^/]*)/')
def GetSchemeFromUrl(url):
m = URI_ALL.match(url)
@ -527,7 +527,7 @@ class Remote(object):
self.projectname = self._Get('projectname')
self.fetch = map(lambda x: RefSpec.FromString(x),
self._Get('fetch', all=True))
self._review_protocol = None
self._review_url = None
def _InsteadOf(self):
globCfg = GitConfig.ForUser()
@ -554,9 +554,8 @@ class Remote(object):
connectionUrl = self._InsteadOf()
return _preconnect(connectionUrl)
@property
def ReviewProtocol(self):
if self._review_protocol is None:
def ReviewUrl(self, userEmail):
if self._review_url is None:
if self.review is None:
return None
@ -565,67 +564,47 @@ class Remote(object):
u = 'http://%s' % u
if u.endswith('/Gerrit'):
u = u[:len(u) - len('/Gerrit')]
if not u.endswith('/ssh_info'):
if not u.endswith('/'):
u += '/'
u += 'ssh_info'
if u.endswith('/ssh_info'):
u = u[:len(u) - len('/ssh_info')]
if not u.endswith('/'):
u += '/'
http_url = u
if u in REVIEW_CACHE:
info = REVIEW_CACHE[u]
self._review_protocol = info[0]
self._review_host = info[1]
self._review_port = info[2]
self._review_url = REVIEW_CACHE[u]
elif 'REPO_HOST_PORT_INFO' in os.environ:
info = os.environ['REPO_HOST_PORT_INFO']
self._review_protocol = 'ssh'
self._review_host = info.split(" ")[0]
self._review_port = info.split(" ")[1]
REVIEW_CACHE[u] = (
self._review_protocol,
self._review_host,
self._review_port)
host, port = os.environ['REPO_HOST_PORT_INFO'].split()
self._review_url = self._SshReviewUrl(userEmail, host, port)
REVIEW_CACHE[u] = self._review_url
else:
try:
info = urllib2.urlopen(u).read()
if info == 'NOT_AVAILABLE':
raise UploadError('%s: SSH disabled' % self.review)
info_url = u + 'ssh_info'
info = urllib2.urlopen(info_url).read()
if '<' in info:
# Assume the server gave us some sort of HTML
# response back, like maybe a login page.
#
raise UploadError('%s: Cannot parse response' % u)
raise UploadError('%s: Cannot parse response' % info_url)
self._review_protocol = 'ssh'
self._review_host = info.split(" ")[0]
self._review_port = info.split(" ")[1]
except urllib2.HTTPError, e:
if e.code == 404:
self._review_protocol = 'http-post'
self._review_host = None
self._review_port = None
if info == 'NOT_AVAILABLE':
# Assume HTTP if SSH is not enabled.
self._review_url = http_url + 'p/'
else:
raise UploadError('Upload over SSH unavailable')
host, port = info.split()
self._review_url = self._SshReviewUrl(userEmail, host, port)
except urllib2.HTTPError, e:
raise UploadError('%s: %s' % (self.review, str(e)))
except urllib2.URLError, e:
raise UploadError('%s: %s' % (self.review, str(e)))
REVIEW_CACHE[u] = (
self._review_protocol,
self._review_host,
self._review_port)
return self._review_protocol
REVIEW_CACHE[u] = self._review_url
return self._review_url + self.projectname
def SshReviewUrl(self, userEmail):
if self.ReviewProtocol != 'ssh':
return None
def _SshReviewUrl(self, userEmail, host, port):
username = self._config.GetString('review.%s.username' % self.review)
if username is None:
username = userEmail.split("@")[0]
return 'ssh://%s@%s:%s/%s' % (
username,
self._review_host,
self._review_port,
self.projectname)
username = userEmail.split('@')[0]
return 'ssh://%s@%s:%s/' % (username, host, port)
def ToLocal(self, rev):
"""Convert a remote revision string to something we have locally.

23
main.py
View File

@ -295,6 +295,24 @@ class _BasicAuthHandler(urllib2.HTTPBasicAuthHandler):
self.retried = 0
raise
class _DigestAuthHandler(urllib2.HTTPDigestAuthHandler):
def http_error_auth_reqed(self, auth_header, host, req, headers):
try:
old_add_header = req.add_header
def _add_header(name, val):
val = val.replace('\n', '')
old_add_header(name, val)
req.add_header = _add_header
return urllib2.AbstractDigestAuthHandler.http_error_auth_reqed(
self, auth_header, host, req, headers)
except:
reset = getattr(self, 'reset_retry_count', None)
if reset is not None:
reset()
elif getattr(self, 'retried', None):
self.retried = 0
raise
def init_http():
handlers = [_UserAgentHandler()]
@ -303,13 +321,14 @@ def init_http():
n = netrc.netrc()
for host in n.hosts:
p = n.hosts[host]
mgr.add_password(None, 'http://%s/' % host, p[0], p[2])
mgr.add_password(None, 'https://%s/' % host, p[0], p[2])
mgr.add_password(p[1], 'http://%s/' % host, p[0], p[2])
mgr.add_password(p[1], 'https://%s/' % host, p[0], p[2])
except netrc.NetrcParseError:
pass
except IOError:
pass
handlers.append(_BasicAuthHandler(mgr))
handlers.append(_DigestAuthHandler(mgr))
if 'http_proxy' in os.environ:
url = os.environ['http_proxy']

View File

@ -498,6 +498,12 @@ class XmlManifest(object):
"project %s path cannot be absolute in %s" % \
(name, self.manifestFile)
rebase = node.getAttribute('rebase')
if not rebase:
rebase = True
else:
rebase = rebase.lower() in ("yes", "true", "1")
if self.IsMirror:
relpath = None
worktree = None
@ -513,7 +519,8 @@ class XmlManifest(object):
worktree = worktree,
relpath = path,
revisionExpr = revisionExpr,
revisionId = None)
revisionId = None,
rebase = rebase)
for n in node.childNodes:
if n.nodeName == 'copyfile':

View File

@ -503,7 +503,8 @@ class Project(object):
worktree,
relpath,
revisionExpr,
revisionId):
revisionId,
rebase = True):
self.manifest = manifest
self.name = name
self.remote = remote
@ -522,6 +523,8 @@ class Project(object):
else:
self.revisionId = revisionId
self.rebase = rebase
self.snapshots = {}
self.copyfiles = []
self.config = GitConfig.ForRepository(
@ -688,7 +691,7 @@ class Project(object):
di = self.work_git.DiffZ('diff-index', '-M', '--cached', HEAD)
df = self.work_git.DiffZ('diff-files')
do = self.work_git.LsOthers()
if not rb and not di and not df and not do:
if not rb and not di and not df and not do and not self.CurrentBranch:
return 'CLEAN'
out = StatusColoring(self.config)
@ -866,31 +869,30 @@ class Project(object):
branch.remote.projectname = self.name
branch.remote.Save()
if branch.remote.ReviewProtocol == 'ssh':
if dest_branch.startswith(R_HEADS):
dest_branch = dest_branch[len(R_HEADS):]
url = branch.remote.ReviewUrl(self.UserEmail)
if url is None:
raise UploadError('review not configured')
cmd = ['push']
if url.startswith('ssh://'):
rp = ['gerrit receive-pack']
for e in people[0]:
rp.append('--reviewer=%s' % sq(e))
for e in people[1]:
rp.append('--cc=%s' % sq(e))
ref_spec = '%s:refs/for/%s' % (R_HEADS + branch.name, dest_branch)
if auto_topic:
ref_spec = ref_spec + '/' + branch.name
cmd = ['push']
cmd.append('--receive-pack=%s' % " ".join(rp))
cmd.append(branch.remote.SshReviewUrl(self.UserEmail))
cmd.append(ref_spec)
if GitCommand(self, cmd, bare = True).Wait() != 0:
raise UploadError('Upload failed')
cmd.append(url)
else:
raise UploadError('Unsupported protocol %s' \
% branch.remote.review)
if dest_branch.startswith(R_HEADS):
dest_branch = dest_branch[len(R_HEADS):]
ref_spec = '%s:refs/for/%s' % (R_HEADS + branch.name, dest_branch)
if auto_topic:
ref_spec = ref_spec + '/' + branch.name
cmd.append(ref_spec)
if GitCommand(self, cmd, bare = True).Wait() != 0:
raise UploadError('Upload failed')
msg = "posted to %s for %s" % (branch.remote.review, dest_branch)
self.bare_git.UpdateRef(R_PUB + branch.name,
@ -900,7 +902,11 @@ class Project(object):
## Sync ##
def Sync_NetworkHalf(self, quiet=False, is_new=None, current_branch_only=False):
def Sync_NetworkHalf(self,
quiet=False,
is_new=None,
current_branch_only=False,
clone_bundle=True):
"""Perform only the network IO portion of the sync process.
Local working directory/branch state is not affected.
"""
@ -923,7 +929,9 @@ class Project(object):
else:
alt_dir = None
if alt_dir is None and self._ApplyCloneBundle(initial=is_new, quiet=quiet):
if clone_bundle \
and alt_dir is None \
and self._ApplyCloneBundle(initial=is_new, quiet=quiet):
is_new = False
if not self._RemoteFetch(initial=is_new, quiet=quiet, alt_dir=alt_dir,
@ -1097,7 +1105,7 @@ class Project(object):
branch.merge = self.revisionExpr
branch.Save()
if cnt_mine > 0:
if cnt_mine > 0 and self.rebase:
def _dorebase():
self._Rebase(upstream = '%s^1' % last_mine, onto = revid)
self._CopyFiles()
@ -1447,6 +1455,8 @@ class Project(object):
remote = self.GetRemote(self.remote.name)
bundle_url = remote.url + '/clone.bundle'
bundle_url = GitConfig.ForUser().UrlInsteadOf(bundle_url)
if GetSchemeFromUrl(bundle_url) in ('persistent-http', 'persistent-https'):
bundle_url = bundle_url[len('persistent-'):]
if GetSchemeFromUrl(bundle_url) not in ('http', 'https'):
return False
@ -1530,7 +1540,7 @@ class Project(object):
p = None
try:
size = r.headers['content-length']
size = r.headers.get('content-length', 0)
unit = 1 << 10
if size and not quiet:

9
repo
View File

@ -28,7 +28,7 @@ if __name__ == '__main__':
del magic
# increment this whenever we make important changes to this script
VERSION = (1, 13)
VERSION = (1, 14)
# increment this if the MAINTAINER_KEYS block is modified
KEYRING_VERSION = (1,0)
@ -154,7 +154,7 @@ def _Init(args):
"""Installs repo by cloning it over the network.
"""
opt, args = init_optparse.parse_args(args)
if args or not opt.manifest_url:
if args:
init_optparse.print_usage()
sys.exit(1)
@ -311,11 +311,12 @@ def _InitHttp():
n = netrc.netrc()
for host in n.hosts:
p = n.hosts[host]
mgr.add_password(None, 'http://%s/' % host, p[0], p[2])
mgr.add_password(None, 'https://%s/' % host, p[0], p[2])
mgr.add_password(p[1], 'http://%s/' % host, p[0], p[2])
mgr.add_password(p[1], 'https://%s/' % host, p[0], p[2])
except:
pass
handlers.append(urllib2.HTTPBasicAuthHandler(mgr))
handlers.append(urllib2.HTTPDigestAuthHandler(mgr))
if 'http_proxy' in os.environ:
url = os.environ['http_proxy']

View File

@ -101,19 +101,6 @@ the following meanings:
all = self.GetProjects(args)
counter = itertools.count()
on = {}
for project in all:
cb = project.CurrentBranch
if cb:
if cb not in on:
on[cb] = []
on[cb].append(project)
branch_names = list(on.keys())
branch_names.sort()
for cb in branch_names:
print '# on branch %s' % cb
if opt.jobs == 1:
for project in all:
state = project.PrintWorkTreeStatus()

View File

@ -86,6 +86,12 @@ specify a custom tag/label.
The -f/--force-broken option can be used to proceed with syncing
other projects if a project sync fails.
The --no-clone-bundle option disables any attempt to use
$URL/clone.bundle to bootstrap a new Git repository from a
resumeable bundle file on a content delivery network. This
may be necessary if there are problems with the local Python
HTTP client or proxy configuration, but the Git binary works.
SSH Connections
---------------
@ -140,6 +146,12 @@ later is required to fix a server side protocol bug.
p.add_option('-j','--jobs',
dest='jobs', action='store', type='int',
help="projects to fetch simultaneously (default %d)" % self.jobs)
p.add_option('-m', '--manifest-name',
dest='manifest_name',
help='temporary manifest to use for this sync', metavar='NAME.xml')
p.add_option('--no-clone-bundle',
dest='no_clone_bundle', action='store_true',
help='disable use of /clone.bundle on HTTP/HTTPS')
if show_smart:
p.add_option('-s', '--smart-sync',
dest='smart_sync', action='store_true',
@ -182,8 +194,10 @@ later is required to fix a server side protocol bug.
# - We always make sure we unlock the lock if we locked it.
try:
try:
success = project.Sync_NetworkHalf(quiet=opt.quiet,
current_branch_only=opt.current_branch_only)
success = project.Sync_NetworkHalf(
quiet=opt.quiet,
current_branch_only=opt.current_branch_only,
clone_bundle=not opt.no_clone_bundle)
# Lock around all the rest of the code, since printing, updating a set
# and Progress.update() are not thread safe.
@ -333,6 +347,15 @@ uncommitted changes are present' % project.relpath
if opt.network_only and opt.local_only:
print >>sys.stderr, 'error: cannot combine -n and -l'
sys.exit(1)
if opt.manifest_name and opt.smart_sync:
print >>sys.stderr, 'error: cannot combine -m and -s'
sys.exit(1)
if opt.manifest_name and opt.smart_tag:
print >>sys.stderr, 'error: cannot combine -m and -t'
sys.exit(1)
if opt.manifest_name:
self.manifest.Override(opt.manifest_name)
if opt.smart_sync or opt.smart_tag:
if not self.manifest.manifest_server: