mirror of
https://gerrit.googlesource.com/git-repo
synced 2025-06-26 20:17:52 +00:00
Compare commits
36 Commits
Author | SHA1 | Date | |
---|---|---|---|
8e91248655 | |||
630876f9e4 | |||
4aa8584ec6 | |||
b550501254 | |||
a535ae4418 | |||
67d6cdf2bc | |||
152032cca2 | |||
a3ac816278 | |||
98bb76577d | |||
d33dce0b77 | |||
89ed8acdbe | |||
71e48b7672 | |||
13576a8caf | |||
2345906d04 | |||
41289c62b4 | |||
c72bd8486a | |||
d53cb9549a | |||
cf0ba48649 | |||
2a089cfee4 | |||
4a478edb44 | |||
6bd89aa657 | |||
9c1fc5bc5d | |||
333c0a499b | |||
fdeb20f43f | |||
bf40957b38 | |||
f9e81c922d | |||
e6601067ed | |||
3001d6a426 | |||
00c5ea3787 | |||
0531a623e1 | |||
2273f46cb3 | |||
7b9b251a5e | |||
6251729cb4 | |||
11b30b91df | |||
198838599c | |||
282d0cae89 |
5
.gitreview
Normal file
5
.gitreview
Normal file
@ -0,0 +1,5 @@
|
||||
[gerrit]
|
||||
host=gerrit-review.googlesource.com
|
||||
scheme=https
|
||||
project=git-repo.git
|
||||
defaultbranch=main
|
@ -3,7 +3,7 @@
|
||||
# Short Version
|
||||
|
||||
- Make small logical changes.
|
||||
- Provide a meaningful commit message.
|
||||
- [Provide a meaningful commit message][commit-message-style].
|
||||
- Check for coding errors and style nits with flake8.
|
||||
- Make sure all code is under the Apache License, 2.0.
|
||||
- Publish your changes for review.
|
||||
@ -26,10 +26,11 @@ yourself with the following relevant bits.
|
||||
|
||||
## Make separate commits for logically separate changes.
|
||||
|
||||
Unless your patch is really trivial, you should not be sending
|
||||
out a patch that was generated between your working tree and your
|
||||
commit head. Instead, always make a commit with complete commit
|
||||
message and generate a series of patches from your repository.
|
||||
Unless your patch is really trivial, you should not be sending out a patch that
|
||||
was generated between your working tree and your commit head.
|
||||
Instead, always make a commit with a complete
|
||||
[commit message][commit-message-style] and generate a series of patches from
|
||||
your repository.
|
||||
It is a good discipline.
|
||||
|
||||
Describe the technical detail of the change(s).
|
||||
@ -171,3 +172,6 @@ After you receive a Code-Review+2 from the maintainer, select the Verified
|
||||
button on the gerrit page for the change. This verifies that you have tested
|
||||
your changes and notifies the maintainer that they are ready to be submitted.
|
||||
The maintainer will then submit your changes to the repository.
|
||||
|
||||
|
||||
[commit-message-style]: https://chris.beams.io/posts/git-commit/
|
||||
|
@ -163,6 +163,7 @@ User controlled settings are initialized when running `repo init`.
|
||||
| repo.clonefilter | `--clone-filter` | Filter setting when using [partial git clones] |
|
||||
| repo.depth | `--depth` | Create shallow checkouts when cloning |
|
||||
| repo.dissociate | `--dissociate` | Dissociate from any reference/mirrors after initial clone |
|
||||
| repo.git-lfs | `--git-lfs` | Enable [Git LFS] support |
|
||||
| repo.mirror | `--mirror` | Checkout is a repo mirror |
|
||||
| repo.partialclone | `--partial-clone` | Create [partial git clones] |
|
||||
| repo.partialcloneexclude | `--partial-clone-exclude` | Comma-delimited list of project names (not paths) to exclude while using [partial git clones] |
|
||||
@ -254,6 +255,7 @@ Repo will create & maintain a few files in the user's home directory.
|
||||
|
||||
|
||||
[git-config]: https://git-scm.com/docs/git-config
|
||||
[Git LFS]: https://git-lfs.github.com/
|
||||
[git worktree]: https://git-scm.com/docs/git-worktree
|
||||
[gitsubmodules]: https://git-scm.com/docs/gitsubmodules
|
||||
[manifest-format.md]: ./manifest-format.md
|
||||
|
18
fetch.py
18
fetch.py
@ -17,8 +17,10 @@
|
||||
import subprocess
|
||||
import sys
|
||||
from urllib.parse import urlparse
|
||||
from urllib.request import urlopen
|
||||
|
||||
def fetch_file(url):
|
||||
|
||||
def fetch_file(url, verbose=False):
|
||||
"""Fetch a file from the specified source using the appropriate protocol.
|
||||
|
||||
Returns:
|
||||
@ -29,13 +31,15 @@ def fetch_file(url):
|
||||
cmd = ['gsutil', 'cat', url]
|
||||
try:
|
||||
result = subprocess.run(
|
||||
cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
|
||||
check=True)
|
||||
if result.stderr and verbose:
|
||||
print('warning: non-fatal error running "gsutil": %s' % result.stderr,
|
||||
file=sys.stderr)
|
||||
return result.stdout
|
||||
except subprocess.CalledProcessError as e:
|
||||
print('fatal: error running "gsutil": %s' % e.output,
|
||||
print('fatal: error running "gsutil": %s' % e.stderr,
|
||||
file=sys.stderr)
|
||||
sys.exit(1)
|
||||
if scheme == 'file':
|
||||
with open(url[len('file://'):], 'rb') as f:
|
||||
return f.read()
|
||||
raise ValueError('unsupported url %s' % url)
|
||||
with urlopen(url) as f:
|
||||
return f.read()
|
||||
|
@ -169,7 +169,8 @@ class GitCommand(object):
|
||||
disable_editor=False,
|
||||
ssh_proxy=None,
|
||||
cwd=None,
|
||||
gitdir=None):
|
||||
gitdir=None,
|
||||
objdir=None):
|
||||
env = self._GetBasicEnv()
|
||||
|
||||
if disable_editor:
|
||||
@ -194,13 +195,24 @@ class GitCommand(object):
|
||||
cwd = project.worktree
|
||||
if not gitdir:
|
||||
gitdir = project.gitdir
|
||||
# Git on Windows wants its paths only using / for reliability.
|
||||
if platform_utils.isWindows():
|
||||
if objdir:
|
||||
objdir = objdir.replace('\\', '/')
|
||||
if gitdir:
|
||||
gitdir = gitdir.replace('\\', '/')
|
||||
|
||||
if objdir:
|
||||
# Set to the place we want to save the objects.
|
||||
env['GIT_OBJECT_DIRECTORY'] = objdir
|
||||
if gitdir:
|
||||
# Allow git to search the original place in case of local or unique refs
|
||||
# that git will attempt to resolve even if we aren't fetching them.
|
||||
env['GIT_ALTERNATE_OBJECT_DIRECTORIES'] = gitdir + '/objects'
|
||||
|
||||
command = [GIT]
|
||||
if bare:
|
||||
if gitdir:
|
||||
# Git on Windows wants its paths only using / for reliability.
|
||||
if platform_utils.isWindows():
|
||||
gitdir = gitdir.replace('\\', '/')
|
||||
env[GIT_DIR] = gitdir
|
||||
cwd = None
|
||||
command.append(cmdv[0])
|
||||
@ -234,6 +246,11 @@ class GitCommand(object):
|
||||
dbg += ': export GIT_DIR=%s\n' % env[GIT_DIR]
|
||||
LAST_GITDIR = env[GIT_DIR]
|
||||
|
||||
if 'GIT_OBJECT_DIRECTORY' in env:
|
||||
dbg += ': export GIT_OBJECT_DIRECTORY=%s\n' % env['GIT_OBJECT_DIRECTORY']
|
||||
if 'GIT_ALTERNATE_OBJECT_DIRECTORIES' in env:
|
||||
dbg += ': export GIT_ALTERNATE_OBJECT_DIRECTORIES=%s\n' % env['GIT_ALTERNATE_OBJECT_DIRECTORIES']
|
||||
|
||||
dbg += ': '
|
||||
dbg += ' '.join(command)
|
||||
if stdin == subprocess.PIPE:
|
||||
|
@ -352,7 +352,7 @@ class GitConfig(object):
|
||||
Trace(': parsing %s', self.file)
|
||||
with open(self._json) as fd:
|
||||
return json.load(fd)
|
||||
except (IOError, ValueErrorl):
|
||||
except (IOError, ValueError):
|
||||
platform_utils.remove(self._json, missing_ok=True)
|
||||
return None
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
.\" DO NOT MODIFY THIS FILE! It was generated by help2man.
|
||||
.TH REPO "1" "September 2021" "repo gitc-init" "Repo Manual"
|
||||
.TH REPO "1" "November 2021" "repo gitc-init" "Repo Manual"
|
||||
.SH NAME
|
||||
repo \- repo gitc-init - manual page for repo gitc-init
|
||||
.SH SYNOPSIS
|
||||
@ -31,10 +31,6 @@ manifest branch or revision (use HEAD for default)
|
||||
\fB\-m\fR NAME.xml, \fB\-\-manifest\-name\fR=\fI\,NAME\/\fR.xml
|
||||
initial manifest file
|
||||
.TP
|
||||
\fB\-\-standalone\-manifest\fR
|
||||
download the manifest as a static file rather then
|
||||
create a git checkout of the manifest repo
|
||||
.TP
|
||||
\fB\-g\fR GROUP, \fB\-\-groups\fR=\fI\,GROUP\/\fR
|
||||
restrict manifest projects to ones with specified
|
||||
group(s) [default|all|G1,G2,G3|G4,\-G5,\-G6]
|
||||
@ -45,6 +41,10 @@ platform group [auto|all|none|linux|darwin|...]
|
||||
.TP
|
||||
\fB\-\-submodules\fR
|
||||
sync any submodules associated with the manifest repo
|
||||
.TP
|
||||
\fB\-\-standalone\-manifest\fR
|
||||
download the manifest as a static file rather then
|
||||
create a git checkout of the manifest repo
|
||||
.SS Manifest (only) checkout options:
|
||||
.TP
|
||||
\fB\-\-current\-branch\fR
|
||||
@ -96,7 +96,8 @@ filter for use with \fB\-\-partial\-clone\fR [default:
|
||||
blob:none]
|
||||
.TP
|
||||
\fB\-\-use\-superproject\fR
|
||||
use the manifest superproject to sync projects
|
||||
use the manifest superproject to sync projects;
|
||||
implies \fB\-c\fR
|
||||
.TP
|
||||
\fB\-\-no\-use\-superproject\fR
|
||||
disable use of manifest superprojects
|
||||
|
@ -1,5 +1,5 @@
|
||||
.\" DO NOT MODIFY THIS FILE! It was generated by help2man.
|
||||
.TH REPO "1" "September 2021" "repo init" "Repo Manual"
|
||||
.TH REPO "1" "November 2021" "repo init" "Repo Manual"
|
||||
.SH NAME
|
||||
repo \- repo init - manual page for repo init
|
||||
.SH SYNOPSIS
|
||||
@ -31,10 +31,6 @@ manifest branch or revision (use HEAD for default)
|
||||
\fB\-m\fR NAME.xml, \fB\-\-manifest\-name\fR=\fI\,NAME\/\fR.xml
|
||||
initial manifest file
|
||||
.TP
|
||||
\fB\-\-standalone\-manifest\fR
|
||||
download the manifest as a static file rather then
|
||||
create a git checkout of the manifest repo
|
||||
.TP
|
||||
\fB\-g\fR GROUP, \fB\-\-groups\fR=\fI\,GROUP\/\fR
|
||||
restrict manifest projects to ones with specified
|
||||
group(s) [default|all|G1,G2,G3|G4,\-G5,\-G6]
|
||||
@ -45,6 +41,10 @@ platform group [auto|all|none|linux|darwin|...]
|
||||
.TP
|
||||
\fB\-\-submodules\fR
|
||||
sync any submodules associated with the manifest repo
|
||||
.TP
|
||||
\fB\-\-standalone\-manifest\fR
|
||||
download the manifest as a static file rather then
|
||||
create a git checkout of the manifest repo
|
||||
.SS Manifest (only) checkout options:
|
||||
.TP
|
||||
\fB\-c\fR, \fB\-\-current\-branch\fR
|
||||
@ -96,7 +96,8 @@ filter for use with \fB\-\-partial\-clone\fR [default:
|
||||
blob:none]
|
||||
.TP
|
||||
\fB\-\-use\-superproject\fR
|
||||
use the manifest superproject to sync projects
|
||||
use the manifest superproject to sync projects;
|
||||
implies \fB\-c\fR
|
||||
.TP
|
||||
\fB\-\-no\-use\-superproject\fR
|
||||
disable use of manifest superprojects
|
||||
|
@ -1,5 +1,5 @@
|
||||
.\" DO NOT MODIFY THIS FILE! It was generated by help2man.
|
||||
.TH REPO "1" "July 2021" "repo manifest" "Repo Manual"
|
||||
.TH REPO "1" "November 2021" "repo manifest" "Repo Manual"
|
||||
.SH NAME
|
||||
repo \- repo manifest - manual page for repo manifest
|
||||
.SH SYNOPSIS
|
||||
@ -161,6 +161,7 @@ CDATA #IMPLIED>
|
||||
<!ELEMENT extend\-project EMPTY>
|
||||
<!ATTLIST extend\-project name CDATA #REQUIRED>
|
||||
<!ATTLIST extend\-project path CDATA #IMPLIED>
|
||||
<!ATTLIST extend\-project dest\-path CDATA #IMPLIED>
|
||||
<!ATTLIST extend\-project groups CDATA #IMPLIED>
|
||||
<!ATTLIST extend\-project revision CDATA #IMPLIED>
|
||||
<!ATTLIST extend\-project remote CDATA #IMPLIED>
|
||||
@ -174,8 +175,9 @@ CDATA #IMPLIED>
|
||||
<!ATTLIST repo\-hooks enabled\-list CDATA #REQUIRED>
|
||||
.IP
|
||||
<!ELEMENT superproject EMPTY>
|
||||
<!ATTLIST superproject name CDATA #REQUIRED>
|
||||
<!ATTLIST superproject remote IDREF #IMPLIED>
|
||||
<!ATTLIST superproject name CDATA #REQUIRED>
|
||||
<!ATTLIST superproject remote IDREF #IMPLIED>
|
||||
<!ATTLIST superproject revision CDATA #IMPLIED>
|
||||
.IP
|
||||
<!ELEMENT contactinfo EMPTY>
|
||||
<!ATTLIST contactinfo bugurl CDATA #REQUIRED>
|
||||
@ -385,6 +387,11 @@ original manifest.
|
||||
Attribute `path`: If specified, limit the change to projects checked out at the
|
||||
specified path, rather than all projects with the given name.
|
||||
.PP
|
||||
Attribute `dest\-path`: If specified, a path relative to the top directory of the
|
||||
repo client where the Git working directory for this project should be placed.
|
||||
This is used to move a project in the checkout by overriding the existing `path`
|
||||
setting.
|
||||
.PP
|
||||
Attribute `groups`: List of additional groups to which this project belongs.
|
||||
Same syntax as the corresponding element of `project`.
|
||||
.PP
|
||||
@ -477,6 +484,10 @@ project](#element\-project) for more information.
|
||||
Attribute `remote`: Name of a previously defined remote element. If not supplied
|
||||
the remote given by the default element is used.
|
||||
.PP
|
||||
Attribute `revision`: Name of the Git branch the manifest wants to track for
|
||||
this superproject. If not supplied the revision given by the remote element is
|
||||
used if applicable, else the default element is used.
|
||||
.PP
|
||||
Element contactinfo
|
||||
.PP
|
||||
*** *Note*: This is currently a WIP. ***
|
||||
|
@ -1,5 +1,5 @@
|
||||
.\" DO NOT MODIFY THIS FILE! It was generated by help2man.
|
||||
.TH REPO "1" "July 2021" "repo smartsync" "Repo Manual"
|
||||
.TH REPO "1" "November 2021" "repo smartsync" "Repo Manual"
|
||||
.SH NAME
|
||||
repo \- repo smartsync - manual page for repo smartsync
|
||||
.SH SYNOPSIS
|
||||
@ -80,7 +80,8 @@ password to authenticate with the manifest server
|
||||
fetch submodules from server
|
||||
.TP
|
||||
\fB\-\-use\-superproject\fR
|
||||
use the manifest superproject to sync projects
|
||||
use the manifest superproject to sync projects;
|
||||
implies \fB\-c\fR
|
||||
.TP
|
||||
\fB\-\-no\-use\-superproject\fR
|
||||
disable use of manifest superprojects
|
||||
@ -89,7 +90,7 @@ disable use of manifest superprojects
|
||||
fetch tags
|
||||
.TP
|
||||
\fB\-\-no\-tags\fR
|
||||
don't fetch tags
|
||||
don't fetch tags (default)
|
||||
.TP
|
||||
\fB\-\-optimized\-fetch\fR
|
||||
only fetch projects fixed to sha1 if revision does not
|
||||
@ -100,6 +101,10 @@ number of times to retry fetches on transient errors
|
||||
.TP
|
||||
\fB\-\-prune\fR
|
||||
delete refs that no longer exist on the remote
|
||||
(default)
|
||||
.TP
|
||||
\fB\-\-no\-prune\fR
|
||||
do not delete refs that no longer exist on the remote
|
||||
.SS Logging options:
|
||||
.TP
|
||||
\fB\-v\fR, \fB\-\-verbose\fR
|
||||
|
@ -1,5 +1,5 @@
|
||||
.\" DO NOT MODIFY THIS FILE! It was generated by help2man.
|
||||
.TH REPO "1" "July 2021" "repo sync" "Repo Manual"
|
||||
.TH REPO "1" "November 2021" "repo sync" "Repo Manual"
|
||||
.SH NAME
|
||||
repo \- repo sync - manual page for repo sync
|
||||
.SH SYNOPSIS
|
||||
@ -80,7 +80,8 @@ password to authenticate with the manifest server
|
||||
fetch submodules from server
|
||||
.TP
|
||||
\fB\-\-use\-superproject\fR
|
||||
use the manifest superproject to sync projects
|
||||
use the manifest superproject to sync projects;
|
||||
implies \fB\-c\fR
|
||||
.TP
|
||||
\fB\-\-no\-use\-superproject\fR
|
||||
disable use of manifest superprojects
|
||||
@ -89,7 +90,7 @@ disable use of manifest superprojects
|
||||
fetch tags
|
||||
.TP
|
||||
\fB\-\-no\-tags\fR
|
||||
don't fetch tags
|
||||
don't fetch tags (default)
|
||||
.TP
|
||||
\fB\-\-optimized\-fetch\fR
|
||||
only fetch projects fixed to sha1 if revision does not
|
||||
@ -100,6 +101,10 @@ number of times to retry fetches on transient errors
|
||||
.TP
|
||||
\fB\-\-prune\fR
|
||||
delete refs that no longer exist on the remote
|
||||
(default)
|
||||
.TP
|
||||
\fB\-\-no\-prune\fR
|
||||
do not delete refs that no longer exist on the remote
|
||||
.TP
|
||||
\fB\-s\fR, \fB\-\-smart\-sync\fR
|
||||
smart sync using manifest from the latest known good
|
||||
|
@ -1,5 +1,5 @@
|
||||
.\" DO NOT MODIFY THIS FILE! It was generated by help2man.
|
||||
.TH REPO "1" "July 2021" "repo" "Repo Manual"
|
||||
.TH REPO "1" "November 2021" "repo" "Repo Manual"
|
||||
.SH NAME
|
||||
repo \- repository management tool built on top of git
|
||||
.SH SYNOPSIS
|
||||
@ -43,7 +43,7 @@ filename of event log to append timeline to
|
||||
.TP
|
||||
\fB\-\-git\-trace2\-event\-log\fR=\fI\,GIT_TRACE2_EVENT_LOG\/\fR
|
||||
directory to write git trace2 event log to
|
||||
.SS "The complete list of recognized repo commands are:"
|
||||
.SS "The complete list of recognized repo commands is:"
|
||||
.TP
|
||||
abandon
|
||||
Permanently abandon a development branch
|
||||
|
@ -666,6 +666,10 @@ https://gerrit.googlesource.com/git-repo/+/HEAD/docs/manifest-format.md
|
||||
def HasSubmodules(self):
|
||||
return self.manifestProject.config.GetBoolean('repo.submodules')
|
||||
|
||||
@property
|
||||
def EnableGitLfs(self):
|
||||
return self.manifestProject.config.GetBoolean('repo.git-lfs')
|
||||
|
||||
def GetDefaultGroupsStr(self):
|
||||
"""Returns the default group string for the platform."""
|
||||
return 'default,platform-' + platform.system().lower()
|
||||
|
238
project.py
238
project.py
@ -457,11 +457,7 @@ class RemoteSpec(object):
|
||||
|
||||
class Project(object):
|
||||
# These objects can be shared between several working trees.
|
||||
shareable_files = ['description', 'info']
|
||||
shareable_dirs = ['hooks', 'objects', 'rr-cache', 'svn']
|
||||
# These objects can only be used by a single working tree.
|
||||
working_tree_files = ['config', 'packed-refs', 'shallow']
|
||||
working_tree_dirs = ['logs', 'refs']
|
||||
shareable_dirs = ['hooks', 'objects', 'rr-cache']
|
||||
|
||||
def __init__(self,
|
||||
manifest,
|
||||
@ -1124,7 +1120,7 @@ class Project(object):
|
||||
self._InitRemote()
|
||||
|
||||
if is_new:
|
||||
alt = os.path.join(self.gitdir, 'objects/info/alternates')
|
||||
alt = os.path.join(self.objdir, 'objects/info/alternates')
|
||||
try:
|
||||
with open(alt) as fd:
|
||||
# This works for both absolute and relative alternate directories.
|
||||
@ -1173,7 +1169,7 @@ class Project(object):
|
||||
mp = self.manifest.manifestProject
|
||||
dissociate = mp.config.GetBoolean('repo.dissociate')
|
||||
if dissociate:
|
||||
alternates_file = os.path.join(self.gitdir, 'objects/info/alternates')
|
||||
alternates_file = os.path.join(self.objdir, 'objects/info/alternates')
|
||||
if os.path.exists(alternates_file):
|
||||
cmd = ['repack', '-a', '-d']
|
||||
p = GitCommand(self, cmd, bare=True, capture_stdout=bool(output_redir),
|
||||
@ -2044,8 +2040,11 @@ class Project(object):
|
||||
|
||||
if current_branch_only:
|
||||
if self.revisionExpr.startswith(R_TAGS):
|
||||
# this is a tag and its sha1 value should never change
|
||||
# This is a tag and its commit id should never change.
|
||||
tag_name = self.revisionExpr[len(R_TAGS):]
|
||||
elif self.upstream and self.upstream.startswith(R_TAGS):
|
||||
# This is a tag and its commit id should never change.
|
||||
tag_name = self.upstream[len(R_TAGS):]
|
||||
|
||||
if is_sha1 or tag_name is not None:
|
||||
if self._CheckForImmutableRevision():
|
||||
@ -2193,8 +2192,10 @@ class Project(object):
|
||||
retry_cur_sleep = retry_sleep_initial_sec
|
||||
ok = prune_tried = False
|
||||
for try_n in range(retry_fetches):
|
||||
gitcmd = GitCommand(self, cmd, bare=True, ssh_proxy=ssh_proxy,
|
||||
merge_output=True, capture_stdout=quiet or bool(output_redir))
|
||||
gitcmd = GitCommand(
|
||||
self, cmd, bare=True, objdir=os.path.join(self.objdir, 'objects'),
|
||||
ssh_proxy=ssh_proxy,
|
||||
merge_output=True, capture_stdout=quiet or bool(output_redir))
|
||||
if gitcmd.stdout and not quiet and output_redir:
|
||||
output_redir.write(gitcmd.stdout)
|
||||
ret = gitcmd.Wait()
|
||||
@ -2310,7 +2311,8 @@ class Project(object):
|
||||
cmd.append(str(f))
|
||||
cmd.append('+refs/tags/*:refs/tags/*')
|
||||
|
||||
ok = GitCommand(self, cmd, bare=True).Wait() == 0
|
||||
ok = GitCommand(
|
||||
self, cmd, bare=True, objdir=os.path.join(self.objdir, 'objects')).Wait() == 0
|
||||
platform_utils.remove(bundle_dst, missing_ok=True)
|
||||
platform_utils.remove(bundle_tmp, missing_ok=True)
|
||||
return ok
|
||||
@ -2440,7 +2442,7 @@ class Project(object):
|
||||
if quiet:
|
||||
cmd.append('-q')
|
||||
if GitCommand(self, cmd).Wait() != 0:
|
||||
raise GitError('%s submodule update --init --recursive %s ' % self.name)
|
||||
raise GitError('%s submodule update --init --recursive ' % self.name)
|
||||
|
||||
def _Rebase(self, upstream, onto=None):
|
||||
cmd = ['rebase']
|
||||
@ -2466,6 +2468,8 @@ class Project(object):
|
||||
os.makedirs(self.objdir)
|
||||
self.bare_objdir.init()
|
||||
|
||||
self._UpdateHooks(quiet=quiet)
|
||||
|
||||
if self.use_git_worktrees:
|
||||
# Enable per-worktree config file support if possible. This is more a
|
||||
# nice-to-have feature for users rather than a hard requirement.
|
||||
@ -2478,10 +2482,9 @@ class Project(object):
|
||||
os.makedirs(self.gitdir)
|
||||
|
||||
if init_obj_dir or init_git_dir:
|
||||
self._ReferenceGitDir(self.objdir, self.gitdir, share_refs=False,
|
||||
copy_all=True)
|
||||
self._ReferenceGitDir(self.objdir, self.gitdir, copy_all=True)
|
||||
try:
|
||||
self._CheckDirReference(self.objdir, self.gitdir, share_refs=False)
|
||||
self._CheckDirReference(self.objdir, self.gitdir)
|
||||
except GitError as e:
|
||||
if force_sync:
|
||||
print("Retrying clone after deleting %s" %
|
||||
@ -2504,8 +2507,8 @@ class Project(object):
|
||||
if ref_dir or mirror_git:
|
||||
if not mirror_git:
|
||||
mirror_git = os.path.join(ref_dir, self.name + '.git')
|
||||
repo_git = os.path.join(ref_dir, '.repo', 'projects',
|
||||
self.relpath + '.git')
|
||||
repo_git = os.path.join(ref_dir, '.repo', 'project-objects',
|
||||
self.name + '.git')
|
||||
worktrees_git = os.path.join(ref_dir, '.repo', 'worktrees',
|
||||
self.name + '.git')
|
||||
|
||||
@ -2523,17 +2526,16 @@ class Project(object):
|
||||
# The alternate directory is relative to the object database.
|
||||
ref_dir = os.path.relpath(ref_dir,
|
||||
os.path.join(self.objdir, 'objects'))
|
||||
_lwrite(os.path.join(self.gitdir, 'objects/info/alternates'),
|
||||
_lwrite(os.path.join(self.objdir, 'objects/info/alternates'),
|
||||
os.path.join(ref_dir, 'objects') + '\n')
|
||||
|
||||
self._UpdateHooks(quiet=quiet)
|
||||
|
||||
m = self.manifest.manifestProject.config
|
||||
for key in ['user.name', 'user.email']:
|
||||
if m.Has(key, include_defaults=False):
|
||||
self.config.SetString(key, m.GetString(key))
|
||||
self.config.SetString('filter.lfs.smudge', 'git-lfs smudge --skip -- %f')
|
||||
self.config.SetString('filter.lfs.process', 'git-lfs filter-process --skip')
|
||||
if not self.manifest.EnableGitLfs:
|
||||
self.config.SetString('filter.lfs.smudge', 'git-lfs smudge --skip -- %f')
|
||||
self.config.SetString('filter.lfs.process', 'git-lfs filter-process --skip')
|
||||
self.config.SetBoolean('core.bare', True if self.manifest.IsMirror else None)
|
||||
except Exception:
|
||||
if init_obj_dir and os.path.exists(self.objdir):
|
||||
@ -2543,13 +2545,21 @@ class Project(object):
|
||||
raise
|
||||
|
||||
def _UpdateHooks(self, quiet=False):
|
||||
if os.path.exists(self.gitdir):
|
||||
if os.path.exists(self.objdir):
|
||||
self._InitHooks(quiet=quiet)
|
||||
|
||||
def _InitHooks(self, quiet=False):
|
||||
hooks = platform_utils.realpath(self._gitdir_path('hooks'))
|
||||
hooks = platform_utils.realpath(os.path.join(self.objdir, 'hooks'))
|
||||
if not os.path.exists(hooks):
|
||||
os.makedirs(hooks)
|
||||
|
||||
# Delete sample hooks. They're noise.
|
||||
for hook in glob.glob(os.path.join(hooks, '*.sample')):
|
||||
try:
|
||||
platform_utils.remove(hook, missing_ok=True)
|
||||
except PermissionError:
|
||||
pass
|
||||
|
||||
for stock_hook in _ProjectHooks():
|
||||
name = os.path.basename(stock_hook)
|
||||
|
||||
@ -2647,40 +2657,16 @@ class Project(object):
|
||||
else:
|
||||
active_git.symbolic_ref('-m', msg, ref, dst)
|
||||
|
||||
def _CheckDirReference(self, srcdir, destdir, share_refs):
|
||||
def _CheckDirReference(self, srcdir, destdir):
|
||||
# Git worktrees don't use symlinks to share at all.
|
||||
if self.use_git_worktrees:
|
||||
return
|
||||
|
||||
symlink_files = self.shareable_files[:]
|
||||
symlink_dirs = self.shareable_dirs[:]
|
||||
if share_refs:
|
||||
symlink_files += self.working_tree_files
|
||||
symlink_dirs += self.working_tree_dirs
|
||||
to_symlink = symlink_files + symlink_dirs
|
||||
for name in set(to_symlink):
|
||||
for name in self.shareable_dirs:
|
||||
# Try to self-heal a bit in simple cases.
|
||||
dst_path = os.path.join(destdir, name)
|
||||
src_path = os.path.join(srcdir, name)
|
||||
|
||||
if name in self.working_tree_dirs:
|
||||
# If the dir is missing under .repo/projects/, create it.
|
||||
if not os.path.exists(src_path):
|
||||
os.makedirs(src_path)
|
||||
|
||||
elif name in self.working_tree_files:
|
||||
# If it's a file under the checkout .git/ and the .repo/projects/ has
|
||||
# nothing, move the file under the .repo/projects/ tree.
|
||||
if not os.path.exists(src_path) and os.path.isfile(dst_path):
|
||||
platform_utils.rename(dst_path, src_path)
|
||||
|
||||
# If the path exists under the .repo/projects/ and there's no symlink
|
||||
# under the checkout .git/, recreate the symlink.
|
||||
if name in self.working_tree_dirs or name in self.working_tree_files:
|
||||
if os.path.exists(src_path) and not os.path.exists(dst_path):
|
||||
platform_utils.symlink(
|
||||
os.path.relpath(src_path, os.path.dirname(dst_path)), dst_path)
|
||||
|
||||
dst = platform_utils.realpath(dst_path)
|
||||
if os.path.lexists(dst):
|
||||
src = platform_utils.realpath(src_path)
|
||||
@ -2693,23 +2679,17 @@ class Project(object):
|
||||
' use `repo sync --force-sync {0}` to '
|
||||
'proceed.'.format(self.relpath))
|
||||
|
||||
def _ReferenceGitDir(self, gitdir, dotgit, share_refs, copy_all):
|
||||
def _ReferenceGitDir(self, gitdir, dotgit, copy_all):
|
||||
"""Update |dotgit| to reference |gitdir|, using symlinks where possible.
|
||||
|
||||
Args:
|
||||
gitdir: The bare git repository. Must already be initialized.
|
||||
dotgit: The repository you would like to initialize.
|
||||
share_refs: If true, |dotgit| will store its refs under |gitdir|.
|
||||
Only one work tree can store refs under a given |gitdir|.
|
||||
copy_all: If true, copy all remaining files from |gitdir| -> |dotgit|.
|
||||
This saves you the effort of initializing |dotgit| yourself.
|
||||
"""
|
||||
symlink_files = self.shareable_files[:]
|
||||
symlink_dirs = self.shareable_dirs[:]
|
||||
if share_refs:
|
||||
symlink_files += self.working_tree_files
|
||||
symlink_dirs += self.working_tree_dirs
|
||||
to_symlink = symlink_files + symlink_dirs
|
||||
to_symlink = symlink_dirs
|
||||
|
||||
to_copy = []
|
||||
if copy_all:
|
||||
@ -2737,11 +2717,6 @@ class Project(object):
|
||||
elif os.path.isfile(src):
|
||||
shutil.copy(src, dst)
|
||||
|
||||
# If the source file doesn't exist, ensure the destination
|
||||
# file doesn't either.
|
||||
if name in symlink_files and not os.path.lexists(src):
|
||||
platform_utils.remove(dst, missing_ok=True)
|
||||
|
||||
except OSError as e:
|
||||
if e.errno == errno.EPERM:
|
||||
raise DownloadError(self._get_symlink_error_message())
|
||||
@ -2778,50 +2753,112 @@ class Project(object):
|
||||
self._InitMRef()
|
||||
|
||||
def _InitWorkTree(self, force_sync=False, submodules=False):
|
||||
realdotgit = os.path.join(self.worktree, '.git')
|
||||
tmpdotgit = realdotgit + '.tmp'
|
||||
init_dotgit = not os.path.exists(realdotgit)
|
||||
if init_dotgit:
|
||||
if self.use_git_worktrees:
|
||||
"""Setup the worktree .git path.
|
||||
|
||||
This is the user-visible path like src/foo/.git/.
|
||||
|
||||
With non-git-worktrees, this will be a symlink to the .repo/projects/ path.
|
||||
With git-worktrees, this will be a .git file using "gitdir: ..." syntax.
|
||||
|
||||
Older checkouts had .git/ directories. If we see that, migrate it.
|
||||
|
||||
This also handles changes in the manifest. Maybe this project was backed
|
||||
by "foo/bar" on the server, but now it's "new/foo/bar". We have to update
|
||||
the path we point to under .repo/projects/ to match.
|
||||
"""
|
||||
dotgit = os.path.join(self.worktree, '.git')
|
||||
|
||||
# If using an old layout style (a directory), migrate it.
|
||||
if not platform_utils.islink(dotgit) and platform_utils.isdir(dotgit):
|
||||
self._MigrateOldWorkTreeGitDir(dotgit)
|
||||
|
||||
init_dotgit = not os.path.exists(dotgit)
|
||||
if self.use_git_worktrees:
|
||||
if init_dotgit:
|
||||
self._InitGitWorktree()
|
||||
self._CopyAndLinkFiles()
|
||||
return
|
||||
|
||||
dotgit = tmpdotgit
|
||||
platform_utils.rmtree(tmpdotgit, ignore_errors=True)
|
||||
os.makedirs(tmpdotgit)
|
||||
self._ReferenceGitDir(self.gitdir, tmpdotgit, share_refs=True,
|
||||
copy_all=False)
|
||||
else:
|
||||
dotgit = realdotgit
|
||||
if not init_dotgit:
|
||||
# See if the project has changed.
|
||||
if platform_utils.realpath(self.gitdir) != platform_utils.realpath(dotgit):
|
||||
platform_utils.remove(dotgit)
|
||||
|
||||
try:
|
||||
self._CheckDirReference(self.gitdir, dotgit, share_refs=True)
|
||||
except GitError as e:
|
||||
if force_sync and not init_dotgit:
|
||||
try:
|
||||
platform_utils.rmtree(dotgit)
|
||||
return self._InitWorkTree(force_sync=False, submodules=submodules)
|
||||
except Exception:
|
||||
raise e
|
||||
raise e
|
||||
if init_dotgit or not os.path.exists(dotgit):
|
||||
os.makedirs(self.worktree, exist_ok=True)
|
||||
platform_utils.symlink(os.path.relpath(self.gitdir, self.worktree), dotgit)
|
||||
|
||||
if init_dotgit:
|
||||
_lwrite(os.path.join(tmpdotgit, HEAD), '%s\n' % self.GetRevisionId())
|
||||
if init_dotgit:
|
||||
_lwrite(os.path.join(dotgit, HEAD), '%s\n' % self.GetRevisionId())
|
||||
|
||||
# Now that the .git dir is fully set up, move it to its final home.
|
||||
platform_utils.rename(tmpdotgit, realdotgit)
|
||||
# Finish checking out the worktree.
|
||||
cmd = ['read-tree', '--reset', '-u', '-v', HEAD]
|
||||
if GitCommand(self, cmd).Wait() != 0:
|
||||
raise GitError('Cannot initialize work tree for ' + self.name)
|
||||
|
||||
# Finish checking out the worktree.
|
||||
cmd = ['read-tree', '--reset', '-u']
|
||||
cmd.append('-v')
|
||||
cmd.append(HEAD)
|
||||
if GitCommand(self, cmd).Wait() != 0:
|
||||
raise GitError('Cannot initialize work tree for ' + self.name)
|
||||
if submodules:
|
||||
self._SyncSubmodules(quiet=True)
|
||||
self._CopyAndLinkFiles()
|
||||
|
||||
if submodules:
|
||||
self._SyncSubmodules(quiet=True)
|
||||
self._CopyAndLinkFiles()
|
||||
@classmethod
|
||||
def _MigrateOldWorkTreeGitDir(cls, dotgit):
|
||||
"""Migrate the old worktree .git/ dir style to a symlink.
|
||||
|
||||
This logic specifically only uses state from |dotgit| to figure out where to
|
||||
move content and not |self|. This way if the backing project also changed
|
||||
places, we only do the .git/ dir to .git symlink migration here. The path
|
||||
updates will happen independently.
|
||||
"""
|
||||
# Figure out where in .repo/projects/ it's pointing to.
|
||||
if not os.path.islink(os.path.join(dotgit, 'refs')):
|
||||
raise GitError(f'{dotgit}: unsupported checkout state')
|
||||
gitdir = os.path.dirname(os.path.realpath(os.path.join(dotgit, 'refs')))
|
||||
|
||||
# Remove known symlink paths that exist in .repo/projects/.
|
||||
KNOWN_LINKS = {
|
||||
'config', 'description', 'hooks', 'info', 'logs', 'objects',
|
||||
'packed-refs', 'refs', 'rr-cache', 'shallow', 'svn',
|
||||
}
|
||||
# Paths that we know will be in both, but are safe to clobber in .repo/projects/.
|
||||
SAFE_TO_CLOBBER = {
|
||||
'COMMIT_EDITMSG', 'FETCH_HEAD', 'HEAD', 'gc.log', 'gitk.cache', 'index',
|
||||
'ORIG_HEAD',
|
||||
}
|
||||
|
||||
# First see if we'd succeed before starting the migration.
|
||||
unknown_paths = []
|
||||
for name in platform_utils.listdir(dotgit):
|
||||
# Ignore all temporary/backup names. These are common with vim & emacs.
|
||||
if name.endswith('~') or (name[0] == '#' and name[-1] == '#'):
|
||||
continue
|
||||
|
||||
dotgit_path = os.path.join(dotgit, name)
|
||||
if name in KNOWN_LINKS:
|
||||
if not platform_utils.islink(dotgit_path):
|
||||
unknown_paths.append(f'{dotgit_path}: should be a symlink')
|
||||
else:
|
||||
gitdir_path = os.path.join(gitdir, name)
|
||||
if name not in SAFE_TO_CLOBBER and os.path.exists(gitdir_path):
|
||||
unknown_paths.append(f'{dotgit_path}: unknown file; please file a bug')
|
||||
if unknown_paths:
|
||||
raise GitError('Aborting migration: ' + '\n'.join(unknown_paths))
|
||||
|
||||
# Now walk the paths and sync the .git/ to .repo/projects/.
|
||||
for name in platform_utils.listdir(dotgit):
|
||||
dotgit_path = os.path.join(dotgit, name)
|
||||
|
||||
# Ignore all temporary/backup names. These are common with vim & emacs.
|
||||
if name.endswith('~') or (name[0] == '#' and name[-1] == '#'):
|
||||
platform_utils.remove(dotgit_path)
|
||||
elif name in KNOWN_LINKS:
|
||||
platform_utils.remove(dotgit_path)
|
||||
else:
|
||||
gitdir_path = os.path.join(gitdir, name)
|
||||
platform_utils.remove(gitdir_path, missing_ok=True)
|
||||
platform_utils.rename(dotgit_path, gitdir_path)
|
||||
|
||||
# Now that the dir should be empty, clear it out, and symlink it over.
|
||||
platform_utils.rmdir(dotgit)
|
||||
platform_utils.symlink(os.path.relpath(gitdir, os.path.dirname(dotgit)), dotgit)
|
||||
|
||||
def _get_symlink_error_message(self):
|
||||
if platform_utils.isWindows():
|
||||
@ -2831,9 +2868,6 @@ class Project(object):
|
||||
'for other options.')
|
||||
return 'filesystem must support symlinks'
|
||||
|
||||
def _gitdir_path(self, path):
|
||||
return platform_utils.realpath(os.path.join(self.gitdir, path))
|
||||
|
||||
def _revlist(self, *args, **kw):
|
||||
a = []
|
||||
a.extend(args)
|
||||
|
10
repo
10
repo
@ -149,7 +149,7 @@ if not REPO_REV:
|
||||
BUG_URL = 'https://bugs.chromium.org/p/gerrit/issues/entry?template=Repo+tool+issue'
|
||||
|
||||
# increment this whenever we make important changes to this script
|
||||
VERSION = (2, 17)
|
||||
VERSION = (2, 21)
|
||||
|
||||
# increment this if the MAINTAINER_KEYS block is modified
|
||||
KEYRING_VERSION = (2, 3)
|
||||
@ -372,7 +372,7 @@ def InitParser(parser, gitc_init=False):
|
||||
help='filter for use with --partial-clone '
|
||||
'[default: %default]')
|
||||
group.add_option('--use-superproject', action='store_true', default=None,
|
||||
help='use the manifest superproject to sync projects')
|
||||
help='use the manifest superproject to sync projects; implies -c')
|
||||
group.add_option('--no-use-superproject', action='store_false',
|
||||
dest='use_superproject',
|
||||
help='disable use of manifest superprojects')
|
||||
@ -382,6 +382,11 @@ def InitParser(parser, gitc_init=False):
|
||||
group.add_option('--no-clone-bundle',
|
||||
dest='clone_bundle', action='store_false',
|
||||
help='disable use of /clone.bundle on HTTP/HTTPS (default if --partial-clone)')
|
||||
group.add_option('--git-lfs', action='store_true',
|
||||
help='enable Git LFS support')
|
||||
group.add_option('--no-git-lfs',
|
||||
dest='git_lfs', action='store_false',
|
||||
help='disable Git LFS support')
|
||||
|
||||
# Tool.
|
||||
group = parser.add_option_group('repo Version options')
|
||||
@ -618,6 +623,7 @@ def _Init(args, gitc_init=False):
|
||||
"REPO_URL set correctly?" % url, file=sys.stderr)
|
||||
|
||||
except CloneFailure:
|
||||
print('fatal: double check your --repo-rev setting.', file=sys.stderr)
|
||||
if opt.quiet:
|
||||
print('fatal: repo init failed; run without --quiet to see why',
|
||||
file=sys.stderr)
|
||||
|
3
ssh.py
3
ssh.py
@ -52,6 +52,9 @@ def version():
|
||||
"""return ssh version as a tuple"""
|
||||
try:
|
||||
return _parse_ssh_version()
|
||||
except FileNotFoundError:
|
||||
print('fatal: ssh not installed', file=sys.stderr)
|
||||
sys.exit(1)
|
||||
except subprocess.CalledProcessError:
|
||||
print('fatal: unable to detect ssh version', file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
@ -151,7 +151,7 @@ is shown, then the branch appears in all projects.
|
||||
fmt = out.write
|
||||
paths = []
|
||||
non_cur_paths = []
|
||||
if i.IsSplitCurrent or (in_cnt < project_cnt - in_cnt):
|
||||
if i.IsSplitCurrent or (in_cnt <= project_cnt - in_cnt):
|
||||
in_type = 'in'
|
||||
for b in i.projects:
|
||||
if not i.IsSplitCurrent or b.current:
|
||||
@ -163,9 +163,9 @@ is shown, then the branch appears in all projects.
|
||||
in_type = 'not in'
|
||||
have = set()
|
||||
for b in i.projects:
|
||||
have.add(b.project)
|
||||
have.add(b.project.relpath)
|
||||
for p in projects:
|
||||
if p not in have:
|
||||
if p.relpath not in have:
|
||||
paths.append(p.relpath)
|
||||
|
||||
s = ' %s %s' % (in_type, ', '.join(paths))
|
||||
|
@ -53,7 +53,7 @@ Displays detailed usage information about a command.
|
||||
self.PrintAllCommandsBody()
|
||||
|
||||
def PrintAllCommandsBody(self):
|
||||
print('The complete list of recognized repo commands are:')
|
||||
print('The complete list of recognized repo commands is:')
|
||||
commandNames = list(sorted(all_commands))
|
||||
self._PrintCommands(commandNames)
|
||||
print("See 'repo help <command>' for more information on a "
|
||||
|
@ -15,7 +15,6 @@
|
||||
import os
|
||||
import platform
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
import urllib.parse
|
||||
|
||||
@ -132,8 +131,8 @@ to update the working directory files.
|
||||
'cannot be re-initialized without --manifest-url/-u')
|
||||
sys.exit(1)
|
||||
|
||||
if opt.standalone_manifest or (
|
||||
was_standalone_manifest and opt.manifest_url):
|
||||
if opt.standalone_manifest or (was_standalone_manifest and
|
||||
opt.manifest_url):
|
||||
m.config.ClearCache()
|
||||
if m.gitdir and os.path.exists(m.gitdir):
|
||||
platform_utils.rmtree(m.gitdir)
|
||||
@ -292,13 +291,22 @@ to update the working directory files.
|
||||
if opt.submodules:
|
||||
m.config.SetBoolean('repo.submodules', opt.submodules)
|
||||
|
||||
if opt.git_lfs is not None:
|
||||
if opt.git_lfs:
|
||||
git_require((2, 17, 0), fail=True, msg='Git LFS support')
|
||||
|
||||
m.config.SetBoolean('repo.git-lfs', opt.git_lfs)
|
||||
if not is_new:
|
||||
print('warning: Changing --git-lfs settings will only affect new project checkouts.\n'
|
||||
' Existing projects will require manual updates.\n', file=sys.stderr)
|
||||
|
||||
if opt.use_superproject is not None:
|
||||
m.config.SetBoolean('repo.superproject', opt.use_superproject)
|
||||
|
||||
if standalone_manifest:
|
||||
if is_new:
|
||||
manifest_name = 'default.xml'
|
||||
manifest_data = fetch.fetch_file(opt.manifest_url)
|
||||
manifest_data = fetch.fetch_file(opt.manifest_url, verbose=opt.verbose)
|
||||
dest = os.path.join(m.worktree, manifest_name)
|
||||
os.makedirs(os.path.dirname(dest), exist_ok=True)
|
||||
with open(dest, 'wb') as f:
|
||||
@ -478,11 +486,16 @@ to update the working directory files.
|
||||
|
||||
# Check this here, else manifest will be tagged "not new" and init won't be
|
||||
# possible anymore without removing the .repo/manifests directory.
|
||||
if opt.archive and opt.mirror:
|
||||
self.OptionParser.error('--mirror and --archive cannot be used together.')
|
||||
if opt.mirror:
|
||||
if opt.archive:
|
||||
self.OptionParser.error('--mirror and --archive cannot be used '
|
||||
'together.')
|
||||
if opt.use_superproject is not None:
|
||||
self.OptionParser.error('--mirror and --use-superproject cannot be '
|
||||
'used together.')
|
||||
|
||||
if opt.standalone_manifest and (
|
||||
opt.manifest_branch or opt.manifest_name != 'default.xml'):
|
||||
if opt.standalone_manifest and (opt.manifest_branch or
|
||||
opt.manifest_name != 'default.xml'):
|
||||
self.OptionParser.error('--manifest-branch and --manifest-name cannot'
|
||||
' be used with --standalone-manifest.')
|
||||
|
||||
@ -516,8 +529,12 @@ to update the working directory files.
|
||||
# Handle new --repo-rev requests.
|
||||
if opt.repo_rev:
|
||||
wrapper = Wrapper()
|
||||
remote_ref, rev = wrapper.check_repo_rev(
|
||||
rp.gitdir, opt.repo_rev, repo_verify=opt.repo_verify, quiet=opt.quiet)
|
||||
try:
|
||||
remote_ref, rev = wrapper.check_repo_rev(
|
||||
rp.gitdir, opt.repo_rev, repo_verify=opt.repo_verify, quiet=opt.quiet)
|
||||
except wrapper.CloneFailure:
|
||||
print('fatal: double check your --repo-rev setting.', file=sys.stderr)
|
||||
sys.exit(1)
|
||||
branch = rp.GetBranch('default')
|
||||
branch.merge = remote_ref
|
||||
rp.work_git.reset('--hard', rev)
|
||||
|
@ -12,7 +12,6 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import errno
|
||||
import functools
|
||||
import http.cookiejar as cookielib
|
||||
import io
|
||||
@ -235,24 +234,25 @@ later is required to fix a server side protocol bug.
|
||||
dest='fetch_submodules', action='store_true',
|
||||
help='fetch submodules from server')
|
||||
p.add_option('--use-superproject', action='store_true',
|
||||
help='use the manifest superproject to sync projects')
|
||||
help='use the manifest superproject to sync projects; implies -c')
|
||||
p.add_option('--no-use-superproject', action='store_false',
|
||||
dest='use_superproject',
|
||||
help='disable use of manifest superprojects')
|
||||
p.add_option('--tags',
|
||||
action='store_false',
|
||||
p.add_option('--tags', action='store_true',
|
||||
help='fetch tags')
|
||||
p.add_option('--no-tags',
|
||||
dest='tags', action='store_false',
|
||||
help="don't fetch tags")
|
||||
help="don't fetch tags (default)")
|
||||
p.add_option('--optimized-fetch',
|
||||
dest='optimized_fetch', action='store_true',
|
||||
help='only fetch projects fixed to sha1 if revision does not exist locally')
|
||||
p.add_option('--retry-fetches',
|
||||
default=0, action='store', type='int',
|
||||
help='number of times to retry fetches on transient errors')
|
||||
p.add_option('--prune', dest='prune', action='store_true',
|
||||
help='delete refs that no longer exist on the remote')
|
||||
p.add_option('--prune', action='store_true',
|
||||
help='delete refs that no longer exist on the remote (default)')
|
||||
p.add_option('--no-prune', dest='prune', action='store_false',
|
||||
help='do not delete refs that no longer exist on the remote')
|
||||
if show_smart:
|
||||
p.add_option('-s', '--smart-sync',
|
||||
dest='smart_sync', action='store_true',
|
||||
@ -448,8 +448,8 @@ later is required to fix a server side protocol bug.
|
||||
else:
|
||||
pm.update(inc=0, msg='warming up')
|
||||
chunksize = 4
|
||||
with multiprocessing.Pool(
|
||||
jobs, initializer=self._FetchInitChild, initargs=(ssh_proxy,)) as pool:
|
||||
with multiprocessing.Pool(jobs, initializer=self._FetchInitChild,
|
||||
initargs=(ssh_proxy,)) as pool:
|
||||
results = pool.imap_unordered(
|
||||
functools.partial(self._FetchProjectList, opt),
|
||||
projects_list,
|
||||
@ -767,7 +767,7 @@ later is required to fix a server side protocol bug.
|
||||
with open(copylinkfile_path, 'rb') as fp:
|
||||
try:
|
||||
old_copylinkfile_paths = json.load(fp)
|
||||
except:
|
||||
except Exception:
|
||||
print('error: %s is not a json formatted file.' %
|
||||
copylinkfile_path, file=sys.stderr)
|
||||
platform_utils.remove(copylinkfile_path)
|
||||
@ -929,6 +929,9 @@ later is required to fix a server side protocol bug.
|
||||
if None in [opt.manifest_server_username, opt.manifest_server_password]:
|
||||
self.OptionParser.error('both -u and -p must be given')
|
||||
|
||||
if opt.prune is None:
|
||||
opt.prune = True
|
||||
|
||||
def Execute(self, opt, args):
|
||||
if opt.jobs:
|
||||
self.jobs = opt.jobs
|
||||
@ -983,6 +986,11 @@ later is required to fix a server side protocol bug.
|
||||
|
||||
load_local_manifests = not self.manifest.HasLocalManifests
|
||||
use_superproject = git_superproject.UseSuperproject(opt, self.manifest)
|
||||
if use_superproject and (self.manifest.IsMirror or self.manifest.IsArchive):
|
||||
# Don't use superproject, because we have no working tree.
|
||||
use_superproject = False
|
||||
if opt.use_superproject is not None:
|
||||
print('Defaulting to no-use-superproject because there is no working tree.')
|
||||
superproject_logging_data = {
|
||||
'superproject': use_superproject,
|
||||
'haslocalmanifests': bool(self.manifest.HasLocalManifests),
|
||||
@ -1118,6 +1126,15 @@ later is required to fix a server side protocol bug.
|
||||
|
||||
|
||||
def _PostRepoUpgrade(manifest, quiet=False):
|
||||
# Link the docs for the internal .repo/ layout for people
|
||||
link = os.path.join(manifest.repodir, 'internal-fs-layout.md')
|
||||
if not platform_utils.islink(link):
|
||||
target = os.path.join('repo', 'docs', 'internal-fs-layout.md')
|
||||
try:
|
||||
platform_utils.symlink(target, link)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
wrapper = Wrapper()
|
||||
if wrapper.NeedSetupGnuPG():
|
||||
wrapper.SetupGnuPG(quiet)
|
||||
|
@ -16,6 +16,7 @@
|
||||
|
||||
import contextlib
|
||||
import os
|
||||
from pathlib import Path
|
||||
import shutil
|
||||
import subprocess
|
||||
import tempfile
|
||||
@ -335,3 +336,76 @@ class LinkFile(CopyLinkTestCase):
|
||||
platform_utils.symlink(self.tempdir, dest)
|
||||
lf._Link()
|
||||
self.assertEqual(os.path.join('git-project', 'foo.txt'), os.readlink(dest))
|
||||
|
||||
|
||||
class MigrateWorkTreeTests(unittest.TestCase):
|
||||
"""Check _MigrateOldWorkTreeGitDir handling."""
|
||||
|
||||
_SYMLINKS = {
|
||||
'config', 'description', 'hooks', 'info', 'logs', 'objects',
|
||||
'packed-refs', 'refs', 'rr-cache', 'shallow', 'svn',
|
||||
}
|
||||
_FILES = {
|
||||
'COMMIT_EDITMSG', 'FETCH_HEAD', 'HEAD', 'index', 'ORIG_HEAD',
|
||||
'unknown-file-should-be-migrated',
|
||||
}
|
||||
_CLEAN_FILES = {
|
||||
'a-vim-temp-file~', '#an-emacs-temp-file#',
|
||||
}
|
||||
|
||||
@classmethod
|
||||
@contextlib.contextmanager
|
||||
def _simple_layout(cls):
|
||||
"""Create a simple repo client checkout to test against."""
|
||||
with tempfile.TemporaryDirectory() as tempdir:
|
||||
tempdir = Path(tempdir)
|
||||
|
||||
gitdir = tempdir / '.repo/projects/src/test.git'
|
||||
gitdir.mkdir(parents=True)
|
||||
cmd = ['git', 'init', '--bare', str(gitdir)]
|
||||
subprocess.check_call(cmd)
|
||||
|
||||
dotgit = tempdir / 'src/test/.git'
|
||||
dotgit.mkdir(parents=True)
|
||||
for name in cls._SYMLINKS:
|
||||
(dotgit / name).symlink_to(f'../../../.repo/projects/src/test.git/{name}')
|
||||
for name in cls._FILES | cls._CLEAN_FILES:
|
||||
(dotgit / name).write_text(name)
|
||||
|
||||
yield tempdir
|
||||
|
||||
def test_standard(self):
|
||||
"""Migrate a standard checkout that we expect."""
|
||||
with self._simple_layout() as tempdir:
|
||||
dotgit = tempdir / 'src/test/.git'
|
||||
project.Project._MigrateOldWorkTreeGitDir(str(dotgit))
|
||||
|
||||
# Make sure the dir was transformed into a symlink.
|
||||
self.assertTrue(dotgit.is_symlink())
|
||||
self.assertEqual(os.readlink(dotgit), '../../.repo/projects/src/test.git')
|
||||
|
||||
# Make sure files were moved over.
|
||||
gitdir = tempdir / '.repo/projects/src/test.git'
|
||||
for name in self._FILES:
|
||||
self.assertEqual(name, (gitdir / name).read_text())
|
||||
# Make sure files were removed.
|
||||
for name in self._CLEAN_FILES:
|
||||
self.assertFalse((gitdir / name).exists())
|
||||
|
||||
def test_unknown(self):
|
||||
"""A checkout with unknown files should abort."""
|
||||
with self._simple_layout() as tempdir:
|
||||
dotgit = tempdir / 'src/test/.git'
|
||||
(tempdir / '.repo/projects/src/test.git/random-file').write_text('one')
|
||||
(dotgit / 'random-file').write_text('two')
|
||||
with self.assertRaises(error.GitError):
|
||||
project.Project._MigrateOldWorkTreeGitDir(str(dotgit))
|
||||
|
||||
# Make sure no content was actually changed.
|
||||
self.assertTrue(dotgit.is_dir())
|
||||
for name in self._FILES:
|
||||
self.assertTrue((dotgit / name).is_file())
|
||||
for name in self._CLEAN_FILES:
|
||||
self.assertTrue((dotgit / name).is_file())
|
||||
for name in self._SYMLINKS:
|
||||
self.assertTrue((dotgit / name).is_symlink())
|
||||
|
Reference in New Issue
Block a user