import os
from pathlib import Path
import re
+import shlex
import subprocess
import sys
return cls.BAD + msg + cls.NORMAL
-def dbg(msg):
+def dbg(*args, **kwargs):
"""Print a debug |msg|."""
if DEBUG:
- print(msg, file=sys.stderr)
+ print(*args, file=sys.stderr, **kwargs)
def fatal(msg):
kwargs.setdefault('stderr', subprocess.STDOUT)
kwargs.setdefault('encoding', 'utf-8')
if DEBUG:
- print('+', 'git', *args)
- return subprocess.run(['git'] + args, **kwargs)
+ dbg('+', 'git', shlex.join(args))
+ ret = subprocess.run(['git'] + args, **kwargs)
+ if DEBUG:
+ if ret.stdout:
+ dbg(ret.stdout.rstrip())
+ if ret.stderr:
+ dbg('stderr =', ret.stderr)
+ if ret.returncode:
+ dbg('++ exit', ret.returncode)
+ return ret
@functools.lru_cache(maxsize=None)
return Path(output).exists()
+@functools.lru_cache(maxsize=None)
+def top_dir() -> Path:
+ """Find the top dir of the git checkout."""
+ output = git(['rev-parse', '--show-toplevel'], stderr=subprocess.PIPE).stdout.strip()
+ return Path(output).resolve()
+
+
+@functools.lru_cache(maxsize=None)
+def git_dir() -> Path:
+ """Find the internal git dir for this project."""
+ output = git(['rev-parse', '--git-dir']).stdout.strip()
+ return Path(output).resolve()
+
+
+@functools.lru_cache(maxsize=None)
+def worktree_is_local(worktree: str) -> bool:
+ """See whether |worktree| is the cwd git repo."""
+ if not worktree:
+ return True
+
+ # NB: worktree path is supposed to be absolute from for-each-ref, but it's
+ # not always, so we have to resolve it. https://crbug.com/git/88
+ worktree = (git_dir() / worktree).resolve()
+ return worktree == top_dir()
+
+
class AppendOption(argparse.Action):
"""Append the command line option (with no arguments) to dest.
# Switch to the top dir in case the working dir doesn't exist in every branch.
try:
- topdir = git(['rev-parse', '--show-toplevel'], stderr=subprocess.PIPE).stdout.strip()
+ topdir = top_dir()
except subprocess.CalledProcessError as e:
sys.exit(f'{os.path.basename(sys.argv[0])}: {Path.cwd()}:\n{e}\n{e.stderr.strip()}')
os.chdir(topdir)
if head == '*':
curr_state = branch
local_count += 1
- elif not (worktreepath and worktreepath != topdir):
+ elif worktree_is_local(worktreepath):
local_count += 1
else:
dbg(f'{worktreepath}:{branch}: Skipping branch checked out in diff worktree')
_, worktreepath, branch, tracking, ahead_behind = line.split('|')
# If it's a branch in another worktree, ignore it.
- if worktreepath and worktreepath != topdir:
+ if not worktree_is_local(worktreepath):
+ dbg(f'{worktreepath}:{branch}: Skipping branch checked out in diff worktree')
continue
print(f'{Color.BRACKET}### {Color.GOOD}{branch:{branch_width}}{Color.NORMAL} ',
else:
result = git(['rebase'] + opts.git_options, check=False)
if result.returncode:
- git(['rebase', '--abort'])
+ git(['rebase', '--abort'], check=False)
print(Color.bad('failed') + '\n' + result.stdout.strip())
else:
print(Color.good('OK'))