mirror of
https://gerrit.googlesource.com/git-repo
synced 2025-06-28 20:17:26 +00:00
Compare commits
67 Commits
Author | SHA1 | Date | |
---|---|---|---|
34acdd2534 | |||
d94aaef39e | |||
bd489c4eaa | |||
2dc810c2e4 | |||
bb1b5f5f86 | |||
e2126652a3 | |||
9a27d0111d | |||
918ff85c1e | |||
3d07da82ab | |||
e15c65abc2 | |||
daa851f6cd | |||
a43f42f9ff | |||
bb8337fe0f | |||
17f85eab24 | |||
b9477bc2dd | |||
5e7127d00b | |||
5d0efdb14a | |||
f35b2d9c31 | |||
e0904f721b | |||
9830553748 | |||
2bc7f5cb3a | |||
b292b98c3e | |||
2f127de752 | |||
7da1314e38 | |||
435370c6f0 | |||
e8f75fa368 | |||
87636f2ac2 | |||
e7a3bcbbb8 | |||
25b51d8cb7 | |||
cef005c3e8 | |||
71cab95b4c | |||
9275fd4329 | |||
13f3da50d4 | |||
3218c13205 | |||
b0f9a02394 | |||
69b1e8aa65 | |||
840ed0fab7 | |||
c024912fb8 | |||
15f6579eb3 | |||
d4cd69bdef | |||
d2dfac81ad | |||
4719901067 | |||
a949fa5d20 | |||
0afac0856c | |||
4c0f670465 | |||
33f0e786bb | |||
57272ba82e | |||
0125ae2fda | |||
a7ce096047 | |||
87bda12e85 | |||
5f947bba69 | |||
b3d2c9214b | |||
7354d88914 | |||
ce86abbe8a | |||
75b87c8a51 | |||
abb7a3dfec | |||
cc6c79643e | |||
2095179bee | |||
b0ca41e19a | |||
1875ddd47c | |||
446c4e5556 | |||
67f4563acb | |||
050e4fd591 | |||
60e679209a | |||
f1a6b14fdc | |||
ca3d8ff4fc | |||
98ea26b8d8 |
@ -5,7 +5,7 @@ Short Version:
|
|||||||
- Make sure all code is under the Apache License, 2.0.
|
- Make sure all code is under the Apache License, 2.0.
|
||||||
- Publish your changes for review:
|
- Publish your changes for review:
|
||||||
|
|
||||||
git push https://gerrit-review.googlesource.com/git-repo HEAD:refs/for/maint
|
git push https://gerrit-review.googlesource.com/git-repo HEAD:refs/for/master
|
||||||
|
|
||||||
|
|
||||||
Long Version:
|
Long Version:
|
||||||
@ -71,7 +71,7 @@ Push your patches over HTTPS to the review server, possibly through
|
|||||||
a remembered remote to make this easier in the future:
|
a remembered remote to make this easier in the future:
|
||||||
|
|
||||||
git config remote.review.url https://gerrit-review.googlesource.com/git-repo
|
git config remote.review.url https://gerrit-review.googlesource.com/git-repo
|
||||||
git config remote.review.push HEAD:refs/for/maint
|
git config remote.review.push HEAD:refs/for/master
|
||||||
|
|
||||||
git push review
|
git push review
|
||||||
|
|
||||||
|
1
color.py
1
color.py
@ -17,7 +17,6 @@ import os
|
|||||||
import sys
|
import sys
|
||||||
|
|
||||||
import pager
|
import pager
|
||||||
from git_config import GitConfig
|
|
||||||
|
|
||||||
COLORS = {None :-1,
|
COLORS = {None :-1,
|
||||||
'normal' :-1,
|
'normal' :-1,
|
||||||
|
@ -70,7 +70,7 @@ class Command(object):
|
|||||||
|
|
||||||
groups = mp.config.GetString('manifest.groups')
|
groups = mp.config.GetString('manifest.groups')
|
||||||
if not groups:
|
if not groups:
|
||||||
groups = 'default,platform-' + platform.system().lower()
|
groups = 'all,-notdefault,platform-' + platform.system().lower()
|
||||||
groups = [x for x in re.split('[,\s]+', groups) if x]
|
groups = [x for x in re.split('[,\s]+', groups) if x]
|
||||||
|
|
||||||
if not args:
|
if not args:
|
||||||
|
@ -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.
|
||||||
@ -118,21 +125,27 @@ Element manifest-server
|
|||||||
|
|
||||||
At most one manifest-server may be specified. The url attribute
|
At most one manifest-server may be specified. The url attribute
|
||||||
is used to specify the URL of a manifest server, which is an
|
is used to specify the URL of a manifest server, which is an
|
||||||
XML RPC service that will return a manifest in which each project
|
XML RPC service.
|
||||||
is pegged to a known good revision for the current branch and
|
|
||||||
target.
|
|
||||||
|
|
||||||
The manifest server should implement:
|
The manifest server should implement the following RPC methods:
|
||||||
|
|
||||||
GetApprovedManifest(branch, target)
|
GetApprovedManifest(branch, target)
|
||||||
|
|
||||||
|
Return a manifest in which each project is pegged to a known good revision
|
||||||
|
for the current branch and target.
|
||||||
|
|
||||||
The target to use is defined by environment variables TARGET_PRODUCT
|
The target to use is defined by environment variables TARGET_PRODUCT
|
||||||
and TARGET_BUILD_VARIANT. These variables are used to create a string
|
and TARGET_BUILD_VARIANT. These variables are used to create a string
|
||||||
of the form $TARGET_PRODUCT-$TARGET_BUILD_VARIANT, e.g. passion-userdebug.
|
of the form $TARGET_PRODUCT-$TARGET_BUILD_VARIANT, e.g. passion-userdebug.
|
||||||
If one of those variables or both are not present, the program will call
|
If one of those variables or both are not present, the program will call
|
||||||
GetApprovedManifest without the target paramater and the manifest server
|
GetApprovedManifest without the target parameter and the manifest server
|
||||||
should choose a reasonable default target.
|
should choose a reasonable default target.
|
||||||
|
|
||||||
|
GetManifest(tag)
|
||||||
|
|
||||||
|
Return a manifest in which each project is pegged to the revision at
|
||||||
|
the specified tag.
|
||||||
|
|
||||||
|
|
||||||
Element project
|
Element project
|
||||||
---------------
|
---------------
|
||||||
@ -149,7 +162,7 @@ URL to configure the Git remote with. The URL gets formed as:
|
|||||||
|
|
||||||
where ${remote_fetch} is the remote's fetch attribute and
|
where ${remote_fetch} is the remote's fetch attribute and
|
||||||
${project_name} is the project's name attribute. The suffix ".git"
|
${project_name} is the project's name attribute. The suffix ".git"
|
||||||
is always appended as repo assumes the upstream is a forrest of
|
is always appended as repo assumes the upstream is a forest of
|
||||||
bare Git repositories.
|
bare Git repositories.
|
||||||
|
|
||||||
The project name must match the name Gerrit knows, if Gerrit is
|
The project name must match the name Gerrit knows, if Gerrit is
|
||||||
@ -171,7 +184,12 @@ 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".
|
"all", and each project automatically belongs to a group of
|
||||||
|
its 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. If you place a project in the
|
||||||
|
group "notdefault", it will not be automatically downloaded by repo.
|
||||||
|
|
||||||
Element annotation
|
Element annotation
|
||||||
------------------
|
------------------
|
||||||
|
@ -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
|
||||||
|
@ -14,7 +14,6 @@
|
|||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import sys
|
|
||||||
from trace import Trace
|
from trace import Trace
|
||||||
|
|
||||||
HEAD = 'HEAD'
|
HEAD = 'HEAD'
|
||||||
|
1
main.py
1
main.py
@ -35,7 +35,6 @@ from git_command import git, GitCommand
|
|||||||
from git_config import init_ssh, close_ssh
|
from git_config import init_ssh, close_ssh
|
||||||
from command import InteractiveCommand
|
from command import InteractiveCommand
|
||||||
from command import MirrorSafeCommand
|
from command import MirrorSafeCommand
|
||||||
from command import PagedCommand
|
|
||||||
from subcmds.version import Version
|
from subcmds.version import Version
|
||||||
from editor import Editor
|
from editor import Editor
|
||||||
from error import DownloadError
|
from error import DownloadError
|
||||||
|
@ -20,7 +20,7 @@ import sys
|
|||||||
import urlparse
|
import urlparse
|
||||||
import xml.dom.minidom
|
import xml.dom.minidom
|
||||||
|
|
||||||
from git_config import GitConfig, IsId
|
from git_config import GitConfig
|
||||||
from project import RemoteSpec, Project, MetaProject, R_HEADS, HEAD
|
from project import RemoteSpec, Project, MetaProject, R_HEADS, HEAD
|
||||||
from error import ManifestParseError
|
from error import ManifestParseError
|
||||||
|
|
||||||
@ -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"""
|
||||||
@ -125,7 +130,7 @@ class XmlManifest(object):
|
|||||||
|
|
||||||
groups = mp.config.GetString('manifest.groups')
|
groups = mp.config.GetString('manifest.groups')
|
||||||
if not groups:
|
if not groups:
|
||||||
groups = 'default'
|
groups = 'all'
|
||||||
groups = [x for x in re.split(r'[,\s]+', groups) if x]
|
groups = [x for x in re.split(r'[,\s]+', groups) if x]
|
||||||
|
|
||||||
doc = xml.dom.minidom.Document()
|
doc = xml.dom.minidom.Document()
|
||||||
@ -206,7 +211,8 @@ class XmlManifest(object):
|
|||||||
ce.setAttribute('dest', c.dest)
|
ce.setAttribute('dest', c.dest)
|
||||||
e.appendChild(ce)
|
e.appendChild(ce)
|
||||||
|
|
||||||
egroups = [g for g in p.groups if g != 'default']
|
default_groups = ['all', 'name:%s' % p.name, 'path:%s' % p.relpath]
|
||||||
|
egroups = [g for g in p.groups if g not in default_groups]
|
||||||
if egroups:
|
if egroups:
|
||||||
e.setAttribute('groups', ','.join(egroups))
|
e.setAttribute('groups', ','.join(egroups))
|
||||||
|
|
||||||
@ -303,12 +309,14 @@ class XmlManifest(object):
|
|||||||
if not root or not root.childNodes:
|
if not root or not root.childNodes:
|
||||||
raise ManifestParseError("no root node in %s" % (path,))
|
raise ManifestParseError("no root node in %s" % (path,))
|
||||||
|
|
||||||
config = root.childNodes[0]
|
for manifest in root.childNodes:
|
||||||
if config.nodeName != 'manifest':
|
if manifest.nodeName == 'manifest':
|
||||||
|
break
|
||||||
|
else:
|
||||||
raise ManifestParseError("no <manifest> in %s" % (path,))
|
raise ManifestParseError("no <manifest> in %s" % (path,))
|
||||||
|
|
||||||
nodes = []
|
nodes = []
|
||||||
for node in config.childNodes:
|
for node in manifest.childNodes:
|
||||||
if node.nodeName == 'include':
|
if node.nodeName == 'include':
|
||||||
name = self._reqatt(node, 'name')
|
name = self._reqatt(node, 'name')
|
||||||
fp = os.path.join(include_root, name)
|
fp = os.path.join(include_root, name)
|
||||||
@ -427,7 +435,7 @@ class XmlManifest(object):
|
|||||||
if name is None:
|
if name is None:
|
||||||
s = m_url.rindex('/') + 1
|
s = m_url.rindex('/') + 1
|
||||||
manifestUrl = self.manifestProject.config.GetString('remote.origin.url')
|
manifestUrl = self.manifestProject.config.GetString('remote.origin.url')
|
||||||
remote = _XmlRemote('origin', m_url[:s], manifestUrl)
|
remote = _XmlRemote('origin', fetch=m_url[:s], manifestUrl=manifestUrl)
|
||||||
name = m_url[s:]
|
name = m_url[s:]
|
||||||
|
|
||||||
if name.endswith('.git'):
|
if name.endswith('.git'):
|
||||||
@ -451,12 +459,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 +577,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
|
||||||
|
202
project.py
202
project.py
@ -20,32 +20,19 @@ import random
|
|||||||
import re
|
import re
|
||||||
import shutil
|
import shutil
|
||||||
import stat
|
import stat
|
||||||
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
import time
|
import time
|
||||||
import urllib2
|
|
||||||
|
|
||||||
try:
|
|
||||||
import threading as _threading
|
|
||||||
except ImportError:
|
|
||||||
import dummy_threading as _threading
|
|
||||||
|
|
||||||
try:
|
|
||||||
from os import SEEK_END
|
|
||||||
except ImportError:
|
|
||||||
SEEK_END = 2
|
|
||||||
|
|
||||||
from color import Coloring
|
from color import Coloring
|
||||||
from git_command import GitCommand
|
from git_command import GitCommand
|
||||||
from git_config import GitConfig, IsId, GetSchemeFromUrl, ID_RE
|
from git_config import GitConfig, IsId, GetSchemeFromUrl, ID_RE
|
||||||
from error import DownloadError
|
from error import GitError, HookError, UploadError
|
||||||
from error import GitError, HookError, ImportError, UploadError
|
|
||||||
from error import ManifestInvalidRevisionError
|
from error import ManifestInvalidRevisionError
|
||||||
from progress import Progress
|
from trace import IsTrace, Trace
|
||||||
|
|
||||||
from git_refs import GitRefs, HEAD, R_HEADS, R_TAGS, R_PUB, R_M
|
from git_refs import GitRefs, HEAD, R_HEADS, R_TAGS, R_PUB, R_M
|
||||||
|
|
||||||
_urllib_lock = _threading.Lock()
|
|
||||||
|
|
||||||
def _lwrite(path, content):
|
def _lwrite(path, content):
|
||||||
lock = '%s.lock' % path
|
lock = '%s.lock' % path
|
||||||
|
|
||||||
@ -91,21 +78,6 @@ def _ProjectHooks():
|
|||||||
_project_hook_list = map(lambda x: os.path.join(d, x), os.listdir(d))
|
_project_hook_list = map(lambda x: os.path.join(d, x), os.listdir(d))
|
||||||
return _project_hook_list
|
return _project_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):
|
class DownloadedChange(object):
|
||||||
_commit_cache = None
|
_commit_cache = None
|
||||||
@ -176,10 +148,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 = {}
|
||||||
@ -659,20 +632,21 @@ class Project(object):
|
|||||||
"""Returns true if the manifest groups specified at init should cause
|
"""Returns true if the manifest groups specified at init should cause
|
||||||
this project to be synced.
|
this project to be synced.
|
||||||
Prefixing a manifest group with "-" inverts the meaning of a group.
|
Prefixing a manifest group with "-" inverts the meaning of a group.
|
||||||
All projects are implicitly labelled with "default".
|
All projects are implicitly labelled with "all".
|
||||||
|
|
||||||
labels are resolved in order. In the example case of
|
labels are resolved in order. In the example case of
|
||||||
project_groups: "default,group1,group2"
|
project_groups: "all,group1,group2"
|
||||||
manifest_groups: "-group1,group2"
|
manifest_groups: "-group1,group2"
|
||||||
the project will be matched.
|
the project will be matched.
|
||||||
"""
|
"""
|
||||||
if self.groups is None:
|
expanded_manifest_groups = manifest_groups or ['all', '-notdefault']
|
||||||
return True
|
expanded_project_groups = ['all'] + (self.groups or [])
|
||||||
|
|
||||||
matched = False
|
matched = False
|
||||||
for group in manifest_groups:
|
for group in expanded_manifest_groups:
|
||||||
if group.startswith('-') and group[1:] in self.groups:
|
if group.startswith('-') and group[1:] in expanded_project_groups:
|
||||||
matched = False
|
matched = False
|
||||||
elif group in self.groups:
|
elif group in expanded_project_groups:
|
||||||
matched = True
|
matched = True
|
||||||
|
|
||||||
return matched
|
return matched
|
||||||
@ -881,7 +855,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 +895,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)
|
||||||
@ -1545,100 +1526,49 @@ class Project(object):
|
|||||||
return ok
|
return ok
|
||||||
|
|
||||||
def _FetchBundle(self, srcUrl, tmpPath, dstPath, quiet):
|
def _FetchBundle(self, srcUrl, tmpPath, dstPath, quiet):
|
||||||
keep = True
|
|
||||||
done = False
|
|
||||||
dest = open(tmpPath, 'a+b')
|
|
||||||
try:
|
|
||||||
dest.seek(0, SEEK_END)
|
|
||||||
pos = dest.tell()
|
|
||||||
|
|
||||||
_urllib_lock.acquire()
|
|
||||||
try:
|
|
||||||
req = urllib2.Request(srcUrl)
|
|
||||||
if pos > 0:
|
|
||||||
req.add_header('Range', 'bytes=%d-' % pos)
|
|
||||||
|
|
||||||
try:
|
|
||||||
r = urllib2.urlopen(req)
|
|
||||||
except urllib2.HTTPError, e:
|
|
||||||
def _content_type():
|
|
||||||
try:
|
|
||||||
return e.info()['content-type']
|
|
||||||
except:
|
|
||||||
return None
|
|
||||||
|
|
||||||
if e.code in (401, 403, 404):
|
|
||||||
keep = False
|
|
||||||
return False
|
|
||||||
elif _content_type() == 'text/plain':
|
|
||||||
try:
|
|
||||||
msg = e.read()
|
|
||||||
if len(msg) > 0 and msg[-1] == '\n':
|
|
||||||
msg = msg[0:-1]
|
|
||||||
msg = ' (%s)' % msg
|
|
||||||
except:
|
|
||||||
msg = ''
|
|
||||||
else:
|
|
||||||
try:
|
|
||||||
from BaseHTTPServer import BaseHTTPRequestHandler
|
|
||||||
res = BaseHTTPRequestHandler.responses[e.code]
|
|
||||||
msg = ' (%s: %s)' % (res[0], res[1])
|
|
||||||
except:
|
|
||||||
msg = ''
|
|
||||||
raise DownloadError('HTTP %s%s' % (e.code, msg))
|
|
||||||
except urllib2.URLError, e:
|
|
||||||
raise DownloadError('%s: %s ' % (req.get_host(), str(e)))
|
|
||||||
finally:
|
|
||||||
_urllib_lock.release()
|
|
||||||
|
|
||||||
p = None
|
|
||||||
try:
|
|
||||||
size = r.headers.get('content-length', 0)
|
|
||||||
unit = 1 << 10
|
|
||||||
|
|
||||||
if size and not quiet:
|
|
||||||
if size > 1024 * 1.3:
|
|
||||||
unit = 1 << 20
|
|
||||||
desc = 'MB'
|
|
||||||
else:
|
|
||||||
desc = 'KB'
|
|
||||||
p = Progress(
|
|
||||||
'Downloading %s' % self.relpath,
|
|
||||||
int(size) / unit,
|
|
||||||
units=desc)
|
|
||||||
if pos > 0:
|
|
||||||
p.update(pos / unit)
|
|
||||||
|
|
||||||
s = 0
|
|
||||||
while True:
|
|
||||||
d = r.read(8192)
|
|
||||||
if d == '':
|
|
||||||
done = True
|
|
||||||
return True
|
|
||||||
dest.write(d)
|
|
||||||
if p:
|
|
||||||
s += len(d)
|
|
||||||
if s >= unit:
|
|
||||||
p.update(s / unit)
|
|
||||||
s = s % unit
|
|
||||||
if p:
|
|
||||||
if s >= unit:
|
|
||||||
p.update(s / unit)
|
|
||||||
else:
|
|
||||||
p.update(1)
|
|
||||||
finally:
|
|
||||||
r.close()
|
|
||||||
if p:
|
|
||||||
p.end()
|
|
||||||
finally:
|
|
||||||
dest.close()
|
|
||||||
|
|
||||||
if os.path.exists(dstPath):
|
if os.path.exists(dstPath):
|
||||||
os.remove(dstPath)
|
os.remove(dstPath)
|
||||||
if done:
|
|
||||||
os.rename(tmpPath, dstPath)
|
cmd = ['curl', '--fail', '--output', tmpPath, '--netrc', '--location']
|
||||||
elif not keep:
|
if quiet:
|
||||||
|
cmd += ['--silent']
|
||||||
|
if os.path.exists(tmpPath):
|
||||||
|
size = os.stat(tmpPath).st_size
|
||||||
|
if size >= 1024:
|
||||||
|
cmd += ['--continue-at', '%d' % (size,)]
|
||||||
|
else:
|
||||||
os.remove(tmpPath)
|
os.remove(tmpPath)
|
||||||
|
if 'http_proxy' in os.environ and 'darwin' == sys.platform:
|
||||||
|
cmd += ['--proxy', os.environ['http_proxy']]
|
||||||
|
cmd += [srcUrl]
|
||||||
|
|
||||||
|
if IsTrace():
|
||||||
|
Trace('%s', ' '.join(cmd))
|
||||||
|
try:
|
||||||
|
proc = subprocess.Popen(cmd)
|
||||||
|
except OSError:
|
||||||
|
return False
|
||||||
|
|
||||||
|
curlret = proc.wait()
|
||||||
|
|
||||||
|
if curlret == 22:
|
||||||
|
# From curl man page:
|
||||||
|
# 22: HTTP page not retrieved. The requested url was not found or
|
||||||
|
# returned another error with the HTTP error code being 400 or above.
|
||||||
|
# This return code only appears if -f, --fail is used.
|
||||||
|
if not quiet:
|
||||||
|
print >> sys.stderr, "Server does not provide clone.bundle; ignoring."
|
||||||
|
return False
|
||||||
|
|
||||||
|
if os.path.exists(tmpPath):
|
||||||
|
if curlret == 0 and os.stat(tmpPath).st_size > 16:
|
||||||
|
os.rename(tmpPath, dstPath)
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
os.remove(tmpPath)
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
def _Checkout(self, rev, quiet=False):
|
def _Checkout(self, rev, quiet=False):
|
||||||
cmd = ['checkout']
|
cmd = ['checkout']
|
||||||
@ -1761,7 +1691,7 @@ class Project(object):
|
|||||||
_error("%s: Not replacing %s hook", self.relpath, name)
|
_error("%s: Not replacing %s hook", self.relpath, name)
|
||||||
continue
|
continue
|
||||||
try:
|
try:
|
||||||
os.symlink(relpath(stock_hook, dst), dst)
|
os.symlink(os.path.relpath(stock_hook, os.path.dirname(dst)), dst)
|
||||||
except OSError, e:
|
except OSError, e:
|
||||||
if e.errno == errno.EPERM:
|
if e.errno == errno.EPERM:
|
||||||
raise GitError('filesystem must support symlinks')
|
raise GitError('filesystem must support symlinks')
|
||||||
@ -1822,7 +1752,7 @@ class Project(object):
|
|||||||
src = os.path.join(self.gitdir, name)
|
src = os.path.join(self.gitdir, name)
|
||||||
dst = os.path.join(dotgit, name)
|
dst = os.path.join(dotgit, name)
|
||||||
if os.path.islink(dst) or not os.path.exists(dst):
|
if os.path.islink(dst) or not os.path.exists(dst):
|
||||||
os.symlink(relpath(src, dst), dst)
|
os.symlink(os.path.relpath(src, os.path.dirname(dst)), dst)
|
||||||
else:
|
else:
|
||||||
raise GitError('cannot overwrite a local work tree')
|
raise GitError('cannot overwrite a local work tree')
|
||||||
except OSError, e:
|
except OSError, e:
|
||||||
@ -2181,7 +2111,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
|
||||||
|
|
||||||
|
@ -13,7 +13,7 @@
|
|||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
import sys, re, string, random, os
|
import sys, re
|
||||||
from command import Command
|
from command import Command
|
||||||
from git_command import GitCommand
|
from git_command import GitCommand
|
||||||
|
|
||||||
|
@ -13,7 +13,6 @@
|
|||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
import os
|
|
||||||
import re
|
import re
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
@ -14,7 +14,6 @@
|
|||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
from optparse import SUPPRESS_HELP
|
|
||||||
from color import Coloring
|
from color import Coloring
|
||||||
from command import PagedCommand
|
from command import PagedCommand
|
||||||
from git_command import git_require, GitCommand
|
from git_command import git_require, GitCommand
|
||||||
|
@ -81,7 +81,8 @@ to update the working directory files.
|
|||||||
help='initial manifest file', metavar='NAME.xml')
|
help='initial manifest file', metavar='NAME.xml')
|
||||||
g.add_option('--mirror',
|
g.add_option('--mirror',
|
||||||
dest='mirror', action='store_true',
|
dest='mirror', action='store_true',
|
||||||
help='mirror the forrest')
|
help='create a replica of the remote repositories '
|
||||||
|
'rather than a client working directory')
|
||||||
g.add_option('--reference',
|
g.add_option('--reference',
|
||||||
dest='reference',
|
dest='reference',
|
||||||
help='location of mirror directory', metavar='DIR')
|
help='location of mirror directory', metavar='DIR')
|
||||||
@ -89,12 +90,12 @@ to update the working directory files.
|
|||||||
dest='depth',
|
dest='depth',
|
||||||
help='create a shallow clone with given depth; see git clone')
|
help='create a shallow clone with given depth; see git clone')
|
||||||
g.add_option('-g', '--groups',
|
g.add_option('-g', '--groups',
|
||||||
dest='groups', default='default',
|
dest='groups', default='all,-notdefault',
|
||||||
help='restrict manifest projects to ones with a specified group',
|
help='restrict manifest projects to ones with a specified group',
|
||||||
metavar='GROUP')
|
metavar='GROUP')
|
||||||
g.add_option('-p', '--platform',
|
g.add_option('-p', '--platform',
|
||||||
dest='platform', default='auto',
|
dest='platform', default='auto',
|
||||||
help='restrict manifest projects to ones with a specified'
|
help='restrict manifest projects to ones with a specified '
|
||||||
'platform group [auto|all|none|linux|darwin|...]',
|
'platform group [auto|all|none|linux|darwin|...]',
|
||||||
metavar='PLATFORM')
|
metavar='PLATFORM')
|
||||||
|
|
||||||
@ -163,7 +164,7 @@ to update the working directory files.
|
|||||||
|
|
||||||
groups = [x for x in groups if x]
|
groups = [x for x in groups if x]
|
||||||
groupstr = ','.join(groups)
|
groupstr = ','.join(groups)
|
||||||
if opt.platform == 'auto' and groupstr == 'default,platform-' + platform.system().lower():
|
if opt.platform == 'auto' and groupstr == 'all,-notdefault,platform-' + platform.system().lower():
|
||||||
groupstr = None
|
groupstr = None
|
||||||
m.config.SetString('manifest.groups', groupstr)
|
m.config.SetString('manifest.groups', groupstr)
|
||||||
|
|
||||||
|
@ -50,6 +50,7 @@ in a Git repository for use during future 'repo init' invocations.
|
|||||||
help='Save revisions as current HEAD')
|
help='Save revisions as current HEAD')
|
||||||
p.add_option('-o', '--output-file',
|
p.add_option('-o', '--output-file',
|
||||||
dest='output_file',
|
dest='output_file',
|
||||||
|
default='-',
|
||||||
help='File to save the manifest to',
|
help='File to save the manifest to',
|
||||||
metavar='-|NAME.xml')
|
metavar='-|NAME.xml')
|
||||||
|
|
||||||
|
80
subcmds/overview.py
Normal file
80
subcmds/overview.py
Normal 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)
|
@ -17,8 +17,6 @@ import sys
|
|||||||
|
|
||||||
from command import Command
|
from command import Command
|
||||||
from git_command import GitCommand
|
from git_command import GitCommand
|
||||||
from git_refs import GitRefs, HEAD, R_HEADS, R_TAGS, R_PUB, R_M
|
|
||||||
from error import GitError
|
|
||||||
|
|
||||||
class Rebase(Command):
|
class Rebase(Command):
|
||||||
common = True
|
common = True
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
|
|
||||||
import sys
|
import sys
|
||||||
from command import Command
|
from command import Command
|
||||||
|
from git_config import IsId
|
||||||
from git_command import git
|
from git_command import git
|
||||||
from progress import Progress
|
from progress import Progress
|
||||||
|
|
||||||
@ -56,6 +57,10 @@ revision specified in the manifest.
|
|||||||
pm = Progress('Starting %s' % nb, len(all))
|
pm = Progress('Starting %s' % nb, len(all))
|
||||||
for project in all:
|
for project in all:
|
||||||
pm.update()
|
pm.update()
|
||||||
|
# If the current revision is a specific SHA1 then we can't push back
|
||||||
|
# to it so substitute the manifest default revision instead.
|
||||||
|
if IsId(project.revisionExpr):
|
||||||
|
project.revisionExpr = self.manifest.default.revisionExpr
|
||||||
if not project.StartBranch(nb):
|
if not project.StartBranch(nb):
|
||||||
err.append(project)
|
err.append(project)
|
||||||
pm.end()
|
pm.end()
|
||||||
|
@ -122,6 +122,7 @@ the following meanings:
|
|||||||
t = _threading.Thread(target=self._StatusHelper,
|
t = _threading.Thread(target=self._StatusHelper,
|
||||||
args=(project, counter, sem, output))
|
args=(project, counter, sem, output))
|
||||||
threads_and_output.append((t, output))
|
threads_and_output.append((t, output))
|
||||||
|
t.daemon = True
|
||||||
t.start()
|
t.start()
|
||||||
for (t, output) in threads_and_output:
|
for (t, output) in threads_and_output:
|
||||||
t.join()
|
t.join()
|
||||||
|
@ -37,13 +37,11 @@ except ImportError:
|
|||||||
return (256, 256)
|
return (256, 256)
|
||||||
|
|
||||||
from git_command import GIT
|
from git_command import GIT
|
||||||
from git_refs import R_HEADS
|
from git_refs import R_HEADS, HEAD
|
||||||
from project import HEAD
|
|
||||||
from project import Project
|
from project import Project
|
||||||
from project import RemoteSpec
|
from project import RemoteSpec
|
||||||
from command import Command, MirrorSafeCommand
|
from command import Command, MirrorSafeCommand
|
||||||
from error import RepoChangedException, GitError
|
from error import RepoChangedException, GitError
|
||||||
from project import R_HEADS
|
|
||||||
from project import SyncBuffer
|
from project import SyncBuffer
|
||||||
from progress import Progress
|
from progress import Progress
|
||||||
|
|
||||||
@ -230,8 +228,10 @@ later is required to fix a server side protocol bug.
|
|||||||
if self.jobs == 1:
|
if self.jobs == 1:
|
||||||
for project in projects:
|
for project in projects:
|
||||||
pm.update()
|
pm.update()
|
||||||
if project.Sync_NetworkHalf(quiet=opt.quiet,
|
if project.Sync_NetworkHalf(
|
||||||
current_branch_only=opt.current_branch_only):
|
quiet=opt.quiet,
|
||||||
|
current_branch_only=opt.current_branch_only,
|
||||||
|
clone_bundle=not opt.no_clone_bundle):
|
||||||
fetched.add(project.gitdir)
|
fetched.add(project.gitdir)
|
||||||
else:
|
else:
|
||||||
print >>sys.stderr, 'error: Cannot fetch %s' % project.name
|
print >>sys.stderr, 'error: Cannot fetch %s' % project.name
|
||||||
@ -259,6 +259,8 @@ later is required to fix a server side protocol bug.
|
|||||||
pm,
|
pm,
|
||||||
sem,
|
sem,
|
||||||
err_event))
|
err_event))
|
||||||
|
# Ensure that Ctrl-C will not freeze the repo process.
|
||||||
|
t.daemon = True
|
||||||
threads.add(t)
|
threads.add(t)
|
||||||
t.start()
|
t.start()
|
||||||
|
|
||||||
@ -402,9 +404,13 @@ uncommitted changes are present' % project.relpath
|
|||||||
else:
|
else:
|
||||||
print >>sys.stderr, 'error: %s' % manifest_str
|
print >>sys.stderr, 'error: %s' % manifest_str
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
except socket.error:
|
except (socket.error, IOError, xmlrpclib.Fault), e:
|
||||||
print >>sys.stderr, 'error: cannot connect to manifest server %s' % (
|
print >>sys.stderr, 'error: cannot connect to manifest server %s:\n%s' % (
|
||||||
self.manifest.manifest_server)
|
self.manifest.manifest_server, e)
|
||||||
|
sys.exit(1)
|
||||||
|
except xmlrpclib.ProtocolError, e:
|
||||||
|
print >>sys.stderr, 'error: cannot connect to manifest server %s:\n%d %s' % (
|
||||||
|
self.manifest.manifest_server, e.errcode, e.errmsg)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
rp = self.manifest.repoProject
|
rp = self.manifest.repoProject
|
||||||
|
@ -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
|
||||||
|
Reference in New Issue
Block a user