Compare commits

...

7 Commits

Author SHA1 Message Date
9830553748 Fix percent done on resumed /clone.bundle
The Content-Length when resuming is the number of bytes that
remain in the file. To compute the total size as expected by
the progress meter, we must add the bytes already stored.

While we are in this method fix uses of % operator to ensure
a tuple is always supplied.

Change-Id: Ic899231b5bc0ab43b3ddb1d29845f6390e820115
2012-08-01 17:41:26 -07:00
2bc7f5cb3a Fix bug in version_tuple to handle strings with -rc#
Example of version string that caused a problem: git version 1.7.11-rc3

Change-Id: I8a68b6b37f7b2ded23a1f8ae0d12131050a8807b
CC: sop@google.com
2012-07-31 22:18:47 -07:00
b292b98c3e Add remote alias support in manifest
The `alias` is an optional attribute in element `remote`. It can be
used to override attibute `name` to be set as the remote name in each
project's .git/config. Its value can be duplicated while attribute
`name` has to be unique across the manifest file. This helps each
project to be able to have same remote name which actually points
to different remote url.

It eases some automation scripts to be able to checkout/push to same
remote name but actually different remote url, like:

repo forall -c "git checkout -b work same_remote/work"
repo forall -c "git push same_remote work:work"

for example:
The manifest with 'alias' will look like:

<?xml version='1.0' encoding='UTF-8'?>
<manifest>
  <remote alias="same_alias" fetch="git://git.external1.org/" name="ext1"
      review="http://review.external1.org"/>
  <remote alias="same_alias" fetch="git://git.external2.org/" name="ext2"
      review="http://review.external2.org"/>
  <remote alias="same_alias" fetch="ssh://git.internal.com:29418" name="int"
      review="http://review.internal.com"/>
  <default remote="int" revision="int-branch" sync-j="2"/>
  <project name="path/to/project1" path="project1" remote="ext1"/>
  <project name="path/to/project2" path="project2" remote="ext2"/>
  <project name="path/to/project3" path="project3"/>
  ...
</manifest>

In each project, use command "git remote -v"

project1:
same_alias  git://git.external1.org/project1 (fetch)
same_alias  git://git.external1.org/project1 (push)

project2:
same_alias  git://git.external2.org/project2 (fetch)
same_alias  git://git.external2.org/project2 (push)

project3:
same_alias  ssh://git.internal.com:29418/project3 (fetch)
same_alias  ssh://git.internal.com:29418/project3 (push)

Change-Id: I2c48263097ff107f0c978f3e83966ae71d06cb90
2012-07-31 22:13:13 -07:00
2f127de752 Add "repo overview" command.
The overview command shows an overview of each branch in all (or the
specified) projects.  The overview lists any local commits that have
not yet been merged into the project.

The report output is inspired by the report displayed following a
"repo prune" event, with the addition of listing the one-line log
messages for each commit that is not yet merged.

The report can also be filtered to show only active branches; by
default all branches that have commits beyond the upstream HEAD will
be listed.

Change-Id: Ibe67793991ad1aa38de3bc9747de4ba64e5591aa
2012-07-31 22:08:32 -07:00
7da1314e38 Inject the project name into each projects groups.
For CrOS, we have scenarios were people checkout a smaller version
of our manifest via groups, and enable individual repositories as
needed for their work.  Previously this was via local_manifest
manipulation, which breaks via manifest-groups would require a
remove-project tag.

Via injecting the projects name into the projects groups, this
allows us to instead manipulate the configured groups allowing
the user to turn on/off projects as necessary.

Change-Id: I07b7918e16cc9dc28eb47e19a46a04dc4fd0be74
2012-07-31 22:05:44 -07:00
435370c6f0 upload: add --draft option.
Change-Id: I6967ff2f8163cd4116027b3f15ddb36875942af4
2012-07-28 15:44:05 -07:00
e8f75fa368 Don't delete the branch config when switching branches.
The fix for issue #46 in 5d016502eb appears to break syncing in some
situations: the branch is deleted after the point where it's been
configured, which deletes part of its configuration and causes the
config to change each time you call `repo init`, alternating between a
configuration that works and one that doesn't.

