git-branchp 1.5 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152
  1. #!/usr/bin/env python3
  2. # vim: set noet sw=4 ts=4:
  3. import pathlib
  4. import subprocess
  5. import sys
  6. def main():
  7. delete = len(sys.argv) == 2 and sys.argv[1] == '-d'
  8. remotes = all_remotes()
  9. to_delete = []
  10. print('will delete:')
  11. for local, remote in iter_locals():
  12. if remote not in remotes:
  13. to_delete.append(local)
  14. sys.stdout.buffer.write(b'\t' + local)
  15. print()
  16. if delete and len(to_delete) > 0:
  17. if has_branchless():
  18. run([b'git', b'hide'] + [b'draft()::' + branch for branch in to_delete])
  19. else:
  20. run([b'git', b'branch', b'-D'] + to_delete)
  21. def all_remotes():
  22. refs = run(['git', 'for-each-ref', '--format=%(refname:short)', 'refs/remotes'], capture_output=True)
  23. return frozenset(refs.stdout.rstrip(b'\n').split(b'\n'))
  24. def iter_locals():
  25. refs = run(['git', 'for-each-ref', '--format=%(refname:short) %(upstream)', 'refs/heads'],
  26. capture_output=True)
  27. for line in refs.stdout.split(b'\n'):
  28. if not line:
  29. continue
  30. local, remote = line.split(b' ', 1)
  31. if not remote:
  32. continue
  33. assert remote.startswith(b'refs/remotes/')
  34. remote = remote.removeprefix(b'refs/remotes/')
  35. yield local, remote
  36. def has_branchless() -> bool:
  37. repo_root = run(['git', 'rev-parse', '--show-toplevel'], capture_output=True, text=True).stdout.rstrip('\n')
  38. return pathlib.Path(repo_root, '.git/branchless/config').is_file()
  39. def run(cmd, capture_output=False, text=None):
  40. return subprocess.run(cmd, capture_output=capture_output, check=True, text=text)
  41. if __name__ == '__main__':
  42. main()