mirror of
https://gerrit.googlesource.com/git-repo
synced 2025-06-26 20:17:52 +00:00
Compare commits
10 Commits
Author | SHA1 | Date | |
---|---|---|---|
e284ad1d1a | |||
3e5481999d | |||
d3c388391e | |||
2450a2987a | |||
f5c25a68d8 | |||
9fa44db94b | |||
c9ef744c7b | |||
438ee1cad9 | |||
23d7781c0b | |||
a54c527ae9 |
@ -1 +1 @@
|
||||
__version__ = 'v1.0-14-gc4f226bc'
|
||||
__version__ = 'v1.0-69-gd1f8508c'
|
||||
|
@ -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
126
docs/manifest-format.txt
Normal 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.
|
2
error.py
2
error.py
@ -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
|
||||
|
@ -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
44
hooks/pre-auto-gc
Executable 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
|
8
main.py
8
main.py
@ -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
|
||||
|
81
manifest.py
81
manifest.py
@ -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,8 +256,13 @@ class Manifest(object):
|
||||
"project %s path cannot be absolute in %s" % \
|
||||
(name, self.manifestFile)
|
||||
|
||||
worktree = os.path.join(self.topdir, path)
|
||||
gitdir = os.path.join(self.repodir, 'projects/%s.git' % path)
|
||||
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)
|
||||
|
||||
project = Project(manifest = self,
|
||||
name = name,
|
||||
@ -248,8 +289,10 @@ 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
|
||||
project.AddCopyFile(src, os.path.join(self.topdir, dest))
|
||||
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):
|
||||
name = node.getAttribute('remote')
|
||||
|
140
project.py
140
project.py
@ -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)
|
||||
|
||||
self.work_git = self._GitGetByExec(self, bare=False)
|
||||
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
|
||||
self._RepairAndroidImportErrors()
|
||||
self._InitMRef()
|
||||
|
||||
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
5
repo
@ -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
42
subcmds/abandon.py
Normal 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)
|
@ -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)
|
||||
|
@ -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,8 +102,9 @@ the manifest.
|
||||
self._Fetch(*missing)
|
||||
|
||||
for project in all:
|
||||
if not project.Sync_LocalHalf():
|
||||
sys.exit(1)
|
||||
if project.worktree:
|
||||
if not project.Sync_LocalHalf():
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def _VerifyTag(project):
|
||||
|
Reference in New Issue
Block a user