mirror of
https://gerrit.googlesource.com/git-repo
synced 2025-06-30 20:17:08 +00:00
Compare commits
12 Commits
Author | SHA1 | Date | |
---|---|---|---|
24c6314fca | |||
7efab539f0 | |||
a3ff64cae5 | |||
776138a938 | |||
5fb9c6a5b3 | |||
859d3d9580 | |||
fa8d939c8f | |||
a6c52f566a | |||
0d130d2da0 | |||
b750b48f50 | |||
6c8b894d8d | |||
b6cfa09500 |
@ -230,12 +230,11 @@ class GitCommand(object):
|
|||||||
stderr = (subprocess.STDOUT if merge_output else
|
stderr = (subprocess.STDOUT if merge_output else
|
||||||
(subprocess.PIPE if capture_stderr else None))
|
(subprocess.PIPE if capture_stderr else None))
|
||||||
|
|
||||||
|
dbg = ''
|
||||||
if IsTrace():
|
if IsTrace():
|
||||||
global LAST_CWD
|
global LAST_CWD
|
||||||
global LAST_GITDIR
|
global LAST_GITDIR
|
||||||
|
|
||||||
dbg = ''
|
|
||||||
|
|
||||||
if cwd and LAST_CWD != cwd:
|
if cwd and LAST_CWD != cwd:
|
||||||
if LAST_GITDIR or LAST_CWD:
|
if LAST_GITDIR or LAST_CWD:
|
||||||
dbg += '\n'
|
dbg += '\n'
|
||||||
@ -263,8 +262,8 @@ class GitCommand(object):
|
|||||||
dbg += ' 2>|'
|
dbg += ' 2>|'
|
||||||
elif stderr == subprocess.STDOUT:
|
elif stderr == subprocess.STDOUT:
|
||||||
dbg += ' 2>&1'
|
dbg += ' 2>&1'
|
||||||
Trace('%s', dbg)
|
|
||||||
|
|
||||||
|
with Trace('git command %s %s with debug: %s', LAST_GITDIR, command, dbg):
|
||||||
try:
|
try:
|
||||||
p = subprocess.Popen(command,
|
p = subprocess.Popen(command,
|
||||||
cwd=cwd,
|
cwd=cwd,
|
||||||
|
@ -219,8 +219,8 @@ class GitConfig(object):
|
|||||||
"""Set the value(s) for a key.
|
"""Set the value(s) for a key.
|
||||||
Only this configuration file is modified.
|
Only this configuration file is modified.
|
||||||
|
|
||||||
The supplied value should be either a string,
|
The supplied value should be either a string, or a list of strings (to
|
||||||
or a list of strings (to store multiple values).
|
store multiple values), or None (to delete the key).
|
||||||
"""
|
"""
|
||||||
key = _key(name)
|
key = _key(name)
|
||||||
|
|
||||||
@ -349,7 +349,7 @@ class GitConfig(object):
|
|||||||
except OSError:
|
except OSError:
|
||||||
return None
|
return None
|
||||||
try:
|
try:
|
||||||
Trace(': parsing %s', self.file)
|
with Trace(': parsing %s', self.file):
|
||||||
with open(self._json) as fd:
|
with open(self._json) as fd:
|
||||||
return json.load(fd)
|
return json.load(fd)
|
||||||
except (IOError, ValueError):
|
except (IOError, ValueError):
|
||||||
|
@ -67,8 +67,7 @@ class GitRefs(object):
|
|||||||
self._LoadAll()
|
self._LoadAll()
|
||||||
|
|
||||||
def _NeedUpdate(self):
|
def _NeedUpdate(self):
|
||||||
Trace(': scan refs %s', self._gitdir)
|
with Trace(': scan refs %s', self._gitdir):
|
||||||
|
|
||||||
for name, mtime in self._mtime.items():
|
for name, mtime in self._mtime.items():
|
||||||
try:
|
try:
|
||||||
if mtime != os.path.getmtime(os.path.join(self._gitdir, name)):
|
if mtime != os.path.getmtime(os.path.join(self._gitdir, name)):
|
||||||
@ -78,7 +77,7 @@ class GitRefs(object):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
def _LoadAll(self):
|
def _LoadAll(self):
|
||||||
Trace(': load refs %s', self._gitdir)
|
with Trace(': load refs %s', self._gitdir):
|
||||||
|
|
||||||
self._phyref = {}
|
self._phyref = {}
|
||||||
self._symref = {}
|
self._symref = {}
|
||||||
|
40
main.py
40
main.py
@ -37,7 +37,7 @@ except ImportError:
|
|||||||
|
|
||||||
from color import SetDefaultColoring
|
from color import SetDefaultColoring
|
||||||
import event_log
|
import event_log
|
||||||
from repo_trace import SetTrace
|
from repo_trace import SetTrace, Trace, SetTraceToStderr
|
||||||
from git_command import user_agent
|
from git_command import user_agent
|
||||||
from git_config import RepoConfig
|
from git_config import RepoConfig
|
||||||
from git_trace2_event_log import EventLog
|
from git_trace2_event_log import EventLog
|
||||||
@ -109,6 +109,9 @@ global_options.add_option('--color',
|
|||||||
global_options.add_option('--trace',
|
global_options.add_option('--trace',
|
||||||
dest='trace', action='store_true',
|
dest='trace', action='store_true',
|
||||||
help='trace git command execution (REPO_TRACE=1)')
|
help='trace git command execution (REPO_TRACE=1)')
|
||||||
|
global_options.add_option('--trace-to-stderr',
|
||||||
|
dest='trace_to_stderr', action='store_true',
|
||||||
|
help='trace outputs go to stderr in addition to .repo/TRACE_FILE')
|
||||||
global_options.add_option('--trace-python',
|
global_options.add_option('--trace-python',
|
||||||
dest='trace_python', action='store_true',
|
dest='trace_python', action='store_true',
|
||||||
help='trace python command execution')
|
help='trace python command execution')
|
||||||
@ -198,9 +201,6 @@ class _Repo(object):
|
|||||||
"""Execute the requested subcommand."""
|
"""Execute the requested subcommand."""
|
||||||
result = 0
|
result = 0
|
||||||
|
|
||||||
if gopts.trace:
|
|
||||||
SetTrace()
|
|
||||||
|
|
||||||
# Handle options that terminate quickly first.
|
# Handle options that terminate quickly first.
|
||||||
if gopts.help or gopts.help_all:
|
if gopts.help or gopts.help_all:
|
||||||
self._PrintHelp(short=False, all_commands=gopts.help_all)
|
self._PrintHelp(short=False, all_commands=gopts.help_all)
|
||||||
@ -216,6 +216,21 @@ class _Repo(object):
|
|||||||
self._PrintHelp(short=True)
|
self._PrintHelp(short=True)
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
|
run = lambda: self._RunLong(name, gopts, argv) or 0
|
||||||
|
with Trace('starting new command: %s', ', '.join([name] + argv),
|
||||||
|
first_trace=True):
|
||||||
|
if gopts.trace_python:
|
||||||
|
import trace
|
||||||
|
tracer = trace.Trace(count=False, trace=True, timing=True,
|
||||||
|
ignoredirs=set(sys.path[1:]))
|
||||||
|
result = tracer.runfunc(run)
|
||||||
|
else:
|
||||||
|
result = run()
|
||||||
|
return result
|
||||||
|
|
||||||
|
def _RunLong(self, name, gopts, argv):
|
||||||
|
"""Execute the (longer running) requested subcommand."""
|
||||||
|
result = 0
|
||||||
SetDefaultColoring(gopts.color)
|
SetDefaultColoring(gopts.color)
|
||||||
|
|
||||||
git_trace2_event_log = EventLog()
|
git_trace2_event_log = EventLog()
|
||||||
@ -652,17 +667,18 @@ def _Main(argv):
|
|||||||
Version.wrapper_path = opt.wrapper_path
|
Version.wrapper_path = opt.wrapper_path
|
||||||
|
|
||||||
repo = _Repo(opt.repodir)
|
repo = _Repo(opt.repodir)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
init_http()
|
init_http()
|
||||||
name, gopts, argv = repo._ParseArgs(argv)
|
name, gopts, argv = repo._ParseArgs(argv)
|
||||||
run = lambda: repo._Run(name, gopts, argv) or 0
|
|
||||||
if gopts.trace_python:
|
if gopts.trace:
|
||||||
import trace
|
SetTrace()
|
||||||
tracer = trace.Trace(count=False, trace=True, timing=True,
|
|
||||||
ignoredirs=set(sys.path[1:]))
|
if gopts.trace_to_stderr:
|
||||||
result = tracer.runfunc(run)
|
SetTraceToStderr()
|
||||||
else:
|
|
||||||
result = run()
|
result = repo._Run(name, gopts, argv) or 0
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
print('aborted by user', file=sys.stderr)
|
print('aborted by user', file=sys.stderr)
|
||||||
result = 1
|
result = 1
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
.\" DO NOT MODIFY THIS FILE! It was generated by help2man.
|
.\" DO NOT MODIFY THIS FILE! It was generated by help2man.
|
||||||
.TH REPO "1" "August 2022" "repo gitc-init" "Repo Manual"
|
.TH REPO "1" "October 2022" "repo gitc-init" "Repo Manual"
|
||||||
.SH NAME
|
.SH NAME
|
||||||
repo \- repo gitc-init - manual page for repo gitc-init
|
repo \- repo gitc-init - manual page for repo gitc-init
|
||||||
.SH SYNOPSIS
|
.SH SYNOPSIS
|
||||||
@ -48,7 +48,7 @@ create a git checkout of the manifest repo
|
|||||||
.TP
|
.TP
|
||||||
\fB\-\-manifest\-depth\fR=\fI\,DEPTH\/\fR
|
\fB\-\-manifest\-depth\fR=\fI\,DEPTH\/\fR
|
||||||
create a shallow clone of the manifest repo with given
|
create a shallow clone of the manifest repo with given
|
||||||
depth; see git clone (default: 1)
|
depth (0 for full clone); see git clone (default: 0)
|
||||||
.SS Manifest (only) checkout options:
|
.SS Manifest (only) checkout options:
|
||||||
.TP
|
.TP
|
||||||
\fB\-\-current\-branch\fR
|
\fB\-\-current\-branch\fR
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
.\" DO NOT MODIFY THIS FILE! It was generated by help2man.
|
.\" DO NOT MODIFY THIS FILE! It was generated by help2man.
|
||||||
.TH REPO "1" "August 2022" "repo init" "Repo Manual"
|
.TH REPO "1" "October 2022" "repo init" "Repo Manual"
|
||||||
.SH NAME
|
.SH NAME
|
||||||
repo \- repo init - manual page for repo init
|
repo \- repo init - manual page for repo init
|
||||||
.SH SYNOPSIS
|
.SH SYNOPSIS
|
||||||
@ -48,7 +48,7 @@ create a git checkout of the manifest repo
|
|||||||
.TP
|
.TP
|
||||||
\fB\-\-manifest\-depth\fR=\fI\,DEPTH\/\fR
|
\fB\-\-manifest\-depth\fR=\fI\,DEPTH\/\fR
|
||||||
create a shallow clone of the manifest repo with given
|
create a shallow clone of the manifest repo with given
|
||||||
depth; see git clone (default: 1)
|
depth (0 for full clone); see git clone (default: 0)
|
||||||
.SS Manifest (only) checkout options:
|
.SS Manifest (only) checkout options:
|
||||||
.TP
|
.TP
|
||||||
\fB\-c\fR, \fB\-\-current\-branch\fR
|
\fB\-c\fR, \fB\-\-current\-branch\fR
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
.\" DO NOT MODIFY THIS FILE! It was generated by help2man.
|
.\" DO NOT MODIFY THIS FILE! It was generated by help2man.
|
||||||
.TH REPO "1" "July 2022" "repo manifest" "Repo Manual"
|
.TH REPO "1" "October 2022" "repo manifest" "Repo Manual"
|
||||||
.SH NAME
|
.SH NAME
|
||||||
repo \- repo manifest - manual page for repo manifest
|
repo \- repo manifest - manual page for repo manifest
|
||||||
.SH SYNOPSIS
|
.SH SYNOPSIS
|
||||||
@ -190,6 +190,8 @@ CDATA #IMPLIED>
|
|||||||
<!ATTLIST extend\-project groups CDATA #IMPLIED>
|
<!ATTLIST extend\-project groups CDATA #IMPLIED>
|
||||||
<!ATTLIST extend\-project revision CDATA #IMPLIED>
|
<!ATTLIST extend\-project revision CDATA #IMPLIED>
|
||||||
<!ATTLIST extend\-project remote CDATA #IMPLIED>
|
<!ATTLIST extend\-project remote CDATA #IMPLIED>
|
||||||
|
<!ATTLIST extend\-project dest\-branch CDATA #IMPLIED>
|
||||||
|
<!ATTLIST extend\-project upstream CDATA #IMPLIED>
|
||||||
.IP
|
.IP
|
||||||
<!ELEMENT remove\-project EMPTY>
|
<!ELEMENT remove\-project EMPTY>
|
||||||
<!ATTLIST remove\-project name CDATA #REQUIRED>
|
<!ATTLIST remove\-project name CDATA #REQUIRED>
|
||||||
@ -485,6 +487,12 @@ project. Same syntax as the corresponding element of `project`.
|
|||||||
Attribute `remote`: If specified, overrides the remote of the original project.
|
Attribute `remote`: If specified, overrides the remote of the original project.
|
||||||
Same syntax as the corresponding element of `project`.
|
Same syntax as the corresponding element of `project`.
|
||||||
.PP
|
.PP
|
||||||
|
Attribute `dest\-branch`: If specified, overrides the dest\-branch of the original
|
||||||
|
project. Same syntax as the corresponding element of `project`.
|
||||||
|
.PP
|
||||||
|
Attribute `upstream`: If specified, overrides the upstream of the original
|
||||||
|
project. Same syntax as the corresponding element of `project`.
|
||||||
|
.PP
|
||||||
Element annotation
|
Element annotation
|
||||||
.PP
|
.PP
|
||||||
Zero or more annotation elements may be specified as children of a project or
|
Zero or more annotation elements may be specified as children of a project or
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
.\" DO NOT MODIFY THIS FILE! It was generated by help2man.
|
.\" DO NOT MODIFY THIS FILE! It was generated by help2man.
|
||||||
.TH REPO "1" "August 2022" "repo smartsync" "Repo Manual"
|
.TH REPO "1" "November 2022" "repo smartsync" "Repo Manual"
|
||||||
.SH NAME
|
.SH NAME
|
||||||
repo \- repo smartsync - manual page for repo smartsync
|
repo \- repo smartsync - manual page for repo smartsync
|
||||||
.SH SYNOPSIS
|
.SH SYNOPSIS
|
||||||
@ -105,6 +105,13 @@ delete refs that no longer exist on the remote
|
|||||||
.TP
|
.TP
|
||||||
\fB\-\-no\-prune\fR
|
\fB\-\-no\-prune\fR
|
||||||
do not delete refs that no longer exist on the remote
|
do not delete refs that no longer exist on the remote
|
||||||
|
.TP
|
||||||
|
\fB\-\-auto\-gc\fR
|
||||||
|
run garbage collection on all synced projects
|
||||||
|
.TP
|
||||||
|
\fB\-\-no\-auto\-gc\fR
|
||||||
|
do not run garbage collection on any projects
|
||||||
|
(default)
|
||||||
.SS Logging options:
|
.SS Logging options:
|
||||||
.TP
|
.TP
|
||||||
\fB\-v\fR, \fB\-\-verbose\fR
|
\fB\-v\fR, \fB\-\-verbose\fR
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
.\" DO NOT MODIFY THIS FILE! It was generated by help2man.
|
.\" DO NOT MODIFY THIS FILE! It was generated by help2man.
|
||||||
.TH REPO "1" "August 2022" "repo sync" "Repo Manual"
|
.TH REPO "1" "November 2022" "repo sync" "Repo Manual"
|
||||||
.SH NAME
|
.SH NAME
|
||||||
repo \- repo sync - manual page for repo sync
|
repo \- repo sync - manual page for repo sync
|
||||||
.SH SYNOPSIS
|
.SH SYNOPSIS
|
||||||
@ -106,6 +106,13 @@ delete refs that no longer exist on the remote
|
|||||||
\fB\-\-no\-prune\fR
|
\fB\-\-no\-prune\fR
|
||||||
do not delete refs that no longer exist on the remote
|
do not delete refs that no longer exist on the remote
|
||||||
.TP
|
.TP
|
||||||
|
\fB\-\-auto\-gc\fR
|
||||||
|
run garbage collection on all synced projects
|
||||||
|
.TP
|
||||||
|
\fB\-\-no\-auto\-gc\fR
|
||||||
|
do not run garbage collection on any projects
|
||||||
|
(default)
|
||||||
|
.TP
|
||||||
\fB\-s\fR, \fB\-\-smart\-sync\fR
|
\fB\-s\fR, \fB\-\-smart\-sync\fR
|
||||||
smart sync using manifest from the latest known good
|
smart sync using manifest from the latest known good
|
||||||
build
|
build
|
||||||
@ -200,6 +207,9 @@ to a sha1 revision if the sha1 revision does not already exist locally.
|
|||||||
The \fB\-\-prune\fR option can be used to remove any refs that no longer exist on the
|
The \fB\-\-prune\fR option can be used to remove any refs that no longer exist on the
|
||||||
remote.
|
remote.
|
||||||
.PP
|
.PP
|
||||||
|
The \fB\-\-auto\-gc\fR option can be used to trigger garbage collection on all projects.
|
||||||
|
By default, repo does not run garbage collection.
|
||||||
|
.PP
|
||||||
SSH Connections
|
SSH Connections
|
||||||
.PP
|
.PP
|
||||||
If at least one project remote URL uses an SSH connection (ssh://, git+ssh://,
|
If at least one project remote URL uses an SSH connection (ssh://, git+ssh://,
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
.\" DO NOT MODIFY THIS FILE! It was generated by help2man.
|
.\" DO NOT MODIFY THIS FILE! It was generated by help2man.
|
||||||
.TH REPO "1" "July 2022" "repo" "Repo Manual"
|
.TH REPO "1" "November 2022" "repo" "Repo Manual"
|
||||||
.SH NAME
|
.SH NAME
|
||||||
repo \- repository management tool built on top of git
|
repo \- repository management tool built on top of git
|
||||||
.SH SYNOPSIS
|
.SH SYNOPSIS
|
||||||
@ -25,6 +25,10 @@ control color usage: auto, always, never
|
|||||||
\fB\-\-trace\fR
|
\fB\-\-trace\fR
|
||||||
trace git command execution (REPO_TRACE=1)
|
trace git command execution (REPO_TRACE=1)
|
||||||
.TP
|
.TP
|
||||||
|
\fB\-\-trace-to-stderr\fR
|
||||||
|
trace outputs go to stderr in addition to
|
||||||
|
\&.repo/TRACE_FILE
|
||||||
|
.TP
|
||||||
\fB\-\-trace\-python\fR
|
\fB\-\-trace\-python\fR
|
||||||
trace python command execution
|
trace python command execution
|
||||||
.TP
|
.TP
|
||||||
|
@ -41,7 +41,7 @@ from error import ManifestInvalidRevisionError, ManifestInvalidPathError
|
|||||||
from error import NoManifestException, ManifestParseError
|
from error import NoManifestException, ManifestParseError
|
||||||
import platform_utils
|
import platform_utils
|
||||||
import progress
|
import progress
|
||||||
from repo_trace import IsTrace, Trace
|
from repo_trace import Trace
|
||||||
|
|
||||||
from git_refs import GitRefs, HEAD, R_HEADS, R_TAGS, R_PUB, R_M, R_WORKTREE_M
|
from git_refs import GitRefs, HEAD, R_HEADS, R_TAGS, R_PUB, R_M, R_WORKTREE_M
|
||||||
|
|
||||||
@ -59,7 +59,7 @@ MAXIMUM_RETRY_SLEEP_SEC = 3600.0
|
|||||||
# +-10% random jitter is added to each Fetches retry sleep duration.
|
# +-10% random jitter is added to each Fetches retry sleep duration.
|
||||||
RETRY_JITTER_PERCENT = 0.1
|
RETRY_JITTER_PERCENT = 0.1
|
||||||
|
|
||||||
# Whether to use alternates.
|
# Whether to use alternates. Switching back and forth is *NOT* supported.
|
||||||
# TODO(vapier): Remove knob once behavior is verified.
|
# TODO(vapier): Remove knob once behavior is verified.
|
||||||
_ALTERNATES = os.environ.get('REPO_USE_ALTERNATES') == '1'
|
_ALTERNATES = os.environ.get('REPO_USE_ALTERNATES') == '1'
|
||||||
|
|
||||||
@ -2416,8 +2416,8 @@ class Project(object):
|
|||||||
srcUrl = 'http' + srcUrl[len('persistent-http'):]
|
srcUrl = 'http' + srcUrl[len('persistent-http'):]
|
||||||
cmd += [srcUrl]
|
cmd += [srcUrl]
|
||||||
|
|
||||||
if IsTrace():
|
proc = None
|
||||||
Trace('%s', ' '.join(cmd))
|
with Trace('Fetching bundle: %s', ' '.join(cmd)):
|
||||||
if verbose:
|
if verbose:
|
||||||
print('%s: Downloading bundle: %s' % (self.name, srcUrl))
|
print('%s: Downloading bundle: %s' % (self.name, srcUrl))
|
||||||
stdout = None if verbose else subprocess.PIPE
|
stdout = None if verbose else subprocess.PIPE
|
||||||
|
5
repo
5
repo
@ -316,9 +316,10 @@ def InitParser(parser, gitc_init=False):
|
|||||||
help='download the manifest as a static file '
|
help='download the manifest as a static file '
|
||||||
'rather then create a git checkout of '
|
'rather then create a git checkout of '
|
||||||
'the manifest repo')
|
'the manifest repo')
|
||||||
group.add_option('--manifest-depth', type='int', default=1, metavar='DEPTH',
|
group.add_option('--manifest-depth', type='int', default=0, metavar='DEPTH',
|
||||||
help='create a shallow clone of the manifest repo with '
|
help='create a shallow clone of the manifest repo with '
|
||||||
'given depth; see git clone (default: %default)')
|
'given depth (0 for full clone); see git clone '
|
||||||
|
'(default: %default)')
|
||||||
|
|
||||||
# Options that only affect manifest project, and not any of the projects
|
# Options that only affect manifest project, and not any of the projects
|
||||||
# specified in the manifest itself.
|
# specified in the manifest itself.
|
||||||
|
111
repo_trace.py
111
repo_trace.py
@ -15,26 +15,129 @@
|
|||||||
"""Logic for tracing repo interactions.
|
"""Logic for tracing repo interactions.
|
||||||
|
|
||||||
Activated via `repo --trace ...` or `REPO_TRACE=1 repo ...`.
|
Activated via `repo --trace ...` or `REPO_TRACE=1 repo ...`.
|
||||||
|
|
||||||
|
Temporary: Tracing is always on. Set `REPO_TRACE=0` to turn off.
|
||||||
|
To also include trace outputs in stderr do `repo --trace_to_stderr ...`
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
import os
|
import os
|
||||||
|
import time
|
||||||
|
from contextlib import ContextDecorator
|
||||||
|
|
||||||
|
import platform_utils
|
||||||
|
|
||||||
# Env var to implicitly turn on tracing.
|
# Env var to implicitly turn on tracing.
|
||||||
REPO_TRACE = 'REPO_TRACE'
|
REPO_TRACE = 'REPO_TRACE'
|
||||||
|
|
||||||
_TRACE = os.environ.get(REPO_TRACE) == '1'
|
# Temporarily set tracing to always on unless user expicitly sets to 0.
|
||||||
|
_TRACE = os.environ.get(REPO_TRACE) != '0'
|
||||||
|
|
||||||
|
_TRACE_TO_STDERR = False
|
||||||
|
|
||||||
|
_TRACE_FILE = None
|
||||||
|
|
||||||
|
_TRACE_FILE_NAME = 'TRACE_FILE'
|
||||||
|
|
||||||
|
_MAX_SIZE = 70 # in mb
|
||||||
|
|
||||||
|
_NEW_COMMAND_SEP = '+++++++++++++++NEW COMMAND+++++++++++++++++++'
|
||||||
|
|
||||||
|
|
||||||
|
def IsStraceToStderr():
|
||||||
|
return _TRACE_TO_STDERR
|
||||||
|
|
||||||
|
|
||||||
def IsTrace():
|
def IsTrace():
|
||||||
return _TRACE
|
return _TRACE
|
||||||
|
|
||||||
|
|
||||||
|
def SetTraceToStderr():
|
||||||
|
global _TRACE_TO_STDERR
|
||||||
|
_TRACE_TO_STDERR = True
|
||||||
|
|
||||||
|
|
||||||
def SetTrace():
|
def SetTrace():
|
||||||
global _TRACE
|
global _TRACE
|
||||||
_TRACE = True
|
_TRACE = True
|
||||||
|
|
||||||
|
|
||||||
def Trace(fmt, *args):
|
def _SetTraceFile():
|
||||||
if IsTrace():
|
global _TRACE_FILE
|
||||||
print(fmt % args, file=sys.stderr)
|
_TRACE_FILE = _GetTraceFile()
|
||||||
|
|
||||||
|
|
||||||
|
class Trace(ContextDecorator):
|
||||||
|
|
||||||
|
def _time(self):
|
||||||
|
"""Generate nanoseconds of time in a py3.6 safe way"""
|
||||||
|
return int(time.time()*1e+9)
|
||||||
|
|
||||||
|
def __init__(self, fmt, *args, first_trace=False):
|
||||||
|
if not IsTrace():
|
||||||
|
return
|
||||||
|
self._trace_msg = fmt % args
|
||||||
|
|
||||||
|
if not _TRACE_FILE:
|
||||||
|
_SetTraceFile()
|
||||||
|
|
||||||
|
if first_trace:
|
||||||
|
_ClearOldTraces()
|
||||||
|
self._trace_msg = '%s %s' % (_NEW_COMMAND_SEP, self._trace_msg)
|
||||||
|
|
||||||
|
|
||||||
|
def __enter__(self):
|
||||||
|
if not IsTrace():
|
||||||
|
return self
|
||||||
|
|
||||||
|
print_msg = f"PID: {os.getpid()} START: {self._time()} :" + self._trace_msg + '\n'
|
||||||
|
|
||||||
|
with open(_TRACE_FILE, 'a') as f:
|
||||||
|
print(print_msg, file=f)
|
||||||
|
|
||||||
|
if _TRACE_TO_STDERR:
|
||||||
|
print(print_msg, file=sys.stderr)
|
||||||
|
|
||||||
|
return self
|
||||||
|
|
||||||
|
def __exit__(self, *exc):
|
||||||
|
if not IsTrace():
|
||||||
|
return False
|
||||||
|
|
||||||
|
print_msg = f"PID: {os.getpid()} END: {self._time()} :" + self._trace_msg + '\n'
|
||||||
|
|
||||||
|
with open(_TRACE_FILE, 'a') as f:
|
||||||
|
print(print_msg, file=f)
|
||||||
|
|
||||||
|
if _TRACE_TO_STDERR:
|
||||||
|
print(print_msg, file=sys.stderr)
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def _GetTraceFile():
|
||||||
|
"""Get the trace file or create one."""
|
||||||
|
# TODO: refactor to pass repodir to Trace.
|
||||||
|
repo_dir = os.path.dirname(os.path.dirname(__file__))
|
||||||
|
trace_file = os.path.join(repo_dir, _TRACE_FILE_NAME)
|
||||||
|
print('Trace outputs in %s' % trace_file, file=sys.stderr)
|
||||||
|
return trace_file
|
||||||
|
|
||||||
|
def _ClearOldTraces():
|
||||||
|
"""Clear the oldest commands if trace file is too big.
|
||||||
|
|
||||||
|
Note: If the trace file contains output from two `repo`
|
||||||
|
commands that were running at the same time, this
|
||||||
|
will not work precisely.
|
||||||
|
"""
|
||||||
|
if os.path.isfile(_TRACE_FILE):
|
||||||
|
while os.path.getsize(_TRACE_FILE)/(1024*1024) > _MAX_SIZE:
|
||||||
|
temp_file = _TRACE_FILE + '.tmp'
|
||||||
|
with open(_TRACE_FILE, 'r', errors='ignore') as fin:
|
||||||
|
with open(temp_file, 'w') as tf:
|
||||||
|
trace_lines = fin.readlines()
|
||||||
|
for i , l in enumerate(trace_lines):
|
||||||
|
if 'END:' in l and _NEW_COMMAND_SEP in l:
|
||||||
|
tf.writelines(trace_lines[i+1:])
|
||||||
|
break
|
||||||
|
platform_utils.rename(temp_file, _TRACE_FILE)
|
||||||
|
@ -20,6 +20,7 @@ import os
|
|||||||
import shutil
|
import shutil
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
|
import repo_trace
|
||||||
|
|
||||||
|
|
||||||
def find_pytest():
|
def find_pytest():
|
||||||
|
9
ssh.py
9
ssh.py
@ -182,8 +182,8 @@ class ProxyManager:
|
|||||||
# be important because we can't tell that that 'git@myhost.com' is the same
|
# be important because we can't tell that that 'git@myhost.com' is the same
|
||||||
# as 'myhost.com' where "User git" is setup in the user's ~/.ssh/config file.
|
# as 'myhost.com' where "User git" is setup in the user's ~/.ssh/config file.
|
||||||
check_command = command_base + ['-O', 'check']
|
check_command = command_base + ['-O', 'check']
|
||||||
|
with Trace('Call to ssh (check call): %s', ' '.join(check_command)):
|
||||||
try:
|
try:
|
||||||
Trace(': %s', ' '.join(check_command))
|
|
||||||
check_process = subprocess.Popen(check_command,
|
check_process = subprocess.Popen(check_command,
|
||||||
stdout=subprocess.PIPE,
|
stdout=subprocess.PIPE,
|
||||||
stderr=subprocess.PIPE)
|
stderr=subprocess.PIPE)
|
||||||
@ -196,13 +196,14 @@ class ProxyManager:
|
|||||||
self._master_keys[key] = True
|
self._master_keys[key] = True
|
||||||
return True
|
return True
|
||||||
except Exception:
|
except Exception:
|
||||||
# Ignore excpetions. We we will fall back to the normal command and print
|
# Ignore excpetions. We we will fall back to the normal command and
|
||||||
# to the log there.
|
# print to the log there.
|
||||||
pass
|
pass
|
||||||
|
|
||||||
command = command_base[:1] + ['-M', '-N'] + command_base[1:]
|
command = command_base[:1] + ['-M', '-N'] + command_base[1:]
|
||||||
|
p = None
|
||||||
try:
|
try:
|
||||||
Trace(': %s', ' '.join(command))
|
with Trace('Call to ssh: %s', ' '.join(command)):
|
||||||
p = subprocess.Popen(command)
|
p = subprocess.Popen(command)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self._master_broken.value = True
|
self._master_broken.value = True
|
||||||
|
@ -68,7 +68,8 @@ use for this GITC client.
|
|||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
manifest_file = opt.manifest_file
|
manifest_file = opt.manifest_file
|
||||||
|
|
||||||
manifest = GitcManifest(self.repodir, gitc_client)
|
manifest = GitcManifest(self.repodir, os.path.join(self.client_dir,
|
||||||
|
'.manifest'))
|
||||||
manifest.Override(manifest_file)
|
manifest.Override(manifest_file)
|
||||||
gitc_utils.generate_gitc_manifest(None, manifest)
|
gitc_utils.generate_gitc_manifest(None, manifest)
|
||||||
print('Please run `cd %s` to view your GITC client.' %
|
print('Please run `cd %s` to view your GITC client.' %
|
||||||
|
116
subcmds/sync.py
116
subcmds/sync.py
@ -60,7 +60,7 @@ from error import RepoChangedException, GitError, ManifestParseError
|
|||||||
import platform_utils
|
import platform_utils
|
||||||
from project import SyncBuffer
|
from project import SyncBuffer
|
||||||
from progress import Progress
|
from progress import Progress
|
||||||
from repo_trace import IsTrace, Trace
|
from repo_trace import Trace
|
||||||
import ssh
|
import ssh
|
||||||
from wrapper import Wrapper
|
from wrapper import Wrapper
|
||||||
from manifest_xml import GitcManifest
|
from manifest_xml import GitcManifest
|
||||||
@ -200,6 +200,9 @@ exist locally.
|
|||||||
The --prune option can be used to remove any refs that no longer
|
The --prune option can be used to remove any refs that no longer
|
||||||
exist on the remote.
|
exist on the remote.
|
||||||
|
|
||||||
|
The --auto-gc option can be used to trigger garbage collection on all
|
||||||
|
projects. By default, repo does not run garbage collection.
|
||||||
|
|
||||||
# SSH Connections
|
# SSH Connections
|
||||||
|
|
||||||
If at least one project remote URL uses an SSH connection (ssh://,
|
If at least one project remote URL uses an SSH connection (ssh://,
|
||||||
@ -309,6 +312,10 @@ later is required to fix a server side protocol bug.
|
|||||||
help='delete refs that no longer exist on the remote (default)')
|
help='delete refs that no longer exist on the remote (default)')
|
||||||
p.add_option('--no-prune', dest='prune', action='store_false',
|
p.add_option('--no-prune', dest='prune', action='store_false',
|
||||||
help='do not delete refs that no longer exist on the remote')
|
help='do not delete refs that no longer exist on the remote')
|
||||||
|
p.add_option('--auto-gc', action='store_true',
|
||||||
|
help='run garbage collection on all synced projects')
|
||||||
|
p.add_option('--no-auto-gc', dest='auto_gc', action='store_false',
|
||||||
|
help='do not run garbage collection on any projects (default)')
|
||||||
if show_smart:
|
if show_smart:
|
||||||
p.add_option('-s', '--smart-sync',
|
p.add_option('-s', '--smart-sync',
|
||||||
dest='smart_sync', action='store_true',
|
dest='smart_sync', action='store_true',
|
||||||
@ -739,7 +746,6 @@ later is required to fix a server side protocol bug.
|
|||||||
bak_dir = os.path.join(objdir, '.repo', 'pack.bak')
|
bak_dir = os.path.join(objdir, '.repo', 'pack.bak')
|
||||||
if not _BACKUP_OBJECTS or not platform_utils.isdir(pack_dir):
|
if not _BACKUP_OBJECTS or not platform_utils.isdir(pack_dir):
|
||||||
return
|
return
|
||||||
saved = []
|
|
||||||
files = set(platform_utils.listdir(pack_dir))
|
files = set(platform_utils.listdir(pack_dir))
|
||||||
to_backup = []
|
to_backup = []
|
||||||
for f in files:
|
for f in files:
|
||||||
@ -751,40 +757,99 @@ later is required to fix a server side protocol bug.
|
|||||||
for fname in to_backup:
|
for fname in to_backup:
|
||||||
bak_fname = os.path.join(bak_dir, fname)
|
bak_fname = os.path.join(bak_dir, fname)
|
||||||
if not os.path.exists(bak_fname):
|
if not os.path.exists(bak_fname):
|
||||||
saved.append(fname)
|
with Trace('%s saved %s', bare_git._project.name, fname):
|
||||||
# Use a tmp file so that we are sure of a complete copy.
|
# Use a tmp file so that we are sure of a complete copy.
|
||||||
shutil.copy(os.path.join(pack_dir, fname), bak_fname + '.tmp')
|
shutil.copy(os.path.join(pack_dir, fname), bak_fname + '.tmp')
|
||||||
shutil.move(bak_fname + '.tmp', bak_fname)
|
shutil.move(bak_fname + '.tmp', bak_fname)
|
||||||
if saved:
|
|
||||||
Trace('%s saved %s', bare_git._project.name, ' '.join(saved))
|
|
||||||
|
|
||||||
def _GCProjects(self, projects, opt, err_event):
|
@staticmethod
|
||||||
pm = Progress('Garbage collecting', len(projects), delay=False, quiet=opt.quiet)
|
def _GetPreciousObjectsState(project: Project, opt):
|
||||||
pm.update(inc=0, msg='prescan')
|
"""Get the preciousObjects state for the project.
|
||||||
|
|
||||||
tidy_dirs = {}
|
Args:
|
||||||
for project in projects:
|
project (Project): the project to examine, and possibly correct.
|
||||||
# Make sure pruning never kicks in with shared projects that do not use
|
opt (optparse.Values): options given to sync.
|
||||||
# alternates to avoid corruption.
|
|
||||||
if (not project.use_git_worktrees and
|
Returns:
|
||||||
len(project.manifest.GetProjectsWithName(project.name, all_manifests=True)) > 1):
|
Expected state of extensions.preciousObjects:
|
||||||
if project.UseAlternates:
|
False: Should be disabled. (not present)
|
||||||
# Undo logic set by previous versions of repo.
|
True: Should be enabled.
|
||||||
project.config.SetString('extensions.preciousObjects', None)
|
"""
|
||||||
project.config.SetString('gc.pruneExpire', None)
|
if project.use_git_worktrees:
|
||||||
else:
|
return False
|
||||||
|
projects = project.manifest.GetProjectsWithName(project.name,
|
||||||
|
all_manifests=True)
|
||||||
|
if len(projects) == 1:
|
||||||
|
return False
|
||||||
|
relpath = project.RelPath(local=opt.this_manifest_only)
|
||||||
|
if len(projects) > 1:
|
||||||
|
# Objects are potentially shared with another project.
|
||||||
|
# See the logic in Project.Sync_NetworkHalf regarding UseAlternates.
|
||||||
|
# - When False, shared projects share (via symlink)
|
||||||
|
# .repo/project-objects/{PROJECT_NAME}.git as the one-and-only objects
|
||||||
|
# directory. All objects are precious, since there is no project with a
|
||||||
|
# complete set of refs.
|
||||||
|
# - When True, shared projects share (via info/alternates)
|
||||||
|
# .repo/project-objects/{PROJECT_NAME}.git as an alternate object store,
|
||||||
|
# which is written only on the first clone of the project, and is not
|
||||||
|
# written subsequently. (When Sync_NetworkHalf sees that it exists, it
|
||||||
|
# makes sure that the alternates file points there, and uses a
|
||||||
|
# project-local .git/objects directory for all syncs going forward.
|
||||||
|
# We do not support switching between the options. The environment
|
||||||
|
# variable is present for testing and migration only.
|
||||||
|
return not project.UseAlternates
|
||||||
|
print(f'\r{relpath}: project not found in manifest.', file=sys.stderr)
|
||||||
|
return False
|
||||||
|
|
||||||
|
def _RepairPreciousObjectsState(self, project: Project, opt):
|
||||||
|
"""Correct the preciousObjects state for the project.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
project (Project): the project to examine, and possibly correct.
|
||||||
|
opt (optparse.Values): options given to sync.
|
||||||
|
"""
|
||||||
|
expected = self._GetPreciousObjectsState(project, opt)
|
||||||
|
actual = project.config.GetBoolean('extensions.preciousObjects') or False
|
||||||
|
relpath = project.RelPath(local = opt.this_manifest_only)
|
||||||
|
|
||||||
|
if (expected != actual and
|
||||||
|
not project.config.GetBoolean('repo.preservePreciousObjects')):
|
||||||
|
# If this is unexpected, log it and repair.
|
||||||
|
Trace(f'{relpath} expected preciousObjects={expected}, got {actual}')
|
||||||
|
if expected:
|
||||||
if not opt.quiet:
|
if not opt.quiet:
|
||||||
print('\r%s: Shared project %s found, disabling pruning.' %
|
print('\r%s: Shared project %s found, disabling pruning.' %
|
||||||
(project.relpath, project.name))
|
(relpath, project.name))
|
||||||
if git_require((2, 7, 0)):
|
if git_require((2, 7, 0)):
|
||||||
project.EnableRepositoryExtension('preciousObjects')
|
project.EnableRepositoryExtension('preciousObjects')
|
||||||
else:
|
else:
|
||||||
# This isn't perfect, but it's the best we can do with old git.
|
# This isn't perfect, but it's the best we can do with old git.
|
||||||
print('\r%s: WARNING: shared projects are unreliable when using old '
|
print('\r%s: WARNING: shared projects are unreliable when using '
|
||||||
'versions of git; please upgrade to git-2.7.0+.'
|
'old versions of git; please upgrade to git-2.7.0+.'
|
||||||
% (project.relpath,),
|
% (relpath,),
|
||||||
file=sys.stderr)
|
file=sys.stderr)
|
||||||
project.config.SetString('gc.pruneExpire', 'never')
|
project.config.SetString('gc.pruneExpire', 'never')
|
||||||
|
else:
|
||||||
|
if not opt.quiet:
|
||||||
|
print(f'\r{relpath}: not shared, disabling pruning.')
|
||||||
|
project.config.SetString('extensions.preciousObjects', None)
|
||||||
|
project.config.SetString('gc.pruneExpire', None)
|
||||||
|
|
||||||
|
def _GCProjects(self, projects, opt, err_event):
|
||||||
|
"""Perform garbage collection.
|
||||||
|
|
||||||
|
If We are skipping garbage collection (opt.auto_gc not set), we still want
|
||||||
|
to potentially mark objects precious, so that `git gc` does not discard
|
||||||
|
shared objects.
|
||||||
|
"""
|
||||||
|
pm = Progress(f'{"" if opt.auto_gc else "NOT "}Garbage collecting',
|
||||||
|
len(projects), delay=False, quiet=opt.quiet)
|
||||||
|
pm.update(inc=0, msg='prescan')
|
||||||
|
|
||||||
|
tidy_dirs = {}
|
||||||
|
for project in projects:
|
||||||
|
self._RepairPreciousObjectsState(project, opt)
|
||||||
|
|
||||||
project.config.SetString('gc.autoDetach', 'false')
|
project.config.SetString('gc.autoDetach', 'false')
|
||||||
# Only call git gc once per objdir, but call pack-refs for the remainder.
|
# Only call git gc once per objdir, but call pack-refs for the remainder.
|
||||||
if project.objdir not in tidy_dirs:
|
if project.objdir not in tidy_dirs:
|
||||||
@ -798,6 +863,10 @@ later is required to fix a server side protocol bug.
|
|||||||
project.bare_git,
|
project.bare_git,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if not opt.auto_gc:
|
||||||
|
pm.end()
|
||||||
|
return
|
||||||
|
|
||||||
jobs = opt.jobs
|
jobs = opt.jobs
|
||||||
|
|
||||||
gc_args = ['--auto']
|
gc_args = ['--auto']
|
||||||
@ -1277,6 +1346,7 @@ later is required to fix a server side protocol bug.
|
|||||||
|
|
||||||
err_network_sync = False
|
err_network_sync = False
|
||||||
err_update_projects = False
|
err_update_projects = False
|
||||||
|
err_update_linkfiles = False
|
||||||
|
|
||||||
self._fetch_times = _FetchTimes(manifest)
|
self._fetch_times = _FetchTimes(manifest)
|
||||||
if not opt.local_only:
|
if not opt.local_only:
|
||||||
|
@ -19,6 +19,7 @@ import tempfile
|
|||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
import git_config
|
import git_config
|
||||||
|
import repo_trace
|
||||||
|
|
||||||
|
|
||||||
def fixture(*paths):
|
def fixture(*paths):
|
||||||
@ -33,9 +34,16 @@ class GitConfigReadOnlyTests(unittest.TestCase):
|
|||||||
def setUp(self):
|
def setUp(self):
|
||||||
"""Create a GitConfig object using the test.gitconfig fixture.
|
"""Create a GitConfig object using the test.gitconfig fixture.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
self.tempdirobj = tempfile.TemporaryDirectory(prefix='repo_tests')
|
||||||
|
repo_trace._TRACE_FILE = os.path.join(self.tempdirobj.name, 'TRACE_FILE_from_test')
|
||||||
|
|
||||||
config_fixture = fixture('test.gitconfig')
|
config_fixture = fixture('test.gitconfig')
|
||||||
self.config = git_config.GitConfig(config_fixture)
|
self.config = git_config.GitConfig(config_fixture)
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
self.tempdirobj.cleanup()
|
||||||
|
|
||||||
def test_GetString_with_empty_config_values(self):
|
def test_GetString_with_empty_config_values(self):
|
||||||
"""
|
"""
|
||||||
Test config entries with no value.
|
Test config entries with no value.
|
||||||
@ -109,9 +117,15 @@ class GitConfigReadWriteTests(unittest.TestCase):
|
|||||||
"""Read/write tests of the GitConfig class."""
|
"""Read/write tests of the GitConfig class."""
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
|
self.tempdirobj = tempfile.TemporaryDirectory(prefix='repo_tests')
|
||||||
|
repo_trace._TRACE_FILE = os.path.join(self.tempdirobj.name, 'TRACE_FILE_from_test')
|
||||||
|
|
||||||
self.tmpfile = tempfile.NamedTemporaryFile()
|
self.tmpfile = tempfile.NamedTemporaryFile()
|
||||||
self.config = self.get_config()
|
self.config = self.get_config()
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
self.tempdirobj.cleanup()
|
||||||
|
|
||||||
def get_config(self):
|
def get_config(self):
|
||||||
"""Get a new GitConfig instance."""
|
"""Get a new GitConfig instance."""
|
||||||
return git_config.GitConfig(self.tmpfile.name)
|
return git_config.GitConfig(self.tmpfile.name)
|
||||||
|
@ -24,6 +24,7 @@ from unittest import mock
|
|||||||
import git_superproject
|
import git_superproject
|
||||||
import git_trace2_event_log
|
import git_trace2_event_log
|
||||||
import manifest_xml
|
import manifest_xml
|
||||||
|
import repo_trace
|
||||||
from test_manifest_xml import sort_attributes
|
from test_manifest_xml import sort_attributes
|
||||||
|
|
||||||
|
|
||||||
@ -39,6 +40,7 @@ class SuperprojectTestCase(unittest.TestCase):
|
|||||||
"""Set up superproject every time."""
|
"""Set up superproject every time."""
|
||||||
self.tempdirobj = tempfile.TemporaryDirectory(prefix='repo_tests')
|
self.tempdirobj = tempfile.TemporaryDirectory(prefix='repo_tests')
|
||||||
self.tempdir = self.tempdirobj.name
|
self.tempdir = self.tempdirobj.name
|
||||||
|
repo_trace._TRACE_FILE = os.path.join(self.tempdir, 'TRACE_FILE_from_test')
|
||||||
self.repodir = os.path.join(self.tempdir, '.repo')
|
self.repodir = os.path.join(self.tempdir, '.repo')
|
||||||
self.manifest_file = os.path.join(
|
self.manifest_file = os.path.join(
|
||||||
self.repodir, manifest_xml.MANIFEST_FILE_NAME)
|
self.repodir, manifest_xml.MANIFEST_FILE_NAME)
|
||||||
|
@ -23,6 +23,7 @@ import xml.dom.minidom
|
|||||||
|
|
||||||
import error
|
import error
|
||||||
import manifest_xml
|
import manifest_xml
|
||||||
|
import repo_trace
|
||||||
|
|
||||||
|
|
||||||
# Invalid paths that we don't want in the filesystem.
|
# Invalid paths that we don't want in the filesystem.
|
||||||
@ -93,6 +94,7 @@ class ManifestParseTestCase(unittest.TestCase):
|
|||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.tempdirobj = tempfile.TemporaryDirectory(prefix='repo_tests')
|
self.tempdirobj = tempfile.TemporaryDirectory(prefix='repo_tests')
|
||||||
self.tempdir = self.tempdirobj.name
|
self.tempdir = self.tempdirobj.name
|
||||||
|
repo_trace._TRACE_FILE = os.path.join(self.tempdir, 'TRACE_FILE_from_test')
|
||||||
self.repodir = os.path.join(self.tempdir, '.repo')
|
self.repodir = os.path.join(self.tempdir, '.repo')
|
||||||
self.manifest_dir = os.path.join(self.repodir, 'manifests')
|
self.manifest_dir = os.path.join(self.repodir, 'manifests')
|
||||||
self.manifest_file = os.path.join(
|
self.manifest_file = os.path.join(
|
||||||
@ -262,10 +264,10 @@ class XmlManifestTests(ManifestParseTestCase):
|
|||||||
'<project name="r" groups="keep"/>'
|
'<project name="r" groups="keep"/>'
|
||||||
'</manifest>')
|
'</manifest>')
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
manifest.ToXml(omit_local=True).toxml(),
|
sort_attributes(manifest.ToXml(omit_local=True).toxml()),
|
||||||
'<?xml version="1.0" ?><manifest>'
|
'<?xml version="1.0" ?><manifest>'
|
||||||
'<remote name="a" fetch=".."/><default remote="a" revision="r"/>'
|
'<remote fetch=".." name="a"/><default remote="a" revision="r"/>'
|
||||||
'<project name="q"/><project name="r" groups="keep"/></manifest>')
|
'<project name="q"/><project groups="keep" name="r"/></manifest>')
|
||||||
|
|
||||||
def test_toxml_with_local(self):
|
def test_toxml_with_local(self):
|
||||||
"""Does include local_manifests projects when omit_local=False."""
|
"""Does include local_manifests projects when omit_local=False."""
|
||||||
@ -277,11 +279,11 @@ class XmlManifestTests(ManifestParseTestCase):
|
|||||||
'<project name="r" groups="keep"/>'
|
'<project name="r" groups="keep"/>'
|
||||||
'</manifest>')
|
'</manifest>')
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
manifest.ToXml(omit_local=False).toxml(),
|
sort_attributes(manifest.ToXml(omit_local=False).toxml()),
|
||||||
'<?xml version="1.0" ?><manifest>'
|
'<?xml version="1.0" ?><manifest>'
|
||||||
'<remote name="a" fetch=".."/><default remote="a" revision="r"/>'
|
'<remote fetch=".." name="a"/><default remote="a" revision="r"/>'
|
||||||
'<project name="p" groups="local::me"/>'
|
'<project groups="local::me" name="p"/>'
|
||||||
'<project name="q"/><project name="r" groups="keep"/></manifest>')
|
'<project name="q"/><project groups="keep" name="r"/></manifest>')
|
||||||
|
|
||||||
def test_repo_hooks(self):
|
def test_repo_hooks(self):
|
||||||
"""Check repo-hooks settings."""
|
"""Check repo-hooks settings."""
|
||||||
|
@ -26,6 +26,7 @@ import git_command
|
|||||||
import git_config
|
import git_config
|
||||||
import platform_utils
|
import platform_utils
|
||||||
import project
|
import project
|
||||||
|
import repo_trace
|
||||||
|
|
||||||
|
|
||||||
@contextlib.contextmanager
|
@contextlib.contextmanager
|
||||||
@ -64,6 +65,13 @@ class FakeProject(object):
|
|||||||
class ReviewableBranchTests(unittest.TestCase):
|
class ReviewableBranchTests(unittest.TestCase):
|
||||||
"""Check ReviewableBranch behavior."""
|
"""Check ReviewableBranch behavior."""
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
self.tempdirobj = tempfile.TemporaryDirectory(prefix='repo_tests')
|
||||||
|
repo_trace._TRACE_FILE = os.path.join(self.tempdirobj.name, 'TRACE_FILE_from_test')
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
self.tempdirobj.cleanup()
|
||||||
|
|
||||||
def test_smoke(self):
|
def test_smoke(self):
|
||||||
"""A quick run through everything."""
|
"""A quick run through everything."""
|
||||||
with TempGitTree() as tempdir:
|
with TempGitTree() as tempdir:
|
||||||
|
@ -11,9 +11,9 @@
|
|||||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
# 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.
|
||||||
|
|
||||||
"""Unittests for the subcmds/sync.py module."""
|
"""Unittests for the subcmds/sync.py module."""
|
||||||
|
|
||||||
|
import unittest
|
||||||
from unittest import mock
|
from unittest import mock
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
@ -21,17 +21,14 @@ import pytest
|
|||||||
from subcmds import sync
|
from subcmds import sync
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize('use_superproject, cli_args, result', [
|
||||||
'use_superproject, cli_args, result',
|
|
||||||
[
|
|
||||||
(True, ['--current-branch'], True),
|
(True, ['--current-branch'], True),
|
||||||
(True, ['--no-current-branch'], True),
|
(True, ['--no-current-branch'], True),
|
||||||
(True, [], True),
|
(True, [], True),
|
||||||
(False, ['--current-branch'], True),
|
(False, ['--current-branch'], True),
|
||||||
(False, ['--no-current-branch'], False),
|
(False, ['--no-current-branch'], False),
|
||||||
(False, [], None),
|
(False, [], None),
|
||||||
]
|
])
|
||||||
)
|
|
||||||
def test_get_current_branch_only(use_superproject, cli_args, result):
|
def test_get_current_branch_only(use_superproject, cli_args, result):
|
||||||
"""Test Sync._GetCurrentBranchOnly logic.
|
"""Test Sync._GetCurrentBranchOnly logic.
|
||||||
|
|
||||||
@ -41,5 +38,49 @@ def test_get_current_branch_only(use_superproject, cli_args, result):
|
|||||||
cmd = sync.Sync()
|
cmd = sync.Sync()
|
||||||
opts, _ = cmd.OptionParser.parse_args(cli_args)
|
opts, _ = cmd.OptionParser.parse_args(cli_args)
|
||||||
|
|
||||||
with mock.patch('git_superproject.UseSuperproject', return_value=use_superproject):
|
with mock.patch('git_superproject.UseSuperproject',
|
||||||
|
return_value=use_superproject):
|
||||||
assert cmd._GetCurrentBranchOnly(opts, cmd.manifest) == result
|
assert cmd._GetCurrentBranchOnly(opts, cmd.manifest) == result
|
||||||
|
|
||||||
|
|
||||||
|
class GetPreciousObjectsState(unittest.TestCase):
|
||||||
|
"""Tests for _GetPreciousObjectsState."""
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
"""Common setup."""
|
||||||
|
self.cmd = sync.Sync()
|
||||||
|
self.project = p = mock.MagicMock(use_git_worktrees=False,
|
||||||
|
UseAlternates=False)
|
||||||
|
p.manifest.GetProjectsWithName.return_value = [p]
|
||||||
|
|
||||||
|
self.opt = mock.Mock(spec_set=['this_manifest_only'])
|
||||||
|
self.opt.this_manifest_only = False
|
||||||
|
|
||||||
|
def test_worktrees(self):
|
||||||
|
"""False for worktrees."""
|
||||||
|
self.project.use_git_worktrees = True
|
||||||
|
self.assertFalse(self.cmd._GetPreciousObjectsState(self.project, self.opt))
|
||||||
|
|
||||||
|
def test_not_shared(self):
|
||||||
|
"""Singleton project."""
|
||||||
|
self.assertFalse(self.cmd._GetPreciousObjectsState(self.project, self.opt))
|
||||||
|
|
||||||
|
def test_shared(self):
|
||||||
|
"""Shared project."""
|
||||||
|
self.project.manifest.GetProjectsWithName.return_value = [
|
||||||
|
self.project, self.project
|
||||||
|
]
|
||||||
|
self.assertTrue(self.cmd._GetPreciousObjectsState(self.project, self.opt))
|
||||||
|
|
||||||
|
def test_shared_with_alternates(self):
|
||||||
|
"""Shared project, with alternates."""
|
||||||
|
self.project.manifest.GetProjectsWithName.return_value = [
|
||||||
|
self.project, self.project
|
||||||
|
]
|
||||||
|
self.project.UseAlternates = True
|
||||||
|
self.assertFalse(self.cmd._GetPreciousObjectsState(self.project, self.opt))
|
||||||
|
|
||||||
|
def test_not_found(self):
|
||||||
|
"""Project not found in manifest."""
|
||||||
|
self.project.manifest.GetProjectsWithName.return_value = []
|
||||||
|
self.assertFalse(self.cmd._GetPreciousObjectsState(self.project, self.opt))
|
||||||
|
Reference in New Issue
Block a user