Compare commits

...

21 Commits

Author SHA1 Message Date
c5b0e23490 project: Set config option to skip lfs process filter
During sync, repo runs `git read-tree --reset -u -v HEAD` which causes
git-lfs's smudge filter to run, which fails because git-lfs does not
work with bare repositories.

This was fixed in I091ff37998131e2e6bbc59aa37ee352fe12d7fcd to
automatically disable this smudge filter. However, later versions of
Git (2.11.0) introduced a new filter protocol [1], to avoid spawning
a new command for each filtered file. This was implemented in Git-LFS
1.5.0 [2].

This patch fixes the issue by setting the git lfs process filter, in
addition to the smudge filter. For any projects that have LFS objects,
`git lfs pull` must still be executed manually afterwards.

[1] edcc85814c
[2] https://github.com/git-lfs/git-lfs/pull/1617

Bug: https://crbug.com/gerrit/10911
Change-Id: I277fc68fdefc91514a2412b3887e3be9106cab48
2019-05-24 12:06:33 +00:00
d92464e8ef Honor --depth during repo init
If a user is asking for a shallow clone of the repos, they probably
expect a shallow clone of the manifest repo too. For very large
manifest repos, this can be a huge space and time savings. For one real-world
repo, a 'repo init --no-tags --current-branch' used 350MB of disk space and
took 7 minutes. Adding --depth 1 and this change reduced it to 10MB and 2.5
minutes.

Change-Id: I6fa662e174e623ede8861efc862ce26d65d4958d
2019-05-21 10:47:21 -06:00
0968570df2 Merge "Print project name when work tree initialization fails" 2019-05-16 23:05:53 +00:00
f25a370a14 Use %topic=topic instead of deprecated /topic syntax on push
Bug: https://crbug.com/gerrit/9930
Change-Id: Iefa202d42ef6e6b8b2b1a3f9b8baa5f0d65cbd60
2019-05-15 10:59:53 +02:00
b554838ce8 Print project name when work tree initialization fails
When syncing a lot of projects in parallel, it is not otherwise
clear which one of them has failed to init work tree.

Change-Id: I8edfb4955023389a499e99cfa511bdc0d2850ba2
2019-05-09 13:07:20 -07:00
2d095da4f1 Ignore submodules when calculating 'git diff-files'
This allows projects to include submodules inside of
projects that use repo without repo incorrectly believing
the area is dirty just because a submodule has updates.
This is in line with git porcelain commands which generally
require a commandline flag to include submodules (git add,
git rebase).

Change-Id: Ide8a292162a42ab35145b5c4ca8ca0d020cdfe81
2019-05-02 18:21:42 -07:00
266f74c888 info: Use the non-formatting printer for headtext
If "repo init" was run in a path containing "%", "repo info" would fail
printing the path with

    File ".repo/repo/color.py", line 173, in f
      return fmt % args
    TypeError: not enough arguments for format string

as the "%" in the path name is interpreted as the start of a formatting
specifier. Avoid that by using the non-formatting printer for headtext
which does not require any formatting so there is no need to try to
expand "%" sequences.

Change-Id: Ie193b912191fe7cdabdce5c97bb100f0714f6e76
Signed-off-by: Sebastian Schuberth <sschuberth@gmail.com>
2019-04-17 19:46:05 +02:00
1f1596b473 Don't print "persistent ref" message when syncing quietly
The newly introduced "Already have persistent ref" message prevents
repo from overwriting the last line when syncing quietly. Omit the
message when syncing quietly to clean up the output and to restore
the previous behaviour.

Change-Id: Idf42751c67f95924d6de50092ba54d4c6fe93096
2019-04-15 14:37:17 +02:00
0d9b16d1d8 sync: deleted unused repos in reversed order (children before parent)
Bug: chromium:950002
Test: repo sync chromeos using release manifest file
Change-Id: I613df6a1973eb36acd806a703e72f5172554bcc7
2019-04-06 00:49:47 +08:00
e57f1146de sync: respect --force-sync when fetching updates
If a tag is rewritten on the server (which is bad), trying to recover
locally with `repo sync --force-sync` doesn't actually work.  People
have to manually delete things themselves to fix syncing.  While tags
should never be rewritten in practice, allow users to easily recover
from broken servers.

