Compare commits

...

10 Commits
v1.0.7 ... v1.1

Author SHA1 Message Date
e284ad1d1a Add 'repo init --mirror' to download a complete forrest
The mirror option downloads a complete forrest (as described by the
manifest) and creates a replica of the remote repositories rather
than a client working directory.  This permits other clients to
sync off the mirror site.

A mirror can be positioned in a "DMZ", where the mirror executes
"repo sync" to obtain changes from the external upstream and
clients inside the protected zone operate off the mirror only,
and therefore do not require direct git:// access to the external
upstream repositories.

Signed-off-by: Shawn O. Pearce <sop@google.com>
2008-11-05 18:08:32 -08:00
3e5481999d Add a basic outline of the repo manifest file format
Signed-off-by: Shawn O. Pearce <sop@google.com>
2008-11-04 11:19:36 -08:00
d3c388391e Update proto_client to notify the user when auth cookies are accessed
Signed-off-by: Shawn O. Pearce <sop@google.com>
2008-11-04 10:49:04 -08:00
2450a2987a Assume the manifest branch matches the branch name in Git
Whatever branch name we checked the manifest out from is the name
we want to reflect throughout the rest of the projects, e.g. within
the special "m/" remote space.

This reduces the difference between different branches within the
manifest file.

Signed-off-by: Shawn O. Pearce <sop@google.com>
2008-11-04 09:45:59 -08:00
f5c25a68d8 Cleanup stale manifest migration code from manifest.py
Prior to open-sourcing repo we had manifests in two different
layouts; one where the manifest was a straight-up git clone, and
one where the manifest was our bare repository with symlink work
tree format (identical to what our projects use).  Only the latter
form is created or used by repo at this point, so the transition
code to handle the straight-up git clone is not necessary.

Signed-off-by: Shawn O. Pearce <sop@google.com>
2008-11-04 09:45:59 -08:00
9fa44db94b Introduce 'repo abandon <branchname>' as an alias for 'git branch -D'
This destroys a local development branch, removing all history
of that branch from ever existing.  If the branch is currently
checked out we move back to the upstream revision.

Signed-off-by: Shawn O. Pearce <sop@google.com>
2008-11-03 11:24:59 -08:00
c9ef744c7b Install a default pre-auto-gc hook in all repositories
This hook is evaluated by `git gc --auto` to determine if it is a
good idea to execute a GC at this time, or defer it to some later
date.  When working on a laptop its a good idea to avoid GC if you
are on battery power as the extra CPU and disk IO would consume a
decent amount of the charge.

The hook is the standard sample hook from git.git contrib/hooks,
last modified in git.git by 84ed4c5d117d72f02cc918e413b9861a9d2846d7.
I added the GPLv2 header to the script to ensure the license notice
is clear, as it does not match repo's own APLv2 license.

We only update hooks during initial repository creation or on
a repo sync.  This way we don't incur huge overheads from the
hook stat operations during "repo status" or even the normal
"repo sync" cases.

Signed-off-by: Shawn O. Pearce <sop@google.com>
2008-11-03 11:00:44 -08:00
438ee1cad9 Catch symlink creation failures and report a better error
Some users have noticed that repo doesn't work on VFAT, as we
require a POSIX filesystem with POSIX symlink support.  Catching the
OSError during our symlink creation and raising a GitError with a
more descriptive message will help users to troubleshoot and fix
their own installation problems.

Signed-off-by: Shawn O. Pearce <sop@google.com>
2008-11-03 09:59:36 -08:00
23d7781c0b Don't print "Already up-to-date" during repo sync
If we are already up-to-date we just want to display no output.
This means we have to avoid calling "git merge" if there aren't
commits to be merged into the working directory.

Signed-off-by: Shawn O. Pearce <sop@google.com>
2008-10-30 11:06:57 -07:00
a54c527ae9 Fast-forward a fully merged topic branch during 'repo sync'
Instead of trying to rebase the changes on a topic branch that
has been fully merged into the upstream branch we track, we should
just fast-forward the topic branch to the new upstream revision.
This way the branch doesn't try to rewrite commits that are already
merged in the upstream.

Signed-off-by: Shawn O. Pearce <sop@google.com>
2008-10-30 11:03:00 -07:00
13 changed files with 472 additions and 77 deletions

View File

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

View File