Instead of deleting the branch with git branch -D, use git update-ref -d
which just deletes the ref (to avoid the rebase) without touching the
configuration for the branch that was set up during the first repo init.

This appears to ensure the config is left in a valid state all the time
no matter what combination of repo init commands you run, without
reintroducing the rebasing issue.

Change-Id: Iaadaa6e56a46840bbc593fa5b35cb5b34cd3ce69
2012-07-20 15:33:17 +01:00
6 changed files with 127 additions and 16 deletions

View File

@ -32,6 +32,7 @@ following DTD:
<!ELEMENT remote (EMPTY)> <!ELEMENT remote (EMPTY)>
<!ATTLIST remote name ID #REQUIRED> <!ATTLIST remote name ID #REQUIRED>
<!ATTLIST remote alias CDATA #IMPLIED>
<!ATTLIST remote fetch CDATA #REQUIRED> <!ATTLIST remote fetch CDATA #REQUIRED>
<!ATTLIST remote review CDATA #IMPLIED> <!ATTLIST remote review CDATA #IMPLIED>
@ -89,6 +90,12 @@ name specified here is used as the remote name in each project's
.git/config, and is therefore automatically available to commands .git/config, and is therefore automatically available to commands
like `git fetch`, `git remote`, `git pull` and `git push`. like `git fetch`, `git remote`, `git pull` and `git push`.
Attribute `alias`: The alias, if specified, is used to override
`name` to be set as the remote name in each project's .git/config.
Its value can be duplicated while attribute `name` has to be unique
in the manifest file. This helps each project to be able to have
same remote name which actually points to different remote url.
Attribute `fetch`: The Git URL prefix for all projects which use Attribute `fetch`: The Git URL prefix for all projects which use
this remote. Each project's name is appended to this prefix to this remote. Each project's name is appended to this prefix to
form the actual URL used to clone the project. form the actual URL used to clone the project.
@ -171,7 +178,11 @@ the default element is used.
Attribute `groups`: List of groups to which this project belongs, Attribute `groups`: List of groups to which this project belongs,
whitespace or comma separated. All projects belong to the group whitespace or comma separated. All projects belong to the group
"default". "default", and each project automatically belongs to a group of
it's name:`name` and path:`path`. E.g. for
<project name="monkeys" path="barrel-of"/>, that project
definition is implicitly in the following manifest groups:
default, name:monkeys, and path:barrel-of.
Element annotation Element annotation
------------------ ------------------

View File

@ -89,7 +89,7 @@ class _GitCall(object):
if ver_str.startswith('git version '): if ver_str.startswith('git version '):
_git_version = tuple( _git_version = tuple(
map(lambda x: int(x), map(lambda x: int(x),
ver_str[len('git version '):].strip().split('.')[0:3] ver_str[len('git version '):].strip().split('-')[0].split('.')[0:3]
)) ))
else: else:
print >>sys.stderr, 'fatal: "%s" unsupported' % ver_str print >>sys.stderr, 'fatal: "%s" unsupported' % ver_str

View File

