codingame_tools.contribution_manager.git_repo¶
git_repo
¶
Low-level git plumbing wrapper backing the git-based contribution repo (main/server/
version-data branches, in a decoupled --git-dir/--work-tree layout--see
codingame_tools.contribution_manager.manager for how these are used, and layout.py for the
branch/tag naming constants).
The only module in this package that shells out to git (subprocess, not GitPython--matches
the plain subprocess.run pattern already used elsewhere in this codebase for external tools,
e.g. the old tree_diff.compute_diff3_merge/merge_tools.launch_merge_tool, both superseded by
this). Every invocation passes --git-dir/--work-tree explicitly (and sets cwd to the work
tree too, for git versions/commands sensitive to it)--cwd-based discovery is never relied on,
since the whole point of this layout is that data/ itself carries no .git marker a human's
plain git command could ever find (see cg contribution git, in the CLI, for how a human
reaches this repo directly).
FALLBACK_IDENTITY_EMAIL
module-attribute
¶
FALLBACK_IDENTITY_EMAIL = 'codingame-tools@localhost'
Author/committer identity used only when git can't resolve one of its own.
.meta/'s repository is local, gitignored scaffolding whose commits are never pushed anywhere,
so the identity on them carries no meaning--but git commit still refuses to run without one.
Git normally auto-detects user@host when nothing is configured, which is why this rarely bites
interactively; it fails on a machine whose hostname has no domain (fv-az123.(none) on a GitHub
Actions runner) or wherever user.useConfigOnly is set. Without this fallback, cg contribution
simply doesn't work for anyone who has never run git config --global user.email.
CgGitError
¶
CgGitError(argv, returncode, stderr)
Bases: Exception
Raised when a git invocation fails (non-zero exit)--wraps the exact argv and stderr for
introspection, structured enough to be useful without needing a library for it.
Source code in codingame_tools/contribution_manager/git_repo.py
35 36 37 38 39 | |
CgGitRepo
¶
CgGitRepo(git_dir, work_tree)
One git repository, addressed by its (possibly work-tree-external) git_dir/work_tree
pair. work_tree is always the contribution's data/ directory in practice--the checkout
of the main branch. server/version-data are never checked out; every method that
writes to them does so via plumbing (a scratch index, or a single-blob tree for
version-data), never touching HEAD, the real index, or anything under work_tree.
Source code in codingame_tools/contribution_manager/git_repo.py
100 101 102 103 | |
set_head
¶
set_head(branch)
Point HEAD at refs/heads/<branch>--used once, right after init_repo(), so a fresh
repo's "currently checked out branch" is unambiguously MAIN_BRANCH_NAME regardless of
git's own init.defaultBranch configuration.
Source code in codingame_tools/contribution_manager/git_repo.py
165 166 167 168 169 | |
rev_parse
¶
rev_parse(*args, check=True)
git rev-parse <args>, stripped. Returns None (rather than raising) if check=False
and the command fails--used for existence checks like rev_parse("--verify",
"MERGE_HEAD", check=False).
Source code in codingame_tools/contribution_manager/git_repo.py
171 172 173 174 175 176 177 178 | |
resolve_ref
¶
resolve_ref(ref)
The commit SHA ref (a branch/tag name) currently points at, or None if it doesn't
exist.
Source code in codingame_tools/contribution_manager/git_repo.py
180 181 182 183 | |
delete_ref
¶
delete_ref(ref)
Delete ref (e.g. refs/heads/server) if it exists--a no-op if it doesn't. The
underlying commit objects aren't pruned (no gc/prune is ever run by this class), so
anything only reachable through the deleted ref remains locally inspectable by SHA
(or via tags--delete_ref never touches those) until a real git gc eventually
collects it.
Source code in codingame_tools/contribution_manager/git_repo.py
188 189 190 191 192 193 194 195 196 | |
reset_index_to
¶
reset_index_to(sha)
Move the branch HEAD currently points at (always main, by construction) to sha,
and reset the real index to match its tree--leaving the working tree untouched (git
reset <sha>, a "mixed" reset). Used after directly building a commit whose tree
already matches what's on disk (import_()/push(), both of which build their tree
via a scratch index--see write_tree_from_dir--that never touches the real index at
all). Without this, the real index is left stale (empty, for a fresh repo) relative to
main's new tip; that doesn't matter for anything routed through checkout_all()
(read-tree --reset -u resyncs it), but a later real git merge reads the index
directly and misbehaves against a stale one--confirmed by direct testing.
Source code in codingame_tools/contribution_manager/git_repo.py
198 199 200 201 202 203 204 205 206 207 208 | |
tag
¶
tag(name, target)
Create (or overwrite) a lightweight tag--used for server.<version>/
version-data.<version>, both purely informational/addressing conveniences, not
something that needs annotation.
Source code in codingame_tools/contribution_manager/git_repo.py
210 211 212 213 214 | |
merge_base
¶
merge_base(a, b)
The best common ancestor of a and b, or None if they share no history (shouldn't
happen for main/server, which always share import_()'s initial commit).
Source code in codingame_tools/contribution_manager/git_repo.py
216 217 218 219 220 221 222 | |
write_tree_from_dir
¶
write_tree_from_dir(source_dir)
Snapshot whatever's currently on disk at source_dir into a tree object, via a scratch
index--never touches the real index, HEAD, or (unless source_dir is self.work_tree)
the real working tree. Used both to build server's tree from main's live content
after a successful push (source_dir=self.work_tree, i.e. write_tree_from_worktree())
and to build it from freshly-fetched content that was never materialized into the
real working tree at all (source_dir is a throwaway temp dir--see fetch()).
Source code in codingame_tools/contribution_manager/git_repo.py
230 231 232 233 234 235 236 237 238 239 240 241 | |
write_tree_from_worktree
¶
write_tree_from_worktree()
write_tree_from_dir(self.work_tree)--see there.
Source code in codingame_tools/contribution_manager/git_repo.py
243 244 245 | |
write_tree_single_file
¶
write_tree_single_file(filename, blob_sha)
Build a tree containing exactly one file (filename -> blob_sha)--used for
version-data's commits, which are never more than contribution-version-data.json.
Source code in codingame_tools/contribution_manager/git_repo.py
247 248 249 250 251 | |
read_file_at
¶
read_file_at(ref, path)
The content of path as it exists in ref's tree (e.g. read_file_at("server",
"cover.png")), or None if path doesn't exist there. Used to reuse a previously-fetched
cover image's bytes straight from the object database, without needing a live cached
file anywhere on disk (see fetch()'s cover-reuse logic).
Source code in codingame_tools/contribution_manager/git_repo.py
266 267 268 269 270 271 272 273 274 | |
read_trailers
¶
read_trailers(commit)
The git trailers (Key: Value lines) on commit's message--via git interpret-
trailers --parse, robust to trailer-format edge cases (folding, repeated keys, etc.)
we don't want to hand-parse ourselves.
Source code in codingame_tools/contribution_manager/git_repo.py
276 277 278 279 280 281 282 283 284 285 286 287 | |
commit_worktree
¶
commit_worktree(message)
add -A && commit -m <message> against the real working tree--the only method here
that touches the real index/HEAD, always on whatever branch is currently checked out
(always main, by construction--server/version-data are never checked out).
Source code in codingame_tools/contribution_manager/git_repo.py
291 292 293 294 295 296 297 298 | |
restage_and_amend_if_dirty
¶
restage_and_amend_if_dirty()
If the real working tree has any uncommitted changes relative to HEAD (staged or
not), stage all of them and fold them into HEAD's existing commit (add -A && commit
--amend --no-edit)--parents (so a merge commit's two parents survive intact) and
message are both left untouched, only the tree changes. Used right after a merge
commit (auto-committed by a clean git merge, or just made by merge_continue()) to
fold in a content-preserving renormalize_test_case_dirs() cleanup, so it doesn't need
its own separate commit. A no-op (returns False) if nothing changed.
Returns:
-
bool–Whether anything was actually amended.
Source code in codingame_tools/contribution_manager/git_repo.py
300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 | |
checkout_all
¶
checkout_all(ref)
Reset the index, the working tree, and remove untracked files/directories, so the
working tree ends up matching ref's content exactly--without moving HEAD or
creating a commit. Used by discard_local()/rebase()'s fast-forward/
merge_discard_local().
Two things confirmed necessary by direct testing, not just one: read-tree --reset -u
(rather than checkout <ref> -- ., which only adds/updates paths present in ref, and
never removes a tracked path that's absent there) handles files that are tracked but
shouldn't be anymore--but it leaves untracked files (never git add-ed at all) alone
entirely, since that's simply outside its scope. clean -fd handles that remaining
case. Together, nothing extra survives, tracked or not.
Source code in codingame_tools/contribution_manager/git_repo.py
319 320 321 322 323 324 325 326 327 328 329 330 331 332 | |
merge_branch
¶
merge_branch(branch)
git merge <branch> against the real working tree.
Returns:
-
bool–True if the merge completed cleanly (a new merge commit was made, or it was already
-
bool–up to date/fast-forwarded); False if it stopped with conflicts (
MERGE_HEADnow -
bool–exists--resolve and call
merge_continue(), ormerge_abort()).
Source code in codingame_tools/contribution_manager/git_repo.py
334 335 336 337 338 339 340 341 342 343 | |
merge_continue
¶
merge_continue()
stage everything currently on disk (add -A--so hand-
editing a conflicted file and running this directly, without git mergetool/manually
git add-ing it first, works the same way it always did in the old marker-scan-based
design) and commit.
Whether a path is conflicted at all is still git's own authoritative index-stage
tracking (status_conflicts()), not content-scanning--but add -A blindly stages
whatever's on disk regardless of content, so a leftover <<<<<<< marker in a path git
does consider conflicted would otherwise get silently committed as real content if
the user forgot to actually resolve it (only relevant for hand-editing; git mergetool
itself already only stages a path once its own diff view reports no conflict left).
So: check current content of just the still-unmerged paths for a leftover marker
first, before staging anything--narrower and cheaper than the old design's whole-tree
content scan, and it can't false-positive on a path that was never conflicted.
Raises:
-
CgGitError–if unresolved conflict markers remain in a still-unmerged path, or (from git itself) if
commitrefuses for any other reason.
Source code in codingame_tools/contribution_manager/git_repo.py
348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 | |
status_conflicts
¶
status_conflicts()
Paths with unresolved merge conflicts (unmerged index stages)--authoritative, unlike
the old content-based <<<<<<< marker scan it replaces.
Source code in codingame_tools/contribution_manager/git_repo.py
383 384 385 386 387 | |
diff_text
¶
diff_text(*refs)
git diff <refs...>--one ref diffs it against the working tree, two diffs between
them.
Source code in codingame_tools/contribution_manager/git_repo.py
389 390 391 392 | |
is_inside_existing_repo
¶
is_inside_existing_repo(path)
Whether path is already inside some other git repository's working tree--checked purely
via plain cwd-based discovery (deliberately: this is the one place that's appropriate,
since we're asking "if I ran plain git here, would it find something"). Used once, at
import_() time, to decide where this contribution's own (unrelated) git-dir should live--
see the module docstring and codingame_tools.contribution_manager.layout.
path itself usually doesn't exist yet (a brand-new cg contribution import target)--the
check walks up to the nearest existing ancestor first, since cwd has to be a real
directory.
Source code in codingame_tools/contribution_manager/git_repo.py
42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 | |
init_repo
¶
init_repo(git_dir, work_tree)
Initialize a new, non-bare git repository whose metadata lives at git_dir and whose
working tree is work_tree--the two need not be related in any way on disk (no .git
entry is ever created inside work_tree).
Source code in codingame_tools/contribution_manager/git_repo.py
65 66 67 68 69 70 71 72 73 74 | |