Files changed: - CHANGES.md - VERSION - tools/CONTRACT.md - tools/chemenu/commands/git_publish.py - tools/chemenu/tests/test_git_publish.py
This commit is contained in:
1 parent
87719bf396
commit
9ef021bea1
5 files changed
+220
-7
No files matched your search
@@ -348,12 +348,22 @@ def _changed_files(paths: list[str]) -> list[str]:
|
||||
|
||||
|
||||
def current_branch() -> Optional[str]:
|
||||
"""The checked-out branch, or None in a detached HEAD / non-checkout."""
|
||||
result = _run(["git", "rev-parse", "--abbrev-ref", "HEAD"])
|
||||
"""The checked-out branch, or None in a detached HEAD / non-checkout.
|
||||
|
||||
`git symbolic-ref` and not `git rev-parse --abbrev-ref`, because the two disagree on
|
||||
exactly the state a brand-new instance is in. On an *unborn* branch - `git init -b main`,
|
||||
no commit yet, which is where `instructions/setup-instance.md` calls `publish` for the
|
||||
first time - `HEAD` points at a ref that does not resolve, so `rev-parse` exits 128 and
|
||||
the caller cannot tell it apart from a real detached HEAD. `symbolic-ref` reads what HEAD
|
||||
*names* rather than what it resolves to, so it answers `main` there, empty (exit 1) on a
|
||||
detached HEAD, and the branch name in the ordinary case - the distinction the branch check
|
||||
below actually needs.
|
||||
"""
|
||||
result = _run(["git", "symbolic-ref", "--short", "-q", "HEAD"])
|
||||
if result.returncode != 0:
|
||||
return None
|
||||
name = result.stdout.strip()
|
||||
return None if not name or name == "HEAD" else name
|
||||
return name or None
|
||||
|
||||
|
||||
def branch_mismatch_message(checked_out: Optional[str], branch: str) -> str:
|
||||
@@ -877,13 +887,39 @@ def reconcile(remote: str, branch: str, confirm_rebase: Optional[str] = None) ->
|
||||
)
|
||||
|
||||
|
||||
def remote_lacks_branch(remote: str, branch: str) -> bool:
|
||||
"""Whether `remote` is reachable *and* has no `branch` on it - the brand-new instance whose
|
||||
remote repository was created empty, as distinct from a remote nobody can reach at all.
|
||||
|
||||
`git ls-remote --exit-code` separates the two by exit code alone: 0 when the ref is there,
|
||||
2 when the remote answered and does not have it, 128 when the remote could not be reached
|
||||
(a bad URL, no network, refused auth) or is not configured. Reading the code rather than the
|
||||
message is not a style preference - git translates `fatal: couldn't find remote ref main`,
|
||||
so matching on the text would work only in an English locale.
|
||||
"""
|
||||
return _run(["git", "ls-remote", "--exit-code", remote, branch]).returncode == 2
|
||||
|
||||
|
||||
def _has_commits(branch: str) -> bool:
|
||||
"""Whether `branch` points at anything yet - false on the unborn branch of a fresh
|
||||
`git init`, where there is nothing that could be ahead of anything."""
|
||||
result = _run(["git", "rev-list", "--count", "-n", "1", branch])
|
||||
return result.returncode == 0 and result.stdout.strip() not in ("", "0")
|
||||
|
||||
|
||||
def _local_ahead_of_remote(remote: str, branch: str) -> bool:
|
||||
"""Whether `branch` currently has a commit `<remote>/<branch>` lacks - true right after a
|
||||
stranded commit from a previous failed push, and also right after `reconcile` rebases local
|
||||
work on top of a moved remote. Used to decide whether an otherwise-empty working tree still
|
||||
has something to push."""
|
||||
if not remote_ref_exists(remote, branch):
|
||||
return False
|
||||
# No tracking ref, which `remote_ref_exists` cannot tell apart from an unreachable
|
||||
# remote - and the very first publish of an instance lands here. A remote that answers
|
||||
# and simply has no such branch yet means every local commit is unpushed, which is
|
||||
# precisely the stranded state above; an unreachable one keeps the old answer, so an
|
||||
# offline or local-only instance sees no new behaviour and the eventual `git push`
|
||||
# (when there is something to stage) still reports the real error.
|
||||
return remote_lacks_branch(remote, branch) and _has_commits(branch)
|
||||
result = _run(["git", "rev-list", "--count", f"{remote}/{branch}..{branch}"])
|
||||
return result.returncode == 0 and result.stdout.strip() not in ("", "0")
|
||||
|
||||
|
||||
Reference in new issue
Block a user