Compare commits

..

8 Commits
v1.0 ... v1.0.6

Author SHA1 Message Date
329c31da7d Repair any mis-directed android-1.0 annotated tags
The initial open source release of the Android 1.0 platform had
some problems with its Perforce->Git imports.  Google was forced
to rewrite some history to redirect users onto more stable upstream
sources and correct errors in the imports.

Not everyone has the correct android-1.0 tags, as some users did
manage to fetch the platform early, before the mirror sites crashed
and the history was rewritten.

This change is a band-aid to ensure any stale android-1.0 tags are
get updated to the corrected version.  It should be backed out at
some point in the near future, when we can be fairly certain that
everyone has the correct android-1.0 tags.

Signed-off-by: Shawn O. Pearce <sop@google.com>
2008-10-24 09:17:25 -07:00
5cc6679fb8 Support user supplied custom .repo/local_manifest.xml files
By creating a .repo/local_manifest.xml the user can add extra
projects into their client space, without touching the main
manifest script.

For example:

  $ cat .repo/local_manifest.xml
  <?xml version="1.0" encoding="UTF-8"?>
  <manifest>
   <project path="android-build"
            name="platform/build"
            remote="korg"
            revision="android-1.0" />
  </manifest>

Signed-off-by: Shawn O. Pearce <sop@google.com>
2008-10-23 16:20:14 -07:00
632768bc65 Teach repo how to download changes to the local checkout
Now `repo download . 1402` would download the change numbered 1402
into the current project and check it out for the user, using a
detached HEAD.  `repo sync .` would back out of the change and
return to the upstream version.

Multiple projects can be fetched at once by listing them out on
the command line as different arguments.

Individual patch sets can be selected by adding a '/n' to indicate
the n-th patch set should be downloaded instead of the default of
patch set 1.

Signed-off-by: Shawn O. Pearce <sop@google.com>
2008-10-23 14:43:28 -07:00
0758d2f1d6 Show which user account each change was uploaded under
This way users are well aware of which account we used when the
uploads are complete, so they can be certain to sign into the web
application with that user identity.