We updated some of these code paths already (see commit 6e53844f1e
"Allow clobbering of existing tags from remote."), but the incremental
update flow was missed.

Bug: b/120778183
Bug: chromium:932651
Test: delete local tag & recreate to diff commit, then check
      `repo sync` & `repo sync --force-sync` behavior
Change-Id: I3648f7d2526732c06016b691a9a36c003157618d
2019-03-18 21:31:03 -04:00
01019d94af docs: fixed typo error.
Change-Id: Ic3ec1bfb150ec932e05ba5eda43537784f1fdcda
2019-03-18 13:38:33 +09:00
834d308a2b Merge "project: Relax the submodule name pattern to accept dots" 2019-03-14 07:14:28 +00:00
c18ee35da6 Merge "sync: Add option '--force-remove-dirty'" 2019-03-11 17:56:00 +00:00
d3c0f5914f sync: Add option '--force-remove-dirty'
Forcefully remove dirty projects if option '--force-remove-dirty' is given.
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.

Change-Id: I844a6e943ded522fdc7b1b942c0a1269768054bc
2019-03-11 13:06:49 -04:00
41a26837d0 project: Relax the submodule name pattern to accept dots
Even if dots are used as separators for Git config keys, they are not
forbidden as part of submodule names. This fixes the issue of submodules
with a name like e.g. "long.js" to be skipped from checkout.

Change-Id: I77da07925ad207fa3d043067dfbbcb4a1ebdac4d
2019-03-11 13:53:38 +01:00
e7379dc5f7 docs: document a Python 3 migration plan
Bug: https://crbug.com/gerrit/10418
Change-Id: I72d82ce3a2d9af45d942bb10de82340110864ea5
2019-02-01 03:06:04 -05:00
13f323b2c2 event_log: turn id generation from a generator to a func call
Running lots of sync processes in parallel can hit the failure:
Fetching projects:  23% (124/523)Exception in thread Thread-201:
Traceback (most recent call last):
  File "/usr/lib/python2.7/threading.py", line 801, in __bootstrap_inner
    self.run()
  File "/usr/lib/python2.7/threading.py", line 754, in run
    self.__target(*self.__args, **self.__kwargs)
  File "/usr/local/src/repo/subcmds/sync.py", line 278, in _FetchProjectList
    success = self._FetchHelper(opt, project, *args, **kwargs)
  File "/usr/local/src/repo/subcmds/sync.py", line 357, in _FetchHelper
    start, finish, success)
  File "/usr/local/src/repo/event_log.py", line 104, in AddSync
    event = self.Add(project.relpath, task_name, start, finish, success)
  File "/usr/local/src/repo/event_log.py", line 74, in Add
    'id': (kind, next(self._next_id)),
ValueError: generator already executing