@ -20,6 +20,7 @@ import md5
import os
import random
import socket
import sys
import time
import urllib
import urllib2
@ -29,6 +30,38 @@ from froofle.protobuf.service import RpcChannel
from froofle.protobuf.service import RpcController
from need_retry_pb2 import RetryRequestLaterResponse;
_cookie_jars = {}
def _open_jar(path):
auth = False
if path is None:
c = cookielib.CookieJar()
else:
c = _cookie_jars.get(path)
if c is None:
c = cookielib.MozillaCookieJar(path)
if os.path.exists(path):
try:
c.load()
auth = True
except (cookielib.LoadError, IOError):
pass
if auth:
print >>sys.stderr, \
'Loaded authentication cookies from %s' \
% path
else:
os.close(os.open(path, os.O_CREAT, 0600))
os.chmod(path, 0600)
_cookie_jars[path] = c
else:
auth = True
return c, auth
class ClientLoginError(urllib2.HTTPError):
"""Raised to indicate an error authenticating with ClientLogin."""
@ -269,6 +302,9 @@ class HttpRpc(RpcChannel):
self._GetAuthCookie(auth_token)
self.authenticated = True
if self.cookie_file is not None:
print >>sys.stderr, \
'Saving authentication cookies to %s' \
% self.cookie_file
self.cookie_jar.save()
return
@ -337,24 +373,8 @@ class HttpRpc(RpcChannel):
opener.add_handler(urllib2.HTTPDefaultErrorHandler())
opener.add_handler(urllib2.HTTPSHandler())
opener.add_handler(urllib2.HTTPErrorProcessor())
if self.cookie_file is not None:
self.cookie_jar = cookielib.MozillaCookieJar(self.cookie_file)
if os.path.exists(self.cookie_file):
try:
self.cookie_jar.load()
self.authenticated = True
except (cookielib.LoadError, IOError):
# Failed to load cookies - just ignore them.
pass
else:
# Create an empty cookie file with mode 600
fd = os.open(self.cookie_file, os.O_CREAT, 0600)
os.close(fd)
# Always chmod the cookie file
os.chmod(self.cookie_file, 0600)
else:
# Don't save cookies across runs of update.py.
self.cookie_jar = cookielib.CookieJar()
self.cookie_jar, \
self.authenticated = _open_jar(self.cookie_file)
opener.add_handler(urllib2.HTTPCookieProcessor(self.cookie_jar))
return opener

126
docs/manifest-format.txt Normal file
View File

@ -0,0 +1,126 @@
repo Manifest Format
====================
A repo manifest describes the structure of a repo client; that is
the directories that are visible and where they should be obtained
from with git.
The basic structure of a manifest is a bare Git repository holding
a single 'default.xml' XML file in the top level directory.
Manifests are inherently version controlled, since they are kept
within a Git repository. Updates to manifests are automatically
obtained by clients during `repo sync`.
XML File Format
---------------
A manifest XML file (e.g. 'default.xml') roughly conforms to the
following DTD:
<!DOCTYPE manifest [
<!ELEMENT manifest (remote*, default?, project*)>
<!ELEMENT remote (EMPTY)>
<!ATTLIST remote name ID #REQUIRED>
<!ATTLIST remote fetch CDATA #REQUIRED>
<!ATTLIST remote review CDATA #IMPLIED>
<!ELEMENT default (EMPTY)>
<!ATTLIST default remote IDREF #IMPLIED>
<!ATTLIST default revision CDATA #IMPLIED>
<!ELEMENT project (remote*)>
<!ATTLIST project name CDATA #REQUIRED>
<!ATTLIST project path CDATA #IMPLIED>
<!ATTLIST project remote IDREF #IMPLIED>
<!ATTLIST project revision CDATA #IMPLIED>
]>
A description of the elements and their attributes follows.
Element manifest
----------------
The root element of the file.
Element remote
--------------
One or more remote elements may be specified. Each remote element
specifies a Git URL shared by one or more projects and (optionally)
the Gerrit review server those projects upload changes through.
Attribute `name`: A short name unique to this manifest file. The
name specified here is used as the remote name in each project's
.git/config, and is therefore automatically available to commands
like `git fetch`, `git remote`, `git pull` and `git push`.
Attribute `fetch`: The Git URL prefix for all projects which use
this remote. Each project's name is appended to this prefix to
form the actual URL used to clone the project.
Attribute `review`: Hostname of the Gerrit server where reviews
are uploaded to by `repo upload`. This attribute is optional;
if not specified then `repo upload` will not function.
Element default
---------------
At most one default element may be specified. Its remote and
revision attributes are used when a project element does not
specify its own remote or revision attribute.
Attribute `remote`: Name of a previously defined remote element.
Project elements lacking a remote attribute of their own will use
this remote.
Attribute `revision`: Name of a Git branch (e.g. `master` or
`refs/heads/master`). Project elements lacking their own
revision attribute will use this revision.
Element project
---------------
One or more project elements may be specified. Each element
describes a single Git repository to be cloned into the repo
client workspace.
Attribute `name`: A unique name for this project. The project's
name is appended onto its remote's fetch URL to generate the actual
URL to configure the Git remote with. The URL gets formed as:
${remote_fetch}/${project_name}.git
where ${remote_fetch} is the remote's fetch attribute and
${project_name} is the project's name attribute. The suffix ".git"
is always appended as repo assumes the upstream is a forrest of
bare Git repositories.
The project name must match the name Gerrit knows, if Gerrit is
being used for code reviews.
Attribute `path`: An optional path relative to the top directory
of the repo client where the Git working directory for this project
should be placed. If not supplied the project name is used.
Attribute `remote`: Name of a previously defined remote element.
If not supplied the remote given by the default element is used.
Attribute `revision`: Name of the Git branch the manifest wants
to track for this project. Names can be relative to refs/heads
(e.g. just "master") or absolute (e.g. "refs/heads/master").
Tags and/or explicit SHA-1s should work in theory, but have not
been extensively tested. If not supplied the revision given by
the default element is used.
Child element `remote`: Described like the top-level remote element,
but adds an additional remote to only this project. These additional
remotes are fetched from first on the initial `repo sync`, causing
the majority of the project's object database to be obtained through
these additional remotes.

