mirror of
https://gerrit.googlesource.com/git-repo
synced 2025-06-30 20:17:08 +00:00
Compare commits
11 Commits
Author | SHA1 | Date | |
---|---|---|---|
4217a82bec | |||
208f344950 | |||
138c8a9ff5 | |||
9b57aa00f6 | |||
b1d1ece2fb | |||
449b23b698 | |||
e5fb6e585f | |||
48e4137eba | |||
172c58398b | |||
aa506db8a7 | |||
14c61d2c9d |
@ -33,9 +33,8 @@ you have newer versions installed, your choices are:
|
|||||||
|
|
||||||
* Modify the [repo launcher]'s shebang to suite your environment.
|
* Modify the [repo launcher]'s shebang to suite your environment.
|
||||||
* Download an older version of the [repo launcher] and don't upgrade it.
|
* Download an older version of the [repo launcher] and don't upgrade it.
|
||||||
Be aware that there is no guarantee old repo launchers are WILL work with
|
Be aware that we do not guarantee old repo launchers will work with current
|
||||||
current versions of repo. Bug reports using old launchers will not be
|
versions of repo. Bug reports using old launchers will not be accepted.
|
||||||
accepted.
|
|
||||||
|
|
||||||
## When to drop support
|
## When to drop support
|
||||||
|
|
||||||
|
@ -38,6 +38,8 @@ import tempfile
|
|||||||
import threading
|
import threading
|
||||||
|
|
||||||
|
|
||||||
|
# Timeout when sending events via socket (applies to connect, send)
|
||||||
|
SOCK_TIMEOUT = 0.5 # in seconds
|
||||||
# BaseEventLog __init__ Counter that is consistent within the same process
|
# BaseEventLog __init__ Counter that is consistent within the same process
|
||||||
p_init_count = 0
|
p_init_count = 0
|
||||||
|
|
||||||
@ -296,6 +298,7 @@ class BaseEventLog:
|
|||||||
with socket.socket(
|
with socket.socket(
|
||||||
socket.AF_UNIX, socket.SOCK_STREAM
|
socket.AF_UNIX, socket.SOCK_STREAM
|
||||||
) as sock:
|
) as sock:
|
||||||
|
sock.settimeout(SOCK_TIMEOUT)
|
||||||
sock.connect(path)
|
sock.connect(path)
|
||||||
self._WriteLog(sock.sendall)
|
self._WriteLog(sock.sendall)
|
||||||
return f"af_unix:stream:{path}"
|
return f"af_unix:stream:{path}"
|
||||||
|
@ -133,8 +133,8 @@ def normalize_url(url: str) -> str:
|
|||||||
url = url.rstrip("/")
|
url = url.rstrip("/")
|
||||||
parsed_url = urllib.parse.urlparse(url)
|
parsed_url = urllib.parse.urlparse(url)
|
||||||
|
|
||||||
# This matches patterns like "git@github.com:foo/bar".
|
# This matches patterns like "git@github.com:foo".
|
||||||
scp_like_url_re = r"^[^:]+@[^:]+:[^/]+/"
|
scp_like_url_re = r"^[^/:]+@[^/:]+:[^/]+"
|
||||||
|
|
||||||
# If our URL is missing a schema and matches git's
|
# If our URL is missing a schema and matches git's
|
||||||
# SCP-like syntax we should convert it to a proper
|
# SCP-like syntax we should convert it to a proper
|
||||||
|
84
project.py
84
project.py
@ -1277,7 +1277,20 @@ class Project:
|
|||||||
if is_new:
|
if is_new:
|
||||||
self._InitGitDir(force_sync=force_sync, quiet=quiet)
|
self._InitGitDir(force_sync=force_sync, quiet=quiet)
|
||||||
else:
|
else:
|
||||||
self._UpdateHooks(quiet=quiet)
|
try:
|
||||||
|
# At this point, it's possible that gitdir points to an old
|
||||||
|
# objdir (e.g. name changed, but objdir exists). Check
|
||||||
|
# references to ensure that's not the case. See
|
||||||
|
# https://issues.gerritcodereview.com/40013418 for more
|
||||||
|
# details.
|
||||||
|
self._CheckDirReference(self.objdir, self.gitdir)
|
||||||
|
|
||||||
|
self._UpdateHooks(quiet=quiet)
|
||||||
|
except GitError as e:
|
||||||
|
if not force_sync:
|
||||||
|
raise e
|
||||||
|
# Let _InitGitDir fix the issue, force_sync is always True here.
|
||||||
|
self._InitGitDir(force_sync=True, quiet=quiet)
|
||||||
self._InitRemote()
|
self._InitRemote()
|
||||||
|
|
||||||
if self.UseAlternates:
|
if self.UseAlternates:
|
||||||
@ -1623,9 +1636,9 @@ class Project:
|
|||||||
elif pub == head:
|
elif pub == head:
|
||||||
# All published commits are merged, and thus we are a
|
# All published commits are merged, and thus we are a
|
||||||
# strict subset. We can fast-forward safely.
|
# strict subset. We can fast-forward safely.
|
||||||
syncbuf.later1(self, _doff)
|
syncbuf.later1(self, _doff, not verbose)
|
||||||
if submodules:
|
if submodules:
|
||||||
syncbuf.later1(self, _dosubmodules)
|
syncbuf.later1(self, _dosubmodules, not verbose)
|
||||||
return
|
return
|
||||||
|
|
||||||
# Examine the local commits not in the remote. Find the
|
# Examine the local commits not in the remote. Find the
|
||||||
@ -1684,10 +1697,10 @@ class Project:
|
|||||||
def _dorebase():
|
def _dorebase():
|
||||||
self._Rebase(upstream="%s^1" % last_mine, onto=revid)
|
self._Rebase(upstream="%s^1" % last_mine, onto=revid)
|
||||||
|
|
||||||
syncbuf.later2(self, _dorebase)
|
syncbuf.later2(self, _dorebase, not verbose)
|
||||||
if submodules:
|
if submodules:
|
||||||
syncbuf.later2(self, _dosubmodules)
|
syncbuf.later2(self, _dosubmodules, not verbose)
|
||||||
syncbuf.later2(self, _docopyandlink)
|
syncbuf.later2(self, _docopyandlink, not verbose)
|
||||||
elif local_changes:
|
elif local_changes:
|
||||||
try:
|
try:
|
||||||
self._ResetHard(revid)
|
self._ResetHard(revid)
|
||||||
@ -1698,9 +1711,9 @@ class Project:
|
|||||||
fail(e)
|
fail(e)
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
syncbuf.later1(self, _doff)
|
syncbuf.later1(self, _doff, not verbose)
|
||||||
if submodules:
|
if submodules:
|
||||||
syncbuf.later1(self, _dosubmodules)
|
syncbuf.later1(self, _dosubmodules, not verbose)
|
||||||
|
|
||||||
def AddCopyFile(self, src, dest, topdir):
|
def AddCopyFile(self, src, dest, topdir):
|
||||||
"""Mark |src| for copying to |dest| (relative to |topdir|).
|
"""Mark |src| for copying to |dest| (relative to |topdir|).
|
||||||
@ -1835,7 +1848,7 @@ class Project:
|
|||||||
platform_utils.remove(path)
|
platform_utils.remove(path)
|
||||||
except OSError as e:
|
except OSError as e:
|
||||||
if e.errno != errno.ENOENT:
|
if e.errno != errno.ENOENT:
|
||||||
logger.error("error: %s: Failed to remove: %s", path, e)
|
logger.warning("%s: Failed to remove: %s", path, e)
|
||||||
failed = True
|
failed = True
|
||||||
errors.append(e)
|
errors.append(e)
|
||||||
dirs[:] = [
|
dirs[:] = [
|
||||||
@ -1854,7 +1867,7 @@ class Project:
|
|||||||
platform_utils.remove(d)
|
platform_utils.remove(d)
|
||||||
except OSError as e:
|
except OSError as e:
|
||||||
if e.errno != errno.ENOENT:
|
if e.errno != errno.ENOENT:
|
||||||
logger.error("error: %s: Failed to remove: %s", d, e)
|
logger.warning("%s: Failed to remove: %s", d, e)
|
||||||
failed = True
|
failed = True
|
||||||
errors.append(e)
|
errors.append(e)
|
||||||
elif not platform_utils.listdir(d):
|
elif not platform_utils.listdir(d):
|
||||||
@ -1862,18 +1875,30 @@ class Project:
|
|||||||
platform_utils.rmdir(d)
|
platform_utils.rmdir(d)
|
||||||
except OSError as e:
|
except OSError as e:
|
||||||
if e.errno != errno.ENOENT:
|
if e.errno != errno.ENOENT:
|
||||||
logger.error("error: %s: Failed to remove: %s", d, e)
|
logger.warning("%s: Failed to remove: %s", d, e)
|
||||||
failed = True
|
failed = True
|
||||||
errors.append(e)
|
errors.append(e)
|
||||||
if failed:
|
if failed:
|
||||||
logger.error(
|
rename_path = (
|
||||||
"error: %s: Failed to delete obsolete checkout.",
|
f"{self.worktree}_repo_to_be_deleted_{int(time.time())}"
|
||||||
self.RelPath(local=False),
|
|
||||||
)
|
)
|
||||||
logger.error(
|
try:
|
||||||
" Remove manually, then run `repo sync -l`.",
|
platform_utils.rename(self.worktree, rename_path)
|
||||||
)
|
logger.warning(
|
||||||
raise DeleteWorktreeError(aggregate_errors=errors)
|
"warning: renamed %s to %s. You can delete it, but you "
|
||||||
|
"might need elevated permissions (e.g. root)",
|
||||||
|
self.worktree,
|
||||||
|
rename_path,
|
||||||
|
)
|
||||||
|
# Rename successful! Clear the errors.
|
||||||
|
errors = []
|
||||||
|
except OSError:
|
||||||
|
logger.error(
|
||||||
|
"%s: Failed to delete obsolete checkout.\n",
|
||||||
|
" Remove manually, then run `repo sync -l`.",
|
||||||
|
self.RelPath(local=False),
|
||||||
|
)
|
||||||
|
raise DeleteWorktreeError(aggregate_errors=errors)
|
||||||
|
|
||||||
# Try deleting parent dirs if they are empty.
|
# Try deleting parent dirs if they are empty.
|
||||||
path = self.worktree
|
path = self.worktree
|
||||||
@ -2870,10 +2895,12 @@ class Project:
|
|||||||
if GitCommand(self, cmd).Wait() != 0:
|
if GitCommand(self, cmd).Wait() != 0:
|
||||||
raise GitError(f"{self.name} rebase {upstream} ", project=self.name)
|
raise GitError(f"{self.name} rebase {upstream} ", project=self.name)
|
||||||
|
|
||||||
def _FastForward(self, head, ffonly=False):
|
def _FastForward(self, head, ffonly=False, quiet=True):
|
||||||
cmd = ["merge", "--no-stat", head]
|
cmd = ["merge", "--no-stat", head]
|
||||||
if ffonly:
|
if ffonly:
|
||||||
cmd.append("--ff-only")
|
cmd.append("--ff-only")
|
||||||
|
if quiet:
|
||||||
|
cmd.append("-q")
|
||||||
if GitCommand(self, cmd).Wait() != 0:
|
if GitCommand(self, cmd).Wait() != 0:
|
||||||
raise GitError(f"{self.name} merge {head} ", project=self.name)
|
raise GitError(f"{self.name} merge {head} ", project=self.name)
|
||||||
|
|
||||||
@ -3746,17 +3773,20 @@ class _Failure:
|
|||||||
|
|
||||||
|
|
||||||
class _Later:
|
class _Later:
|
||||||
def __init__(self, project, action):
|
def __init__(self, project, action, quiet):
|
||||||
self.project = project
|
self.project = project
|
||||||
self.action = action
|
self.action = action
|
||||||
|
self.quiet = quiet
|
||||||
|
|
||||||
def Run(self, syncbuf):
|
def Run(self, syncbuf):
|
||||||
out = syncbuf.out
|
out = syncbuf.out
|
||||||
out.project("project %s/", self.project.RelPath(local=False))
|
if not self.quiet:
|
||||||
out.nl()
|
out.project("project %s/", self.project.RelPath(local=False))
|
||||||
|
out.nl()
|
||||||
try:
|
try:
|
||||||
self.action()
|
self.action()
|
||||||
out.nl()
|
if not self.quiet:
|
||||||
|
out.nl()
|
||||||
return True
|
return True
|
||||||
except GitError:
|
except GitError:
|
||||||
out.nl()
|
out.nl()
|
||||||
@ -3792,11 +3822,11 @@ class SyncBuffer:
|
|||||||
self._failures.append(_Failure(project, err))
|
self._failures.append(_Failure(project, err))
|
||||||
self._MarkUnclean()
|
self._MarkUnclean()
|
||||||
|
|
||||||
def later1(self, project, what):
|
def later1(self, project, what, quiet):
|
||||||
self._later_queue1.append(_Later(project, what))
|
self._later_queue1.append(_Later(project, what, quiet))
|
||||||
|
|
||||||
def later2(self, project, what):
|
def later2(self, project, what, quiet):
|
||||||
self._later_queue2.append(_Later(project, what))
|
self._later_queue2.append(_Later(project, what, quiet))
|
||||||
|
|
||||||
def Finish(self):
|
def Finish(self):
|
||||||
self._PrintMessages()
|
self._PrintMessages()
|
||||||
|
41
repo
41
repo
@ -79,7 +79,7 @@ def check_python_version():
|
|||||||
major = ver.major
|
major = ver.major
|
||||||
minor = ver.minor
|
minor = ver.minor
|
||||||
|
|
||||||
# Try to re-exec the version specific Python 3 if needed.
|
# Try to re-exec the version specific Python if needed.
|
||||||
if (major, minor) < MIN_PYTHON_VERSION_SOFT:
|
if (major, minor) < MIN_PYTHON_VERSION_SOFT:
|
||||||
# Python makes releases ~once a year, so try our min version +10 to help
|
# Python makes releases ~once a year, so try our min version +10 to help
|
||||||
# bridge the gap. This is the fallback anyways so perf isn't critical.
|
# bridge the gap. This is the fallback anyways so perf isn't critical.
|
||||||
@ -96,36 +96,10 @@ def check_python_version():
|
|||||||
break
|
break
|
||||||
reexec(f"python{min_major}.{min_minor - inc}")
|
reexec(f"python{min_major}.{min_minor - inc}")
|
||||||
|
|
||||||
# Try the generic Python 3 wrapper, but only if it's new enough. If it
|
|
||||||
# isn't, we want to just give up below and make the user resolve things.
|
|
||||||
try:
|
|
||||||
proc = subprocess.Popen(
|
|
||||||
[
|
|
||||||
"python3",
|
|
||||||
"-c",
|
|
||||||
"import sys; "
|
|
||||||
"print(sys.version_info.major, sys.version_info.minor)",
|
|
||||||
],
|
|
||||||
stdout=subprocess.PIPE,
|
|
||||||
stderr=subprocess.PIPE,
|
|
||||||
)
|
|
||||||
(output, _) = proc.communicate()
|
|
||||||
python3_ver = tuple(int(x) for x in output.decode("utf-8").split())
|
|
||||||
except (OSError, subprocess.CalledProcessError):
|
|
||||||
python3_ver = None
|
|
||||||
|
|
||||||
# If the python3 version looks like it's new enough, give it a try.
|
|
||||||
if (
|
|
||||||
python3_ver
|
|
||||||
and python3_ver >= MIN_PYTHON_VERSION_HARD
|
|
||||||
and python3_ver != (major, minor)
|
|
||||||
):
|
|
||||||
reexec("python3")
|
|
||||||
|
|
||||||
# We're still here, so diagnose things for the user.
|
# We're still here, so diagnose things for the user.
|
||||||
if (major, minor) < MIN_PYTHON_VERSION_HARD:
|
if (major, minor) < MIN_PYTHON_VERSION_HARD:
|
||||||
print(
|
print(
|
||||||
"repo: error: Python 3 version is too old; "
|
"repo: error: Python version is too old; "
|
||||||
"Please use Python {}.{} or newer.".format(
|
"Please use Python {}.{} or newer.".format(
|
||||||
*MIN_PYTHON_VERSION_HARD
|
*MIN_PYTHON_VERSION_HARD
|
||||||
),
|
),
|
||||||
@ -1245,7 +1219,6 @@ class Requirements:
|
|||||||
with open(path, "rb") as f:
|
with open(path, "rb") as f:
|
||||||
data = f.read()
|
data = f.read()
|
||||||
except OSError:
|
except OSError:
|
||||||
# NB: EnvironmentError is used for Python 2 & 3 compatibility.
|
|
||||||
# If we couldn't open the file, assume it's an old source tree.
|
# If we couldn't open the file, assume it's an old source tree.
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@ -1364,13 +1337,9 @@ def _Version():
|
|||||||
print(f"git {ParseGitVersion().full}")
|
print(f"git {ParseGitVersion().full}")
|
||||||
print(f"Python {sys.version}")
|
print(f"Python {sys.version}")
|
||||||
uname = platform.uname()
|
uname = platform.uname()
|
||||||
if sys.version_info.major < 3:
|
print(f"OS {uname.system} {uname.release} ({uname.version})")
|
||||||
# Python 3 returns a named tuple, but Python 2 is simpler.
|
processor = uname.processor if uname.processor else "unknown"
|
||||||
print(uname)
|
print(f"CPU {uname.machine} ({processor})")
|
||||||
else:
|
|
||||||
print(f"OS {uname.system} {uname.release} ({uname.version})")
|
|
||||||
processor = uname.processor if uname.processor else "unknown"
|
|
||||||
print(f"CPU {uname.machine} ({processor})")
|
|
||||||
print("Bug reports:", BUG_URL)
|
print("Bug reports:", BUG_URL)
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
|
|
||||||
|
@ -618,7 +618,7 @@ later is required to fix a server side protocol bug.
|
|||||||
|
|
||||||
if not use_super:
|
if not use_super:
|
||||||
continue
|
continue
|
||||||
m.superproject.SetQuiet(opt.quiet)
|
m.superproject.SetQuiet(not opt.verbose)
|
||||||
print_messages = git_superproject.PrintMessages(
|
print_messages = git_superproject.PrintMessages(
|
||||||
opt.use_superproject, m
|
opt.use_superproject, m
|
||||||
)
|
)
|
||||||
@ -1501,7 +1501,7 @@ later is required to fix a server side protocol bug.
|
|||||||
buf = TeeStringIO(sys.stdout)
|
buf = TeeStringIO(sys.stdout)
|
||||||
try:
|
try:
|
||||||
result = mp.Sync_NetworkHalf(
|
result = mp.Sync_NetworkHalf(
|
||||||
quiet=opt.quiet,
|
quiet=not opt.verbose,
|
||||||
output_redir=buf,
|
output_redir=buf,
|
||||||
verbose=opt.verbose,
|
verbose=opt.verbose,
|
||||||
current_branch_only=self._GetCurrentBranchOnly(
|
current_branch_only=self._GetCurrentBranchOnly(
|
||||||
|
@ -72,3 +72,12 @@ def tmp_home_dir(monkeypatch, tmp_path_factory):
|
|||||||
the function scope.
|
the function scope.
|
||||||
"""
|
"""
|
||||||
return _set_home(monkeypatch, tmp_path_factory.mktemp("home"))
|
return _set_home(monkeypatch, tmp_path_factory.mktemp("home"))
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(autouse=True)
|
||||||
|
def setup_user_identity(monkeysession, scope="session"):
|
||||||
|
"""Set env variables for author and committer name and email."""
|
||||||
|
monkeysession.setenv("GIT_AUTHOR_NAME", "Foo Bar")
|
||||||
|
monkeysession.setenv("GIT_COMMITTER_NAME", "Foo Bar")
|
||||||
|
monkeysession.setenv("GIT_AUTHOR_EMAIL", "foo@bar.baz")
|
||||||
|
monkeysession.setenv("GIT_COMMITTER_EMAIL", "foo@bar.baz")
|
||||||
|
@ -1139,6 +1139,20 @@ class NormalizeUrlTests(ManifestParseTestCase):
|
|||||||
"http://foo.com/bar/baz", manifest_xml.normalize_url(url)
|
"http://foo.com/bar/baz", manifest_xml.normalize_url(url)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
url = "http://foo.com/bar/"
|
||||||
|
self.assertEqual("http://foo.com/bar", manifest_xml.normalize_url(url))
|
||||||
|
|
||||||
|
def test_has_leading_slash(self):
|
||||||
|
"""SCP-like syntax except a / comes before the : which git disallows."""
|
||||||
|
url = "/git@foo.com:bar/baf"
|
||||||
|
self.assertEqual(url, manifest_xml.normalize_url(url))
|
||||||
|
|
||||||
|
url = "gi/t@foo.com:bar/baf"
|
||||||
|
self.assertEqual(url, manifest_xml.normalize_url(url))
|
||||||
|
|
||||||
|
url = "git@fo/o.com:bar/baf"
|
||||||
|
self.assertEqual(url, manifest_xml.normalize_url(url))
|
||||||
|
|
||||||
def test_has_no_scheme(self):
|
def test_has_no_scheme(self):
|
||||||
"""Deal with cases where we have no scheme, but we also
|
"""Deal with cases where we have no scheme, but we also
|
||||||
aren't dealing with the git SCP-like syntax
|
aren't dealing with the git SCP-like syntax
|
||||||
@ -1146,9 +1160,15 @@ class NormalizeUrlTests(ManifestParseTestCase):
|
|||||||
url = "foo.com/baf/bat"
|
url = "foo.com/baf/bat"
|
||||||
self.assertEqual(url, manifest_xml.normalize_url(url))
|
self.assertEqual(url, manifest_xml.normalize_url(url))
|
||||||
|
|
||||||
|
url = "foo.com/baf"
|
||||||
|
self.assertEqual(url, manifest_xml.normalize_url(url))
|
||||||
|
|
||||||
url = "git@foo.com/baf/bat"
|
url = "git@foo.com/baf/bat"
|
||||||
self.assertEqual(url, manifest_xml.normalize_url(url))
|
self.assertEqual(url, manifest_xml.normalize_url(url))
|
||||||
|
|
||||||
|
url = "git@foo.com/baf"
|
||||||
|
self.assertEqual(url, manifest_xml.normalize_url(url))
|
||||||
|
|
||||||
url = "/file/path/here"
|
url = "/file/path/here"
|
||||||
self.assertEqual(url, manifest_xml.normalize_url(url))
|
self.assertEqual(url, manifest_xml.normalize_url(url))
|
||||||
|
|
||||||
@ -1157,3 +1177,30 @@ class NormalizeUrlTests(ManifestParseTestCase):
|
|||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
"ssh://git@foo.com/bar/baf", manifest_xml.normalize_url(url)
|
"ssh://git@foo.com/bar/baf", manifest_xml.normalize_url(url)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
url = "git@foo.com:bar/"
|
||||||
|
self.assertEqual(
|
||||||
|
"ssh://git@foo.com/bar", manifest_xml.normalize_url(url)
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_remote_url_resolution(self):
|
||||||
|
remote = manifest_xml._XmlRemote(
|
||||||
|
name="foo",
|
||||||
|
fetch="git@github.com:org2/",
|
||||||
|
manifestUrl="git@github.com:org2/custom_manifest.git",
|
||||||
|
)
|
||||||
|
self.assertEqual("ssh://git@github.com/org2", remote.resolvedFetchUrl)
|
||||||
|
|
||||||
|
remote = manifest_xml._XmlRemote(
|
||||||
|
name="foo",
|
||||||
|
fetch="ssh://git@github.com/org2/",
|
||||||
|
manifestUrl="git@github.com:org2/custom_manifest.git",
|
||||||
|
)
|
||||||
|
self.assertEqual("ssh://git@github.com/org2", remote.resolvedFetchUrl)
|
||||||
|
|
||||||
|
remote = manifest_xml._XmlRemote(
|
||||||
|
name="foo",
|
||||||
|
fetch="git@github.com:org2/",
|
||||||
|
manifestUrl="ssh://git@github.com/org2/custom_manifest.git",
|
||||||
|
)
|
||||||
|
self.assertEqual("ssh://git@github.com/org2", remote.resolvedFetchUrl)
|
||||||
|
Reference in New Issue
Block a user