It looks like, while we lock the multiprocessing value correctly, the
generator that wraps the value isn't parallel safe.  Since we don't
have a way of doing that (as it's part of the language), turn it into
a plain function call instead.

Bug: https://crbug.com/gerrit/10293
Change-Id: I0db03601986ca0370a1699bab32adb03e7b2910a
2019-01-14 16:11:08 -05:00
12ee5446e9 init: Remove -c short option for --current-branch
This option conflicts with the gitc-init -c short option.

Bug: https://crbug.com/gerrit/10200
Change-Id: I06f37564429ca0bd4c0bbea6066daae4f663c838
2018-12-20 19:55:02 +00:00
e158e3802d Merge "README: link in new bug tracker" 2018-12-20 07:21:02 +00:00
3bbbcaf99d README: link in new bug tracker
Change-Id: I043afc0b77e709919e49ce548dff47776fddaddf
2018-12-20 02:11:46 -05:00
d4b13c280b Leverage the next keyword from python 2.7
This is literally what the next keyword is for.
https://www.python.org/dev/peps/pep-3114/

Change-Id: I843755910b847737b077ff2361ba3e04409db0f0
2018-12-19 11:06:35 -08:00
10 changed files with 96 additions and 41 deletions

View File

@ -5,6 +5,6 @@
<pydev_pathproperty name="org.python.pydev.PROJECT_SOURCE_PATH">
<path>/git-repo</path>
</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_project>

View File

@ -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.
* 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/
* Overview: https://source.android.com/source/developing.html
* Docs: https://source.android.com/source/using-repo.html

View File

@ -66,7 +66,7 @@ following DTD:
<!ATTLIST project groups CDATA #IMPLIED>
<!ATTLIST project sync-c 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 clone-depth CDATA #IMPLIED>
<!ATTLIST project force-path CDATA #IMPLIED>

32
docs/python-support.md Normal file
View 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

View File

@ -18,8 +18,6 @@ from __future__ import print_function
import json
import multiprocessing
from pyversion import is_python3
TASK_COMMAND = 'command'
TASK_SYNC_NETWORK = 'sync-network'
TASK_SYNC_LOCAL = 'sync-local'
@ -53,7 +51,6 @@ class EventLog(object):
def __init__(self):
"""Initializes the event log."""
self._log = []
self._next_id = _EventIdGenerator()
self._parent = None
def Add(self, name, task_name, start, finish=None, success=None,
@ -73,7 +70,7 @@ class EventLog(object):
A dictionary of the event added to the log.
"""
event = {
'id': (kind, self._next_id.__next__() if is_python3() else self._next_id.next()),
'id': (kind, _NextEventId()),
'name': name,
'task_name': task_name,
'start_time': start,
@ -164,16 +161,16 @@ class EventLog(object):
f.write('\n')
def _EventIdGenerator():
"""Returns multi-process safe iterator that generates locally unique id.
# An integer id that is unique across this invocation of the program.
_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.
"""
eid = multiprocessing.Value('i', 1)
while True:
with eid.get_lock():
val = eid.value
eid.value += 1
yield val
with _EVENT_ID.get_lock():
val = _EVENT_ID.value
_EVENT_ID.value += 1
return val

View File

@ -1172,10 +1172,11 @@ class Project(object):
ref_spec = '%s:refs/%s/%s' % (R_HEADS + branch.name, upload_type,
dest_branch)
opts = []
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]]
if notify:
opts += ['notify=' + notify]
@ -1306,7 +1307,7 @@ class Project(object):
not self._RemoteFetch(initial=is_new, quiet=quiet, alt_dir=alt_dir,
current_branch_only=current_branch_only,
no_tags=no_tags, prune=prune, depth=depth,
submodules=submodules)):
submodules=submodules, force_sync=force_sync)):
return False
mp = self.manifest.manifestProject
@ -1807,8 +1808,8 @@ class Project(object):
submodules.append((sub_rev, sub_path, sub_url))
return submodules
re_path = re.compile(r'^submodule\.([^.]+)\.path=(.*)$')
re_url = re.compile(r'^submodule\.([^.]+)\.url=(.*)$')
re_path = re.compile(r'^submodule\.(.+)\.path=(.*)$')
re_url = re.compile(r'^submodule\.(.+)\.url=(.*)$')
def parse_gitmodules(gitdir, rev):
cmd = ['cat-file', 'blob', '%s:.gitmodules' % rev]
@ -1955,7 +1956,8 @@ class Project(object):
no_tags=False,
prune=False,
depth=None,
submodules=False):
submodules=False,
force_sync=False):
is_sha1 = False
tag_name = None
@ -1979,8 +1981,9 @@ class Project(object):
if is_sha1 or tag_name is not None:
if self._CheckForImmutableRevision():
print('Skipped fetching project %s (already have persistent ref)'
% self.name)
if not quiet:
print('Skipped fetching project %s (already have persistent ref)'
% self.name)
return True
if is_sha1 and not depth:
# When syncing a specific commit and --depth is not set:
@ -2068,6 +2071,9 @@ class Project(object):
else:
cmd.append('--tags')
if force_sync:
cmd.append('--force')
if prune:
cmd.append('--prune')
@ -2393,6 +2399,7 @@ class Project(object):
if m.Has(key, include_defaults=False):
self.config.SetString(key, m.GetString(key))
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:
self.config.SetString('core.bare', 'true')
else:
@ -2584,7 +2591,7 @@ class Project(object):
cmd.append('-v')
cmd.append(HEAD)
if GitCommand(self, cmd).Wait() != 0:
raise GitError("cannot initialize work tree")
raise GitError("cannot initialize work tree for " + self.name)
if submodules:
self._SyncSubmodules(quiet=True)
@ -2684,6 +2691,7 @@ class Project(object):
def DiffZ(self, name, *args):
cmd = [name]
cmd.append('-z')
cmd.append('--ignore-submodules')
cmd.extend(args)
p = GitCommand(self._project,
cmd,

17
repo
View File

@ -23,7 +23,7 @@ REPO_REV = 'stable'
# limitations under the License.
# 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
KEYRING_VERSION = (1, 2)
@ -113,7 +113,7 @@ repodir = '.repo' # name of repo's private directory
S_repo = 'repo' # special repo repository
S_manifests = 'manifests' # special manifest repository
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_FS_ROOT_DIR = '/gitc/manifest-rw/'
@ -152,9 +152,12 @@ def _print(*objects, **kwargs):
# Python version check
ver = sys.version_info
if (ver[0], ver[1]) < MIN_PYTHON_VERSION:
_print('error: Python version %s unsupported.\n'
'Please use Python 2.6 - 2.7 instead.'
% sys.version.split(' ')[0], file=sys.stderr)
_print('error: Python version {} unsupported.\n'
'Please use Python {}.{} instead.'.format(
sys.version.split(' ')[0],
MIN_PYTHON_VERSION[0],
MIN_PYTHON_VERSION[1],
), file=sys.stderr)
sys.exit(1)
home_dot_repo = os.path.expanduser('~/.repoconfig')
@ -180,7 +183,7 @@ group.add_option('-b', '--manifest-branch',
group.add_option('-m', '--manifest-name',
dest='manifest_name',
help='initial manifest file', metavar='NAME.xml')
group.add_option('-c', '--current-branch',
group.add_option('--current-branch',
dest='current_branch_only', action='store_true',
help='fetch only current manifest branch from server')
group.add_option('--mirror',
@ -909,6 +912,6 @@ def main(orig_args):
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.',
'Please use Python 2.7 instead.',
file=sys.stderr)
main(sys.argv[1:])

View File

@ -45,7 +45,7 @@ class Info(PagedCommand):
def Execute(self, opt, args):
self.out = _Coloring(self.manifest.globalConfig)
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.sha = self.out.printer("sha", fg = 'yellow')
self.text = self.out.nofmt_printer('text')

View File

@ -95,7 +95,7 @@ to update the working directory files.
g.add_option('-b', '--manifest-branch',
dest='manifest_branch',
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',
help='fetch only current manifest branch from server')
g.add_option('-m', '--manifest-name',
@ -197,6 +197,8 @@ to update the working directory files.
else:
m.PreSync()
self._ConfigureDepth(opt)
if opt.manifest_url:
r = m.GetRemote(m.remote.name)
r.url = opt.manifest_url
@ -429,6 +431,4 @@ to update the working directory files.
self._ConfigureUser()
self._ConfigureColor()
self._ConfigureDepth(opt)
self._DisplayResult()

View File

@ -136,6 +136,11 @@ directories if they have previously been linked to a different
object direcotry. WARNING: This may cause data to be lost since
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
$URL/clone.bundle to bootstrap a new Git repository from a
resumeable bundle file on a content delivery network. This
@ -197,6 +202,11 @@ later is required to fix a server side protocol bug.
help="overwrite an existing git directory if it needs to "
"point to a different object directory. WARNING: this "
"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',
dest='local_only', action='store_true',
help="only update working tree, don't fetch")
@ -525,7 +535,7 @@ later is required to fix a server side protocol bug.
return 0
def UpdateProjectList(self):
def UpdateProjectList(self, opt):
new_project_paths = []
for project in self.GetProjects(None, missing_ok=True):
if project.relpath:
@ -540,7 +550,8 @@ later is required to fix a server side protocol bug.
old_project_paths = fd.read().split('\n')
finally:
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:
continue
if path not in new_project_paths:
@ -559,7 +570,11 @@ later is required to fix a server side protocol bug.
revisionId = 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 '
'are present' % project.relpath, file=sys.stderr)
print(' commit changes, then run sync again',
@ -827,7 +842,7 @@ later is required to fix a server side protocol bug.
# bail out now, we have no working tree
return
if self.UpdateProjectList():
if self.UpdateProjectList(opt):
sys.exit(1)
syncbuf = SyncBuffer(mp.config,