Skip to content
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
Show all changes
83 commits
Select commit Hold shift + click to select a range
d93fe60
sync: Handle KeyboardInterrupt during checkout
sokcevicG Jan 8, 2025
41a27eb
gc: extract deletion from Execute method
sokcevicG Jan 14, 2025
3405446
gc: Add repack option
sokcevicG Jan 14, 2025
db111d3
sync: Recover from errors during read-tree
sokcevicG Jan 15, 2025
1711bc2
git_config: prefer XDG config location
Jan 23, 2025
747ec83
run_tests: update to python 3.11 & pytest 8.3.4
vapier Jan 30, 2025
dfdf577
docs: smart-sync: split out & expand details
vapier Jan 31, 2025
5ae8292
Revert "sync: skip network half on repo upgrade"
sokcevicG Jan 31, 2025
cf9a2a2
Update internal filesystem layout for submodules
quic-kaushikl Dec 17, 2024
66685f0
Use 'gitfile' in submodule checkouts
quic-kaushikl Dec 17, 2024
99eca45
Activate submodules
quic-kaushikl Dec 17, 2024
8d5f032
gc: Add tags to remote pack list
sokcevicG Feb 5, 2025
fc901b9
sync: Refresh index before updating repo
sokcevicG Mar 12, 2025
4b94e77
Sync: Fix full submodule sync while shallow specified
Feb 17, 2025
243df20
launcher: change RunResult to subprocess.CompletedProcess
vapier Mar 22, 2025
91f4280
run_tests: run all tests all the time
vapier Mar 25, 2025
d508739
run_tests: move CQ test skips here
vapier Mar 25, 2025
8310436
run_tests: move test filtering to pytest markers
vapier Mar 25, 2025
cd391e7
black: update to v25
vapier Mar 25, 2025
507d463
tox: sync black settings with run_tests
vapier Mar 27, 2025
59b81c8
launcher: change collections.namedtuple to typing.NamedTuple
vapier Mar 27, 2025
dc8185f
launcher: change RunError to subprocess.CalledProcessError
vapier Mar 27, 2025
9ecb80b
pager: drop unused global vars
vapier Apr 2, 2025
f070331
Fix EROFS error when root fs is mounted read-only
egor-duda Mar 6, 2025
85ee173
run_tests: enable Python 3.8 CI coverage
vapier Apr 2, 2025
3667de1
run_tests: fix running when cwd is not the root
vapier Apr 2, 2025
daebd6c
sync: Warn about excessive job counts
gavinmak Apr 9, 2025
0214730
launcher: switch command quoting to shlex.quote
vapier Apr 9, 2025
97dc5c1
project: use --netrc-optional instead of --netrc
gavinmak Apr 10, 2025
a94457d
Fallback to full sync when depth enabled fetch of a sha1 fails
quic-kaushikl Apr 8, 2025
c061593
manifest: Remove redundant re-raise of BaseExceptions
Apr 21, 2025
c8da28c
man: regenerate man pages
vapier Apr 22, 2025
0f200bb
flake8: Ignore .venv directory
Apr 23, 2025
21cbcc5
update-manpages: include in unittests
vapier Apr 22, 2025
c448ba9
run_tests: only allow help2man skipping in CI
vapier Apr 30, 2025
1acbc14
manifest: generalize --json as --format=<format>
vapier Apr 30, 2025
8d37f61
upload: Add superproject identifier as push option
gavinmak May 5, 2025
06338ab
subcmds: delete redundant dest= settings
vapier May 22, 2025
3c8bae2
info: print superproject revision
May 27, 2025
08815ad
upload: Add rev to rootRepo push option
gavinmak May 23, 2025
0cb88a8
git_superproject: Replace walrus operator
gavinmak Jun 4, 2025
044e52e
hooks: add internal check for external hook API
vapier Jun 5, 2025
b262d0e
info: fix mismatched format args and wrong symbol name
Jun 10, 2025
8535282
sync: Add scaffolding for interleaved sync
gavinmak Jun 11, 2025
f91f446
upload: fix FileNotFoundError when no superproject
gavinmak Jun 17, 2025
b4b323a
sync: Add orchestration logic for --interleaved
gavinmak Jun 17, 2025
7b6ffed
sync: Implement --interleaved sync worker
gavinmak Jun 14, 2025
6b8e9fc
sync: clarify job flags when using interleaved
gavinmak Jun 18, 2025
f7a3f99
sync: Share self-update logic between sync modes
gavinmak Jun 23, 2025
df3c401
sync: Share manifest list update logic between sync modes
gavinmak Jun 18, 2025
99b5a17
sync: Share final error handling logic between sync modes
gavinmak Jun 18, 2025
21269c3
init: Add environment variable for git-lfs
Sep 4, 2024
82d500e
sync: support post-sync hook in <repo-hooks>
Chao-Shun-Cheng Jun 2, 2025
5d95ba8
progress: Make end() idempotent
gavinmak Jun 26, 2025
74edacd
project: Use plumbing commands to manage HEAD
gavinmak Jul 17, 2025
2e6d088
sync: Improve UI and error reporting for interleaved mode
gavinmak Jul 17, 2025
52bab0b
project: Use git rev-parse to read HEAD
gavinmak Jul 21, 2025
25858c8
sync: Default to interleaved mode
gavinmak Jul 21, 2025
720bd1e
sync: Don't checkout if no worktree
gavinmak Jul 23, 2025
7f7d70e
project: Fix GetHead to handle detached HEADs
gavinmak Jul 25, 2025
d3eec0a
sync: fix connection error on macOS for interleaved sync
kcwu Jul 25, 2025
239fad7
hooks: verify hooks project has worktree before running
gavinmak Jul 25, 2025
8c3585f
project: fallback to reading HEAD when rev-parse fails
gavinmak Aug 4, 2025
d9cc0a1
Fix shallow clones when upstream attribute is present
kwesolowski-at-volvocars Jul 23, 2025
380bf95
sync: always show sync result stderr_text on error
gavinmak Aug 13, 2025
a6e1a59
sync: Avoid duplicate projects in error text
gavinmak Aug 13, 2025
3e6acf2
progress: Fix race condition causing fileno crash
gavinmak Aug 13, 2025
a64149a
sync: Record and propagate errors from deferred actions
gavinmak Aug 14, 2025
d534a55
sync: Fix missing error details in interleaved summary
gavinmak Aug 14, 2025
854fe44
git_superproject: fix AttributeError in Superproject logging
gavinmak Aug 14, 2025
38d2fe1
Revert "Fix shallow clones when upstream attribute is present"
gavinmak Aug 14, 2025
58a59fd
CONTRIBUTING: rename doc per Google OSS policies
vapier Aug 20, 2025
5ed12ec
standardize file header wrt licensing
vapier Aug 21, 2025
c615c96
man: regen after sync updates
vapier Aug 21, 2025
80d1a5a
run_tests: add file header checker for licensing blocks
vapier Aug 21, 2025
d30414b
forall: fix crash with no command
vapier Sep 15, 2025
67383bd
Follow up "Fix shallow clones when upstream attribute is present"
quic-kaushikl Sep 9, 2025
4623264
Fix submodule initialization in interleaved sync mode
quic-kaushikl Sep 11, 2025
e4872ac
sync: Use 'git rebase' during 'repo sync --rebase'
Oct 15, 2025
2719a8e
run_tests: log each command run
vapier Oct 15, 2025
1afe96a
sync: fix saving of fetch times and local state
gavinmak Oct 20, 2025
4ab2284
manifest: Make extend-project support copyfile, linkfile and annotation
Oct 16, 2025
877ef91
man: Regenerate after manifest update
Nov 6, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/internal-fs-layout.md
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ Instead, you should use standard Git workflows like [git worktree] or
(e.g. a local mirror & a public review server) while avoiding duplicating
the content. However, this can run into problems if different remotes use
the same path on their respective servers. Best to avoid that.
* `subprojects/`: Like `projects/`, but for git submodules.
* `modules/`: Like `projects/`, but for git submodules.
* `subproject-objects/`: Like `project-objects/`, but for git submodules.
* `worktrees/`: Bare checkouts of every project synced by the manifest. The
filesystem layout matches the `<project name=...` setting in the manifest
Expand Down
7 changes: 6 additions & 1 deletion manifest_xml.py
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CAUTION