@ -41,12 +41,14 @@ class _Default(object):
class _XmlRemote(object): class _XmlRemote(object):
def __init__(self, def __init__(self,
name, name,
alias=None,
fetch=None, fetch=None,
manifestUrl=None, manifestUrl=None,
review=None): review=None):
self.name = name self.name = name
self.fetchUrl = fetch self.fetchUrl = fetch
self.manifestUrl = manifestUrl self.manifestUrl = manifestUrl
self.remoteAlias = alias
self.reviewUrl = review self.reviewUrl = review
self.resolvedFetchUrl = self._resolveFetchUrl() self.resolvedFetchUrl = self._resolveFetchUrl()
@ -62,7 +64,10 @@ class _XmlRemote(object):
def ToRemoteSpec(self, projectName): def ToRemoteSpec(self, projectName):
url = self.resolvedFetchUrl.rstrip('/') + '/' + projectName url = self.resolvedFetchUrl.rstrip('/') + '/' + projectName
return RemoteSpec(self.name, url, self.reviewUrl) remoteName = self.name
if self.remoteAlias:
remoteName = self.remoteAlias
return RemoteSpec(remoteName, url, self.reviewUrl)
class XmlManifest(object): class XmlManifest(object):
"""manages the repo configuration file""" """manages the repo configuration file"""
@ -451,12 +456,15 @@ class XmlManifest(object):
reads a <remote> element from the manifest file reads a <remote> element from the manifest file
""" """
name = self._reqatt(node, 'name') name = self._reqatt(node, 'name')
alias = node.getAttribute('alias')
if alias == '':
alias = None
fetch = self._reqatt(node, 'fetch') fetch = self._reqatt(node, 'fetch')
review = node.getAttribute('review') review = node.getAttribute('review')
if review == '': if review == '':
review = None review = None
manifestUrl = self.manifestProject.config.GetString('remote.origin.url') manifestUrl = self.manifestProject.config.GetString('remote.origin.url')
return _XmlRemote(name, fetch, manifestUrl, review) return _XmlRemote(name, alias, fetch, manifestUrl, review)
def _ParseDefault(self, node): def _ParseDefault(self, node):
""" """
@ -566,8 +574,9 @@ class XmlManifest(object):
if node.hasAttribute('groups'): if node.hasAttribute('groups'):
groups = node.getAttribute('groups') groups = node.getAttribute('groups')
groups = [x for x in re.split('[,\s]+', groups) if x] groups = [x for x in re.split('[,\s]+', groups) if x]
if 'default' not in groups:
groups.append('default') default_groups = ['default', 'name:%s' % name, 'path:%s' % path]
groups.extend(set(default_groups).difference(groups))
if self.IsMirror: if self.IsMirror:
relpath = None relpath = None

View File

@ -176,10 +176,11 @@ class ReviewableBranch(object):
R_HEADS + self.name, R_HEADS + self.name,
'--') '--')
def UploadForReview(self, people, auto_topic=False): def UploadForReview(self, people, auto_topic=False, draft=False):
self.project.UploadForReview(self.name, self.project.UploadForReview(self.name,
people, people,
auto_topic=auto_topic) auto_topic=auto_topic,
draft=draft)
def GetPublishedRefs(self): def GetPublishedRefs(self):
refs = {} refs = {}
@ -881,7 +882,8 @@ class Project(object):
def UploadForReview(self, branch=None, def UploadForReview(self, branch=None,
people=([],[]), people=([],[]),
auto_topic=False): auto_topic=False,
draft=False):
"""Uploads the named branch for code review. """Uploads the named branch for code review.
""" """
if branch is None: if branch is None:
@ -920,7 +922,13 @@ class Project(object):
if dest_branch.startswith(R_HEADS): if dest_branch.startswith(R_HEADS):
dest_branch = dest_branch[len(R_HEADS):] dest_branch = dest_branch[len(R_HEADS):]
ref_spec = '%s:refs/for/%s' % (R_HEADS + branch.name, dest_branch)
upload_type = 'for'
if draft:
upload_type = 'drafts'
ref_spec = '%s:refs/%s/%s' % (R_HEADS + branch.name, upload_type,
dest_branch)
if auto_topic: if auto_topic:
ref_spec = ref_spec + '/' + branch.name ref_spec = ref_spec + '/' + branch.name
cmd.append(ref_spec) cmd.append(ref_spec)
@ -1556,7 +1564,7 @@ class Project(object):
try: try:
req = urllib2.Request(srcUrl) req = urllib2.Request(srcUrl)
if pos > 0: if pos > 0:
req.add_header('Range', 'bytes=%d-' % pos) req.add_header('Range', 'bytes=%d-' % (pos,))
try: try:
r = urllib2.urlopen(req) r = urllib2.urlopen(req)
@ -1575,7 +1583,7 @@ class Project(object):
msg = e.read() msg = e.read()
if len(msg) > 0 and msg[-1] == '\n': if len(msg) > 0 and msg[-1] == '\n':
msg = msg[0:-1] msg = msg[0:-1]
msg = ' (%s)' % msg msg = ' (%s)' % (msg,)
except: except:
msg = '' msg = ''
else: else:
@ -1593,7 +1601,7 @@ class Project(object):
p = None p = None
try: try:
size = r.headers.get('content-length', 0) size = pos + r.headers.get('content-length', 0)
unit = 1 << 10 unit = 1 << 10
if size and not quiet: if size and not quiet:
@ -1603,7 +1611,7 @@ class Project(object):
else: else:
desc = 'KB' desc = 'KB'
p = Progress( p = Progress(
'Downloading %s' % self.relpath, 'Downloading %s' % (self.relpath,),
int(size) / unit, int(size) / unit,
units=desc) units=desc)
if pos > 0: if pos > 0:
@ -2181,7 +2189,7 @@ class MetaProject(Project):
syncbuf.Finish() syncbuf.Finish()
return GitCommand(self, return GitCommand(self,
['branch', '-D', 'default'], ['update-ref', '-d', 'refs/heads/default'],
capture_stdout = True, capture_stdout = True,
capture_stderr = True).Wait() == 0 capture_stderr = True).Wait() == 0

