mirror of
https://gerrit.googlesource.com/git-repo
synced 2025-07-02 20:17:19 +00:00
Compare commits
49 Commits
Author | SHA1 | Date | |
---|---|---|---|
745be2ede1 | |||
87fb5a1894 | |||
ab85fe7c53 | |||
4f42a97067 | |||
2b7daff8cb | |||
242fcdd93b | |||
ca540aed19 | |||
f88b2fe569 | |||
6db1b9e282 | |||
490e16385d | |||
ec558df074 | |||
81f5c59671 | |||
1b9adab75a | |||
3698ab7c92 | |||
0c0e934b69 | |||
9e71842fbf | |||
61b2d41f26 | |||
da9e200f1d | |||
c92ce5c7dc | |||
f601376e13 | |||
31067c0ac5 | |||
35159abbeb | |||
24ee29e468 | |||
1b291fc2e7 | |||
a26c49ead4 | |||
c745350ab9 | |||
025704e946 | |||
c5b0e23490 | |||
d92464e8ef | |||
0968570df2 | |||
f25a370a14 | |||
b554838ce8 | |||
2d095da4f1 | |||
266f74c888 | |||
1f1596b473 | |||
0d9b16d1d8 | |||
a84df06160 | |||
e57f1146de | |||
01019d94af | |||
834d308a2b | |||
c18ee35da6 | |||
d3c0f5914f | |||
41a26837d0 | |||
e7379dc5f7 | |||
13f323b2c2 | |||
12ee5446e9 | |||
e158e3802d | |||
3bbbcaf99d | |||
d4b13c280b |
@ -5,6 +5,6 @@
|
|||||||
<pydev_pathproperty name="org.python.pydev.PROJECT_SOURCE_PATH">
|
<pydev_pathproperty name="org.python.pydev.PROJECT_SOURCE_PATH">
|
||||||
<path>/git-repo</path>
|
<path>/git-repo</path>
|
||||||
</pydev_pathproperty>
|
</pydev_pathproperty>
|
||||||
<pydev_property name="org.python.pydev.PYTHON_PROJECT_VERSION">python 2.6</pydev_property>
|
<pydev_property name="org.python.pydev.PYTHON_PROJECT_VERSION">python 2.7</pydev_property>
|
||||||
<pydev_property name="org.python.pydev.PYTHON_PROJECT_INTERPRETER">Default</pydev_property>
|
<pydev_property name="org.python.pydev.PYTHON_PROJECT_INTERPRETER">Default</pydev_property>
|
||||||
</pydev_project>
|
</pydev_project>
|
||||||
|
@ -7,7 +7,7 @@ easier to work with Git. The repo command is an executable Python script
|
|||||||
that you can put anywhere in your path.
|
that you can put anywhere in your path.
|
||||||
|
|
||||||
* Homepage: https://gerrit.googlesource.com/git-repo/
|
* Homepage: https://gerrit.googlesource.com/git-repo/
|
||||||
* Bug reports: https://code.google.com/p/git-repo/issues/
|
* Bug reports: https://bugs.chromium.org/p/gerrit/issues/list?q=component:repo
|
||||||
* Source: https://gerrit.googlesource.com/git-repo/
|
* Source: https://gerrit.googlesource.com/git-repo/
|
||||||
* Overview: https://source.android.com/source/developing.html
|
* Overview: https://source.android.com/source/developing.html
|
||||||
* Docs: https://source.android.com/source/using-repo.html
|
* Docs: https://source.android.com/source/using-repo.html
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
[TOC]
|
||||||
|
|
||||||
# Short Version
|
# Short Version
|
||||||
|
|
||||||
- Make small logical changes.
|
- Make small logical changes.
|
||||||
@ -52,17 +54,29 @@ Run `flake8` on changes modules:
|
|||||||
|
|
||||||
flake8 file.py
|
flake8 file.py
|
||||||
|
|
||||||
Note that repo generally follows [Google's python style guide]
|
Note that repo generally follows [Google's python style guide] rather than
|
||||||
(https://google.github.io/styleguide/pyguide.html) rather than [PEP 8]
|
[PEP 8], so it's possible that the output of `flake8` will be quite noisy.
|
||||||
(https://www.python.org/dev/peps/pep-0008/), so it's possible that
|
It's not mandatory to avoid all warnings, but at least the maximum line
|
||||||
the output of `flake8` will be quite noisy. It's not mandatory to
|
length should be followed.
|
||||||
avoid all warnings, but at least the maximum line length should be
|
|
||||||
followed.
|
|
||||||
|
|
||||||
If there are many occurrences of the same warning that cannot be
|
If there are many occurrences of the same warning that cannot be
|
||||||
avoided without going against the Google style guide, these may be
|
avoided without going against the Google style guide, these may be
|
||||||
suppressed in the included `.flake8` file.
|
suppressed in the included `.flake8` file.
|
||||||
|
|
||||||
|
[Google's python style guide]: https://google.github.io/styleguide/pyguide.html
|
||||||
|
[PEP 8]: https://www.python.org/dev/peps/pep-0008/
|
||||||
|
|
||||||
|
|
||||||
|
## Running tests
|
||||||
|
|
||||||
|
There is a [`./run_tests`](./run_tests) helper script for quickly invoking all
|
||||||
|
of our unittests. The coverage isn't great currently, but it should still be
|
||||||
|
run for all commits.
|
||||||
|
|
||||||
|
Adding more unittests for changes you make would be greatly appreciated :).
|
||||||
|
Check out the [tests/](./tests/) subdirectory for more details.
|
||||||
|
|
||||||
|
|
||||||
## Check the license
|
## Check the license
|
||||||
|
|
||||||
repo is licensed under the Apache License, 2.0.
|
repo is licensed under the Apache License, 2.0.
|
||||||
|
1
color.py
1
color.py
@ -1,3 +1,4 @@
|
|||||||
|
# -*- coding:utf-8 -*-
|
||||||
#
|
#
|
||||||
# Copyright (C) 2008 The Android Open Source Project
|
# Copyright (C) 2008 The Android Open Source Project
|
||||||
#
|
#
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
# -*- coding:utf-8 -*-
|
||||||
#
|
#
|
||||||
# Copyright (C) 2008 The Android Open Source Project
|
# Copyright (C) 2008 The Android Open Source Project
|
||||||
#
|
#
|
||||||
|
@ -66,7 +66,7 @@ following DTD:
|
|||||||
<!ATTLIST project groups CDATA #IMPLIED>
|
<!ATTLIST project groups CDATA #IMPLIED>
|
||||||
<!ATTLIST project sync-c CDATA #IMPLIED>
|
<!ATTLIST project sync-c CDATA #IMPLIED>
|
||||||
<!ATTLIST project sync-s CDATA #IMPLIED>
|
<!ATTLIST project sync-s CDATA #IMPLIED>
|
||||||
<!ATTLIST default sync-tags CDATA #IMPLIED>
|
<!ATTLIST project sync-tags CDATA #IMPLIED>
|
||||||
<!ATTLIST project upstream CDATA #IMPLIED>
|
<!ATTLIST project upstream CDATA #IMPLIED>
|
||||||
<!ATTLIST project clone-depth CDATA #IMPLIED>
|
<!ATTLIST project clone-depth CDATA #IMPLIED>
|
||||||
<!ATTLIST project force-path CDATA #IMPLIED>
|
<!ATTLIST project force-path CDATA #IMPLIED>
|
||||||
|
32
docs/python-support.md
Normal file
32
docs/python-support.md
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
# Supported Python Versions
|
||||||
|
|
||||||
|
With Python 2.7 officially going EOL on [01 Jan 2020](https://pythonclock.org/),
|
||||||
|
we need a support plan for the repo project itself.
|
||||||
|
Inevitably, there will be a long tail of users who still want to use Python 2 on
|
||||||
|
their old LTS/corp systems and have little power to change the system.
|
||||||
|
|
||||||
|
## Summary
|
||||||
|
|
||||||
|
* Python 3.6 (released Dec 2016) is required by default starting with repo-1.14.
|
||||||
|
* Older versions of Python (e.g. v2.7) may use the legacy feature-frozen branch
|
||||||
|
based on repo-1.13.
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
We provide a branch for Python 2 users that is feature-frozen.
|
||||||
|
Bugfixes may be added on a best-effort basis or from the community, but largely
|
||||||
|
no new features will be added, nor is support guaranteed.
|
||||||
|
|
||||||
|
Users can select this during `repo init` time via the [repo launcher].
|
||||||
|
Otherwise the default branches (e.g. stable & master) will be used which will
|
||||||
|
require Python 3.
|
||||||
|
|
||||||
|
This means the [repo launcher] needs to support both Python 2 & Python 3, but
|
||||||
|
since it doesn't import any other repo code, this shouldn't be too problematic.
|
||||||
|
|
||||||
|
The master branch will require Python 3.6 at a minimum.
|
||||||
|
If the system has an older version of Python 3, then users will have to select
|
||||||
|
the legacy Python 2 branch instead.
|
||||||
|
|
||||||
|
|
||||||
|
[repo launcher]: ../repo
|
167
docs/release-process.md
Normal file
167
docs/release-process.md
Normal file
@ -0,0 +1,167 @@
|
|||||||
|
# repo release process
|
||||||
|
|
||||||
|
This is the process for creating a new release of repo, as well as all the
|
||||||
|
related topics and flows.
|
||||||
|
|
||||||
|
[TOC]
|
||||||
|
|
||||||
|
## Launcher script
|
||||||
|
|
||||||
|
The main repo script serves as a standalone program and is often referred to as
|
||||||
|
the "launcher script".
|
||||||
|
This makes it easy to copy around and install as you don't have to install any
|
||||||
|
other files from the git repo.
|
||||||
|
|
||||||
|
Whenever major changes are made to the launcher script, you should increment the
|
||||||
|
`VERSION` variable in the launcher itself.
|
||||||
|
At runtime, repo will check this to see if it needs to be updated (and notify
|
||||||
|
the user automatically).
|
||||||
|
|
||||||
|
## Key management
|
||||||
|
|
||||||
|
Every release has a git tag that is signed with a key that repo recognizes.
|
||||||
|
Those keys are hardcoded inside of the repo launcher itself -- look for the
|
||||||
|
`KEYRING_VERSION` and `MAINTAINER_KEYS` settings.
|
||||||
|
|
||||||
|
Adding new keys to the repo launcher will allow tags to be recognized by new
|
||||||
|
keys, but only people using that updated version will be able to.
|
||||||
|
Since the majority of users will be using an official launcher version, their
|
||||||
|
version will simply ignore any new signed tags.
|
||||||
|
|
||||||
|
If you want to add new keys, it's best to register them long ahead of time,
|
||||||
|
and then wait for that updated launcher to make its way out to everyone.
|
||||||
|
Even then, there will be a long tail of users with outdated launchers, so be
|
||||||
|
prepared for people asking questions.
|
||||||
|
|
||||||
|
### Registering a new key
|
||||||
|
|
||||||
|
The process of actually adding a new key is quite simple.
|
||||||
|
|
||||||
|
1. Add the public half of the key to `MAINTAINER_KEYS`.
|
||||||
|
2. Increment `KEYRING_VERSION` so repo knows it needs to update.
|
||||||
|
3. Wait a long time after that version is in a release (~months) before trying
|
||||||
|
to create a new release using those new keys.
|
||||||
|
|
||||||
|
## Self update algorithm
|
||||||
|
|
||||||
|
When creating a new repo checkout with `repo init`, there are a few options that
|
||||||
|
control how repo finds updates:
|
||||||
|
|
||||||
|
* `--repo-url`: This tells repo where to clone the full repo project itself.
|
||||||
|
It defaults to the official project (`REPO_URL` in the launcher script).
|
||||||
|
* `--repo-branch`: This tells repo which branch to use for the full project.
|
||||||
|
It defaults to the `stable` branch (`REPO_REV` in the launcher script).
|
||||||
|
|
||||||
|
Whenever `repo sync` is run, repo will check to see if an update is available.
|
||||||
|
It fetches the latest repo-branch from the repo-url.
|
||||||
|
Then it verifies that the latest commit in the branch has a valid signed tag
|
||||||
|
using `git tag -v` (which uses gpg).
|
||||||
|
If the tag is valid, then repo will update its internal checkout to it.
|
||||||
|
|
||||||
|
If the latest commit doesn't have a signed tag, repo will fall back to the
|
||||||
|
most recent tag it can find (via `git describe`).
|
||||||
|
If that tag is valid, then repo will warn and use that commit instead.
|
||||||
|
|
||||||
|
If that tag cannot be verified, it gives up and forces the user to resolve.
|
||||||
|
|
||||||
|
## Branch management
|
||||||
|
|
||||||
|
All development happens on the `master` branch and should generally be stable.
|
||||||
|
|
||||||
|
Since the repo launcher defaults to tracking the `stable` branch, it is not
|
||||||
|
normally updated until a new release is available.
|
||||||
|
If something goes wrong with a new release, an older release can be force pushed
|
||||||
|
and clients will automatically downgrade.
|
||||||
|
|
||||||
|
The `maint` branch is used to track the previous major release of repo.
|
||||||
|
It is not normally meant to be used by people as `stable` should be good enough.
|
||||||
|
Once a new major release is pushed to the `stable` branch, then the previous
|
||||||
|
major release can be pushed to `maint`.
|
||||||
|
For example, when `stable` moves from `v1.10.x` to `v1.11.x`, then the `maint`
|
||||||
|
branch will be updated from `v1.9.x` to `v1.10.x`.
|
||||||
|
|
||||||
|
We don't have parallel release branches/series.
|
||||||
|
Typically all tags are made against the `master` branch and then pushed to the
|
||||||
|
`stable` branch to make it available to the rest of the world.
|
||||||
|
Since repo doesn't typically see a lot of changes, this tends to be OK.
|
||||||
|
|
||||||
|
## Creating a new release
|
||||||
|
|
||||||
|
When you want to create a new release, you'll need to select a good version and
|
||||||
|
create a signed tag using a key registered in repo itself.
|
||||||
|
Typically we just tag the latest version of the `master` branch.
|
||||||
|
The tag could be pushed now, but it won't be used by clients normally (since the
|
||||||
|
default `repo-branch` setting is `stable`).
|
||||||
|
This would allow some early testing on systems who explicitly select `master`.
|
||||||
|
|
||||||
|
### Creating a signed tag
|
||||||
|
|
||||||
|
Lets assume your keys live in a dedicated directory, e.g. `~/.gnupg/repo/`.
|
||||||
|
|
||||||
|
*** note
|
||||||
|
If you need access to the official keys, check out the internal documentation
|
||||||
|
at [go/repo-release].
|
||||||
|
Note that only official maintainers of repo will have access as it describes
|
||||||
|
internal processes for accessing the restricted keys.
|
||||||
|
***
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# Set the gpg key directory.
|
||||||
|
$ export GNUPGHOME=~/.gnupg/repo/
|
||||||
|
|
||||||
|
# Verify the listed key is “Repo Maintainer”.
|
||||||
|
$ gpg -K
|
||||||
|
|
||||||
|
# Pick whatever branch or commit you want to tag.
|
||||||
|
$ r=master
|
||||||
|
|
||||||
|
# Pick the new version.
|
||||||
|
$ t=1.12.10
|
||||||
|
|
||||||
|
# Create the signed tag.
|
||||||
|
$ git tag -s v$t -u "Repo Maintainer <repo@android.kernel.org>" -m "repo $t" $r
|
||||||
|
|
||||||
|
# Verify the signed tag.
|
||||||
|
$ git show v$t
|
||||||
|
```
|
||||||
|
|
||||||
|
### Push the new release
|
||||||
|
|
||||||
|
Once you're ready to make the release available to everyone, push it to the
|
||||||
|
`stable` branch.
|
||||||
|
|
||||||
|
Make sure you never push the tag itself to the stable branch!
|
||||||
|
Only push the commit -- notice the use of `$t` and `$r` below.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
$ git push https://gerrit-review.googlesource.com/git-repo v$t
|
||||||
|
$ git push https://gerrit-review.googlesource.com/git-repo $r:stable
|
||||||
|
```
|
||||||
|
|
||||||
|
If something goes horribly wrong, you can force push the previous version to the
|
||||||
|
`stable` branch and people should automatically recover.
|
||||||
|
Again, make sure you never push the tag itself!
|
||||||
|
|
||||||
|
```sh
|
||||||
|
$ oldrev="whatever-old-commit"
|
||||||
|
$ git push https://gerrit-review.googlesource.com/git-repo $oldrev:stable --force
|
||||||
|
```
|
||||||
|
|
||||||
|
### Announce the release
|
||||||
|
|
||||||
|
Once you do push a new release to `stable`, make sure to announce it on the
|
||||||
|
[repo-discuss@googlegroups.com] group.
|
||||||
|
Here is an [example announcement].
|
||||||
|
|
||||||
|
You can create a short changelog using the command:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# If you haven't pushed to the stable branch yet, you can use origin/stable.
|
||||||
|
# If you have pushed, change origin/stable to the previous release tag.
|
||||||
|
$ git log --format="%h (%aN) %s" --no-merges origin/stable..$r
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
[example announcement]: https://groups.google.com/d/topic/repo-discuss/UGBNismWo1M/discussion
|
||||||
|
[repo-discuss@googlegroups.com]: https://groups.google.com/forum/#!forum/repo-discuss
|
||||||
|
[go/repo-release]: https://goto.google.com/repo-release
|
@ -1,3 +1,4 @@
|
|||||||
|
# -*- coding:utf-8 -*-
|
||||||
#
|
#
|
||||||
# Copyright (C) 2008 The Android Open Source Project
|
# Copyright (C) 2008 The Android Open Source Project
|
||||||
#
|
#
|
||||||
|
1
error.py
1
error.py
@ -1,3 +1,4 @@
|
|||||||
|
# -*- coding:utf-8 -*-
|
||||||
#
|
#
|
||||||
# Copyright (C) 2008 The Android Open Source Project
|
# Copyright (C) 2008 The Android Open Source Project
|
||||||
#
|
#
|
||||||
|
26
event_log.py
26
event_log.py
@ -1,3 +1,4 @@
|
|||||||
|
# -*- coding:utf-8 -*-
|
||||||
#
|
#
|
||||||
# Copyright (C) 2017 The Android Open Source Project
|
# Copyright (C) 2017 The Android Open Source Project
|
||||||
#
|
#
|
||||||
@ -18,8 +19,6 @@ from __future__ import print_function
|
|||||||
import json
|
import json
|
||||||
import multiprocessing
|
import multiprocessing
|
||||||
|
|
||||||
from pyversion import is_python3
|
|
||||||
|
|
||||||
TASK_COMMAND = 'command'
|
TASK_COMMAND = 'command'
|
||||||
TASK_SYNC_NETWORK = 'sync-network'
|
TASK_SYNC_NETWORK = 'sync-network'
|
||||||
TASK_SYNC_LOCAL = 'sync-local'
|
TASK_SYNC_LOCAL = 'sync-local'
|
||||||
@ -53,7 +52,6 @@ class EventLog(object):
|
|||||||
def __init__(self):
|
def __init__(self):
|
||||||
"""Initializes the event log."""
|
"""Initializes the event log."""
|
||||||
self._log = []
|
self._log = []
|
||||||
self._next_id = _EventIdGenerator()
|
|
||||||
self._parent = None
|
self._parent = None
|
||||||
|
|
||||||
def Add(self, name, task_name, start, finish=None, success=None,
|
def Add(self, name, task_name, start, finish=None, success=None,
|
||||||
@ -73,7 +71,7 @@ class EventLog(object):
|
|||||||
A dictionary of the event added to the log.
|
A dictionary of the event added to the log.
|
||||||
"""
|
"""
|
||||||
event = {
|
event = {
|
||||||
'id': (kind, self._next_id.__next__() if is_python3() else self._next_id.next()),
|
'id': (kind, _NextEventId()),
|
||||||
'name': name,
|
'name': name,
|
||||||
'task_name': task_name,
|
'task_name': task_name,
|
||||||
'start_time': start,
|
'start_time': start,
|
||||||
@ -164,16 +162,16 @@ class EventLog(object):
|
|||||||
f.write('\n')
|
f.write('\n')
|
||||||
|
|
||||||
|
|
||||||
def _EventIdGenerator():
|
# An integer id that is unique across this invocation of the program.
|
||||||
"""Returns multi-process safe iterator that generates locally unique id.
|
_EVENT_ID = multiprocessing.Value('i', 1)
|
||||||
|
|
||||||
Yields:
|
def _NextEventId():
|
||||||
|
"""Helper function for grabbing the next unique id.
|
||||||
|
|
||||||
|
Returns:
|
||||||
A unique, to this invocation of the program, integer id.
|
A unique, to this invocation of the program, integer id.
|
||||||
"""
|
"""
|
||||||
eid = multiprocessing.Value('i', 1)
|
with _EVENT_ID.get_lock():
|
||||||
|
val = _EVENT_ID.value
|
||||||
while True:
|
_EVENT_ID.value += 1
|
||||||
with eid.get_lock():
|
return val
|
||||||
val = eid.value
|
|
||||||
eid.value += 1
|
|
||||||
yield val
|
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
# -*- coding:utf-8 -*-
|
||||||
#
|
#
|
||||||
# Copyright (C) 2008 The Android Open Source Project
|
# Copyright (C) 2008 The Android Open Source Project
|
||||||
#
|
#
|
||||||
@ -79,22 +80,12 @@ def terminate_ssh_clients():
|
|||||||
_git_version = None
|
_git_version = None
|
||||||
|
|
||||||
class _GitCall(object):
|
class _GitCall(object):
|
||||||
def version(self):
|
|
||||||
p = GitCommand(None, ['--version'], capture_stdout=True)
|
|
||||||
if p.Wait() == 0:
|
|
||||||
if hasattr(p.stdout, 'decode'):
|
|
||||||
return p.stdout.decode('utf-8')
|
|
||||||
else:
|
|
||||||
return p.stdout
|
|
||||||
return None
|
|
||||||
|
|
||||||
def version_tuple(self):
|
def version_tuple(self):
|
||||||
global _git_version
|
global _git_version
|
||||||
if _git_version is None:
|
if _git_version is None:
|
||||||
ver_str = git.version()
|
_git_version = Wrapper().ParseGitVersion()
|
||||||
_git_version = Wrapper().ParseGitVersion(ver_str)
|
|
||||||
if _git_version is None:
|
if _git_version is None:
|
||||||
print('fatal: "%s" unsupported' % ver_str, file=sys.stderr)
|
print('fatal: unable to detect git version', file=sys.stderr)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
return _git_version
|
return _git_version
|
||||||
|
|
||||||
@ -107,13 +98,15 @@ class _GitCall(object):
|
|||||||
return fun
|
return fun
|
||||||
git = _GitCall()
|
git = _GitCall()
|
||||||
|
|
||||||
def git_require(min_version, fail=False):
|
def git_require(min_version, fail=False, msg=''):
|
||||||
git_version = git.version_tuple()
|
git_version = git.version_tuple()
|
||||||
if min_version <= git_version:
|
if min_version <= git_version:
|
||||||
return True
|
return True
|
||||||
if fail:
|
if fail:
|
||||||
need = '.'.join(map(str, min_version))
|
need = '.'.join(map(str, min_version))
|
||||||
print('fatal: git %s or later required' % need, file=sys.stderr)
|
if msg:
|
||||||
|
msg = ' for ' + msg
|
||||||
|
print('fatal: git %s or later required%s' % (need, msg), file=sys.stderr)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
# -*- coding:utf-8 -*-
|
||||||
#
|
#
|
||||||
# Copyright (C) 2008 The Android Open Source Project
|
# Copyright (C) 2008 The Android Open Source Project
|
||||||
#
|
#
|
||||||
@ -656,13 +657,14 @@ class Remote(object):
|
|||||||
info = urllib.request.urlopen(info_url, context=context).read()
|
info = urllib.request.urlopen(info_url, context=context).read()
|
||||||
else:
|
else:
|
||||||
info = urllib.request.urlopen(info_url).read()
|
info = urllib.request.urlopen(info_url).read()
|
||||||
if info == 'NOT_AVAILABLE' or '<' in info:
|
if info == b'NOT_AVAILABLE' or b'<' in info:
|
||||||
# If `info` contains '<', we assume the server gave us some sort
|
# If `info` contains '<', we assume the server gave us some sort
|
||||||
# of HTML response back, like maybe a login page.
|
# of HTML response back, like maybe a login page.
|
||||||
#
|
#
|
||||||
# Assume HTTP if SSH is not enabled or ssh_info doesn't look right.
|
# Assume HTTP if SSH is not enabled or ssh_info doesn't look right.
|
||||||
self._review_url = http_url
|
self._review_url = http_url
|
||||||
else:
|
else:
|
||||||
|
info = info.decode('utf-8')
|
||||||
host, port = info.split()
|
host, port = info.split()
|
||||||
self._review_url = self._SshReviewUrl(userEmail, host, port)
|
self._review_url = self._SshReviewUrl(userEmail, host, port)
|
||||||
except urllib.error.HTTPError as e:
|
except urllib.error.HTTPError as e:
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
# -*- coding:utf-8 -*-
|
||||||
#
|
#
|
||||||
# Copyright (C) 2009 The Android Open Source Project
|
# Copyright (C) 2009 The Android Open Source Project
|
||||||
#
|
#
|
||||||
|
15
git_ssh
15
git_ssh
@ -1,2 +1,17 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
|
#
|
||||||
|
# Copyright (C) 2009 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.
|
||||||
|
|
||||||
exec ssh -o "ControlMaster no" -o "ControlPath $REPO_SSH_SOCK" "$@"
|
exec ssh -o "ControlMaster no" -o "ControlPath $REPO_SSH_SOCK" "$@"
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
# -*- coding:utf-8 -*-
|
||||||
#
|
#
|
||||||
# Copyright (C) 2015 The Android Open Source Project
|
# Copyright (C) 2015 The Android Open Source Project
|
||||||
#
|
#
|
||||||
@ -58,8 +59,8 @@ def _set_project_revisions(projects):
|
|||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
revisionExpr = gitcmd.stdout.split('\t')[0]
|
revisionExpr = gitcmd.stdout.split('\t')[0]
|
||||||
if not revisionExpr:
|
if not revisionExpr:
|
||||||
raise(ManifestParseError('Invalid SHA-1 revision project %s (%s)' %
|
raise ManifestParseError('Invalid SHA-1 revision project %s (%s)' %
|
||||||
(proj.remote.url, proj.revisionExpr)))
|
(proj.remote.url, proj.revisionExpr))
|
||||||
proj.revisionExpr = revisionExpr
|
proj.revisionExpr = revisionExpr
|
||||||
|
|
||||||
def _manifest_groups(manifest):
|
def _manifest_groups(manifest):
|
||||||
@ -87,7 +88,7 @@ def generate_gitc_manifest(gitc_manifest, manifest, paths=None):
|
|||||||
print('Generating GITC Manifest by fetching revision SHAs for each '
|
print('Generating GITC Manifest by fetching revision SHAs for each '
|
||||||
'project.')
|
'project.')
|
||||||
if paths is None:
|
if paths is None:
|
||||||
paths = manifest.paths.keys()
|
paths = list(manifest.paths.keys())
|
||||||
|
|
||||||
groups = [x for x in re.split(r'[,\s]+', _manifest_groups(manifest)) if x]
|
groups = [x for x in re.split(r'[,\s]+', _manifest_groups(manifest)) if x]
|
||||||
|
|
||||||
@ -96,7 +97,7 @@ def generate_gitc_manifest(gitc_manifest, manifest, paths=None):
|
|||||||
projects = [p for p in projects if p.MatchesGroups(groups)]
|
projects = [p for p in projects if p.MatchesGroups(groups)]
|
||||||
|
|
||||||
if gitc_manifest is not None:
|
if gitc_manifest is not None:
|
||||||
for path, proj in manifest.paths.iteritems():
|
for path, proj in manifest.paths.items():
|
||||||
if not proj.MatchesGroups(groups):
|
if not proj.MatchesGroups(groups):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
@ -124,7 +125,7 @@ def generate_gitc_manifest(gitc_manifest, manifest, paths=None):
|
|||||||
index += NUM_BATCH_RETRIEVE_REVISIONID
|
index += NUM_BATCH_RETRIEVE_REVISIONID
|
||||||
|
|
||||||
if gitc_manifest is not None:
|
if gitc_manifest is not None:
|
||||||
for path, proj in gitc_manifest.paths.iteritems():
|
for path, proj in gitc_manifest.paths.items():
|
||||||
if proj.old_revision and path in paths:
|
if proj.old_revision and path in paths:
|
||||||
# If we updated a project that has been started, keep the old-revision
|
# If we updated a project that has been started, keep the old-revision
|
||||||
# updated.
|
# updated.
|
||||||
@ -133,7 +134,7 @@ def generate_gitc_manifest(gitc_manifest, manifest, paths=None):
|
|||||||
repo_proj.revisionExpr = None
|
repo_proj.revisionExpr = None
|
||||||
|
|
||||||
# Convert URLs from relative to absolute.
|
# Convert URLs from relative to absolute.
|
||||||
for _name, remote in manifest.remotes.iteritems():
|
for _name, remote in manifest.remotes.items():
|
||||||
remote.fetchUrl = remote.resolvedFetchUrl
|
remote.fetchUrl = remote.resolvedFetchUrl
|
||||||
|
|
||||||
# Save the manifest.
|
# Save the manifest.
|
||||||
|
9
main.py
9
main.py
@ -1,4 +1,5 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
|
# -*- coding:utf-8 -*-
|
||||||
#
|
#
|
||||||
# Copyright (C) 2008 The Android Open Source Project
|
# Copyright (C) 2008 The Android Open Source Project
|
||||||
#
|
#
|
||||||
@ -14,6 +15,12 @@
|
|||||||
# 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.
|
||||||
|
|
||||||
|
"""The repo tool.
|
||||||
|
|
||||||
|
People shouldn't run this directly; instead, they should use the `repo` wrapper
|
||||||
|
which takes care of execing this entry point.
|
||||||
|
"""
|
||||||
|
|
||||||
from __future__ import print_function
|
from __future__ import print_function
|
||||||
import getpass
|
import getpass
|
||||||
import imp
|
import imp
|
||||||
@ -316,7 +323,7 @@ def _UserAgent():
|
|||||||
_user_agent = 'git-repo/%s (%s) git/%s Python/%d.%d.%d' % (
|
_user_agent = 'git-repo/%s (%s) git/%s Python/%d.%d.%d' % (
|
||||||
repo_version,
|
repo_version,
|
||||||
os_name,
|
os_name,
|
||||||
'.'.join(map(str, git.version_tuple())),
|
git.version_tuple().full,
|
||||||
py_version[0], py_version[1], py_version[2])
|
py_version[0], py_version[1], py_version[2])
|
||||||
return _user_agent
|
return _user_agent
|
||||||
|
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
# -*- coding:utf-8 -*-
|
||||||
#
|
#
|
||||||
# Copyright (C) 2008 The Android Open Source Project
|
# Copyright (C) 2008 The Android Open Source Project
|
||||||
#
|
#
|
||||||
@ -135,6 +136,7 @@ class XmlManifest(object):
|
|||||||
self.globalConfig = GitConfig.ForUser()
|
self.globalConfig = GitConfig.ForUser()
|
||||||
self.localManifestWarning = False
|
self.localManifestWarning = False
|
||||||
self.isGitcClient = False
|
self.isGitcClient = False
|
||||||
|
self._load_local_manifests = True
|
||||||
|
|
||||||
self.repoProject = MetaProject(self, 'repo',
|
self.repoProject = MetaProject(self, 'repo',
|
||||||
gitdir = os.path.join(repodir, 'repo/.git'),
|
gitdir = os.path.join(repodir, 'repo/.git'),
|
||||||
@ -146,15 +148,26 @@ class XmlManifest(object):
|
|||||||
|
|
||||||
self._Unload()
|
self._Unload()
|
||||||
|
|
||||||
def Override(self, name):
|
def Override(self, name, load_local_manifests=True):
|
||||||
"""Use a different manifest, just for the current instantiation.
|
"""Use a different manifest, just for the current instantiation.
|
||||||
"""
|
"""
|
||||||
path = os.path.join(self.manifestProject.worktree, name)
|
path = None
|
||||||
if not os.path.isfile(path):
|
|
||||||
raise ManifestParseError('manifest %s not found' % name)
|
# Look for a manifest by path in the filesystem (including the cwd).
|
||||||
|
if not load_local_manifests:
|
||||||
|
local_path = os.path.abspath(name)
|
||||||
|
if os.path.isfile(local_path):
|
||||||
|
path = local_path
|
||||||
|
|
||||||
|
# Look for manifests by name from the manifests repo.
|
||||||
|
if path is None:
|
||||||
|
path = os.path.join(self.manifestProject.worktree, name)
|
||||||
|
if not os.path.isfile(path):
|
||||||
|
raise ManifestParseError('manifest %s not found' % name)
|
||||||
|
|
||||||
old = self.manifestFile
|
old = self.manifestFile
|
||||||
try:
|
try:
|
||||||
|
self._load_local_manifests = load_local_manifests
|
||||||
self.manifestFile = path
|
self.manifestFile = path
|
||||||
self._Unload()
|
self._Unload()
|
||||||
self._Load()
|
self._Load()
|
||||||
@ -400,6 +413,12 @@ class XmlManifest(object):
|
|||||||
self._Load()
|
self._Load()
|
||||||
return self._manifest_server
|
return self._manifest_server
|
||||||
|
|
||||||
|
@property
|
||||||
|
def CloneFilter(self):
|
||||||
|
if self.manifestProject.config.GetBoolean('repo.partialclone'):
|
||||||
|
return self.manifestProject.config.GetString('repo.clonefilter')
|
||||||
|
return None
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def IsMirror(self):
|
def IsMirror(self):
|
||||||
return self.manifestProject.config.GetBoolean('repo.mirror')
|
return self.manifestProject.config.GetBoolean('repo.mirror')
|
||||||
@ -435,23 +454,26 @@ class XmlManifest(object):
|
|||||||
nodes.append(self._ParseManifestXml(self.manifestFile,
|
nodes.append(self._ParseManifestXml(self.manifestFile,
|
||||||
self.manifestProject.worktree))
|
self.manifestProject.worktree))
|
||||||
|
|
||||||
local = os.path.join(self.repodir, LOCAL_MANIFEST_NAME)
|
if self._load_local_manifests:
|
||||||
if os.path.exists(local):
|
local = os.path.join(self.repodir, LOCAL_MANIFEST_NAME)
|
||||||
if not self.localManifestWarning:
|
if os.path.exists(local):
|
||||||
self.localManifestWarning = True
|
if not self.localManifestWarning:
|
||||||
print('warning: %s is deprecated; put local manifests in `%s` instead'
|
self.localManifestWarning = True
|
||||||
% (LOCAL_MANIFEST_NAME, os.path.join(self.repodir, LOCAL_MANIFESTS_DIR_NAME)),
|
print('warning: %s is deprecated; put local manifests '
|
||||||
file=sys.stderr)
|
'in `%s` instead' % (LOCAL_MANIFEST_NAME,
|
||||||
nodes.append(self._ParseManifestXml(local, self.repodir))
|
os.path.join(self.repodir, LOCAL_MANIFESTS_DIR_NAME)),
|
||||||
|
file=sys.stderr)
|
||||||
|
nodes.append(self._ParseManifestXml(local, self.repodir))
|
||||||
|
|
||||||
local_dir = os.path.abspath(os.path.join(self.repodir, LOCAL_MANIFESTS_DIR_NAME))
|
local_dir = os.path.abspath(os.path.join(self.repodir,
|
||||||
try:
|
LOCAL_MANIFESTS_DIR_NAME))
|
||||||
for local_file in sorted(platform_utils.listdir(local_dir)):
|
try:
|
||||||
if local_file.endswith('.xml'):
|
for local_file in sorted(platform_utils.listdir(local_dir)):
|
||||||
local = os.path.join(local_dir, local_file)
|
if local_file.endswith('.xml'):
|
||||||
nodes.append(self._ParseManifestXml(local, self.repodir))
|
local = os.path.join(local_dir, local_file)
|
||||||
except OSError:
|
nodes.append(self._ParseManifestXml(local, self.repodir))
|
||||||
pass
|
except OSError:
|
||||||
|
pass
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self._ParseManifest(nodes)
|
self._ParseManifest(nodes)
|
||||||
@ -498,7 +520,7 @@ class XmlManifest(object):
|
|||||||
raise
|
raise
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise ManifestParseError(
|
raise ManifestParseError(
|
||||||
"failed parsing included manifest %s: %s", (name, e))
|
"failed parsing included manifest %s: %s" % (name, e))
|
||||||
else:
|
else:
|
||||||
nodes.append(node)
|
nodes.append(node)
|
||||||
return nodes
|
return nodes
|
||||||
|
1
pager.py
1
pager.py
@ -1,3 +1,4 @@
|
|||||||
|
# -*- coding:utf-8 -*-
|
||||||
#
|
#
|
||||||
# Copyright (C) 2008 The Android Open Source Project
|
# Copyright (C) 2008 The Android Open Source Project
|
||||||
#
|
#
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
# -*- coding:utf-8 -*-
|
||||||
#
|
#
|
||||||
# Copyright (C) 2016 The Android Open Source Project
|
# Copyright (C) 2016 The Android Open Source Project
|
||||||
#
|
#
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
# -*- coding:utf-8 -*-
|
||||||
#
|
#
|
||||||
# Copyright (C) 2016 The Android Open Source Project
|
# Copyright (C) 2016 The Android Open Source Project
|
||||||
#
|
#
|
||||||
@ -17,7 +18,7 @@ import errno
|
|||||||
|
|
||||||
from ctypes import WinDLL, get_last_error, FormatError, WinError, addressof
|
from ctypes import WinDLL, get_last_error, FormatError, WinError, addressof
|
||||||
from ctypes import c_buffer
|
from ctypes import c_buffer
|
||||||
from ctypes.wintypes import BOOL, LPCWSTR, DWORD, HANDLE, POINTER, c_ubyte
|
from ctypes.wintypes import BOOL, BOOLEAN, LPCWSTR, DWORD, HANDLE, POINTER, c_ubyte
|
||||||
from ctypes.wintypes import WCHAR, USHORT, LPVOID, Structure, Union, ULONG
|
from ctypes.wintypes import WCHAR, USHORT, LPVOID, Structure, Union, ULONG
|
||||||
from ctypes.wintypes import byref
|
from ctypes.wintypes import byref
|
||||||
|
|
||||||
@ -33,7 +34,7 @@ ERROR_PRIVILEGE_NOT_HELD = 1314
|
|||||||
|
|
||||||
# Win32 API entry points
|
# Win32 API entry points
|
||||||
CreateSymbolicLinkW = kernel32.CreateSymbolicLinkW
|
CreateSymbolicLinkW = kernel32.CreateSymbolicLinkW
|
||||||
CreateSymbolicLinkW.restype = BOOL
|
CreateSymbolicLinkW.restype = BOOLEAN
|
||||||
CreateSymbolicLinkW.argtypes = (LPCWSTR, # lpSymlinkFileName In
|
CreateSymbolicLinkW.argtypes = (LPCWSTR, # lpSymlinkFileName In
|
||||||
LPCWSTR, # lpTargetFileName In
|
LPCWSTR, # lpTargetFileName In
|
||||||
DWORD) # dwFlags In
|
DWORD) # dwFlags In
|
||||||
@ -145,19 +146,12 @@ def create_dirsymlink(source, link_name):
|
|||||||
|
|
||||||
|
|
||||||
def _create_symlink(source, link_name, dwFlags):
|
def _create_symlink(source, link_name, dwFlags):
|
||||||
# Note: Win32 documentation for CreateSymbolicLink is incorrect.
|
if not CreateSymbolicLinkW(link_name, source, dwFlags | SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE):
|
||||||
# On success, the function returns "1".
|
|
||||||
# On error, the function returns some random value (e.g. 1280).
|
|
||||||
# The best bet seems to use "GetLastError" and check for error/success.
|
|
||||||
CreateSymbolicLinkW(link_name, source, dwFlags | SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE)
|
|
||||||
code = get_last_error()
|
|
||||||
if code != ERROR_SUCCESS:
|
|
||||||
# See https://github.com/golang/go/pull/24307/files#diff-b87bc12e4da2497308f9ef746086e4f0
|
# See https://github.com/golang/go/pull/24307/files#diff-b87bc12e4da2497308f9ef746086e4f0
|
||||||
# "the unprivileged create flag is unsupported below Windows 10 (1703, v10.0.14972).
|
# "the unprivileged create flag is unsupported below Windows 10 (1703, v10.0.14972).
|
||||||
# retry without it."
|
# retry without it."
|
||||||
CreateSymbolicLinkW(link_name, source, dwFlags)
|
if not CreateSymbolicLinkW(link_name, source, dwFlags):
|
||||||
code = get_last_error()
|
code = get_last_error()
|
||||||
if code != ERROR_SUCCESS:
|
|
||||||
error_desc = FormatError(code).strip()
|
error_desc = FormatError(code).strip()
|
||||||
if code == ERROR_PRIVILEGE_NOT_HELD:
|
if code == ERROR_PRIVILEGE_NOT_HELD:
|
||||||
raise OSError(errno.EPERM, error_desc, link_name)
|
raise OSError(errno.EPERM, error_desc, link_name)
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
# -*- coding:utf-8 -*-
|
||||||
#
|
#
|
||||||
# Copyright (C) 2009 The Android Open Source Project
|
# Copyright (C) 2009 The Android Open Source Project
|
||||||
#
|
#
|
||||||
|
67
project.py
67
project.py
@ -1,3 +1,5 @@
|
|||||||
|
# -*- coding:utf-8 -*-
|
||||||
|
#
|
||||||
# Copyright (C) 2008 The Android Open Source Project
|
# Copyright (C) 2008 The Android Open Source Project
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@ -1172,10 +1174,11 @@ class Project(object):
|
|||||||
|
|
||||||
ref_spec = '%s:refs/%s/%s' % (R_HEADS + branch.name, upload_type,
|
ref_spec = '%s:refs/%s/%s' % (R_HEADS + branch.name, upload_type,
|
||||||
dest_branch)
|
dest_branch)
|
||||||
|
opts = []
|
||||||
if auto_topic:
|
if auto_topic:
|
||||||
ref_spec = ref_spec + '/' + branch.name
|
opts += ['topic=' + branch.name]
|
||||||
|
|
||||||
opts = ['r=%s' % p for p in people[0]]
|
opts += ['r=%s' % p for p in people[0]]
|
||||||
opts += ['cc=%s' % p for p in people[1]]
|
opts += ['cc=%s' % p for p in people[1]]
|
||||||
if notify:
|
if notify:
|
||||||
opts += ['notify=' + notify]
|
opts += ['notify=' + notify]
|
||||||
@ -1223,7 +1226,8 @@ class Project(object):
|
|||||||
archive=False,
|
archive=False,
|
||||||
optimized_fetch=False,
|
optimized_fetch=False,
|
||||||
prune=False,
|
prune=False,
|
||||||
submodules=False):
|
submodules=False,
|
||||||
|
clone_filter=None):
|
||||||
"""Perform only the network IO portion of the sync process.
|
"""Perform only the network IO portion of the sync process.
|
||||||
Local working directory/branch state is not affected.
|
Local working directory/branch state is not affected.
|
||||||
"""
|
"""
|
||||||
@ -1306,7 +1310,8 @@ class Project(object):
|
|||||||
not self._RemoteFetch(initial=is_new, quiet=quiet, alt_dir=alt_dir,
|
not self._RemoteFetch(initial=is_new, quiet=quiet, alt_dir=alt_dir,
|
||||||
current_branch_only=current_branch_only,
|
current_branch_only=current_branch_only,
|
||||||
no_tags=no_tags, prune=prune, depth=depth,
|
no_tags=no_tags, prune=prune, depth=depth,
|
||||||
submodules=submodules)):
|
submodules=submodules, force_sync=force_sync,
|
||||||
|
clone_filter=clone_filter)):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
mp = self.manifest.manifestProject
|
mp = self.manifest.manifestProject
|
||||||
@ -1807,8 +1812,8 @@ class Project(object):
|
|||||||
submodules.append((sub_rev, sub_path, sub_url))
|
submodules.append((sub_rev, sub_path, sub_url))
|
||||||
return submodules
|
return submodules
|
||||||
|
|
||||||
re_path = re.compile(r'^submodule\.([^.]+)\.path=(.*)$')
|
re_path = re.compile(r'^submodule\.(.+)\.path=(.*)$')
|
||||||
re_url = re.compile(r'^submodule\.([^.]+)\.url=(.*)$')
|
re_url = re.compile(r'^submodule\.(.+)\.url=(.*)$')
|
||||||
|
|
||||||
def parse_gitmodules(gitdir, rev):
|
def parse_gitmodules(gitdir, rev):
|
||||||
cmd = ['cat-file', 'blob', '%s:.gitmodules' % rev]
|
cmd = ['cat-file', 'blob', '%s:.gitmodules' % rev]
|
||||||
@ -1955,7 +1960,9 @@ class Project(object):
|
|||||||
no_tags=False,
|
no_tags=False,
|
||||||
prune=False,
|
prune=False,
|
||||||
depth=None,
|
depth=None,
|
||||||
submodules=False):
|
submodules=False,
|
||||||
|
force_sync=False,
|
||||||
|
clone_filter=None):
|
||||||
|
|
||||||
is_sha1 = False
|
is_sha1 = False
|
||||||
tag_name = None
|
tag_name = None
|
||||||
@ -1979,8 +1986,9 @@ class Project(object):
|
|||||||
|
|
||||||
if is_sha1 or tag_name is not None:
|
if is_sha1 or tag_name is not None:
|
||||||
if self._CheckForImmutableRevision():
|
if self._CheckForImmutableRevision():
|
||||||
print('Skipped fetching project %s (already have persistent ref)'
|
if not quiet:
|
||||||
% self.name)
|
print('Skipped fetching project %s (already have persistent ref)'
|
||||||
|
% self.name)
|
||||||
return True
|
return True
|
||||||
if is_sha1 and not depth:
|
if is_sha1 and not depth:
|
||||||
# When syncing a specific commit and --depth is not set:
|
# When syncing a specific commit and --depth is not set:
|
||||||
@ -2045,6 +2053,11 @@ class Project(object):
|
|||||||
|
|
||||||
cmd = ['fetch']
|
cmd = ['fetch']
|
||||||
|
|
||||||
|
if clone_filter:
|
||||||
|
git_require((2, 19, 0), fail=True, msg='partial clones')
|
||||||
|
cmd.append('--filter=%s' % clone_filter)
|
||||||
|
self.config.SetString('extensions.partialclone', self.remote.name)
|
||||||
|
|
||||||
if depth:
|
if depth:
|
||||||
cmd.append('--depth=%s' % depth)
|
cmd.append('--depth=%s' % depth)
|
||||||
else:
|
else:
|
||||||
@ -2068,6 +2081,9 @@ class Project(object):
|
|||||||
else:
|
else:
|
||||||
cmd.append('--tags')
|
cmd.append('--tags')
|
||||||
|
|
||||||
|
if force_sync:
|
||||||
|
cmd.append('--force')
|
||||||
|
|
||||||
if prune:
|
if prune:
|
||||||
cmd.append('--prune')
|
cmd.append('--prune')
|
||||||
|
|
||||||
@ -2142,12 +2158,12 @@ class Project(object):
|
|||||||
return self._RemoteFetch(name=name,
|
return self._RemoteFetch(name=name,
|
||||||
current_branch_only=current_branch_only,
|
current_branch_only=current_branch_only,
|
||||||
initial=False, quiet=quiet, alt_dir=alt_dir,
|
initial=False, quiet=quiet, alt_dir=alt_dir,
|
||||||
depth=None)
|
depth=None, clone_filter=clone_filter)
|
||||||
else:
|
else:
|
||||||
# Avoid infinite recursion: sync all branches with depth set to None
|
# Avoid infinite recursion: sync all branches with depth set to None
|
||||||
return self._RemoteFetch(name=name, current_branch_only=False,
|
return self._RemoteFetch(name=name, current_branch_only=False,
|
||||||
initial=False, quiet=quiet, alt_dir=alt_dir,
|
initial=False, quiet=quiet, alt_dir=alt_dir,
|
||||||
depth=None)
|
depth=None, clone_filter=clone_filter)
|
||||||
|
|
||||||
return ok
|
return ok
|
||||||
|
|
||||||
@ -2209,13 +2225,17 @@ class Project(object):
|
|||||||
cmd += ['--continue-at', '%d' % (size,)]
|
cmd += ['--continue-at', '%d' % (size,)]
|
||||||
else:
|
else:
|
||||||
platform_utils.remove(tmpPath)
|
platform_utils.remove(tmpPath)
|
||||||
if 'http_proxy' in os.environ and 'darwin' == sys.platform:
|
with GetUrlCookieFile(srcUrl, quiet) as (cookiefile, proxy):
|
||||||
cmd += ['--proxy', os.environ['http_proxy']]
|
|
||||||
with GetUrlCookieFile(srcUrl, quiet) as (cookiefile, _proxy):
|
|
||||||
if cookiefile:
|
if cookiefile:
|
||||||
cmd += ['--cookie', cookiefile, '--cookie-jar', cookiefile]
|
cmd += ['--cookie', cookiefile, '--cookie-jar', cookiefile]
|
||||||
if srcUrl.startswith('persistent-'):
|
if proxy:
|
||||||
srcUrl = srcUrl[len('persistent-'):]
|
cmd += ['--proxy', proxy]
|
||||||
|
elif 'http_proxy' in os.environ and 'darwin' == sys.platform:
|
||||||
|
cmd += ['--proxy', os.environ['http_proxy']]
|
||||||
|
if srcUrl.startswith('persistent-https'):
|
||||||
|
srcUrl = 'http' + srcUrl[len('persistent-https'):]
|
||||||
|
elif srcUrl.startswith('persistent-http'):
|
||||||
|
srcUrl = 'http' + srcUrl[len('persistent-http'):]
|
||||||
cmd += [srcUrl]
|
cmd += [srcUrl]
|
||||||
|
|
||||||
if IsTrace():
|
if IsTrace():
|
||||||
@ -2249,8 +2269,8 @@ class Project(object):
|
|||||||
|
|
||||||
def _IsValidBundle(self, path, quiet):
|
def _IsValidBundle(self, path, quiet):
|
||||||
try:
|
try:
|
||||||
with open(path) as f:
|
with open(path, 'rb') as f:
|
||||||
if f.read(16) == '# v2 git bundle\n':
|
if f.read(16) == b'# v2 git bundle\n':
|
||||||
return True
|
return True
|
||||||
else:
|
else:
|
||||||
if not quiet:
|
if not quiet:
|
||||||
@ -2393,6 +2413,7 @@ class Project(object):
|
|||||||
if m.Has(key, include_defaults=False):
|
if m.Has(key, include_defaults=False):
|
||||||
self.config.SetString(key, m.GetString(key))
|
self.config.SetString(key, m.GetString(key))
|
||||||
self.config.SetString('filter.lfs.smudge', 'git-lfs smudge --skip -- %f')
|
self.config.SetString('filter.lfs.smudge', 'git-lfs smudge --skip -- %f')
|
||||||
|
self.config.SetString('filter.lfs.process', 'git-lfs filter-process --skip')
|
||||||
if self.manifest.IsMirror:
|
if self.manifest.IsMirror:
|
||||||
self.config.SetString('core.bare', 'true')
|
self.config.SetString('core.bare', 'true')
|
||||||
else:
|
else:
|
||||||
@ -2584,7 +2605,7 @@ class Project(object):
|
|||||||
cmd.append('-v')
|
cmd.append('-v')
|
||||||
cmd.append(HEAD)
|
cmd.append(HEAD)
|
||||||
if GitCommand(self, cmd).Wait() != 0:
|
if GitCommand(self, cmd).Wait() != 0:
|
||||||
raise GitError("cannot initialize work tree")
|
raise GitError("cannot initialize work tree for " + self.name)
|
||||||
|
|
||||||
if submodules:
|
if submodules:
|
||||||
self._SyncSubmodules(quiet=True)
|
self._SyncSubmodules(quiet=True)
|
||||||
@ -2684,6 +2705,7 @@ class Project(object):
|
|||||||
def DiffZ(self, name, *args):
|
def DiffZ(self, name, *args):
|
||||||
cmd = [name]
|
cmd = [name]
|
||||||
cmd.append('-z')
|
cmd.append('-z')
|
||||||
|
cmd.append('--ignore-submodules')
|
||||||
cmd.extend(args)
|
cmd.extend(args)
|
||||||
p = GitCommand(self._project,
|
p = GitCommand(self._project,
|
||||||
cmd,
|
cmd,
|
||||||
@ -2801,15 +2823,10 @@ class Project(object):
|
|||||||
gitdir=self._gitdir,
|
gitdir=self._gitdir,
|
||||||
capture_stdout=True,
|
capture_stdout=True,
|
||||||
capture_stderr=True)
|
capture_stderr=True)
|
||||||
r = []
|
|
||||||
for line in p.process.stdout:
|
|
||||||
if line[-1] == '\n':
|
|
||||||
line = line[:-1]
|
|
||||||
r.append(line)
|
|
||||||
if p.Wait() != 0:
|
if p.Wait() != 0:
|
||||||
raise GitError('%s rev-list %s: %s' %
|
raise GitError('%s rev-list %s: %s' %
|
||||||
(self._project.name, str(args), p.stderr))
|
(self._project.name, str(args), p.stderr))
|
||||||
return r
|
return p.stdout.splitlines()
|
||||||
|
|
||||||
def __getattr__(self, name):
|
def __getattr__(self, name):
|
||||||
"""Allow arbitrary git commands using pythonic syntax.
|
"""Allow arbitrary git commands using pythonic syntax.
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
# -*- coding:utf-8 -*-
|
||||||
#
|
#
|
||||||
# Copyright (C) 2013 The Android Open Source Project
|
# Copyright (C) 2013 The Android Open Source Project
|
||||||
#
|
#
|
||||||
|
222
repo
222
repo
@ -1,4 +1,14 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
|
# -*- coding:utf-8 -*-
|
||||||
|
|
||||||
|
"""Repo launcher.
|
||||||
|
|
||||||
|
This is a standalone tool that people may copy to anywhere in their system.
|
||||||
|
It is used to get an initial repo client checkout, and after that it runs the
|
||||||
|
copy of repo in the checkout.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import print_function
|
||||||
|
|
||||||
# repo default configuration
|
# repo default configuration
|
||||||
#
|
#
|
||||||
@ -23,7 +33,7 @@ REPO_REV = 'stable'
|
|||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
# increment this whenever we make important changes to this script
|
# increment this whenever we make important changes to this script
|
||||||
VERSION = (1, 24)
|
VERSION = (1, 25)
|
||||||
|
|
||||||
# increment this if the MAINTAINER_KEYS block is modified
|
# increment this if the MAINTAINER_KEYS block is modified
|
||||||
KEYRING_VERSION = (1, 2)
|
KEYRING_VERSION = (1, 2)
|
||||||
@ -113,11 +123,12 @@ repodir = '.repo' # name of repo's private directory
|
|||||||
S_repo = 'repo' # special repo repository
|
S_repo = 'repo' # special repo repository
|
||||||
S_manifests = 'manifests' # special manifest repository
|
S_manifests = 'manifests' # special manifest repository
|
||||||
REPO_MAIN = S_repo + '/main.py' # main script
|
REPO_MAIN = S_repo + '/main.py' # main script
|
||||||
MIN_PYTHON_VERSION = (2, 6) # minimum supported python version
|
MIN_PYTHON_VERSION = (2, 7) # minimum supported python version
|
||||||
GITC_CONFIG_FILE = '/gitc/.config'
|
GITC_CONFIG_FILE = '/gitc/.config'
|
||||||
GITC_FS_ROOT_DIR = '/gitc/manifest-rw/'
|
GITC_FS_ROOT_DIR = '/gitc/manifest-rw/'
|
||||||
|
|
||||||
|
|
||||||
|
import collections
|
||||||
import errno
|
import errno
|
||||||
import optparse
|
import optparse
|
||||||
import platform
|
import platform
|
||||||
@ -138,23 +149,15 @@ else:
|
|||||||
urllib.error = urllib2
|
urllib.error = urllib2
|
||||||
|
|
||||||
|
|
||||||
def _print(*objects, **kwargs):
|
|
||||||
sep = kwargs.get('sep', ' ')
|
|
||||||
end = kwargs.get('end', '\n')
|
|
||||||
out = kwargs.get('file', sys.stdout)
|
|
||||||
out.write(sep.join(objects) + end)
|
|
||||||
|
|
||||||
# On Windows stderr is buffered, so flush to maintain the order of error messages.
|
|
||||||
if out == sys.stderr and platform.system() == "Windows":
|
|
||||||
out.flush()
|
|
||||||
|
|
||||||
|
|
||||||
# Python version check
|
# Python version check
|
||||||
ver = sys.version_info
|
ver = sys.version_info
|
||||||
if (ver[0], ver[1]) < MIN_PYTHON_VERSION:
|
if (ver[0], ver[1]) < MIN_PYTHON_VERSION:
|
||||||
_print('error: Python version %s unsupported.\n'
|
print('error: Python version {} unsupported.\n'
|
||||||
'Please use Python 2.6 - 2.7 instead.'
|
'Please use Python {}.{} instead.'.format(
|
||||||
% sys.version.split(' ')[0], file=sys.stderr)
|
sys.version.split(' ')[0],
|
||||||
|
MIN_PYTHON_VERSION[0],
|
||||||
|
MIN_PYTHON_VERSION[1],
|
||||||
|
), file=sys.stderr)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
home_dot_repo = os.path.expanduser('~/.repoconfig')
|
home_dot_repo = os.path.expanduser('~/.repoconfig')
|
||||||
@ -180,7 +183,7 @@ group.add_option('-b', '--manifest-branch',
|
|||||||
group.add_option('-m', '--manifest-name',
|
group.add_option('-m', '--manifest-name',
|
||||||
dest='manifest_name',
|
dest='manifest_name',
|
||||||
help='initial manifest file', metavar='NAME.xml')
|
help='initial manifest file', metavar='NAME.xml')
|
||||||
group.add_option('-c', '--current-branch',
|
group.add_option('--current-branch',
|
||||||
dest='current_branch_only', action='store_true',
|
dest='current_branch_only', action='store_true',
|
||||||
help='fetch only current manifest branch from server')
|
help='fetch only current manifest branch from server')
|
||||||
group.add_option('--mirror',
|
group.add_option('--mirror',
|
||||||
@ -196,6 +199,13 @@ group.add_option('--dissociate',
|
|||||||
group.add_option('--depth', type='int', default=None,
|
group.add_option('--depth', type='int', default=None,
|
||||||
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')
|
||||||
|
group.add_option('--partial-clone', action='store_true',
|
||||||
|
dest='partial_clone',
|
||||||
|
help='perform partial clone (https://git-scm.com/'
|
||||||
|
'docs/gitrepository-layout#_code_partialclone_code)')
|
||||||
|
group.add_option('--clone-filter', action='store', default='blob:none',
|
||||||
|
dest='clone_filter',
|
||||||
|
help='filter for use with --partial-clone [default: %default]')
|
||||||
group.add_option('--archive',
|
group.add_option('--archive',
|
||||||
dest='archive', action='store_true',
|
dest='archive', action='store_true',
|
||||||
help='checkout an archive instead of a git repository for '
|
help='checkout an archive instead of a git repository for '
|
||||||
@ -320,21 +330,21 @@ def _Init(args, gitc_init=False):
|
|||||||
if branch.startswith('refs/heads/'):
|
if branch.startswith('refs/heads/'):
|
||||||
branch = branch[len('refs/heads/'):]
|
branch = branch[len('refs/heads/'):]
|
||||||
if branch.startswith('refs/'):
|
if branch.startswith('refs/'):
|
||||||
_print("fatal: invalid branch name '%s'" % branch, file=sys.stderr)
|
print("fatal: invalid branch name '%s'" % branch, file=sys.stderr)
|
||||||
raise CloneFailure()
|
raise CloneFailure()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if gitc_init:
|
if gitc_init:
|
||||||
gitc_manifest_dir = get_gitc_manifest_dir()
|
gitc_manifest_dir = get_gitc_manifest_dir()
|
||||||
if not gitc_manifest_dir:
|
if not gitc_manifest_dir:
|
||||||
_print('fatal: GITC filesystem is not available. Exiting...',
|
print('fatal: GITC filesystem is not available. Exiting...',
|
||||||
file=sys.stderr)
|
file=sys.stderr)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
gitc_client = opt.gitc_client
|
gitc_client = opt.gitc_client
|
||||||
if not gitc_client:
|
if not gitc_client:
|
||||||
gitc_client = gitc_parse_clientdir(os.getcwd())
|
gitc_client = gitc_parse_clientdir(os.getcwd())
|
||||||
if not gitc_client:
|
if not gitc_client:
|
||||||
_print('fatal: GITC client (-c) is required.', file=sys.stderr)
|
print('fatal: GITC client (-c) is required.', file=sys.stderr)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
client_dir = os.path.join(gitc_manifest_dir, gitc_client)
|
client_dir = os.path.join(gitc_manifest_dir, gitc_client)
|
||||||
if not os.path.exists(client_dir):
|
if not os.path.exists(client_dir):
|
||||||
@ -347,8 +357,8 @@ def _Init(args, gitc_init=False):
|
|||||||
os.mkdir(repodir)
|
os.mkdir(repodir)
|
||||||
except OSError as e:
|
except OSError as e:
|
||||||
if e.errno != errno.EEXIST:
|
if e.errno != errno.EEXIST:
|
||||||
_print('fatal: cannot make %s directory: %s'
|
print('fatal: cannot make %s directory: %s'
|
||||||
% (repodir, e.strerror), file=sys.stderr)
|
% (repodir, e.strerror), file=sys.stderr)
|
||||||
# Don't raise CloneFailure; that would delete the
|
# Don't raise CloneFailure; that would delete the
|
||||||
# name. Instead exit immediately.
|
# name. Instead exit immediately.
|
||||||
#
|
#
|
||||||
@ -372,55 +382,73 @@ def _Init(args, gitc_init=False):
|
|||||||
_Checkout(dst, branch, rev, opt.quiet)
|
_Checkout(dst, branch, rev, opt.quiet)
|
||||||
|
|
||||||
if not os.path.isfile(os.path.join(dst, 'repo')):
|
if not os.path.isfile(os.path.join(dst, 'repo')):
|
||||||
_print("warning: '%s' does not look like a git-repo repository, is "
|
print("warning: '%s' does not look like a git-repo repository, is "
|
||||||
"REPO_URL set correctly?" % url, file=sys.stderr)
|
"REPO_URL set correctly?" % url, file=sys.stderr)
|
||||||
|
|
||||||
except CloneFailure:
|
except CloneFailure:
|
||||||
if opt.quiet:
|
if opt.quiet:
|
||||||
_print('fatal: repo init failed; run without --quiet to see why',
|
print('fatal: repo init failed; run without --quiet to see why',
|
||||||
file=sys.stderr)
|
file=sys.stderr)
|
||||||
raise
|
raise
|
||||||
|
|
||||||
|
|
||||||
def ParseGitVersion(ver_str):
|
# The git version info broken down into components for easy analysis.
|
||||||
|
# Similar to Python's sys.version_info.
|
||||||
|
GitVersion = collections.namedtuple(
|
||||||
|
'GitVersion', ('major', 'minor', 'micro', 'full'))
|
||||||
|
|
||||||
|
def ParseGitVersion(ver_str=None):
|
||||||
|
if ver_str is None:
|
||||||
|
# Load the version ourselves.
|
||||||
|
ver_str = _GetGitVersion()
|
||||||
|
|
||||||
if not ver_str.startswith('git version '):
|
if not ver_str.startswith('git version '):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
num_ver_str = ver_str[len('git version '):].strip().split('-')[0]
|
full_version = ver_str[len('git version '):].strip()
|
||||||
|
num_ver_str = full_version.split('-')[0]
|
||||||
to_tuple = []
|
to_tuple = []
|
||||||
for num_str in num_ver_str.split('.')[:3]:
|
for num_str in num_ver_str.split('.')[:3]:
|
||||||
if num_str.isdigit():
|
if num_str.isdigit():
|
||||||
to_tuple.append(int(num_str))
|
to_tuple.append(int(num_str))
|
||||||
else:
|
else:
|
||||||
to_tuple.append(0)
|
to_tuple.append(0)
|
||||||
return tuple(to_tuple)
|
to_tuple.append(full_version)
|
||||||
|
return GitVersion(*to_tuple)
|
||||||
|
|
||||||
|
|
||||||
def _CheckGitVersion():
|
def _GetGitVersion():
|
||||||
cmd = [GIT, '--version']
|
cmd = [GIT, '--version']
|
||||||
try:
|
try:
|
||||||
proc = subprocess.Popen(cmd, stdout=subprocess.PIPE)
|
proc = subprocess.Popen(cmd, stdout=subprocess.PIPE)
|
||||||
except OSError as e:
|
except OSError as e:
|
||||||
_print(file=sys.stderr)
|
print(file=sys.stderr)
|
||||||
_print("fatal: '%s' is not available" % GIT, file=sys.stderr)
|
print("fatal: '%s' is not available" % GIT, file=sys.stderr)
|
||||||
_print('fatal: %s' % e, file=sys.stderr)
|
print('fatal: %s' % e, file=sys.stderr)
|
||||||
_print(file=sys.stderr)
|
print(file=sys.stderr)
|
||||||
_print('Please make sure %s is installed and in your path.' % GIT,
|
print('Please make sure %s is installed and in your path.' % GIT,
|
||||||
file=sys.stderr)
|
file=sys.stderr)
|
||||||
raise CloneFailure()
|
raise
|
||||||
|
|
||||||
ver_str = proc.stdout.read().strip()
|
ver_str = proc.stdout.read().strip()
|
||||||
proc.stdout.close()
|
proc.stdout.close()
|
||||||
proc.wait()
|
proc.wait()
|
||||||
|
return ver_str.decode('utf-8')
|
||||||
|
|
||||||
|
|
||||||
|
def _CheckGitVersion():
|
||||||
|
try:
|
||||||
|
ver_act = ParseGitVersion()
|
||||||
|
except OSError:
|
||||||
|
raise CloneFailure()
|
||||||
|
|
||||||
ver_act = ParseGitVersion(ver_str)
|
|
||||||
if ver_act is None:
|
if ver_act is None:
|
||||||
_print('error: "%s" unsupported' % ver_str, file=sys.stderr)
|
print('error: "%s" unsupported' % ver_str, file=sys.stderr)
|
||||||
raise CloneFailure()
|
raise CloneFailure()
|
||||||
|
|
||||||
if ver_act < MIN_GIT_VERSION:
|
if ver_act < MIN_GIT_VERSION:
|
||||||
need = '.'.join(map(str, MIN_GIT_VERSION))
|
need = '.'.join(map(str, MIN_GIT_VERSION))
|
||||||
_print('fatal: git %s or later required' % need, file=sys.stderr)
|
print('fatal: git %s or later required' % need, file=sys.stderr)
|
||||||
raise CloneFailure()
|
raise CloneFailure()
|
||||||
|
|
||||||
|
|
||||||
@ -447,16 +475,16 @@ def SetupGnuPG(quiet):
|
|||||||
os.mkdir(home_dot_repo)
|
os.mkdir(home_dot_repo)
|
||||||
except OSError as e:
|
except OSError as e:
|
||||||
if e.errno != errno.EEXIST:
|
if e.errno != errno.EEXIST:
|
||||||
_print('fatal: cannot make %s directory: %s'
|
print('fatal: cannot make %s directory: %s'
|
||||||
% (home_dot_repo, e.strerror), file=sys.stderr)
|
% (home_dot_repo, e.strerror), file=sys.stderr)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
os.mkdir(gpg_dir, stat.S_IRWXU)
|
os.mkdir(gpg_dir, stat.S_IRWXU)
|
||||||
except OSError as e:
|
except OSError as e:
|
||||||
if e.errno != errno.EEXIST:
|
if e.errno != errno.EEXIST:
|
||||||
_print('fatal: cannot make %s directory: %s' % (gpg_dir, e.strerror),
|
print('fatal: cannot make %s directory: %s' % (gpg_dir, e.strerror),
|
||||||
file=sys.stderr)
|
file=sys.stderr)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
env = os.environ.copy()
|
env = os.environ.copy()
|
||||||
@ -472,18 +500,18 @@ def SetupGnuPG(quiet):
|
|||||||
stdin=subprocess.PIPE)
|
stdin=subprocess.PIPE)
|
||||||
except OSError as e:
|
except OSError as e:
|
||||||
if not quiet:
|
if not quiet:
|
||||||
_print('warning: gpg (GnuPG) is not available.', file=sys.stderr)
|
print('warning: gpg (GnuPG) is not available.', file=sys.stderr)
|
||||||
_print('warning: Installing it is strongly encouraged.', file=sys.stderr)
|
print('warning: Installing it is strongly encouraged.', file=sys.stderr)
|
||||||
_print(file=sys.stderr)
|
print(file=sys.stderr)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
proc.stdin.write(MAINTAINER_KEYS)
|
proc.stdin.write(MAINTAINER_KEYS)
|
||||||
proc.stdin.close()
|
proc.stdin.close()
|
||||||
|
|
||||||
if proc.wait() != 0:
|
if proc.wait() != 0:
|
||||||
_print('fatal: registering repo maintainer keys failed', file=sys.stderr)
|
print('fatal: registering repo maintainer keys failed', file=sys.stderr)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
_print()
|
print()
|
||||||
|
|
||||||
fd = open(os.path.join(home_dot_repo, 'keyring-version'), 'w')
|
fd = open(os.path.join(home_dot_repo, 'keyring-version'), 'w')
|
||||||
fd.write('.'.join(map(str, KEYRING_VERSION)) + '\n')
|
fd.write('.'.join(map(str, KEYRING_VERSION)) + '\n')
|
||||||
@ -526,7 +554,7 @@ def _InitHttp():
|
|||||||
|
|
||||||
def _Fetch(url, local, src, quiet):
|
def _Fetch(url, local, src, quiet):
|
||||||
if not quiet:
|
if not quiet:
|
||||||
_print('Get %s' % url, file=sys.stderr)
|
print('Get %s' % url, file=sys.stderr)
|
||||||
|
|
||||||
cmd = [GIT, 'fetch']
|
cmd = [GIT, 'fetch']
|
||||||
if quiet:
|
if quiet:
|
||||||
@ -576,19 +604,19 @@ def _DownloadBundle(url, local, quiet):
|
|||||||
except urllib.error.HTTPError as e:
|
except urllib.error.HTTPError as e:
|
||||||
if e.code in [401, 403, 404, 501]:
|
if e.code in [401, 403, 404, 501]:
|
||||||
return False
|
return False
|
||||||
_print('fatal: Cannot get %s' % url, file=sys.stderr)
|
print('fatal: Cannot get %s' % url, file=sys.stderr)
|
||||||
_print('fatal: HTTP error %s' % e.code, file=sys.stderr)
|
print('fatal: HTTP error %s' % e.code, file=sys.stderr)
|
||||||
raise CloneFailure()
|
raise CloneFailure()
|
||||||
except urllib.error.URLError as e:
|
except urllib.error.URLError as e:
|
||||||
_print('fatal: Cannot get %s' % url, file=sys.stderr)
|
print('fatal: Cannot get %s' % url, file=sys.stderr)
|
||||||
_print('fatal: error %s' % e.reason, file=sys.stderr)
|
print('fatal: error %s' % e.reason, file=sys.stderr)
|
||||||
raise CloneFailure()
|
raise CloneFailure()
|
||||||
try:
|
try:
|
||||||
if not quiet:
|
if not quiet:
|
||||||
_print('Get %s' % url, file=sys.stderr)
|
print('Get %s' % url, file=sys.stderr)
|
||||||
while True:
|
while True:
|
||||||
buf = r.read(8192)
|
buf = r.read(8192)
|
||||||
if buf == '':
|
if not buf:
|
||||||
return True
|
return True
|
||||||
dest.write(buf)
|
dest.write(buf)
|
||||||
finally:
|
finally:
|
||||||
@ -611,23 +639,23 @@ def _Clone(url, local, quiet, clone_bundle):
|
|||||||
try:
|
try:
|
||||||
os.mkdir(local)
|
os.mkdir(local)
|
||||||
except OSError as e:
|
except OSError as e:
|
||||||
_print('fatal: cannot make %s directory: %s' % (local, e.strerror),
|
print('fatal: cannot make %s directory: %s' % (local, e.strerror),
|
||||||
file=sys.stderr)
|
file=sys.stderr)
|
||||||
raise CloneFailure()
|
raise CloneFailure()
|
||||||
|
|
||||||
cmd = [GIT, 'init', '--quiet']
|
cmd = [GIT, 'init', '--quiet']
|
||||||
try:
|
try:
|
||||||
proc = subprocess.Popen(cmd, cwd=local)
|
proc = subprocess.Popen(cmd, cwd=local)
|
||||||
except OSError as e:
|
except OSError as e:
|
||||||
_print(file=sys.stderr)
|
print(file=sys.stderr)
|
||||||
_print("fatal: '%s' is not available" % GIT, file=sys.stderr)
|
print("fatal: '%s' is not available" % GIT, file=sys.stderr)
|
||||||
_print('fatal: %s' % e, file=sys.stderr)
|
print('fatal: %s' % e, file=sys.stderr)
|
||||||
_print(file=sys.stderr)
|
print(file=sys.stderr)
|
||||||
_print('Please make sure %s is installed and in your path.' % GIT,
|
print('Please make sure %s is installed and in your path.' % GIT,
|
||||||
file=sys.stderr)
|
file=sys.stderr)
|
||||||
raise CloneFailure()
|
raise CloneFailure()
|
||||||
if proc.wait() != 0:
|
if proc.wait() != 0:
|
||||||
_print('fatal: could not create %s' % local, file=sys.stderr)
|
print('fatal: could not create %s' % local, file=sys.stderr)
|
||||||
raise CloneFailure()
|
raise CloneFailure()
|
||||||
|
|
||||||
_InitHttp()
|
_InitHttp()
|
||||||
@ -655,18 +683,18 @@ def _Verify(cwd, branch, quiet):
|
|||||||
proc.stderr.close()
|
proc.stderr.close()
|
||||||
|
|
||||||
if proc.wait() != 0 or not cur:
|
if proc.wait() != 0 or not cur:
|
||||||
_print(file=sys.stderr)
|
print(file=sys.stderr)
|
||||||
_print("fatal: branch '%s' has not been signed" % branch, file=sys.stderr)
|
print("fatal: branch '%s' has not been signed" % branch, file=sys.stderr)
|
||||||
raise CloneFailure()
|
raise CloneFailure()
|
||||||
|
|
||||||
m = re.compile(r'^(.*)-[0-9]{1,}-g[0-9a-f]{1,}$').match(cur)
|
m = re.compile(r'^(.*)-[0-9]{1,}-g[0-9a-f]{1,}$').match(cur)
|
||||||
if m:
|
if m:
|
||||||
cur = m.group(1)
|
cur = m.group(1)
|
||||||
if not quiet:
|
if not quiet:
|
||||||
_print(file=sys.stderr)
|
print(file=sys.stderr)
|
||||||
_print("info: Ignoring branch '%s'; using tagged release '%s'"
|
print("info: Ignoring branch '%s'; using tagged release '%s'"
|
||||||
% (branch, cur), file=sys.stderr)
|
% (branch, cur), file=sys.stderr)
|
||||||
_print(file=sys.stderr)
|
print(file=sys.stderr)
|
||||||
|
|
||||||
env = os.environ.copy()
|
env = os.environ.copy()
|
||||||
try:
|
try:
|
||||||
@ -687,10 +715,10 @@ def _Verify(cwd, branch, quiet):
|
|||||||
proc.stderr.close()
|
proc.stderr.close()
|
||||||
|
|
||||||
if proc.wait() != 0:
|
if proc.wait() != 0:
|
||||||
_print(file=sys.stderr)
|
print(file=sys.stderr)
|
||||||
_print(out, file=sys.stderr)
|
print(out, file=sys.stderr)
|
||||||
_print(err, file=sys.stderr)
|
print(err, file=sys.stderr)
|
||||||
_print(file=sys.stderr)
|
print(file=sys.stderr)
|
||||||
raise CloneFailure()
|
raise CloneFailure()
|
||||||
return '%s^0' % cur
|
return '%s^0' % cur
|
||||||
|
|
||||||
@ -761,7 +789,7 @@ def _Usage():
|
|||||||
if get_gitc_manifest_dir():
|
if get_gitc_manifest_dir():
|
||||||
gitc_usage = " gitc-init Initialize a GITC Client.\n"
|
gitc_usage = " gitc-init Initialize a GITC Client.\n"
|
||||||
|
|
||||||
_print(
|
print(
|
||||||
"""usage: repo COMMAND [ARGS]
|
"""usage: repo COMMAND [ARGS]
|
||||||
|
|
||||||
repo is not yet installed. Use "repo init" to install it here.
|
repo is not yet installed. Use "repo init" to install it here.
|
||||||
@ -773,8 +801,8 @@ The most commonly used repo commands are:
|
|||||||
""" help Display detailed help on a command
|
""" help Display detailed help on a command
|
||||||
|
|
||||||
For access to the full online help, install repo ("repo init").
|
For access to the full online help, install repo ("repo init").
|
||||||
""", file=sys.stderr)
|
""")
|
||||||
sys.exit(1)
|
sys.exit(0)
|
||||||
|
|
||||||
|
|
||||||
def _Help(args):
|
def _Help(args):
|
||||||
@ -787,23 +815,23 @@ def _Help(args):
|
|||||||
init_optparse.print_help()
|
init_optparse.print_help()
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
else:
|
else:
|
||||||
_print("error: '%s' is not a bootstrap command.\n"
|
print("error: '%s' is not a bootstrap command.\n"
|
||||||
' For access to online help, install repo ("repo init").'
|
' For access to online help, install repo ("repo init").'
|
||||||
% args[0], file=sys.stderr)
|
% args[0], file=sys.stderr)
|
||||||
else:
|
else:
|
||||||
_Usage()
|
_Usage()
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
def _NotInstalled():
|
def _NotInstalled():
|
||||||
_print('error: repo is not installed. Use "repo init" to install it here.',
|
print('error: repo is not installed. Use "repo init" to install it here.',
|
||||||
file=sys.stderr)
|
file=sys.stderr)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
def _NoCommands(cmd):
|
def _NoCommands(cmd):
|
||||||
_print("""error: command '%s' requires repo to be installed first.
|
print("""error: command '%s' requires repo to be installed first.
|
||||||
Use "repo init" to install it here.""" % cmd, file=sys.stderr)
|
Use "repo init" to install it here.""" % cmd, file=sys.stderr)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
@ -840,7 +868,7 @@ def _SetDefaultsTo(gitdir):
|
|||||||
proc.stderr.close()
|
proc.stderr.close()
|
||||||
|
|
||||||
if proc.wait() != 0:
|
if proc.wait() != 0:
|
||||||
_print('fatal: %s has no current branch' % gitdir, file=sys.stderr)
|
print('fatal: %s has no current branch' % gitdir, file=sys.stderr)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
@ -857,10 +885,10 @@ def main(orig_args):
|
|||||||
|
|
||||||
cwd = os.getcwd()
|
cwd = os.getcwd()
|
||||||
if get_gitc_manifest_dir() and cwd.startswith(get_gitc_manifest_dir()):
|
if get_gitc_manifest_dir() and cwd.startswith(get_gitc_manifest_dir()):
|
||||||
_print('error: repo cannot be used in the GITC local manifest directory.'
|
print('error: repo cannot be used in the GITC local manifest directory.'
|
||||||
'\nIf you want to work on this GITC client please rerun this '
|
'\nIf you want to work on this GITC client please rerun this '
|
||||||
'command from the corresponding client under /gitc/',
|
'command from the corresponding client under /gitc/',
|
||||||
file=sys.stderr)
|
file=sys.stderr)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
if not repo_main:
|
if not repo_main:
|
||||||
if opt.help:
|
if opt.help:
|
||||||
@ -876,8 +904,8 @@ def main(orig_args):
|
|||||||
_Init(args, gitc_init=(cmd == 'gitc-init'))
|
_Init(args, gitc_init=(cmd == 'gitc-init'))
|
||||||
except CloneFailure:
|
except CloneFailure:
|
||||||
path = os.path.join(repodir, S_repo)
|
path = os.path.join(repodir, S_repo)
|
||||||
_print("fatal: cloning the git-repo repository failed, will remove "
|
print("fatal: cloning the git-repo repository failed, will remove "
|
||||||
"'%s' " % path, file=sys.stderr)
|
"'%s' " % path, file=sys.stderr)
|
||||||
shutil.rmtree(path, ignore_errors=True)
|
shutil.rmtree(path, ignore_errors=True)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
repo_main, rel_repo_dir = _FindRepo()
|
repo_main, rel_repo_dir = _FindRepo()
|
||||||
@ -901,14 +929,10 @@ def main(orig_args):
|
|||||||
else:
|
else:
|
||||||
os.execv(sys.executable, me)
|
os.execv(sys.executable, me)
|
||||||
except OSError as e:
|
except OSError as e:
|
||||||
_print("fatal: unable to start %s" % repo_main, file=sys.stderr)
|
print("fatal: unable to start %s" % repo_main, file=sys.stderr)
|
||||||
_print("fatal: %s" % e, file=sys.stderr)
|
print("fatal: %s" % e, file=sys.stderr)
|
||||||
sys.exit(148)
|
sys.exit(148)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
if ver[0] == 3:
|
|
||||||
_print('warning: Python 3 support is currently experimental. YMMV.\n'
|
|
||||||
'Please use Python 2.6 - 2.7 instead.',
|
|
||||||
file=sys.stderr)
|
|
||||||
main(sys.argv[1:])
|
main(sys.argv[1:])
|
||||||
|
54
run_tests
Executable file
54
run_tests
Executable file
@ -0,0 +1,54 @@
|
|||||||
|
#!/usr/bin/python
|
||||||
|
# -*- coding:utf-8 -*-
|
||||||
|
# Copyright 2019 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.
|
||||||
|
|
||||||
|
"""Wrapper to run pytest with the right settings."""
|
||||||
|
|
||||||
|
from __future__ import print_function
|
||||||
|
|
||||||
|
import errno
|
||||||
|
import os
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
|
||||||
|
|
||||||
|
def run_pytest(cmd, argv):
|
||||||
|
"""Run the unittests via |cmd|."""
|
||||||
|
try:
|
||||||
|
subprocess.check_call([cmd] + argv)
|
||||||
|
return 0
|
||||||
|
except OSError as e:
|
||||||
|
if e.errno == errno.ENOENT:
|
||||||
|
print('%s: unable to run `%s`: %s' % (__file__, cmd, e), file=sys.stderr)
|
||||||
|
print('%s: Try installing pytest: sudo apt-get install python-pytest' %
|
||||||
|
(__file__,), file=sys.stderr)
|
||||||
|
return 1
|
||||||
|
else:
|
||||||
|
raise
|
||||||
|
|
||||||
|
|
||||||
|
def main(argv):
|
||||||
|
"""The main entry."""
|
||||||
|
# Add the repo tree to PYTHONPATH as the tests expect to be able to import
|
||||||
|
# modules directly.
|
||||||
|
topdir = os.path.dirname(os.path.realpath(__file__))
|
||||||
|
pythonpath = os.environ.get('PYTHONPATH', '')
|
||||||
|
os.environ['PYTHONPATH'] = '%s:%s' % (topdir, pythonpath)
|
||||||
|
|
||||||
|
return run_pytest('pytest', argv)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
sys.exit(main(sys.argv[1:]))
|
@ -1,3 +1,4 @@
|
|||||||
|
# -*- coding:utf-8 -*-
|
||||||
#
|
#
|
||||||
# Copyright (C) 2008 The Android Open Source Project
|
# Copyright (C) 2008 The Android Open Source Project
|
||||||
#
|
#
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
# -*- coding:utf-8 -*-
|
||||||
#
|
#
|
||||||
# Copyright (C) 2008 The Android Open Source Project
|
# Copyright (C) 2008 The Android Open Source Project
|
||||||
#
|
#
|
||||||
@ -58,7 +59,7 @@ It is equivalent to "git branch -D <branchname>".
|
|||||||
pm.update()
|
pm.update()
|
||||||
|
|
||||||
if opt.all:
|
if opt.all:
|
||||||
branches = project.GetBranches().keys()
|
branches = list(project.GetBranches().keys())
|
||||||
else:
|
else:
|
||||||
branches = [nb]
|
branches = [nb]
|
||||||
|
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
# -*- coding:utf-8 -*-
|
||||||
#
|
#
|
||||||
# Copyright (C) 2009 The Android Open Source Project
|
# Copyright (C) 2009 The Android Open Source Project
|
||||||
#
|
#
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
# -*- coding:utf-8 -*-
|
||||||
#
|
#
|
||||||
# Copyright (C) 2009 The Android Open Source Project
|
# Copyright (C) 2009 The Android Open Source Project
|
||||||
#
|
#
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
# -*- coding:utf-8 -*-
|
||||||
#
|
#
|
||||||
# Copyright (C) 2010 The Android Open Source Project
|
# Copyright (C) 2010 The Android Open Source Project
|
||||||
#
|
#
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
# -*- coding:utf-8 -*-
|
||||||
#
|
#
|
||||||
# Copyright (C) 2008 The Android Open Source Project
|
# Copyright (C) 2008 The Android Open Source Project
|
||||||
#
|
#
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
# -*- coding:utf-8 -*-
|
||||||
#
|
#
|
||||||
# Copyright (C) 2014 The Android Open Source Project
|
# Copyright (C) 2014 The Android Open Source Project
|
||||||
#
|
#
|
||||||
@ -190,12 +191,12 @@ synced and their revisions won't be found.
|
|||||||
self.printProject = self.printAdded = self.printRemoved = self.printRevision = self.printText
|
self.printProject = self.printAdded = self.printRemoved = self.printRevision = self.printText
|
||||||
|
|
||||||
manifest1 = XmlManifest(self.manifest.repodir)
|
manifest1 = XmlManifest(self.manifest.repodir)
|
||||||
manifest1.Override(args[0])
|
manifest1.Override(args[0], load_local_manifests=False)
|
||||||
if len(args) == 1:
|
if len(args) == 1:
|
||||||
manifest2 = self.manifest
|
manifest2 = self.manifest
|
||||||
else:
|
else:
|
||||||
manifest2 = XmlManifest(self.manifest.repodir)
|
manifest2 = XmlManifest(self.manifest.repodir)
|
||||||
manifest2.Override(args[1])
|
manifest2.Override(args[1], load_local_manifests=False)
|
||||||
|
|
||||||
diff = manifest1.projectsDiff(manifest2)
|
diff = manifest1.projectsDiff(manifest2)
|
||||||
if opt.raw:
|
if opt.raw:
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
# -*- coding:utf-8 -*-
|
||||||
#
|
#
|
||||||
# Copyright (C) 2008 The Android Open Source Project
|
# Copyright (C) 2008 The Android Open Source Project
|
||||||
#
|
#
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
# -*- coding:utf-8 -*-
|
||||||
#
|
#
|
||||||
# Copyright (C) 2008 The Android Open Source Project
|
# Copyright (C) 2008 The Android Open Source Project
|
||||||
#
|
#
|
||||||
@ -104,7 +105,7 @@ following <command>.
|
|||||||
|
|
||||||
Example: to list projects:
|
Example: to list projects:
|
||||||
|
|
||||||
%prog% forall -c 'echo $REPO_PROJECT'
|
%prog -c 'echo $REPO_PROJECT'
|
||||||
|
|
||||||
Notice that $REPO_PROJECT is quoted to ensure it is expanded in
|
Notice that $REPO_PROJECT is quoted to ensure it is expanded in
|
||||||
the context of running <command> instead of in the calling shell.
|
the context of running <command> instead of in the calling shell.
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
# -*- coding:utf-8 -*-
|
||||||
#
|
#
|
||||||
# Copyright (C) 2015 The Android Open Source Project
|
# Copyright (C) 2015 The Android Open Source Project
|
||||||
#
|
#
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
# -*- coding:utf-8 -*-
|
||||||
#
|
#
|
||||||
# Copyright (C) 2015 The Android Open Source Project
|
# Copyright (C) 2015 The Android Open Source Project
|
||||||
#
|
#
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
# -*- coding:utf-8 -*-
|
||||||
#
|
#
|
||||||
# Copyright (C) 2009 The Android Open Source Project
|
# Copyright (C) 2009 The Android Open Source Project
|
||||||
#
|
#
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
# -*- coding:utf-8 -*-
|
||||||
#
|
#
|
||||||
# Copyright (C) 2008 The Android Open Source Project
|
# Copyright (C) 2008 The Android Open Source Project
|
||||||
#
|
#
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
# -*- coding:utf-8 -*-
|
||||||
#
|
#
|
||||||
# Copyright (C) 2012 The Android Open Source Project
|
# Copyright (C) 2012 The Android Open Source Project
|
||||||
#
|
#
|
||||||
@ -45,7 +46,7 @@ class Info(PagedCommand):
|
|||||||
def Execute(self, opt, args):
|
def Execute(self, opt, args):
|
||||||
self.out = _Coloring(self.manifest.globalConfig)
|
self.out = _Coloring(self.manifest.globalConfig)
|
||||||
self.heading = self.out.printer('heading', attr = 'bold')
|
self.heading = self.out.printer('heading', attr = 'bold')
|
||||||
self.headtext = self.out.printer('headtext', fg = 'yellow')
|
self.headtext = self.out.nofmt_printer('headtext', fg = 'yellow')
|
||||||
self.redtext = self.out.printer('redtext', fg = 'red')
|
self.redtext = self.out.printer('redtext', fg = 'red')
|
||||||
self.sha = self.out.printer("sha", fg = 'yellow')
|
self.sha = self.out.printer("sha", fg = 'yellow')
|
||||||
self.text = self.out.nofmt_printer('text')
|
self.text = self.out.nofmt_printer('text')
|
||||||
@ -99,7 +100,7 @@ class Info(PagedCommand):
|
|||||||
self.headtext(p.revisionExpr)
|
self.headtext(p.revisionExpr)
|
||||||
self.out.nl()
|
self.out.nl()
|
||||||
|
|
||||||
localBranches = p.GetBranches().keys()
|
localBranches = list(p.GetBranches().keys())
|
||||||
self.heading("Local Branches: ")
|
self.heading("Local Branches: ")
|
||||||
self.redtext(str(len(localBranches)))
|
self.redtext(str(len(localBranches)))
|
||||||
if len(localBranches) > 0:
|
if len(localBranches) > 0:
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
# -*- coding:utf-8 -*-
|
||||||
#
|
#
|
||||||
# Copyright (C) 2008 The Android Open Source Project
|
# Copyright (C) 2008 The Android Open Source Project
|
||||||
#
|
#
|
||||||
@ -95,7 +96,7 @@ to update the working directory files.
|
|||||||
g.add_option('-b', '--manifest-branch',
|
g.add_option('-b', '--manifest-branch',
|
||||||
dest='manifest_branch',
|
dest='manifest_branch',
|
||||||
help='manifest branch or revision', metavar='REVISION')
|
help='manifest branch or revision', metavar='REVISION')
|
||||||
g.add_option('-c', '--current-branch',
|
g.add_option('--current-branch',
|
||||||
dest='current_branch_only', action='store_true',
|
dest='current_branch_only', action='store_true',
|
||||||
help='fetch only current manifest branch from server')
|
help='fetch only current manifest branch from server')
|
||||||
g.add_option('-m', '--manifest-name',
|
g.add_option('-m', '--manifest-name',
|
||||||
@ -114,6 +115,13 @@ to update the working directory files.
|
|||||||
g.add_option('--depth', type='int', default=None,
|
g.add_option('--depth', type='int', default=None,
|
||||||
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('--partial-clone', action='store_true',
|
||||||
|
dest='partial_clone',
|
||||||
|
help='perform partial clone (https://git-scm.com/'
|
||||||
|
'docs/gitrepository-layout#_code_partialclone_code)')
|
||||||
|
g.add_option('--clone-filter', action='store', default='blob:none',
|
||||||
|
dest='clone_filter',
|
||||||
|
help='filter for use with --partial-clone [default: %default]')
|
||||||
g.add_option('--archive',
|
g.add_option('--archive',
|
||||||
dest='archive', action='store_true',
|
dest='archive', action='store_true',
|
||||||
help='checkout an archive instead of a git repository for '
|
help='checkout an archive instead of a git repository for '
|
||||||
@ -197,6 +205,8 @@ to update the working directory files.
|
|||||||
else:
|
else:
|
||||||
m.PreSync()
|
m.PreSync()
|
||||||
|
|
||||||
|
self._ConfigureDepth(opt)
|
||||||
|
|
||||||
if opt.manifest_url:
|
if opt.manifest_url:
|
||||||
r = m.GetRemote(m.remote.name)
|
r = m.GetRemote(m.remote.name)
|
||||||
r.url = opt.manifest_url
|
r.url = opt.manifest_url
|
||||||
@ -250,13 +260,25 @@ to update the working directory files.
|
|||||||
'in another location.', file=sys.stderr)
|
'in another location.', file=sys.stderr)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
|
if opt.partial_clone:
|
||||||
|
if opt.mirror:
|
||||||
|
print('fatal: --mirror and --partial-clone are mutually exclusive',
|
||||||
|
file=sys.stderr)
|
||||||
|
sys.exit(1)
|
||||||
|
m.config.SetString('repo.partialclone', 'true')
|
||||||
|
if opt.clone_filter:
|
||||||
|
m.config.SetString('repo.clonefilter', opt.clone_filter)
|
||||||
|
else:
|
||||||
|
opt.clone_filter = None
|
||||||
|
|
||||||
if opt.submodules:
|
if opt.submodules:
|
||||||
m.config.SetString('repo.submodules', 'true')
|
m.config.SetString('repo.submodules', 'true')
|
||||||
|
|
||||||
if not m.Sync_NetworkHalf(is_new=is_new, quiet=opt.quiet,
|
if not m.Sync_NetworkHalf(is_new=is_new, quiet=opt.quiet,
|
||||||
clone_bundle=not opt.no_clone_bundle,
|
clone_bundle=not opt.no_clone_bundle,
|
||||||
current_branch_only=opt.current_branch_only,
|
current_branch_only=opt.current_branch_only,
|
||||||
no_tags=opt.no_tags, submodules=opt.submodules):
|
no_tags=opt.no_tags, submodules=opt.submodules,
|
||||||
|
clone_filter=opt.clone_filter):
|
||||||
r = m.GetRemote(m.remote.name)
|
r = m.GetRemote(m.remote.name)
|
||||||
print('fatal: cannot obtain manifest %s' % r.url, file=sys.stderr)
|
print('fatal: cannot obtain manifest %s' % r.url, file=sys.stderr)
|
||||||
|
|
||||||
@ -291,7 +313,9 @@ to update the working directory files.
|
|||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
def _Prompt(self, prompt, value):
|
def _Prompt(self, prompt, value):
|
||||||
sys.stdout.write('%-10s [%s]: ' % (prompt, value))
|
print('%-10s [%s]: ' % (prompt, value), end='')
|
||||||
|
# TODO: When we require Python 3, use flush=True w/print above.
|
||||||
|
sys.stdout.flush()
|
||||||
a = sys.stdin.readline().strip()
|
a = sys.stdin.readline().strip()
|
||||||
if a == '':
|
if a == '':
|
||||||
return value
|
return value
|
||||||
@ -325,7 +349,9 @@ to update the working directory files.
|
|||||||
|
|
||||||
print()
|
print()
|
||||||
print('Your identity is: %s <%s>' % (name, email))
|
print('Your identity is: %s <%s>' % (name, email))
|
||||||
sys.stdout.write('is this correct [y/N]? ')
|
print('is this correct [y/N]? ', end='')
|
||||||
|
# TODO: When we require Python 3, use flush=True w/print above.
|
||||||
|
sys.stdout.flush()
|
||||||
a = sys.stdin.readline().strip().lower()
|
a = sys.stdin.readline().strip().lower()
|
||||||
if a in ('yes', 'y', 't', 'true'):
|
if a in ('yes', 'y', 't', 'true'):
|
||||||
break
|
break
|
||||||
@ -367,7 +393,9 @@ to update the working directory files.
|
|||||||
out.printer(fg='black', attr=c)(' %-6s ', c)
|
out.printer(fg='black', attr=c)(' %-6s ', c)
|
||||||
out.nl()
|
out.nl()
|
||||||
|
|
||||||
sys.stdout.write('Enable color display in this user account (y/N)? ')
|
print('Enable color display in this user account (y/N)? ', end='')
|
||||||
|
# TODO: When we require Python 3, use flush=True w/print above.
|
||||||
|
sys.stdout.flush()
|
||||||
a = sys.stdin.readline().strip().lower()
|
a = sys.stdin.readline().strip().lower()
|
||||||
if a in ('y', 'yes', 't', 'true', 'on'):
|
if a in ('y', 'yes', 't', 'true', 'on'):
|
||||||
gc.SetString('color.ui', 'auto')
|
gc.SetString('color.ui', 'auto')
|
||||||
@ -429,6 +457,4 @@ to update the working directory files.
|
|||||||
self._ConfigureUser()
|
self._ConfigureUser()
|
||||||
self._ConfigureColor()
|
self._ConfigureColor()
|
||||||
|
|
||||||
self._ConfigureDepth(opt)
|
|
||||||
|
|
||||||
self._DisplayResult()
|
self._DisplayResult()
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
# -*- coding:utf-8 -*-
|
||||||
#
|
#
|
||||||
# Copyright (C) 2011 The Android Open Source Project
|
# Copyright (C) 2011 The Android Open Source Project
|
||||||
#
|
#
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
# -*- coding:utf-8 -*-
|
||||||
#
|
#
|
||||||
# Copyright (C) 2009 The Android Open Source Project
|
# Copyright (C) 2009 The Android Open Source Project
|
||||||
#
|
#
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
# -*- coding:utf-8 -*-
|
||||||
#
|
#
|
||||||
# Copyright (C) 2012 The Android Open Source Project
|
# Copyright (C) 2012 The Android Open Source Project
|
||||||
#
|
#
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
# -*- coding:utf-8 -*-
|
||||||
#
|
#
|
||||||
# Copyright (C) 2008 The Android Open Source Project
|
# Copyright (C) 2008 The Android Open Source Project
|
||||||
#
|
#
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
# -*- coding:utf-8 -*-
|
||||||
#
|
#
|
||||||
# Copyright (C) 2010 The Android Open Source Project
|
# Copyright (C) 2010 The Android Open Source Project
|
||||||
#
|
#
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
# -*- coding:utf-8 -*-
|
||||||
#
|
#
|
||||||
# Copyright (C) 2009 The Android Open Source Project
|
# Copyright (C) 2009 The Android Open Source Project
|
||||||
#
|
#
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
# -*- coding:utf-8 -*-
|
||||||
#
|
#
|
||||||
# Copyright (C) 2010 The Android Open Source Project
|
# Copyright (C) 2010 The Android Open Source Project
|
||||||
#
|
#
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
# -*- coding:utf-8 -*-
|
||||||
#
|
#
|
||||||
# Copyright (C) 2008 The Android Open Source Project
|
# Copyright (C) 2008 The Android Open Source Project
|
||||||
#
|
#
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
# -*- coding:utf-8 -*-
|
||||||
#
|
#
|
||||||
# Copyright (C) 2008 The Android Open Source Project
|
# Copyright (C) 2008 The Android Open Source Project
|
||||||
#
|
#
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
# -*- coding:utf-8 -*-
|
||||||
#
|
#
|
||||||
# Copyright (C) 2008 The Android Open Source Project
|
# Copyright (C) 2008 The Android Open Source Project
|
||||||
#
|
#
|
||||||
@ -13,6 +14,8 @@
|
|||||||
# 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.
|
||||||
|
|
||||||
|
from __future__ import print_function
|
||||||
|
|
||||||
from command import PagedCommand
|
from command import PagedCommand
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
204
subcmds/sync.py
204
subcmds/sync.py
@ -1,3 +1,4 @@
|
|||||||
|
# -*- coding:utf-8 -*-
|
||||||
#
|
#
|
||||||
# Copyright (C) 2008 The Android Open Source Project
|
# Copyright (C) 2008 The Android Open Source Project
|
||||||
#
|
#
|
||||||
@ -84,6 +85,9 @@ class _FetchError(Exception):
|
|||||||
"""Internal error thrown in _FetchHelper() when we don't want stack trace."""
|
"""Internal error thrown in _FetchHelper() when we don't want stack trace."""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
class _CheckoutError(Exception):
|
||||||
|
"""Internal error thrown in _CheckoutOne() when we don't want stack trace."""
|
||||||
|
|
||||||
class Sync(Command, MirrorSafeCommand):
|
class Sync(Command, MirrorSafeCommand):
|
||||||
jobs = 1
|
jobs = 1
|
||||||
common = True
|
common = True
|
||||||
@ -136,6 +140,11 @@ directories if they have previously been linked to a different
|
|||||||
object direcotry. WARNING: This may cause data to be lost since
|
object direcotry. WARNING: This may cause data to be lost since
|
||||||
refs may be removed when overwriting.
|
refs may be removed when overwriting.
|
||||||
|
|
||||||
|
The --force-remove-dirty option can be used to remove previously used
|
||||||
|
projects with uncommitted changes. WARNING: This may cause data to be
|
||||||
|
lost since uncommitted changes may be removed with projects that no longer
|
||||||
|
exist in the manifest.
|
||||||
|
|
||||||
The --no-clone-bundle option disables any attempt to use
|
The --no-clone-bundle option disables any attempt to use
|
||||||
$URL/clone.bundle to bootstrap a new Git repository from a
|
$URL/clone.bundle to bootstrap a new Git repository from a
|
||||||
resumeable bundle file on a content delivery network. This
|
resumeable bundle file on a content delivery network. This
|
||||||
@ -197,6 +206,11 @@ later is required to fix a server side protocol bug.
|
|||||||
help="overwrite an existing git directory if it needs to "
|
help="overwrite an existing git directory if it needs to "
|
||||||
"point to a different object directory. WARNING: this "
|
"point to a different object directory. WARNING: this "
|
||||||
"may cause loss of data")
|
"may cause loss of data")
|
||||||
|
p.add_option('--force-remove-dirty',
|
||||||
|
dest='force_remove_dirty', action='store_true',
|
||||||
|
help="force remove projects with uncommitted modifications if "
|
||||||
|
"projects no longer exist in the manifest. "
|
||||||
|
"WARNING: this may cause loss of data")
|
||||||
p.add_option('-l', '--local-only',
|
p.add_option('-l', '--local-only',
|
||||||
dest='local_only', action='store_true',
|
dest='local_only', action='store_true',
|
||||||
help="only update working tree, don't fetch")
|
help="only update working tree, don't fetch")
|
||||||
@ -255,7 +269,7 @@ later is required to fix a server side protocol bug.
|
|||||||
help=SUPPRESS_HELP)
|
help=SUPPRESS_HELP)
|
||||||
|
|
||||||
def _FetchProjectList(self, opt, projects, sem, *args, **kwargs):
|
def _FetchProjectList(self, opt, projects, sem, *args, **kwargs):
|
||||||
"""Main function of the fetch threads when jobs are > 1.
|
"""Main function of the fetch threads.
|
||||||
|
|
||||||
Delegates most of the work to _FetchHelper.
|
Delegates most of the work to _FetchHelper.
|
||||||
|
|
||||||
@ -275,7 +289,8 @@ later is required to fix a server side protocol bug.
|
|||||||
finally:
|
finally:
|
||||||
sem.release()
|
sem.release()
|
||||||
|
|
||||||
def _FetchHelper(self, opt, project, lock, fetched, pm, err_event):
|
def _FetchHelper(self, opt, project, lock, fetched, pm, err_event,
|
||||||
|
clone_filter):
|
||||||
"""Fetch git objects for a single project.
|
"""Fetch git objects for a single project.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@ -289,6 +304,7 @@ later is required to fix a server side protocol bug.
|
|||||||
lock held).
|
lock held).
|
||||||
err_event: We'll set this event in the case of an error (after printing
|
err_event: We'll set this event in the case of an error (after printing
|
||||||
out info about the error).
|
out info about the error).
|
||||||
|
clone_filter: Filter for use in a partial clone.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Whether the fetch was successful.
|
Whether the fetch was successful.
|
||||||
@ -301,7 +317,6 @@ later is required to fix a server side protocol bug.
|
|||||||
|
|
||||||
# Encapsulate everything in a try/except/finally so that:
|
# Encapsulate everything in a try/except/finally so that:
|
||||||
# - We always set err_event in the case of an exception.
|
# - We always set err_event in the case of an exception.
|
||||||
# - We always make sure we call sem.release().
|
|
||||||
# - We always make sure we unlock the lock if we locked it.
|
# - We always make sure we unlock the lock if we locked it.
|
||||||
start = time.time()
|
start = time.time()
|
||||||
success = False
|
success = False
|
||||||
@ -314,7 +329,8 @@ later is required to fix a server side protocol bug.
|
|||||||
clone_bundle=not opt.no_clone_bundle,
|
clone_bundle=not opt.no_clone_bundle,
|
||||||
no_tags=opt.no_tags, archive=self.manifest.IsArchive,
|
no_tags=opt.no_tags, archive=self.manifest.IsArchive,
|
||||||
optimized_fetch=opt.optimized_fetch,
|
optimized_fetch=opt.optimized_fetch,
|
||||||
prune=opt.prune)
|
prune=opt.prune,
|
||||||
|
clone_filter=clone_filter)
|
||||||
self._fetch_times.Set(project, time.time() - start)
|
self._fetch_times.Set(project, time.time() - start)
|
||||||
|
|
||||||
# Lock around all the rest of the code, since printing, updating a set
|
# Lock around all the rest of the code, since printing, updating a set
|
||||||
@ -378,7 +394,8 @@ later is required to fix a server side protocol bug.
|
|||||||
lock=lock,
|
lock=lock,
|
||||||
fetched=fetched,
|
fetched=fetched,
|
||||||
pm=pm,
|
pm=pm,
|
||||||
err_event=err_event)
|
err_event=err_event,
|
||||||
|
clone_filter=self.manifest.CloneFilter)
|
||||||
if self.jobs > 1:
|
if self.jobs > 1:
|
||||||
t = _threading.Thread(target = self._FetchProjectList,
|
t = _threading.Thread(target = self._FetchProjectList,
|
||||||
kwargs = kwargs)
|
kwargs = kwargs)
|
||||||
@ -405,6 +422,148 @@ later is required to fix a server side protocol bug.
|
|||||||
|
|
||||||
return fetched
|
return fetched
|
||||||
|
|
||||||
|
def _CheckoutWorker(self, opt, sem, project, *args, **kwargs):
|
||||||
|
"""Main function of the fetch threads.
|
||||||
|
|
||||||
|
Delegates most of the work to _CheckoutOne.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
opt: Program options returned from optparse. See _Options().
|
||||||
|
projects: Projects to fetch.
|
||||||
|
sem: We'll release() this semaphore when we exit so that another thread
|
||||||
|
can be started up.
|
||||||
|
*args, **kwargs: Remaining arguments to pass to _CheckoutOne. See the
|
||||||
|
_CheckoutOne docstring for details.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
success = self._CheckoutOne(opt, project, *args, **kwargs)
|
||||||
|
if not success:
|
||||||
|
sys.exit(1)
|
||||||
|
finally:
|
||||||
|
sem.release()
|
||||||
|
|
||||||
|
def _CheckoutOne(self, opt, project, lock, pm, err_event):
|
||||||
|
"""Checkout work tree for one project
|
||||||
|
|
||||||
|
Args:
|
||||||
|
opt: Program options returned from optparse. See _Options().
|
||||||
|
project: Project object for the project to checkout.
|
||||||
|
lock: Lock for accessing objects that are shared amongst multiple
|
||||||
|
_CheckoutWorker() threads.
|
||||||
|
pm: Instance of a Project object. We will call pm.update() (with our
|
||||||
|
lock held).
|
||||||
|
err_event: We'll set this event in the case of an error (after printing
|
||||||
|
out info about the error).
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Whether the fetch was successful.
|
||||||
|
"""
|
||||||
|
# We'll set to true once we've locked the lock.
|
||||||
|
did_lock = False
|
||||||
|
|
||||||
|
if not opt.quiet:
|
||||||
|
print('Checking out project %s' % project.name)
|
||||||
|
|
||||||
|
# Encapsulate everything in a try/except/finally so that:
|
||||||
|
# - We always set err_event in the case of an exception.
|
||||||
|
# - We always make sure we unlock the lock if we locked it.
|
||||||
|
start = time.time()
|
||||||
|
syncbuf = SyncBuffer(self.manifest.manifestProject.config,
|
||||||
|
detach_head=opt.detach_head)
|
||||||
|
success = False
|
||||||
|
try:
|
||||||
|
try:
|
||||||
|
project.Sync_LocalHalf(syncbuf, force_sync=opt.force_sync)
|
||||||
|
success = syncbuf.Finish()
|
||||||
|
|
||||||
|
# Lock around all the rest of the code, since printing, updating a set
|
||||||
|
# and Progress.update() are not thread safe.
|
||||||
|
lock.acquire()
|
||||||
|
did_lock = True
|
||||||
|
|
||||||
|
if not success:
|
||||||
|
err_event.set()
|
||||||
|
print('error: Cannot checkout %s' % (project.name),
|
||||||
|
file=sys.stderr)
|
||||||
|
raise _CheckoutError()
|
||||||
|
|
||||||
|
pm.update()
|
||||||
|
except _CheckoutError:
|
||||||
|
pass
|
||||||
|
except Exception as e:
|
||||||
|
print('error: Cannot checkout %s: %s: %s' %
|
||||||
|
(project.name, type(e).__name__, str(e)),
|
||||||
|
file=sys.stderr)
|
||||||
|
err_event.set()
|
||||||
|
raise
|
||||||
|
finally:
|
||||||
|
if did_lock:
|
||||||
|
lock.release()
|
||||||
|
finish = time.time()
|
||||||
|
self.event_log.AddSync(project, event_log.TASK_SYNC_LOCAL,
|
||||||
|
start, finish, success)
|
||||||
|
|
||||||
|
return success
|
||||||
|
|
||||||
|
def _Checkout(self, all_projects, opt):
|
||||||
|
"""Checkout projects listed in all_projects
|
||||||
|
|
||||||
|
Args:
|
||||||
|
all_projects: List of all projects that should be checked out.
|
||||||
|
opt: Program options returned from optparse. See _Options().
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Perform checkouts in multiple threads when we are using partial clone.
|
||||||
|
# Without partial clone, all needed git objects are already downloaded,
|
||||||
|
# in this situation it's better to use only one process because the checkout
|
||||||
|
# would be mostly disk I/O; with partial clone, the objects are only
|
||||||
|
# downloaded when demanded (at checkout time), which is similar to the
|
||||||
|
# Sync_NetworkHalf case and parallelism would be helpful.
|
||||||
|
if self.manifest.CloneFilter:
|
||||||
|
syncjobs = self.jobs
|
||||||
|
else:
|
||||||
|
syncjobs = 1
|
||||||
|
|
||||||
|
lock = _threading.Lock()
|
||||||
|
pm = Progress('Syncing work tree', len(all_projects))
|
||||||
|
|
||||||
|
threads = set()
|
||||||
|
sem = _threading.Semaphore(syncjobs)
|
||||||
|
err_event = _threading.Event()
|
||||||
|
|
||||||
|
for project in all_projects:
|
||||||
|
# Check for any errors before running any more tasks.
|
||||||
|
# ...we'll let existing threads finish, though.
|
||||||
|
if err_event.isSet() and not opt.force_broken:
|
||||||
|
break
|
||||||
|
|
||||||
|
sem.acquire()
|
||||||
|
if project.worktree:
|
||||||
|
kwargs = dict(opt=opt,
|
||||||
|
sem=sem,
|
||||||
|
project=project,
|
||||||
|
lock=lock,
|
||||||
|
pm=pm,
|
||||||
|
err_event=err_event)
|
||||||
|
if syncjobs > 1:
|
||||||
|
t = _threading.Thread(target=self._CheckoutWorker,
|
||||||
|
kwargs=kwargs)
|
||||||
|
# Ensure that Ctrl-C will not freeze the repo process.
|
||||||
|
t.daemon = True
|
||||||
|
threads.add(t)
|
||||||
|
t.start()
|
||||||
|
else:
|
||||||
|
self._CheckoutWorker(**kwargs)
|
||||||
|
|
||||||
|
for t in threads:
|
||||||
|
t.join()
|
||||||
|
|
||||||
|
pm.end()
|
||||||
|
# If we saw an error, exit with code 1 so that other scripts can check.
|
||||||
|
if err_event.isSet():
|
||||||
|
print('\nerror: Exited sync due to checkout errors', file=sys.stderr)
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
def _GCProjects(self, projects):
|
def _GCProjects(self, projects):
|
||||||
gc_gitdirs = {}
|
gc_gitdirs = {}
|
||||||
for project in projects:
|
for project in projects:
|
||||||
@ -425,7 +584,7 @@ later is required to fix a server side protocol bug.
|
|||||||
bare_git.gc('--auto')
|
bare_git.gc('--auto')
|
||||||
return
|
return
|
||||||
|
|
||||||
config = {'pack.threads': cpu_count / jobs if cpu_count > jobs else 1}
|
config = {'pack.threads': cpu_count // jobs if cpu_count > jobs else 1}
|
||||||
|
|
||||||
threads = set()
|
threads = set()
|
||||||
sem = _threading.Semaphore(jobs)
|
sem = _threading.Semaphore(jobs)
|
||||||
@ -525,7 +684,7 @@ later is required to fix a server side protocol bug.
|
|||||||
|
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
def UpdateProjectList(self):
|
def UpdateProjectList(self, opt):
|
||||||
new_project_paths = []
|
new_project_paths = []
|
||||||
for project in self.GetProjects(None, missing_ok=True):
|
for project in self.GetProjects(None, missing_ok=True):
|
||||||
if project.relpath:
|
if project.relpath:
|
||||||
@ -540,7 +699,8 @@ later is required to fix a server side protocol bug.
|
|||||||
old_project_paths = fd.read().split('\n')
|
old_project_paths = fd.read().split('\n')
|
||||||
finally:
|
finally:
|
||||||
fd.close()
|
fd.close()
|
||||||
for path in old_project_paths:
|
# In reversed order, so subfolders are deleted before parent folder.
|
||||||
|
for path in sorted(old_project_paths, reverse=True):
|
||||||
if not path:
|
if not path:
|
||||||
continue
|
continue
|
||||||
if path not in new_project_paths:
|
if path not in new_project_paths:
|
||||||
@ -559,7 +719,11 @@ later is required to fix a server side protocol bug.
|
|||||||
revisionId = None,
|
revisionId = None,
|
||||||
groups = None)
|
groups = None)
|
||||||
|
|
||||||
if project.IsDirty():
|
if project.IsDirty() and opt.force_remove_dirty:
|
||||||
|
print('WARNING: Removing dirty project "%s": uncommitted changes '
|
||||||
|
'erased' % project.relpath, file=sys.stderr)
|
||||||
|
self._DeleteProject(project.worktree)
|
||||||
|
elif project.IsDirty():
|
||||||
print('error: Cannot remove project "%s": uncommitted changes '
|
print('error: Cannot remove project "%s": uncommitted changes '
|
||||||
'are present' % project.relpath, file=sys.stderr)
|
'are present' % project.relpath, file=sys.stderr)
|
||||||
print(' commit changes, then run sync again',
|
print(' commit changes, then run sync again',
|
||||||
@ -582,7 +746,7 @@ later is required to fix a server side protocol bug.
|
|||||||
self.jobs = opt.jobs
|
self.jobs = opt.jobs
|
||||||
if self.jobs > 1:
|
if self.jobs > 1:
|
||||||
soft_limit, _ = _rlimit_nofile()
|
soft_limit, _ = _rlimit_nofile()
|
||||||
self.jobs = min(self.jobs, (soft_limit - 5) / 3)
|
self.jobs = min(self.jobs, (soft_limit - 5) // 3)
|
||||||
|
|
||||||
if opt.network_only and opt.detach_head:
|
if opt.network_only and opt.detach_head:
|
||||||
print('error: cannot combine -n and -d', file=sys.stderr)
|
print('error: cannot combine -n and -d', file=sys.stderr)
|
||||||
@ -730,7 +894,8 @@ later is required to fix a server side protocol bug.
|
|||||||
current_branch_only=opt.current_branch_only,
|
current_branch_only=opt.current_branch_only,
|
||||||
no_tags=opt.no_tags,
|
no_tags=opt.no_tags,
|
||||||
optimized_fetch=opt.optimized_fetch,
|
optimized_fetch=opt.optimized_fetch,
|
||||||
submodules=self.manifest.HasSubmodules)
|
submodules=self.manifest.HasSubmodules,
|
||||||
|
clone_filter=self.manifest.CloneFilter)
|
||||||
finish = time.time()
|
finish = time.time()
|
||||||
self.event_log.AddSync(mp, event_log.TASK_SYNC_NETWORK,
|
self.event_log.AddSync(mp, event_log.TASK_SYNC_NETWORK,
|
||||||
start, finish, success)
|
start, finish, success)
|
||||||
@ -827,23 +992,10 @@ later is required to fix a server side protocol bug.
|
|||||||
# bail out now, we have no working tree
|
# bail out now, we have no working tree
|
||||||
return
|
return
|
||||||
|
|
||||||
if self.UpdateProjectList():
|
if self.UpdateProjectList(opt):
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
syncbuf = SyncBuffer(mp.config,
|
self._Checkout(all_projects, opt)
|
||||||
detach_head = opt.detach_head)
|
|
||||||
pm = Progress('Syncing work tree', len(all_projects))
|
|
||||||
for project in all_projects:
|
|
||||||
pm.update()
|
|
||||||
if project.worktree:
|
|
||||||
start = time.time()
|
|
||||||
project.Sync_LocalHalf(syncbuf, force_sync=opt.force_sync)
|
|
||||||
self.event_log.AddSync(project, event_log.TASK_SYNC_LOCAL,
|
|
||||||
start, time.time(), syncbuf.Recently())
|
|
||||||
pm.end()
|
|
||||||
print(file=sys.stderr)
|
|
||||||
if not syncbuf.Finish():
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
# If there's a notice that's supposed to print at the end of the sync, print
|
# If there's a notice that's supposed to print at the end of the sync, print
|
||||||
# it now...
|
# it now...
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
# -*- coding:utf-8 -*-
|
||||||
#
|
#
|
||||||
# Copyright (C) 2008 The Android Open Source Project
|
# Copyright (C) 2008 The Android Open Source Project
|
||||||
#
|
#
|
||||||
@ -220,7 +221,9 @@ Gerrit Code Review: https://www.gerritcodereview.com/
|
|||||||
for commit in commit_list:
|
for commit in commit_list:
|
||||||
print(' %s' % commit)
|
print(' %s' % commit)
|
||||||
|
|
||||||
sys.stdout.write('to %s (y/N)? ' % remote.review)
|
print('to %s (y/N)? ' % remote.review, end='')
|
||||||
|
# TODO: When we require Python 3, use flush=True w/print above.
|
||||||
|
sys.stdout.flush()
|
||||||
answer = sys.stdin.readline().strip().lower()
|
answer = sys.stdin.readline().strip().lower()
|
||||||
answer = answer in ('y', 'yes', '1', 'true', 't')
|
answer = answer in ('y', 'yes', '1', 'true', 't')
|
||||||
|
|
||||||
@ -359,10 +362,13 @@ Gerrit Code Review: https://www.gerritcodereview.com/
|
|||||||
|
|
||||||
# if they want to auto upload, let's not ask because it could be automated
|
# if they want to auto upload, let's not ask because it could be automated
|
||||||
if answer is None:
|
if answer is None:
|
||||||
sys.stdout.write('Uncommitted changes in ' + branch.project.name)
|
print()
|
||||||
sys.stdout.write(' (did you forget to amend?):\n')
|
print('Uncommitted changes in %s (did you forget to amend?):'
|
||||||
sys.stdout.write('\n'.join(changes) + '\n')
|
% branch.project.name)
|
||||||
sys.stdout.write('Continue uploading? (y/N) ')
|
print('\n'.join(changes))
|
||||||
|
print('Continue uploading? (y/N) ', end='')
|
||||||
|
# TODO: When we require Python 3, use flush=True w/print above.
|
||||||
|
sys.stdout.flush()
|
||||||
a = sys.stdin.readline().strip().lower()
|
a = sys.stdin.readline().strip().lower()
|
||||||
if a not in ('y', 'yes', 't', 'true', 'on'):
|
if a not in ('y', 'yes', 't', 'true', 'on'):
|
||||||
print("skipping upload", file=sys.stderr)
|
print("skipping upload", file=sys.stderr)
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
# -*- coding:utf-8 -*-
|
||||||
#
|
#
|
||||||
# Copyright (C) 2009 The Android Open Source Project
|
# Copyright (C) 2009 The Android Open Source Project
|
||||||
#
|
#
|
||||||
@ -40,5 +41,5 @@ class Version(Command, MirrorSafeCommand):
|
|||||||
print('repo launcher version %s' % Version.wrapper_version)
|
print('repo launcher version %s' % Version.wrapper_version)
|
||||||
print(' (from %s)' % Version.wrapper_path)
|
print(' (from %s)' % Version.wrapper_path)
|
||||||
|
|
||||||
print(git.version().strip())
|
print('git %s' % git.version_tuple().full)
|
||||||
print('Python %s' % sys.version)
|
print('Python %s' % sys.version)
|
||||||
|
2
tests/fixtures/.gitignore
vendored
Normal file
2
tests/fixtures/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
/.repo_not.present.gitconfig.json
|
||||||
|
/.repo_test.gitconfig.json
|
45
tests/test_git_command.py
Normal file
45
tests/test_git_command.py
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
# -*- coding:utf-8 -*-
|
||||||
|
#
|
||||||
|
# Copyright 2019 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 unittest
|
||||||
|
|
||||||
|
import git_command
|
||||||
|
|
||||||
|
|
||||||
|
class GitCallUnitTest(unittest.TestCase):
|
||||||
|
"""Tests the _GitCall class (via git_command.git)."""
|
||||||
|
|
||||||
|
def test_version_tuple(self):
|
||||||
|
"""Check git.version_tuple() handling."""
|
||||||
|
ver = git_command.git.version_tuple()
|
||||||
|
self.assertIsNotNone(ver)
|
||||||
|
|
||||||
|
# We don't dive too deep into the values here to avoid having to update
|
||||||
|
# whenever git versions change. We do check relative to this min version
|
||||||
|
# as this is what `repo` itself requires via MIN_GIT_VERSION.
|
||||||
|
MIN_GIT_VERSION = (1, 7, 2)
|
||||||
|
self.assertTrue(isinstance(ver.major, int))
|
||||||
|
self.assertTrue(isinstance(ver.minor, int))
|
||||||
|
self.assertTrue(isinstance(ver.micro, int))
|
||||||
|
|
||||||
|
self.assertGreater(ver.major, MIN_GIT_VERSION[0] - 1)
|
||||||
|
self.assertGreaterEqual(ver.micro, 0)
|
||||||
|
self.assertGreaterEqual(ver.major, 0)
|
||||||
|
|
||||||
|
self.assertGreaterEqual(ver, MIN_GIT_VERSION)
|
||||||
|
self.assertLess(ver, (9999, 9999, 9999))
|
||||||
|
|
||||||
|
self.assertNotEqual('', ver.full)
|
@ -1,3 +1,19 @@
|
|||||||
|
# -*- coding:utf-8 -*-
|
||||||
|
#
|
||||||
|
# Copyright (C) 2009 The Android Open Source Project
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
# -*- coding:utf-8 -*-
|
||||||
#
|
#
|
||||||
# Copyright (C) 2015 The Android Open Source Project
|
# Copyright (C) 2015 The Android Open Source Project
|
||||||
#
|
#
|
||||||
|
1
trace.py
1
trace.py
@ -1,3 +1,4 @@
|
|||||||
|
# -*- coding:utf-8 -*-
|
||||||
#
|
#
|
||||||
# Copyright (C) 2008 The Android Open Source Project
|
# Copyright (C) 2008 The Android Open Source Project
|
||||||
#
|
#
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
#!/usr/bin/env python
|
# -*- coding:utf-8 -*-
|
||||||
#
|
#
|
||||||
# Copyright (C) 2014 The Android Open Source Project
|
# Copyright (C) 2014 The Android Open Source Project
|
||||||
#
|
#
|
||||||
|
Reference in New Issue
Block a user