LINT IS FLAMMABLE

Original file line number Diff line number Diff line change
Expand Up @@ -2056,7 +2056,12 @@ def GetSubprojectPaths(self, parent, name, path):
path = path.rstrip("/")
name = name.rstrip("/")
relpath = self._JoinRelpath(parent.relpath, path)
gitdir = os.path.join(parent.gitdir, "subprojects", "%s.git" % path)
subprojects = os.path.join(parent.gitdir, "subprojects", f"{path}.git")
modules = os.path.join(parent.gitdir, "modules", path)
if platform_utils.isdir(subprojects):
gitdir = subprojects
else:
gitdir = modules
objdir = os.path.join(
parent.gitdir, "subproject-objects", "%s.git" % name
)
Expand Down
98 changes: 91 additions & 7 deletions project.py
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some of these file-handling routines seem a little fragile to me, although it might be the compromises necessary for cross-platform support.

Original file line number Diff line number Diff line change
Expand Up @@ -642,6 +642,10 @@ def __init__(
# project containing repo hooks.
self.enabled_repo_hooks = []

# This will be updated later if the project has submodules and
# if they will be synced.
self.has_subprojects = False

def RelPath(self, local=True):
"""Return the path for the project relative to a manifest.

Expand Down Expand Up @@ -1560,6 +1564,11 @@ def fail(error: Exception):
return

self._InitWorkTree(force_sync=force_sync, submodules=submodules)
# TODO(https://git-scm.com/docs/git-worktree#_bugs): Re-evaluate if
# submodules can be init when using worktrees once its support is
# complete.
if self.has_subprojects and not self.use_git_worktrees:
self._InitSubmodules()
all_refs = self.bare_ref.all
self.CleanPublishedCache(all_refs)
revid = self.GetRevisionId(all_refs)
Expand Down Expand Up @@ -2347,6 +2356,8 @@ def GetDerivedSubprojects(self):
)
result.append(subproject)
result.extend(subproject.GetDerivedSubprojects())
if result:
self.has_subprojects = True
return result

def EnableRepositoryExtension(self, key, value="true", version=1):
Expand Down Expand Up @@ -2997,6 +3008,17 @@ def _SyncSubmodules(self, quiet=True):
project=self.name,
)

def _InitSubmodules(self, quiet=True):
"""Initialize the submodules for the project."""
cmd = ["submodule", "init"]
if quiet:
cmd.append("-q")
if GitCommand(self, cmd).Wait() != 0:
raise GitError(
f"{self.name} submodule init",
project=self.name,
)

def _Rebase(self, upstream, onto=None):
cmd = ["rebase"]
if onto is not None:
Expand Down Expand Up @@ -3415,6 +3437,11 @@ def _InitWorkTree(self, force_sync=False, submodules=False):
"""
dotgit = os.path.join(self.worktree, ".git")

# If bare checkout of the submodule is stored under the subproject dir,
# migrate it.
if self.parent:
self._MigrateOldSubmoduleDir()

# If using an old layout style (a directory), migrate it.
if not platform_utils.islink(dotgit) and platform_utils.isdir(dotgit):
self._MigrateOldWorkTreeGitDir(dotgit, project=self.name)
Expand All @@ -3425,20 +3452,21 @@ def _InitWorkTree(self, force_sync=False, submodules=False):
self._InitGitWorktree()
self._CopyAndLinkFiles()
else:
# Remove old directory symbolic links for submodules.
if self.parent and platform_utils.islink(dotgit):
platform_utils.remove(dotgit)
init_dotgit = True

if not init_dotgit:
# See if the project has changed.
if os.path.realpath(self.gitdir) != os.path.realpath(dotgit):
platform_utils.remove(dotgit)
self._removeBadGitDirLink(dotgit)

if init_dotgit or not os.path.exists(dotgit):
os.makedirs(self.worktree, exist_ok=True)
platform_utils.symlink(
os.path.relpath(self.gitdir, self.worktree), dotgit
)
self._createDotGit(dotgit)

if init_dotgit:
_lwrite(
os.path.join(dotgit, HEAD), "%s\n" % self.GetRevisionId()
os.path.join(self.gitdir, HEAD), f"{self.GetRevisionId()}\n"
)

# Finish checking out the worktree.
Expand All @@ -3460,6 +3488,40 @@ def _InitWorkTree(self, force_sync=False, submodules=False):
self._SyncSubmodules(quiet=True)
self._CopyAndLinkFiles()

def _createDotGit(self, dotgit):
"""Initialize .git path.

For submodule projects, create a '.git' file using the gitfile
mechanism, and for the rest, create a symbolic link.
"""
os.makedirs(self.worktree, exist_ok=True)
if self.parent:
_lwrite(
dotgit,
f"gitdir: {os.path.relpath(self.gitdir, self.worktree)}\n",
)
else:
platform_utils.symlink(
os.path.relpath(self.gitdir, self.worktree), dotgit
)

def _removeBadGitDirLink(self, dotgit):
"""Verify .git is initialized correctly, otherwise delete it."""
if self.parent and os.path.isfile(dotgit):
with open(dotgit) as fp:
setting = fp.read()
if not setting.startswith("gitdir:"):
raise GitError(
f"'.git' in {self.worktree} must start with 'gitdir:'",
project=self.name,
)
gitdir = setting.split(":", 1)[1].strip()
dotgit_path = os.path.normpath(os.path.join(self.worktree, gitdir))
else:
dotgit_path = os.path.realpath(dotgit)
if os.path.realpath(self.gitdir) != dotgit_path:
platform_utils.remove(dotgit)

@classmethod
def _MigrateOldWorkTreeGitDir(cls, dotgit, project=None):
"""Migrate the old worktree .git/ dir style to a symlink.
Expand Down Expand Up @@ -3548,6 +3610,28 @@ def _MigrateOldWorkTreeGitDir(cls, dotgit, project=None):
dotgit,
)

def _MigrateOldSubmoduleDir(self):
"""Move the old bare checkout in 'subprojects' to 'modules'
as bare checkouts of submodules are now in 'modules' dir.
"""
subprojects = os.path.join(self.parent.gitdir, "subprojects")
if not platform_utils.isdir(subprojects):
return

modules = os.path.join(self.parent.gitdir, "modules")
old = self.gitdir
new = os.path.splitext(self.gitdir.replace(subprojects, modules))[0]

if all(map(platform_utils.isdir, [old, new])):
platform_utils.rmtree(old, ignore_errors=True)
else:
os.makedirs(modules, exist_ok=True)
platform_utils.rename(old, new)
self.gitdir = new
self.UpdatePaths(self.relpath, self.worktree, self.gitdir, self.objdir)
if platform_utils.isdir(subprojects) and not os.listdir(subprojects):
platform_utils.rmtree(subprojects, ignore_errors=True)

def _get_symlink_error_message(self):
if platform_utils.isWindows():
return (
Expand Down