View File

@ -64,3 +64,5 @@ class RepoChangedException(Exception):
repo or manifest repositories. In this special case we must
use exec to re-execute repo with the new code and manifest.
"""
def __init__(self, extra_args=[]):
self.extra_args = extra_args

View File

@ -285,12 +285,14 @@ class Remote(object):
return True
return False
def ResetFetch(self):
def ResetFetch(self, mirror=False):
"""Set the fetch refspec to its default value.
"""
self.fetch = [RefSpec(True,
'refs/heads/*',
'refs/remotes/%s/*' % self.name)]
if mirror:
dst = 'refs/heads/*'
else:
dst = 'refs/remotes/%s/*' % self.name
self.fetch = [RefSpec(True, 'refs/heads/*', dst)]
def Save(self):
"""Save this remote to the configuration.

44
hooks/pre-auto-gc Executable file
View File

@ -0,0 +1,44 @@
#!/bin/sh
#
# An example hook script to verify if you are on battery, in case you
# are running Linux or OS X. Called by git-gc --auto with no arguments.
# The hook should exit with non-zero status after issuing an appropriate
# message if it wants to stop the auto repacking.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
if test -x /sbin/on_ac_power && /sbin/on_ac_power
then
exit 0
elif test "$(cat /sys/class/power_supply/AC/online 2>/dev/null)" = 1
then
exit 0
elif grep -q 'on-line' /proc/acpi/ac_adapter/AC/state 2>/dev/null
then
exit 0
elif grep -q '0x01$' /proc/apm 2>/dev/null
then
exit 0
elif grep -q "AC Power \+: 1" /proc/pmu/info 2>/dev/null
then
exit 0
elif test -x /usr/bin/pmset && /usr/bin/pmset -g batt |
grep -q "Currently drawing from 'AC Power'"
then
exit 0
fi
echo "Auto packing deferred; not on AC"
exit 1

View File

@ -186,11 +186,13 @@ def _Main(argv):
repo._Run(argv)
except KeyboardInterrupt:
sys.exit(1)
except RepoChangedException:
# If the repo or manifest changed, re-exec ourselves.
except RepoChangedException, rce:
# If repo changed, re-exec ourselves.
#
argv = list(sys.argv)
argv.extend(rce.extra_args)
try:
os.execv(__file__, sys.argv)
os.execv(__file__, argv)
except OSError, e:
print >>sys.stderr, 'fatal: cannot restart repo after upgrade'
print >>sys.stderr, 'fatal: %s' % e

View File

