mirror of
https://gerrit.googlesource.com/git-repo
synced 2025-06-30 20:17:08 +00:00
Compare commits
17 Commits
Author | SHA1 | Date | |
---|---|---|---|
73356f1d5c | |||
09fc214a79 | |||
3762b17e98 | |||
ae419e1e01 | |||
a3a7372612 | |||
fff1d2d74c | |||
4b01a242d8 | |||
46790229fc | |||
edadb25c02 | |||
96edb9b573 | |||
5554572f02 | |||
97ca50f5f9 | |||
8896b68926 | |||
fec8cd6704 | |||
b8139bdcf8 | |||
26fa3180fb | |||
d379e77f44 |
@ -202,7 +202,7 @@ still support them.
|
|||||||
Things in italics are things we used to care about but probably don't anymore.
|
Things in italics are things we used to care about but probably don't anymore.
|
||||||
|
|
||||||
| Date | EOL | [Git][rel-g] | [Python][rel-p] | [SSH][rel-o] | [Ubuntu][rel-u] / [Debian][rel-d] | Git | Python | SSH |
|
| Date | EOL | [Git][rel-g] | [Python][rel-p] | [SSH][rel-o] | [Ubuntu][rel-u] / [Debian][rel-d] | Git | Python | SSH |
|
||||||
|:--------:|:------------:|:------------:|:---------------:|:------------:|-----------------------------------|-----|--------|-----|
|
|:--------:|:------------:|:------------:|:---------------:|:------------:|-----------------------------------|:---:|:------:|:---:|
|
||||||
| Apr 2008 | | | | 5.0 |
|
| Apr 2008 | | | | 5.0 |
|
||||||
| Jun 2008 | | | | 5.1 |
|
| Jun 2008 | | | | 5.1 |
|
||||||
| Oct 2008 | *Oct 2013* | | 2.6.0 | | *10.04 Lucid* - 10.10 Maverick / *Squeeze* |
|
| Oct 2008 | *Oct 2013* | | 2.6.0 | | *10.04 Lucid* - 10.10 Maverick / *Squeeze* |
|
||||||
@ -241,7 +241,7 @@ Things in italics are things we used to care about but probably don't anymore.
|
|||||||
| Feb 2014 | *Dec 2014* | **1.9.0** | | | *14.04 Trusty* |
|
| Feb 2014 | *Dec 2014* | **1.9.0** | | | *14.04 Trusty* |
|
||||||
| Mar 2014 | *Mar 2019* | | *3.4.0* | | *14.04 Trusty* - 15.10 Wily / *Jessie* |
|
| Mar 2014 | *Mar 2019* | | *3.4.0* | | *14.04 Trusty* - 15.10 Wily / *Jessie* |
|
||||||
| Mar 2014 | | | | 6.6 | *14.04 Trusty* - 14.10 Utopic |
|
| Mar 2014 | | | | 6.6 | *14.04 Trusty* - 14.10 Utopic |
|
||||||
| Apr 2014 | *Apr 2022* | | | | *14.04 Trusty* | 1.9.1 | 2.7.5 3.4.0 | 6.6 |
|
| Apr 2014 | *Apr 2024* | | | | *14.04 Trusty* | 1.9.1 | 2.7.5 3.4.0 | 6.6 |
|
||||||
| May 2014 | *Dec 2014* | 2.0.0 |
|
| May 2014 | *Dec 2014* | 2.0.0 |
|
||||||
| Aug 2014 | *Dec 2014* | *2.1.0* | | | 14.10 Utopic - 15.04 Vivid / *Jessie* |
|
| Aug 2014 | *Dec 2014* | *2.1.0* | | | 14.10 Utopic - 15.04 Vivid / *Jessie* |
|
||||||
| Oct 2014 | | | | 6.7 | 15.04 Vivid |
|
| Oct 2014 | | | | 6.7 | 15.04 Vivid |
|
||||||
@ -262,7 +262,7 @@ Things in italics are things we used to care about but probably don't anymore.
|
|||||||
| Jan 2016 | *Jul 2017* | *2.7.0* | | | *16.04 Xenial* |
|
| Jan 2016 | *Jul 2017* | *2.7.0* | | | *16.04 Xenial* |
|
||||||
| Feb 2016 | | | | 7.2 | *16.04 Xenial* |
|
| Feb 2016 | | | | 7.2 | *16.04 Xenial* |
|
||||||
| Mar 2016 | *Jul 2017* | 2.8.0 |
|
| Mar 2016 | *Jul 2017* | 2.8.0 |
|
||||||
| Apr 2016 | *Apr 2024* | | | | *16.04 Xenial* | 2.7.4 | 2.7.11 3.5.1 | 7.2 |
|
| Apr 2016 | *Apr 2026* | | | | *16.04 Xenial* | 2.7.4 | 2.7.11 3.5.1 | 7.2 |
|
||||||
| Jun 2016 | *Jul 2017* | 2.9.0 | | | 16.10 Yakkety |
|
| Jun 2016 | *Jul 2017* | 2.9.0 | | | 16.10 Yakkety |
|
||||||
| Jul 2016 | | | | 7.3 | 16.10 Yakkety |
|
| Jul 2016 | | | | 7.3 | 16.10 Yakkety |
|
||||||
| Sep 2016 | *Sep 2017* | 2.10.0 |
|
| Sep 2016 | *Sep 2017* | 2.10.0 |
|
||||||
@ -312,14 +312,33 @@ Things in italics are things we used to care about but probably don't anymore.
|
|||||||
| Oct 2020 | | | | | 20.10 Groovy | 2.27.0 | 2.7.18 3.8.6 | 8.3 |
|
| Oct 2020 | | | | | 20.10 Groovy | 2.27.0 | 2.7.18 3.8.6 | 8.3 |
|
||||||
| Oct 2020 | **Oct 2025** | | 3.9.0 | | 21.04 Hirsute / **Bullseye** |
|
| Oct 2020 | **Oct 2025** | | 3.9.0 | | 21.04 Hirsute / **Bullseye** |
|
||||||
| Dec 2020 | *Mar 2021* | 2.30.0 | | | 21.04 Hirsute / **Bullseye** |
|
| Dec 2020 | *Mar 2021* | 2.30.0 | | | 21.04 Hirsute / **Bullseye** |
|
||||||
| Mar 2021 | | 2.31.0 |
|
| Mar 2021 | | 2.31.0 | | 8.5 |
|
||||||
| Mar 2021 | | | | 8.5 |
|
|
||||||
| Apr 2021 | | | | 8.6 |
|
| Apr 2021 | | | | 8.6 |
|
||||||
| Apr 2021 | *Jan 2022* | | | | 21.04 Hirsute | 2.30.2 | 2.7.18 3.9.4 | 8.4 |
|
| Apr 2021 | *Jan 2022* | | | | 21.04 Hirsute | 2.30.2 | 2.7.18 3.9.4 | 8.4 |
|
||||||
| Jun 2021 | | 2.32.0 |
|
| Jun 2021 | | 2.32.0 |
|
||||||
| Aug 2021 | | 2.33.0 |
|
| Aug 2021 | | 2.33.0 | | 8.7 |
|
||||||
| Aug 2021 | | | | 8.7 |
|
|
||||||
| Aug 2021 | **Aug 2026** | | | | **Debian 11 Bullseye** | 2.30.2 | 2.7.18 3.9.2 | 8.4 |
|
| Aug 2021 | **Aug 2026** | | | | **Debian 11 Bullseye** | 2.30.2 | 2.7.18 3.9.2 | 8.4 |
|
||||||
|
| Sep 2021 | | | | 8.8 |
|
||||||
|
| Oct 2021 | | 2.34.0 | 3.10.0 | | **22.04 Jammy** |
|
||||||
|
| Jan 2022 | | 2.35.0 |
|
||||||
|
| Feb 2022 | | | | 8.9 | **22.04 Jammy** |
|
||||||
|
| Apr 2022 | | 2.36.0 | | 9.0 |
|
||||||
|
| Apr 2022 | **Apr 2032** | | | | **22.04 Jammy** | 2.34.1 | 2.7.18 3.10.6 | 8.9 |
|
||||||
|
| Jun 2022 | | 2.37.0 |
|
||||||
|
| Oct 2022 | | 2.38.0 | | 9.1 |
|
||||||
|
| Oct 2022 | | | 3.11.0 | | **Bookworm** |
|
||||||
|
| Dec 2022 | | 2.39.0 | | | **Bookworm** |
|
||||||
|
| Feb 2023 | | | | 9.2 | **Bookworm** |
|
||||||
|
| Mar 2023 | | 2.40.0 | | 9.3 |
|
||||||
|
| Jun 2023 | | 2.41.0 |
|
||||||
|
| Jun 2023 | **Jun 2028** | | | | **Debian 12 Bookworm** | 2.39.2 | 3.11.2 | 9.2 |
|
||||||
|
| Aug 2023 | | 2.42.0 | | 9.4 |
|
||||||
|
| Oct 2023 | | | 3.12.0 | 9.5 |
|
||||||
|
| Nov 2022 | | 2.43.0 |
|
||||||
|
| Dec 2023 | | | | 9.6 |
|
||||||
|
| Feb 2024 | | 2.44.0 |
|
||||||
|
| Mar 2024 | | | | 9.7 |
|
||||||
|
| Oct 2024 | | | 3.13.0 |
|
||||||
| **Date** | **EOL** | **[Git][rel-g]** | **[Python][rel-p]** | **[SSH][rel-o]** | **[Ubuntu][rel-u] / [Debian][rel-d]** | **Git** | **Python** | **SSH** |
|
| **Date** | **EOL** | **[Git][rel-g]** | **[Python][rel-p]** | **[SSH][rel-o]** | **[Ubuntu][rel-u] / [Debian][rel-d]** | **Git** | **Python** | **SSH** |
|
||||||
|
|
||||||
|
|
||||||
@ -328,7 +347,7 @@ Things in italics are things we used to care about but probably don't anymore.
|
|||||||
[rel-g]: https://en.wikipedia.org/wiki/Git#Releases
|
[rel-g]: https://en.wikipedia.org/wiki/Git#Releases
|
||||||
[rel-o]: https://www.openssh.com/releasenotes.html
|
[rel-o]: https://www.openssh.com/releasenotes.html
|
||||||
[rel-p]: https://en.wikipedia.org/wiki/History_of_Python#Table_of_versions
|
[rel-p]: https://en.wikipedia.org/wiki/History_of_Python#Table_of_versions
|
||||||
[rel-u]: https://en.wikipedia.org/wiki/Ubuntu_version_history#Table_of_versions
|
[rel-u]: https://wiki.ubuntu.com/Releases
|
||||||
[example announcement]: https://groups.google.com/d/topic/repo-discuss/UGBNismWo1M/discussion
|
[example announcement]: https://groups.google.com/d/topic/repo-discuss/UGBNismWo1M/discussion
|
||||||
[repo-discuss@googlegroups.com]: https://groups.google.com/forum/#!forum/repo-discuss
|
[repo-discuss@googlegroups.com]: https://groups.google.com/forum/#!forum/repo-discuss
|
||||||
[go/repo-release]: https://goto.google.com/repo-release
|
[go/repo-release]: https://goto.google.com/repo-release
|
||||||
|
@ -135,6 +135,8 @@ def GetEventTargetPath():
|
|||||||
if retval == 0:
|
if retval == 0:
|
||||||
# Strip trailing carriage-return in path.
|
# Strip trailing carriage-return in path.
|
||||||
path = p.stdout.rstrip("\n")
|
path = p.stdout.rstrip("\n")
|
||||||
|
if path == "":
|
||||||
|
return None
|
||||||
elif retval != 1:
|
elif retval != 1:
|
||||||
# `git config --get` is documented to produce an exit status of `1`
|
# `git config --get` is documented to produce an exit status of `1`
|
||||||
# if the requested variable is not present in the configuration.
|
# if the requested variable is not present in the configuration.
|
||||||
|
13
main.py
13
main.py
@ -270,10 +270,14 @@ class _Repo:
|
|||||||
self._PrintHelp(short=True)
|
self._PrintHelp(short=True)
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
run = lambda: self._RunLong(name, gopts, argv) or 0
|
git_trace2_event_log = EventLog()
|
||||||
|
run = (
|
||||||
|
lambda: self._RunLong(name, gopts, argv, git_trace2_event_log) or 0
|
||||||
|
)
|
||||||
with Trace(
|
with Trace(
|
||||||
"starting new command: %s",
|
"starting new command: %s [sid=%s]",
|
||||||
", ".join([name] + argv),
|
", ".join([name] + argv),
|
||||||
|
git_trace2_event_log.full_sid,
|
||||||
first_trace=True,
|
first_trace=True,
|
||||||
):
|
):
|
||||||
if gopts.trace_python:
|
if gopts.trace_python:
|
||||||
@ -290,12 +294,11 @@ class _Repo:
|
|||||||
result = run()
|
result = run()
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def _RunLong(self, name, gopts, argv):
|
def _RunLong(self, name, gopts, argv, git_trace2_event_log):
|
||||||
"""Execute the (longer running) requested subcommand."""
|
"""Execute the (longer running) requested subcommand."""
|
||||||
result = 0
|
result = 0
|
||||||
SetDefaultColoring(gopts.color)
|
SetDefaultColoring(gopts.color)
|
||||||
|
|
||||||
git_trace2_event_log = EventLog()
|
|
||||||
outer_client = RepoClient(self.repodir)
|
outer_client = RepoClient(self.repodir)
|
||||||
repo_client = outer_client
|
repo_client = outer_client
|
||||||
if gopts.submanifest_path:
|
if gopts.submanifest_path:
|
||||||
@ -422,7 +425,7 @@ class _Repo:
|
|||||||
error_info = json.dumps(
|
error_info = json.dumps(
|
||||||
{
|
{
|
||||||
"ErrorType": type(error).__name__,
|
"ErrorType": type(error).__name__,
|
||||||
"Project": project,
|
"Project": str(project),
|
||||||
"Message": str(error),
|
"Message": str(error),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
45
project.py
45
project.py
@ -21,6 +21,7 @@ import random
|
|||||||
import re
|
import re
|
||||||
import shutil
|
import shutil
|
||||||
import stat
|
import stat
|
||||||
|
import string
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
import tarfile
|
import tarfile
|
||||||
@ -266,6 +267,7 @@ class ReviewableBranch:
|
|||||||
dest_branch=None,
|
dest_branch=None,
|
||||||
validate_certs=True,
|
validate_certs=True,
|
||||||
push_options=None,
|
push_options=None,
|
||||||
|
patchset_description=None,
|
||||||
):
|
):
|
||||||
self.project.UploadForReview(
|
self.project.UploadForReview(
|
||||||
branch=self.name,
|
branch=self.name,
|
||||||
@ -281,6 +283,7 @@ class ReviewableBranch:
|
|||||||
dest_branch=dest_branch,
|
dest_branch=dest_branch,
|
||||||
validate_certs=validate_certs,
|
validate_certs=validate_certs,
|
||||||
push_options=push_options,
|
push_options=push_options,
|
||||||
|
patchset_description=patchset_description,
|
||||||
)
|
)
|
||||||
|
|
||||||
def GetPublishedRefs(self):
|
def GetPublishedRefs(self):
|
||||||
@ -1089,6 +1092,7 @@ class Project:
|
|||||||
dest_branch=None,
|
dest_branch=None,
|
||||||
validate_certs=True,
|
validate_certs=True,
|
||||||
push_options=None,
|
push_options=None,
|
||||||
|
patchset_description=None,
|
||||||
):
|
):
|
||||||
"""Uploads the named branch for code review."""
|
"""Uploads the named branch for code review."""
|
||||||
if branch is None:
|
if branch is None:
|
||||||
@ -1171,6 +1175,10 @@ class Project:
|
|||||||
opts += ["wip"]
|
opts += ["wip"]
|
||||||
if ready:
|
if ready:
|
||||||
opts += ["ready"]
|
opts += ["ready"]
|
||||||
|
if patchset_description:
|
||||||
|
opts += [
|
||||||
|
f"m={self._encode_patchset_description(patchset_description)}"
|
||||||
|
]
|
||||||
if opts:
|
if opts:
|
||||||
ref_spec = ref_spec + "%" + ",".join(opts)
|
ref_spec = ref_spec + "%" + ",".join(opts)
|
||||||
cmd.append(ref_spec)
|
cmd.append(ref_spec)
|
||||||
@ -1183,6 +1191,30 @@ class Project:
|
|||||||
R_PUB + branch.name, R_HEADS + branch.name, message=msg
|
R_PUB + branch.name, R_HEADS + branch.name, message=msg
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _encode_patchset_description(original):
|
||||||
|
"""Applies percent-encoding for strings sent as patchset description.
|
||||||
|
|
||||||
|
The encoding used is based on but stricter than URL encoding (Section
|
||||||
|
2.1 of RFC 3986). The only non-escaped characters are alphanumerics, and
|
||||||
|
'SPACE' (U+0020) can be represented as 'LOW LINE' (U+005F) or
|
||||||
|
'PLUS SIGN' (U+002B).
|
||||||
|
|
||||||
|
For more information, see the Gerrit docs here:
|
||||||
|
https://gerrit-review.googlesource.com/Documentation/user-upload.html#patch_set_description
|
||||||
|
"""
|
||||||
|
SAFE = {ord(x) for x in string.ascii_letters + string.digits}
|
||||||
|
|
||||||
|
def _enc(b):
|
||||||
|
if b in SAFE:
|
||||||
|
return chr(b)
|
||||||
|
elif b == ord(" "):
|
||||||
|
return "_"
|
||||||
|
else:
|
||||||
|
return f"%{b:02x}"
|
||||||
|
|
||||||
|
return "".join(_enc(x) for x in original.encode("utf-8"))
|
||||||
|
|
||||||
def _ExtractArchive(self, tarpath, path=None):
|
def _ExtractArchive(self, tarpath, path=None):
|
||||||
"""Extract the given tar on its current location
|
"""Extract the given tar on its current location
|
||||||
|
|
||||||
@ -1483,6 +1515,7 @@ class Project:
|
|||||||
self,
|
self,
|
||||||
syncbuf,
|
syncbuf,
|
||||||
force_sync=False,
|
force_sync=False,
|
||||||
|
force_checkout=False,
|
||||||
submodules=False,
|
submodules=False,
|
||||||
errors=None,
|
errors=None,
|
||||||
verbose=False,
|
verbose=False,
|
||||||
@ -1570,7 +1603,7 @@ class Project:
|
|||||||
syncbuf.info(self, "discarding %d commits", len(lost))
|
syncbuf.info(self, "discarding %d commits", len(lost))
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self._Checkout(revid, quiet=True)
|
self._Checkout(revid, force_checkout=force_checkout, quiet=True)
|
||||||
if submodules:
|
if submodules:
|
||||||
self._SyncSubmodules(quiet=True)
|
self._SyncSubmodules(quiet=True)
|
||||||
except GitError as e:
|
except GitError as e:
|
||||||
@ -1780,11 +1813,11 @@ class Project:
|
|||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
msg = (
|
msg = (
|
||||||
"error: %s: Cannot remove project: uncommitted"
|
"error: %s: Cannot remove project: uncommitted "
|
||||||
"changes are present.\n" % self.RelPath(local=False)
|
"changes are present.\n" % self.RelPath(local=False)
|
||||||
)
|
)
|
||||||
logger.error(msg)
|
logger.error(msg)
|
||||||
raise DeleteDirtyWorktreeError(msg, project=self)
|
raise DeleteDirtyWorktreeError(msg, project=self.name)
|
||||||
|
|
||||||
if verbose:
|
if verbose:
|
||||||
print(f"{self.RelPath(local=False)}: Deleting obsolete checkout.")
|
print(f"{self.RelPath(local=False)}: Deleting obsolete checkout.")
|
||||||
@ -2825,10 +2858,12 @@ class Project:
|
|||||||
except OSError:
|
except OSError:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def _Checkout(self, rev, quiet=False):
|
def _Checkout(self, rev, force_checkout=False, quiet=False):
|
||||||
cmd = ["checkout"]
|
cmd = ["checkout"]
|
||||||
if quiet:
|
if quiet:
|
||||||
cmd.append("-q")
|
cmd.append("-q")
|
||||||
|
if force_checkout:
|
||||||
|
cmd.append("-f")
|
||||||
cmd.append(rev)
|
cmd.append(rev)
|
||||||
cmd.append("--")
|
cmd.append("--")
|
||||||
if GitCommand(self, cmd).Wait() != 0:
|
if GitCommand(self, cmd).Wait() != 0:
|
||||||
@ -3306,7 +3341,7 @@ class Project:
|
|||||||
if not platform_utils.islink(dotgit) and platform_utils.isdir(dotgit):
|
if not platform_utils.islink(dotgit) and platform_utils.isdir(dotgit):
|
||||||
self._MigrateOldWorkTreeGitDir(dotgit, project=self.name)
|
self._MigrateOldWorkTreeGitDir(dotgit, project=self.name)
|
||||||
|
|
||||||
init_dotgit = not os.path.exists(dotgit)
|
init_dotgit = not os.path.lexists(dotgit)
|
||||||
if self.use_git_worktrees:
|
if self.use_git_worktrees:
|
||||||
if init_dotgit:
|
if init_dotgit:
|
||||||
self._InitGitWorktree()
|
self._InitGitWorktree()
|
||||||
|
9
repo
9
repo
@ -1,4 +1,4 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python3
|
||||||
#
|
#
|
||||||
# Copyright (C) 2008 The Android Open Source Project
|
# Copyright (C) 2008 The Android Open Source Project
|
||||||
#
|
#
|
||||||
@ -124,7 +124,7 @@ if not REPO_REV:
|
|||||||
BUG_URL = "https://issues.gerritcodereview.com/issues/new?component=1370071"
|
BUG_URL = "https://issues.gerritcodereview.com/issues/new?component=1370071"
|
||||||
|
|
||||||
# increment this whenever we make important changes to this script
|
# increment this whenever we make important changes to this script
|
||||||
VERSION = (2, 40)
|
VERSION = (2, 45)
|
||||||
|
|
||||||
# increment this if the MAINTAINER_KEYS block is modified
|
# increment this if the MAINTAINER_KEYS block is modified
|
||||||
KEYRING_VERSION = (2, 3)
|
KEYRING_VERSION = (2, 3)
|
||||||
@ -210,9 +210,8 @@ GIT = "git" # our git command
|
|||||||
# NB: The version of git that the repo launcher requires may be much older than
|
# NB: The version of git that the repo launcher requires may be much older than
|
||||||
# the version of git that the main repo source tree requires. Keeping this at
|
# the version of git that the main repo source tree requires. Keeping this at
|
||||||
# an older version also makes it easier for users to upgrade/rollback as needed.
|
# an older version also makes it easier for users to upgrade/rollback as needed.
|
||||||
#
|
# See requirements.json for versions.
|
||||||
# git-1.7 is in (EOL) Ubuntu Precise.
|
MIN_GIT_VERSION = (1, 7, 9) # minimum supported git version
|
||||||
MIN_GIT_VERSION = (1, 7, 2) # minimum supported git version
|
|
||||||
repodir = ".repo" # name of repo's private directory
|
repodir = ".repo" # name of repo's private directory
|
||||||
S_repo = "repo" # special repo repository
|
S_repo = "repo" # special repo repository
|
||||||
S_manifests = "manifests" # special manifest repository
|
S_manifests = "manifests" # special manifest repository
|
||||||
|
@ -46,12 +46,16 @@
|
|||||||
|
|
||||||
# Supported git versions.
|
# Supported git versions.
|
||||||
#
|
#
|
||||||
# git-1.7.2 is in Debian Squeeze.
|
|
||||||
# git-1.7.9 is in Ubuntu Precise.
|
# git-1.7.9 is in Ubuntu Precise.
|
||||||
# git-1.9.1 is in Ubuntu Trusty.
|
|
||||||
# git-1.7.10 is in Debian Wheezy.
|
# git-1.7.10 is in Debian Wheezy.
|
||||||
|
# git-1.9.1 is in Ubuntu Trusty.
|
||||||
|
# git-2.1.4 is in Debian Jessie.
|
||||||
|
# git-2.7.4 is in Ubuntu Xenial.
|
||||||
|
# git-2.11.0 is in Debian Stretch.
|
||||||
|
# git-2.17.0 is in Ubuntu Bionic.
|
||||||
|
# git-2.20.1 is in Debian Buster.
|
||||||
"git": {
|
"git": {
|
||||||
"hard": [1, 7, 2],
|
"hard": [1, 7, 9],
|
||||||
"soft": [1, 9, 1]
|
"soft": [2, 7, 4]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
8
ssh.py
8
ssh.py
@ -57,8 +57,12 @@ def version():
|
|||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
print("fatal: ssh not installed", file=sys.stderr)
|
print("fatal: ssh not installed", file=sys.stderr)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
except subprocess.CalledProcessError:
|
except subprocess.CalledProcessError as e:
|
||||||
print("fatal: unable to detect ssh version", file=sys.stderr)
|
print(
|
||||||
|
"fatal: unable to detect ssh version"
|
||||||
|
f" (code={e.returncode}, output={e.stdout})",
|
||||||
|
file=sys.stderr,
|
||||||
|
)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
|
118
subcmds/sync.py
118
subcmds/sync.py
@ -21,6 +21,7 @@ import multiprocessing
|
|||||||
import netrc
|
import netrc
|
||||||
import optparse
|
import optparse
|
||||||
import os
|
import os
|
||||||
|
from pathlib import Path
|
||||||
import sys
|
import sys
|
||||||
import tempfile
|
import tempfile
|
||||||
import time
|
import time
|
||||||
@ -82,16 +83,54 @@ from wrapper import Wrapper
|
|||||||
|
|
||||||
_ONE_DAY_S = 24 * 60 * 60
|
_ONE_DAY_S = 24 * 60 * 60
|
||||||
|
|
||||||
# Env var to implicitly turn auto-gc back on. This was added to allow a user to
|
|
||||||
# revert a change in default behavior in v2.29.9. Remove after 2023-04-01.
|
|
||||||
_REPO_AUTO_GC = "REPO_AUTO_GC"
|
|
||||||
_AUTO_GC = os.environ.get(_REPO_AUTO_GC) == "1"
|
|
||||||
|
|
||||||
_REPO_ALLOW_SHALLOW = os.environ.get("REPO_ALLOW_SHALLOW")
|
_REPO_ALLOW_SHALLOW = os.environ.get("REPO_ALLOW_SHALLOW")
|
||||||
|
|
||||||
logger = RepoLogger(__file__)
|
logger = RepoLogger(__file__)
|
||||||
|
|
||||||
|
|
||||||
|
def _SafeCheckoutOrder(checkouts: List[Project]) -> List[List[Project]]:
|
||||||
|
"""Generate a sequence of checkouts that is safe to perform. The client
|
||||||
|
should checkout everything from n-th index before moving to n+1.
|
||||||
|
|
||||||
|
This is only useful if manifest contains nested projects.
|
||||||
|
|
||||||
|
E.g. if foo, foo/bar and foo/bar/baz are project paths, then foo needs to
|
||||||
|
finish before foo/bar can proceed, and foo/bar needs to finish before
|
||||||
|
foo/bar/baz."""
|
||||||
|
res = [[]]
|
||||||
|
current = res[0]
|
||||||
|
|
||||||
|
# depth_stack contains a current stack of parent paths.
|
||||||
|
depth_stack = []
|
||||||
|
# Checkouts are iterated in the hierarchical order. That way, it can easily
|
||||||
|
# be determined if the previous checkout is parent of the current checkout.
|
||||||
|
# We are splitting by the path separator so the final result is
|
||||||
|
# hierarchical, and not just lexicographical. For example, if the projects
|
||||||
|
# are: foo, foo/bar, foo-bar, lexicographical order produces foo, foo-bar
|
||||||
|
# and foo/bar, which doesn't work.
|
||||||
|
for checkout in sorted(checkouts, key=lambda x: x.relpath.split("/")):
|
||||||
|
checkout_path = Path(checkout.relpath)
|
||||||
|
while depth_stack:
|
||||||
|
try:
|
||||||
|
checkout_path.relative_to(depth_stack[-1])
|
||||||
|
except ValueError:
|
||||||
|
# Path.relative_to returns ValueError if paths are not relative.
|
||||||
|
# TODO(sokcevic): Switch to is_relative_to once min supported
|
||||||
|
# version is py3.9.
|
||||||
|
depth_stack.pop()
|
||||||
|
else:
|
||||||
|
if len(depth_stack) >= len(res):
|
||||||
|
# Another depth created.
|
||||||
|
res.append([])
|
||||||
|
break
|
||||||
|
|
||||||
|
current = res[len(depth_stack)]
|
||||||
|
current.append(checkout)
|
||||||
|
depth_stack.append(checkout_path)
|
||||||
|
|
||||||
|
return res
|
||||||
|
|
||||||
|
|
||||||
class _FetchOneResult(NamedTuple):
|
class _FetchOneResult(NamedTuple):
|
||||||
"""_FetchOne return value.
|
"""_FetchOne return value.
|
||||||
|
|
||||||
@ -243,6 +282,11 @@ directories if they have previously been linked to a different
|
|||||||
object directory. WARNING: This may cause data to be lost since
|
object directory. WARNING: This may cause data to be lost since
|
||||||
refs may be removed when overwriting.
|
refs may be removed when overwriting.
|
||||||
|
|
||||||
|
The --force-checkout option can be used to force git to switch revs even if the
|
||||||
|
index or the working tree differs from HEAD, and if there are untracked files.
|
||||||
|
WARNING: This may cause data to be lost since uncommitted changes may be
|
||||||
|
removed.
|
||||||
|
|
||||||
The --force-remove-dirty option can be used to remove previously used
|
The --force-remove-dirty option can be used to remove previously used
|
||||||
projects with uncommitted changes. WARNING: This may cause data to be
|
projects with uncommitted changes. WARNING: This may cause data to be
|
||||||
lost since uncommitted changes may be removed with projects that no longer
|
lost since uncommitted changes may be removed with projects that no longer
|
||||||
@ -340,6 +384,14 @@ later is required to fix a server side protocol bug.
|
|||||||
"point to a different object directory. WARNING: this "
|
"point to a different object directory. WARNING: this "
|
||||||
"may cause loss of data",
|
"may cause loss of data",
|
||||||
)
|
)
|
||||||
|
p.add_option(
|
||||||
|
"--force-checkout",
|
||||||
|
dest="force_checkout",
|
||||||
|
action="store_true",
|
||||||
|
help="force checkout even if it results in throwing away "
|
||||||
|
"uncommitted modifications. "
|
||||||
|
"WARNING: this may cause loss of data",
|
||||||
|
)
|
||||||
p.add_option(
|
p.add_option(
|
||||||
"--force-remove-dirty",
|
"--force-remove-dirty",
|
||||||
dest="force_remove_dirty",
|
dest="force_remove_dirty",
|
||||||
@ -956,12 +1008,17 @@ later is required to fix a server side protocol bug.
|
|||||||
|
|
||||||
return _FetchMainResult(all_projects)
|
return _FetchMainResult(all_projects)
|
||||||
|
|
||||||
def _CheckoutOne(self, detach_head, force_sync, verbose, project):
|
def _CheckoutOne(
|
||||||
|
self, detach_head, force_sync, force_checkout, verbose, project
|
||||||
|
):
|
||||||
"""Checkout work tree for one project
|
"""Checkout work tree for one project
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
detach_head: Whether to leave a detached HEAD.
|
detach_head: Whether to leave a detached HEAD.
|
||||||
force_sync: Force checking out of the repo.
|
force_sync: Force checking out of .git directory (e.g. overwrite
|
||||||
|
existing git directory that was previously linked to a different
|
||||||
|
object directory).
|
||||||
|
force_checkout: Force checking out of the repo content.
|
||||||
verbose: Whether to show verbose messages.
|
verbose: Whether to show verbose messages.
|
||||||
project: Project object for the project to checkout.
|
project: Project object for the project to checkout.
|
||||||
|
|
||||||
@ -976,7 +1033,11 @@ later is required to fix a server side protocol bug.
|
|||||||
errors = []
|
errors = []
|
||||||
try:
|
try:
|
||||||
project.Sync_LocalHalf(
|
project.Sync_LocalHalf(
|
||||||
syncbuf, force_sync=force_sync, errors=errors, verbose=verbose
|
syncbuf,
|
||||||
|
force_sync=force_sync,
|
||||||
|
force_checkout=force_checkout,
|
||||||
|
errors=errors,
|
||||||
|
verbose=verbose,
|
||||||
)
|
)
|
||||||
success = syncbuf.Finish()
|
success = syncbuf.Finish()
|
||||||
except GitError as e:
|
except GitError as e:
|
||||||
@ -1040,15 +1101,22 @@ later is required to fix a server side protocol bug.
|
|||||||
pm.update(msg=project.name)
|
pm.update(msg=project.name)
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
proc_res = self.ExecuteInParallel(
|
for projects in _SafeCheckoutOrder(all_projects):
|
||||||
opt.jobs_checkout,
|
proc_res = self.ExecuteInParallel(
|
||||||
functools.partial(
|
opt.jobs_checkout,
|
||||||
self._CheckoutOne, opt.detach_head, opt.force_sync, opt.verbose
|
functools.partial(
|
||||||
),
|
self._CheckoutOne,
|
||||||
all_projects,
|
opt.detach_head,
|
||||||
callback=_ProcessResults,
|
opt.force_sync,
|
||||||
output=Progress("Checking out", len(all_projects), quiet=opt.quiet),
|
opt.force_checkout,
|
||||||
)
|
opt.verbose,
|
||||||
|
),
|
||||||
|
projects,
|
||||||
|
callback=_ProcessResults,
|
||||||
|
output=Progress(
|
||||||
|
"Checking out", len(all_projects), quiet=opt.quiet
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
self._local_sync_state.Save()
|
self._local_sync_state.Save()
|
||||||
return proc_res and not err_results
|
return proc_res and not err_results
|
||||||
@ -1544,9 +1612,7 @@ later is required to fix a server side protocol bug.
|
|||||||
mp, event_log.TASK_SYNC_LOCAL, start, time.time(), clean
|
mp, event_log.TASK_SYNC_LOCAL, start, time.time(), clean
|
||||||
)
|
)
|
||||||
if not clean:
|
if not clean:
|
||||||
raise UpdateManifestError(
|
raise UpdateManifestError(aggregate_errors=errors)
|
||||||
aggregate_errors=errors, project=mp.name
|
|
||||||
)
|
|
||||||
self._ReloadManifest(manifest_name, mp.manifest)
|
self._ReloadManifest(manifest_name, mp.manifest)
|
||||||
|
|
||||||
def ValidateOptions(self, opt, args):
|
def ValidateOptions(self, opt, args):
|
||||||
@ -1577,16 +1643,6 @@ later is required to fix a server side protocol bug.
|
|||||||
if opt.prune is None:
|
if opt.prune is None:
|
||||||
opt.prune = True
|
opt.prune = True
|
||||||
|
|
||||||
if opt.auto_gc is None and _AUTO_GC:
|
|
||||||
logger.error(
|
|
||||||
"Will run `git gc --auto` because %s is set. %s is deprecated "
|
|
||||||
"and will be removed in a future release. Use `--auto-gc` "
|
|
||||||
"instead.",
|
|
||||||
_REPO_AUTO_GC,
|
|
||||||
_REPO_AUTO_GC,
|
|
||||||
)
|
|
||||||
opt.auto_gc = True
|
|
||||||
|
|
||||||
def _ValidateOptionsWithManifest(self, opt, mp):
|
def _ValidateOptionsWithManifest(self, opt, mp):
|
||||||
"""Like ValidateOptions, but after we've updated the manifest.
|
"""Like ValidateOptions, but after we've updated the manifest.
|
||||||
|
|
||||||
@ -1630,7 +1686,7 @@ later is required to fix a server side protocol bug.
|
|||||||
errors = []
|
errors = []
|
||||||
try:
|
try:
|
||||||
self._ExecuteHelper(opt, args, errors)
|
self._ExecuteHelper(opt, args, errors)
|
||||||
except RepoExitError:
|
except (RepoExitError, RepoChangedException):
|
||||||
raise
|
raise
|
||||||
except (KeyboardInterrupt, Exception) as e:
|
except (KeyboardInterrupt, Exception) as e:
|
||||||
raise RepoUnhandledExceptionError(e, aggregate_errors=errors)
|
raise RepoUnhandledExceptionError(e, aggregate_errors=errors)
|
||||||
|
@ -244,6 +244,12 @@ Gerrit Code Review: https://www.gerritcodereview.com/
|
|||||||
default=[],
|
default=[],
|
||||||
help="add a label when uploading",
|
help="add a label when uploading",
|
||||||
)
|
)
|
||||||
|
p.add_option(
|
||||||
|
"--pd",
|
||||||
|
"--patchset-description",
|
||||||
|
dest="patchset_description",
|
||||||
|
help="description for patchset",
|
||||||
|
)
|
||||||
p.add_option(
|
p.add_option(
|
||||||
"--re",
|
"--re",
|
||||||
"--reviewers",
|
"--reviewers",
|
||||||
@ -655,6 +661,7 @@ Gerrit Code Review: https://www.gerritcodereview.com/
|
|||||||
dest_branch=destination,
|
dest_branch=destination,
|
||||||
validate_certs=opt.validate_certs,
|
validate_certs=opt.validate_certs,
|
||||||
push_options=opt.push_options,
|
push_options=opt.push_options,
|
||||||
|
patchset_description=opt.patchset_description,
|
||||||
)
|
)
|
||||||
|
|
||||||
branch.uploaded = True
|
branch.uploaded = True
|
||||||
|
@ -107,6 +107,16 @@ class ReviewableBranchTests(unittest.TestCase):
|
|||||||
self.assertTrue(rb.date)
|
self.assertTrue(rb.date)
|
||||||
|
|
||||||
|
|
||||||
|
class ProjectTests(unittest.TestCase):
|
||||||
|
"""Check Project behavior."""
|
||||||
|
|
||||||
|
def test_encode_patchset_description(self):
|
||||||
|
self.assertEqual(
|
||||||
|
project.Project._encode_patchset_description("abcd00!! +"),
|
||||||
|
"abcd00%21%21_%2b",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class CopyLinkTestCase(unittest.TestCase):
|
class CopyLinkTestCase(unittest.TestCase):
|
||||||
"""TestCase for stub repo client checkouts.
|
"""TestCase for stub repo client checkouts.
|
||||||
|
|
||||||
|
@ -304,6 +304,57 @@ class LocalSyncState(unittest.TestCase):
|
|||||||
self.assertEqual(self.state.GetFetchTime(projA), 5)
|
self.assertEqual(self.state.GetFetchTime(projA), 5)
|
||||||
|
|
||||||
|
|
||||||
|
class FakeProject:
|
||||||
|
def __init__(self, relpath):
|
||||||
|
self.relpath = relpath
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return f"project: {self.relpath}"
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return str(self)
|
||||||
|
|
||||||
|
|
||||||
|
class SafeCheckoutOrder(unittest.TestCase):
|
||||||
|
def test_no_nested(self):
|
||||||
|
p_f = FakeProject("f")
|
||||||
|
p_foo = FakeProject("foo")
|
||||||
|
out = sync._SafeCheckoutOrder([p_f, p_foo])
|
||||||
|
self.assertEqual(out, [[p_f, p_foo]])
|
||||||
|
|
||||||
|
def test_basic_nested(self):
|
||||||
|
p_foo = p_foo = FakeProject("foo")
|
||||||
|
p_foo_bar = FakeProject("foo/bar")
|
||||||
|
out = sync._SafeCheckoutOrder([p_foo, p_foo_bar])
|
||||||
|
self.assertEqual(out, [[p_foo], [p_foo_bar]])
|
||||||
|
|
||||||
|
def test_complex_nested(self):
|
||||||
|
p_foo = FakeProject("foo")
|
||||||
|
p_foobar = FakeProject("foobar")
|
||||||
|
p_foo_dash_bar = FakeProject("foo-bar")
|
||||||
|
p_foo_bar = FakeProject("foo/bar")
|
||||||
|
p_foo_bar_baz_baq = FakeProject("foo/bar/baz/baq")
|
||||||
|
p_bar = FakeProject("bar")
|
||||||
|
out = sync._SafeCheckoutOrder(
|
||||||
|
[
|
||||||
|
p_foo_bar_baz_baq,
|
||||||
|
p_foo,
|
||||||
|
p_foobar,
|
||||||
|
p_foo_dash_bar,
|
||||||
|
p_foo_bar,
|
||||||
|
p_bar,
|
||||||
|
]
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
out,
|
||||||
|
[
|
||||||
|
[p_bar, p_foo, p_foo_dash_bar, p_foobar],
|
||||||
|
[p_foo_bar],
|
||||||
|
[p_foo_bar_baz_baq],
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class GetPreciousObjectsState(unittest.TestCase):
|
class GetPreciousObjectsState(unittest.TestCase):
|
||||||
"""Tests for _GetPreciousObjectsState."""
|
"""Tests for _GetPreciousObjectsState."""
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user