93 lines
2.5 KiB
Python
93 lines
2.5 KiB
Python
"""Git operations for update checking and pulling. Stdlib only."""
|
|
|
|
import os
|
|
|
|
from . import gitwin
|
|
from .util import run, REPO_ROOT, die
|
|
|
|
|
|
def _git(args, capture=True, check=True):
|
|
git = gitwin.find_git()
|
|
if not git:
|
|
die(
|
|
"git was not found. On Windows run `python -m manager sync` to "
|
|
"install a local copy; elsewhere install git with your package "
|
|
"manager (or xcode-select --install on macOS)."
|
|
)
|
|
# skip LFS payloads — the repo may carry LFS files not needed at runtime
|
|
env = os.environ.copy()
|
|
env["GIT_LFS_SKIP_SMUDGE"] = "1"
|
|
return run([git] + args, cwd=REPO_ROOT, capture=capture, check=check, env=env)
|
|
|
|
|
|
def current_branch():
|
|
_, out = _git(["rev-parse", "--abbrev-ref", "HEAD"])
|
|
return out
|
|
|
|
|
|
def current_commit():
|
|
_, out = _git(["rev-parse", "--short", "HEAD"])
|
|
return out
|
|
|
|
|
|
def is_dirty():
|
|
_, out = _git(["status", "--porcelain"])
|
|
# untracked files don't block updates; modified/staged tracked files do
|
|
for line in (out or "").splitlines():
|
|
if not line.startswith("??"):
|
|
return True
|
|
return False
|
|
|
|
|
|
def fetch():
|
|
code, _ = _git(["fetch", "--quiet"], capture=False, check=False)
|
|
return code == 0
|
|
|
|
|
|
def upstream():
|
|
code, out = _git(
|
|
["rev-parse", "--abbrev-ref", "--symbolic-full-name", "@{u}"], check=False
|
|
)
|
|
if code != 0:
|
|
return None
|
|
return out
|
|
|
|
|
|
def behind_count():
|
|
"""Commits the local branch is behind its upstream. None if no upstream."""
|
|
up = upstream()
|
|
if not up:
|
|
return None
|
|
code, out = _git(["rev-list", "--count", "HEAD..@{u}"], check=False)
|
|
if code != 0 or out is None:
|
|
return None
|
|
try:
|
|
return int(out)
|
|
except ValueError:
|
|
return None
|
|
|
|
|
|
def remote_commit():
|
|
code, out = _git(["rev-parse", "--short", "@{u}"], check=False)
|
|
return out if code == 0 else None
|
|
|
|
|
|
def incoming_log(limit=15):
|
|
code, out = _git(
|
|
["log", "--oneline", "HEAD..@{u}", "--max-count=%d" % limit], check=False
|
|
)
|
|
if code != 0 or not out:
|
|
return []
|
|
return out.splitlines()
|
|
|
|
|
|
def pull_ff():
|
|
"""Fast-forward pull. Dies with guidance on failure."""
|
|
code, _ = _git(["pull", "--ff-only"], capture=False, check=False)
|
|
if code != 0:
|
|
die(
|
|
"git pull --ff-only failed. Your branch has local commits or "
|
|
"conflicts with the remote. Resolve manually (git stash / git rebase) "
|
|
"and re-run the update."
|
|
)
|