@ -18,7 +18,7 @@ import sys
import xml.dom.minidom
from git_config import GitConfig, IsId
from project import Project, MetaProject, R_TAGS
from project import Project, MetaProject, R_HEADS
from remote import Remote
from error import ManifestParseError
@ -45,16 +45,9 @@ class Manifest(object):
gitdir = os.path.join(repodir, 'repo/.git'),
worktree = os.path.join(repodir, 'repo'))
wt = os.path.join(repodir, 'manifests')
gd_new = os.path.join(repodir, 'manifests.git')
gd_old = os.path.join(wt, '.git')
if os.path.exists(gd_new) or not os.path.exists(gd_old):
gd = gd_new
else:
gd = gd_old
self.manifestProject = MetaProject(self, 'manifests',
gitdir = gd,
worktree = wt)
gitdir = os.path.join(repodir, 'manifests.git'),
worktree = os.path.join(repodir, 'manifests'))
self._Unload()
@ -95,6 +88,10 @@ class Manifest(object):
self._Load()
return self._default
@property
def IsMirror(self):
return self.manifestProject.config.GetBoolean('repo.mirror')
def _Unload(self):
self._loaded = False
self._projects = {}
@ -104,6 +101,12 @@ class Manifest(object):
def _Load(self):
if not self._loaded:
m = self.manifestProject
b = m.GetBranch(m.CurrentBranch).merge
if b.startswith(R_HEADS):
b = b[len(R_HEADS):]
self.branch = b
self._ParseManifest(True)
local = os.path.join(self.repodir, LOCAL_MANIFEST_NAME)
@ -115,6 +118,10 @@ class Manifest(object):
finally:
self.manifestFile = real
if self.IsMirror:
self._AddMetaProjectMirror(self.repoProject)
self._AddMetaProjectMirror(self.manifestProject)
self._loaded = True
def _ParseManifest(self, is_root_file):
@ -130,11 +137,6 @@ class Manifest(object):
"no <manifest> in %s" % \
self.manifestFile
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':
remote = self._ParseRemote(node)
@ -163,6 +165,40 @@ class Manifest(object):
(project.name, self.manifestFile)
self._projects[project.name] = project
def _AddMetaProjectMirror(self, m):
name = None
m_url = m.GetRemote(m.remote.name).url
if m_url.endswith('/.git'):
raise ManifestParseError, 'refusing to mirror %s' % m_url
if self._default and self._default.remote:
url = self._default.remote.fetchUrl
if not url.endswith('/'):
url += '/'
if m_url.startswith(url):
remote = self._default.remote
name = m_url[len(url):]
if name is None:
s = m_url.rindex('/') + 1
remote = Remote('origin', fetch = m_url[:s])
name = m_url[s:]
if name.endswith('.git'):
name = name[:-4]
if name not in self._projects:
m.PreSync()
gitdir = os.path.join(self.topdir, '%s.git' % name)
project = Project(manifest = self,
name = name,
remote = remote,
gitdir = gitdir,
worktree = None,
relpath = None,
revision = m.revision)
self._projects[project.name] = project
def _ParseRemote(self, node):
"""
reads a <remote> element from the manifest file
@ -220,6 +256,11 @@ class Manifest(object):
"project %s path cannot be absolute in %s" % \
(name, self.manifestFile)
if self.IsMirror:
relpath = None
worktree = None
gitdir = os.path.join(self.topdir, '%s.git' % name)
else:
worktree = os.path.join(self.topdir, path)
gitdir = os.path.join(self.repodir, 'projects/%s.git' % path)
@ -248,7 +289,9 @@ class Manifest(object):
def _ParseCopyFile(self, project, node):
src = self._reqatt(node, 'src')
dest = self._reqatt(node, 'dest')
# src is project relative, and dest is relative to the top of the tree
if not self.IsMirror:
# src is project relative;
# dest is relative to the top of the tree
project.AddCopyFile(src, os.path.join(self.topdir, dest))
def _get_remote(self, node):

View File

@ -12,6 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
import errno
import filecmp
import os
import re
@ -45,6 +46,32 @@ def _info(fmt, *args):
def not_rev(r):
return '^' + r
hook_list = None
def repo_hooks():
global hook_list
if hook_list is None:
d = os.path.abspath(os.path.dirname(__file__))
d = os.path.join(d , 'hooks')
hook_list = map(lambda x: os.path.join(d, x), os.listdir(d))
return hook_list
def relpath(dst, src):
src = os.path.dirname(src)
top = os.path.commonprefix([dst, src])
if top.endswith('/'):
top = top[:-1]
else:
top = os.path.dirname(top)
tmp = src
rel = ''
while top != tmp:
rel += '../'
tmp = os.path.dirname(tmp)
return rel + dst[len(top) + 1:]
class DownloadedChange(object):
_commit_cache = None
@ -184,7 +211,10 @@ class Project(object):
gitdir = self.gitdir,
defaults = self.manifest.globalConfig)
if self.worktree:
self.work_git = self._GitGetByExec(self, bare=False)
else:
self.work_git = None
self.bare_git = self._GitGetByExec(self, bare=True)
@property
@ -462,16 +492,28 @@ class Project(object):
print >>sys.stderr
print >>sys.stderr, 'Initializing project %s ...' % self.name
self._InitGitDir()
self._InitRemote()
for r in self.extraRemotes.values():
if not self._RemoteFetch(r.name):
return False
if not self._RemoteFetch():
return False
if self.worktree:
self._RepairAndroidImportErrors()
self._InitMRef()
else:
self._InitMirrorHead()
try:
os.remove(os.path.join(self.gitdir, 'FETCH_HEAD'))
except OSError:
pass
return True
def PostRepoUpgrade(self):
self._InitHooks()
def _CopyFiles(self):
for file in self.copyfiles:
file._Copy()
@ -563,6 +605,19 @@ class Project(object):
_info("[%s] Consider merging or rebasing the"
" unpublished commits.", self.name)
return True
elif upstream_gain:
# We can fast-forward safely.
#
try:
self._FastForward(rev)
except GitError:
return False
self._CopyFiles()
return True
else:
# Trivially no changes in the upstream.
#
return True
if merge == rev:
try:
@ -667,6 +722,22 @@ class Project(object):
else:
raise GitError('%s checkout %s ' % (self.name, rev))
def AbandonBranch(self, name):
"""Destroy a local topic branch.
"""
try:
tip_rev = self.bare_git.rev_parse(R_HEADS + name)
except GitError:
return
if self.CurrentBranch == name:
self._Checkout(
self.GetRemote(self.remote.name).ToLocal(self.revision),
quiet=True)
cmd = ['branch', '-D', name]
GitCommand(self, cmd, capture_stdout=True).Wait()
def PruneHeads(self):
"""Prune any topic branches already merged into upstream.
"""
@ -733,9 +804,11 @@ class Project(object):
def _RemoteFetch(self, name=None):
if not name:
name = self.remote.name
return GitCommand(self,
['fetch', name],
bare = True).Wait() == 0
cmd = ['fetch']
if not self.worktree:
cmd.append('--update-head-ok')
cmd.append(name)
return GitCommand(self, cmd, bare = True).Wait() == 0
def _Checkout(self, rev, quiet=False):
cmd = ['checkout']
@ -781,14 +854,29 @@ class Project(object):
to_rm = []
for old_hook in to_rm:
os.remove(os.path.join(hooks, old_hook))
# TODO(sop) install custom repo hooks
self._InitHooks()
m = self.manifest.manifestProject.config
for key in ['user.name', 'user.email']:
if m.Has(key, include_defaults = False):
self.config.SetString(key, m.GetString(key))
def _InitHooks(self):
hooks = self._gitdir_path('hooks')
if not os.path.exists(hooks):
os.makedirs(hooks)
for stock_hook in repo_hooks():
dst = os.path.join(hooks, os.path.basename(stock_hook))
try:
os.symlink(relpath(stock_hook, dst), dst)
except OSError, e:
if e.errno == errno.EEXIST:
pass
elif e.errno == errno.EPERM:
raise GitError('filesystem must support symlinks')
else:
raise
def _InitRemote(self):
if self.remote.fetchUrl:
remote = self.GetRemote(self.remote.name)
@ -800,7 +888,10 @@ class Project(object):
remote.url = url
remote.review = self.remote.reviewUrl
remote.ResetFetch()
if self.worktree:
remote.ResetFetch(mirror=False)
else:
remote.ResetFetch(mirror=True)
remote.Save()
for r in self.extraRemotes.values():
@ -823,24 +914,16 @@ class Project(object):
dst = remote.ToLocal(self.revision)
self.bare_git.symbolic_ref('-m', msg, ref, dst)
def _InitMirrorHead(self):
dst = self.GetRemote(self.remote.name).ToLocal(self.revision)
msg = 'manifest set to %s' % self.revision
self.bare_git.SetHead(dst, message=msg)
def _InitWorkTree(self):
dotgit = os.path.join(self.worktree, '.git')
if not os.path.exists(dotgit):
os.makedirs(dotgit)
topdir = os.path.commonprefix([self.gitdir, dotgit])
if topdir.endswith('/'):
topdir = topdir[:-1]
else:
topdir = os.path.dirname(topdir)
tmpdir = dotgit
relgit = ''
while topdir != tmpdir:
relgit += '../'
tmpdir = os.path.dirname(tmpdir)
relgit += self.gitdir[len(topdir) + 1:]
for name in ['config',
'description',
'hooks',
@ -851,8 +934,15 @@ class Project(object):
'refs',
'rr-cache',
'svn']:
os.symlink(os.path.join(relgit, name),
os.path.join(dotgit, name))
try:
src = os.path.join(self.gitdir, name)
dst = os.path.join(dotgit, name)
os.symlink(relpath(src, dst), dst)
except OSError, e:
if e.errno == errno.EPERM:
raise GitError('filesystem must support symlinks')
else:
raise
rev = self.GetRemote(self.remote.name).ToLocal(self.revision)
rev = self.bare_git.rev_parse('%s^0' % rev)

5
repo
View File

@ -28,7 +28,7 @@ if __name__ == '__main__':
del magic
# increment this whenever we make important changes to this script
VERSION = (1, 6)
VERSION = (1, 7)
# increment this if the MAINTAINER_KEYS block is modified
KEYRING_VERSION = (1,0)
@ -115,6 +115,9 @@ group.add_option('-b', '--manifest-branch',
group.add_option('-m', '--manifest-name',
dest='manifest_name',
help='initial manifest file', metavar='NAME.xml')
group.add_option('--mirror',
dest='mirror', action='store_true',
help='mirror the forrest')
# Tool
group = init_optparse.add_option_group('Version options')

42
subcmds/abandon.py Normal file
View File

@ -0,0 +1,42 @@
#
# 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 sys
from command import Command
from git_command import git
class Abandon(Command):
common = True
helpSummary = "Permanently abandon a development branch"
helpUsage = """
%prog <branchname> [<project>...]
This subcommand permanently abandons a development branch by
deleting it (and all its history) from your local repository.
It is equivalent to "git branch -D <branchname>".
"""
def Execute(self, opt, args):
if not args:
self.Usage()
nb = args[0]
if not git.check_ref_format('heads/%s' % nb):
print >>sys.stderr, "error: '%s' is not a valid name" % nb
sys.exit(1)
for project in self.GetProjects(args[1:]):
project.AbandonBranch(nb)

View File

@ -57,6 +57,10 @@ default.xml will be used.
g.add_option('-m', '--manifest-name',
dest='manifest_name', default='default.xml',
help='initial manifest file', metavar='NAME.xml')
g.add_option('--mirror',
dest='mirror', action='store_true',
help='mirror the forrest')
# Tool
g = p.add_option_group('Version options')
@ -112,6 +116,9 @@ default.xml will be used.
r.ResetFetch()
r.Save()
if opt.mirror:
m.config.SetString('repo.mirror', 'true')
m.Sync_NetworkHalf()
m.Sync_LocalHalf()
m.StartBranch('default')
@ -185,9 +192,14 @@ default.xml will be used.
self._SyncManifest(opt)
self._LinkManifest(opt.manifest_name)
if os.isatty(0) and os.isatty(1):
if os.isatty(0) and os.isatty(1) and not opt.mirror:
self._ConfigureUser()
self._ConfigureColor()
if opt.mirror:
type = 'mirror '
else:
type = ''
print ''
print 'repo initialized in %s' % self.manifest.topdir
print 'repo %sinitialized in %s' % (type, self.manifest.topdir)

View File

@ -49,6 +49,9 @@ the manifest.
p.add_option('--no-repo-verify',
dest='no_repo_verify', action='store_true',
help='do not verify repo source code')
p.add_option('--repo-upgraded',
dest='repo_upgraded', action='store_true',
help='perform additional actions after a repo upgrade')
def _Fetch(self, *projects):
fetched = set()
@ -67,6 +70,11 @@ the manifest.
mp = self.manifest.manifestProject
mp.PreSync()
if opt.repo_upgraded:
for project in self.manifest.projects.values():
if project.Exists:
project.PostRepoUpgrade()
all = self.GetProjects(args, missing_ok=True)
fetched = self._Fetch(rp, mp, *all)
@ -77,7 +85,7 @@ the manifest.
if not rp.Sync_LocalHalf():
sys.exit(1)
print >>sys.stderr, 'info: Restarting repo with latest version'
raise RepoChangedException()
raise RepoChangedException(['--repo-upgraded'])
else:
print >>sys.stderr, 'warning: Skipped upgrade to unverified version'
@ -94,6 +102,7 @@ the manifest.
self._Fetch(*missing)
for project in all:
if project.worktree:
if not project.Sync_LocalHalf():
sys.exit(1)