80
subcmds/overview.py Normal file
View File

@ -0,0 +1,80 @@
#
# Copyright (C) 2012 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.
from color import Coloring
from command import PagedCommand
class Overview(PagedCommand):
common = True
helpSummary = "Display overview of unmerged project branches"
helpUsage = """
%prog [--current-branch] [<project>...]
"""
helpDescription = """
The '%prog' command is used to display an overview of the projects branches,
and list any local commits that have not yet been merged into the project.
The -b/--current-branch option can be used to restrict the output to only
branches currently checked out in each project. By default, all branches
are displayed.
"""
def _Options(self, p):
p.add_option('-b', '--current-branch',
dest="current_branch", action="store_true",
help="Consider only checked out branches")
def Execute(self, opt, args):
all = []
for project in self.GetProjects(args):
br = [project.GetUploadableBranch(x)
for x in project.GetBranches().keys()]
br = [x for x in br if x]
if opt.current_branch:
br = [x for x in br if x.name == project.CurrentBranch]
all.extend(br)
if not all:
return
class Report(Coloring):
def __init__(self, config):
Coloring.__init__(self, config, 'status')
self.project = self.printer('header', attr='bold')
out = Report(all[0].project.config)
out.project('Projects Overview')
out.nl()
project = None
for branch in all:
if project != branch.project:
project = branch.project
out.nl()
out.project('project %s/' % project.relpath)
out.nl()
commits = branch.commits
date = branch.date
print '%s %-33s (%2d commit%s, %s)' % (
branch.name == project.CurrentBranch and '*' or ' ',
branch.name,
len(commits),
len(commits) != 1 and 's' or ' ',
date)
for commit in commits:
print '%-35s - %s' % ('', commit)

View File

@ -134,6 +134,9 @@ Gerrit Code Review: http://code.google.com/p/gerrit/
p.add_option('--cbr', '--current-branch', p.add_option('--cbr', '--current-branch',
dest='current_branch', action='store_true', dest='current_branch', action='store_true',
help='Upload current git branch.') help='Upload current git branch.')
p.add_option('-d', '--draft',
action='store_true', dest='draft', default=False,
help='If specified, upload as a draft.')
# Options relating to upload hook. Note that verify and no-verify are NOT # Options relating to upload hook. Note that verify and no-verify are NOT
# opposites of each other, which is why they store to different locations. # opposites of each other, which is why they store to different locations.
@ -324,7 +327,7 @@ Gerrit Code Review: http://code.google.com/p/gerrit/
key = 'review.%s.uploadtopic' % branch.project.remote.review key = 'review.%s.uploadtopic' % branch.project.remote.review
opt.auto_topic = branch.project.config.GetBoolean(key) opt.auto_topic = branch.project.config.GetBoolean(key)
branch.UploadForReview(people, auto_topic=opt.auto_topic) branch.UploadForReview(people, auto_topic=opt.auto_topic, draft=opt.draft)
branch.uploaded = True branch.uploaded = True
except UploadError, e: except UploadError, e:
branch.error = e branch.error = e