Signed-off-by: Shawn O. Pearce <sop@google.com>
2008-10-22 13:13:40 -07:00
bb0ee80571 Change RPC client to only use Google Accounts for authentication
Hosted domain account (such as "@google.com" itself) don't work on the
Google App Engine service unless the user specifically creates their
own Google Account (https://www.google.com/accounts/NewAccount) with
the same email address.

When both such accounts exist we must *only* use the Google Account in
our auth request, as that is all Google App Engine will honor when we
send it the session cookie.

However, Google has internal servers that may also be running Gerrit
based applications.  In those case we must use the hosted auth login
for @google.com user accounts, as the internal servers honor only the
hosted account and not the public Google Account database.

In the future we may need to add other domains to the "HOSTED" list
if other Gerrit instances are setup on hosted domains and locked to
only those domain's user accounts, similar to how a server that is
internal to Google would be setup.  Since this is currently not a
likely occurrence I'm not worrying about making it configurable at
this juncture.

Signed-off-by: Shawn O. Pearce <sop@google.com>
2008-10-22 13:10:29 -07:00
02dbb6d120 Fix StopIteration exception during repo {sync,status}
If we run out of entries next() will throw StopIteration.

Signed-off-by: Shawn O. Pearce <sop@google.com>
2008-10-21 13:59:08 -07:00
7542d664de Remove the Python 2.4 dependency and use just 'python'
Many Linux distributions are including python2.5 by default, as
it is the latest stable release of the language.  Using python2.4
(and asking users to specifically install it) is just cruel and
unusual punishment.

Signed-off-by: Shawn O. Pearce <sop@google.com>
2008-10-21 07:12:42 -07:00
0734600ce0 Fix 'repo sync' when the remote reflog has only 1 entry
If the reflog for the upstream branch has only 1 entry in it, as
the branch has been updated only once, we can get back the 0{40}
object id from `git rev-parse upstream@{1}`, in which case we should
consider it to be the same as if upstream@{1} is not defined.

Signed-off-by: Shawn O. Pearce <sop@google.com>
2008-10-21 07:12:36 -07:00
8 changed files with 195 additions and 16 deletions

View File

@ -1 +1 @@
__version__ = 'v1.0'
__version__ = 'v1.0-14-gc4f226bc'

View File

@ -167,6 +167,10 @@ class HttpRpc(RpcChannel):
Returns:
The authentication token returned by ClientLogin.
"""
account_type = 'GOOGLE'
if self.host.endswith('.google.com'):
account_type = 'HOSTED'
req = self._CreateRequest(
url="https://www.google.com/accounts/ClientLogin",
data=urllib.urlencode({
@ -174,7 +178,7 @@ class HttpRpc(RpcChannel):
"Passwd": password,
"service": "ah",
"source": "gerrit-codereview-client",
"accountType": "HOSTED_OR_GOOGLE",
"accountType": account_type,
})
)
try:
@ -214,7 +218,6 @@ class HttpRpc(RpcChannel):
response.info()["location"] != continue_location):
raise urllib2.HTTPError(req.get_full_url(), response.code, response.msg,
response.headers, response.fp)
self.authenticated = True
def _GetXsrfToken(self):
"""Fetches /proto/_token for use in X-XSRF-Token HTTP header.
@ -253,10 +256,18 @@ class HttpRpc(RpcChannel):
authentication cookie, it returns a 401 response and directs us to
authenticate ourselves with ClientLogin.
"""
for i in range(3):
credentials = self.auth_function()
auth_token = self._GetAuthToken(credentials[0], credentials[1])
attempts = 0
while True:
attempts += 1
try:
cred = self.auth_function()
auth_token = self._GetAuthToken(cred[0], cred[1])
except ClientLoginError:
if attempts < 3:
continue
raise
self._GetAuthCookie(auth_token)
self.authenticated = True
if self.cookie_file is not None:
self.cookie_jar.save()
return

View File

@ -15,7 +15,7 @@
# limitations under the License.
magic='--calling-python-from-/bin/sh--'
"""exec" python2.4 -E "$0" "$@" """#$magic"
"""exec" python -E "$0" "$@" """#$magic"
if __name__ == '__main__':
import sys
if sys.argv[-1] == '#%s' % magic:

View File

@ -26,6 +26,7 @@ from remote import Remote
from error import ManifestParseError
MANIFEST_FILE_NAME = 'manifest.xml'
LOCAL_MANIFEST_NAME = 'local_manifest.xml'
class _Default(object):
"""Project defaults within the manifest."""
@ -108,10 +109,20 @@ class Manifest(object):
def _Load(self):
if not self._loaded:
self._ParseManifest()
self._ParseManifest(True)
local = os.path.join(self.repodir, LOCAL_MANIFEST_NAME)
if os.path.exists(local):
try:
real = self.manifestFile
self.manifestFile = local
self._ParseManifest(False)
finally:
self.manifestFile = real
self._loaded = True
def _ParseManifest(self):
def _ParseManifest(self, is_root_file):
root = xml.dom.minidom.parse(self.manifestFile)
if not root or not root.childNodes:
raise ManifestParseError, \
@ -124,9 +135,10 @@ class Manifest(object):
"no <manifest> in %s" % \
self.manifestFile
self.branch = config.getAttribute('branch')
if not self.branch:
self.branch = 'default'
if is_root_file:
self.branch = config.getAttribute('branch')
if not self.branch:
self.branch = 'default'
for node in config.childNodes:
if node.nodeName == 'remote':

View File

@ -45,6 +45,31 @@ def _info(fmt, *args):
def not_rev(r):
return '^' + r
class DownloadedChange(object):
_commit_cache = None
def __init__(self, project, base, change_id, ps_id, commit):
self.project = project
self.base = base
self.change_id = change_id
self.ps_id = ps_id
self.commit = commit
@property
def commits(self):
if self._commit_cache is None:
self._commit_cache = self.project.bare_git.rev_list(
'--abbrev=8',
'--abbrev-commit',
'--pretty=oneline',
'--reverse',
'--date-order',
not_rev(self.base),
self.commit,
'--')
return self._commit_cache
class ReviewableBranch(object):
_commit_cache = None
@ -88,6 +113,10 @@ class ReviewableBranch(object):
commit = self.project.bare_git.rev_parse(R_HEADS + self.name)
return 'http://%s/r/%s' % (me.remote.review, commit[0:12])
@property
def owner_email(self):
return self.project.UserEmail
class StatusColoring(Coloring):
def __init__(self, config):
@ -441,6 +470,7 @@ class Project(object):
return False
if not self._RemoteFetch():
return False
self._RepairAndroidImportErrors()
self._InitMRef()
return True
@ -448,6 +478,30 @@ class Project(object):
for file in self.copyfiles:
file._Copy()
def _RepairAndroidImportErrors(self):
if self.name in ['platform/external/iptables',
'platform/external/libpcap',
'platform/external/tcpdump',
'platform/external/webkit',
'platform/system/wlan/ti']:
# I hate myself for doing this...
#
# In the initial Android 1.0 release these projects were
# shipped, some users got them, and then the history had
# to be rewritten to correct problems with their imports.
# The 'android-1.0' tag may still be pointing at the old
# history, so we need to drop the tag and fetch it again.
#
try:
remote = self.GetRemote(self.remote.name)
relname = remote.ToLocal(R_HEADS + 'release-1.0')
tagname = R_TAGS + 'android-1.0'
if self._revlist(not_rev(relname), tagname):
cmd = ['fetch', remote.name, '+%s:%s' % (tagname, tagname)]
GitCommand(self, cmd, bare = True).Wait()
except GitError:
pass
def Sync_LocalHalf(self):
"""Perform only the local IO portion of the sync process.
Network access is not required.
@ -517,6 +571,9 @@ class Project(object):
old_merge = self.bare_git.rev_parse('%s@{1}' % merge)
except GitError:
old_merge = merge
if old_merge == '0000000000000000000000000000000000000000' \
or old_merge == '':
old_merge = merge
else:
# The upstream switched on us. Time to cross our fingers
# and pray that the old upstream also wasn't in the habit
@ -605,6 +662,23 @@ class Project(object):
src = os.path.join(self.worktree, src)
self.copyfiles.append(_CopyFile(src, dest))
def DownloadPatchSet(self, change_id, patch_id):
"""Download a single patch set of a single change to FETCH_HEAD.
"""
remote = self.GetRemote(self.remote.name)
cmd = ['fetch', remote.name]
cmd.append('refs/changes/%2.2d/%d/%d' \
% (change_id % 100, change_id, patch_id))
cmd.extend(map(lambda x: str(x), remote.fetch))
if GitCommand(self, cmd, bare=True).Wait() != 0:
return None
return DownloadedChange(self,
remote.ToLocal(self.revision),
change_id,
patch_id,
self.bare_git.rev_parse('FETCH_HEAD'))
## Branch Management ##
@ -917,8 +991,11 @@ class Project(object):
if out:
out = iter(out[:-1].split('\0'))
while out:
info = out.next()
path = out.next()
try:
info = out.next()
path = out.next()
except StopIteration:
break
class _Info(object):
def __init__(self, path, omode, nmode, oid, nid, state):

4
repo
View File

@ -20,7 +20,7 @@ REPO_REV='stable'
# limitations under the License.
magic='--calling-python-from-/bin/sh--'
"""exec" python2.4 -E "$0" "$@" """#$magic"
"""exec" python -E "$0" "$@" """#$magic"
if __name__ == '__main__':
import sys
if sys.argv[-1] == '#%s' % magic:
@ -28,7 +28,7 @@ if __name__ == '__main__':
del magic
# increment this whenever we make important changes to this script
VERSION = (1, 4)
VERSION = (1, 5)
# increment this if the MAINTAINER_KEYS block is modified
KEYRING_VERSION = (1,0)

78
subcmds/download.py Normal file
View File

@ -0,0 +1,78 @@
#
# Copyright (C) 2008 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import os
import re
import sys
from command import Command
CHANGE_RE = re.compile(r'^([1-9][0-9]*)(?:[/\.-]([1-9][0-9]*))?$')
class Download(Command):
common = True
helpSummary = "Download and checkout a change"
helpUsage = """
%prog {project change[/patchset]}...
"""
helpDescription = """
The '%prog' command downloads a change from the review system and
makes it available in your project's local working directory.
"""
def _Options(self, p):
pass
def _ParseChangeIds(self, args):
to_get = []
project = None
for a in args:
m = CHANGE_RE.match(a)
if m:
if not project:
self.Usage()
chg_id = int(m.group(1))
if m.group(2):
ps_id = int(m.group(2))
else:
ps_id = 1
to_get.append((project, chg_id, ps_id))
else:
project = self.GetProjects([a])[0]
return to_get
def Execute(self, opt, args):
for project, change_id, ps_id in self._ParseChangeIds(args):
dl = project.DownloadPatchSet(change_id, ps_id)
if not dl:
print >>sys.stderr, \
'[%s] change %d/%d not found' \
% (project.name, change_id, ps_id)
sys.exit(1)
if not dl.commits:
print >>sys.stderr, \
'[%s] change %d/%d has already been merged' \
% (project.name, change_id, ps_id)
continue
if len(dl.commits) > 1:
print >>sys.stderr, \
'[%s] %d/%d depends on %d unmerged changes:' \
% (project.name, change_id, ps_id, len(dl.commits))
for c in dl.commits:
print >>sys.stderr, ' %s' % (c)
project._Checkout(dl.commit)

View File

@ -158,6 +158,7 @@ changes in all projects listed in the manifest.
branch.project.relpath + '/',
branch.name)
print >>sys.stderr, '%s' % branch.tip_url
print >>sys.stderr, '(as %s)' % branch.owner_email
print >>sys.stderr, ''
if have_errors: