git-url 2.3 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879
  1. #!/usr/bin/env python3
  2. from __future__ import annotations
  3. import re
  4. import subprocess
  5. import sys
  6. import typing
  7. import urllib.parse
  8. if typing.TYPE_CHECKING:
  9. from _typeshed import StrOrBytesPath
  10. def oneliner(*args) -> str:
  11. return subprocess.run(args, check=True, capture_output=True, text=True).stdout.strip()
  12. commit = oneliner('git', 'rev-parse', 'HEAD')
  13. local_branch = oneliner('git', 'branch', '--show-current')
  14. tag = tracking_branch = None
  15. if local_branch.startswith('tags/'):
  16. tag = local_branch[5:]
  17. if tag.endswith('^0'):
  18. tag = tag[:-2]
  19. remote_url = oneliner('git', 'config', 'remote.origin.url')
  20. else:
  21. try:
  22. tracking_branch = oneliner('git', 'config', 'branch.%s.merge' % local_branch)
  23. if tracking_branch.startswith('refs/heads/'):
  24. tracking_branch = tracking_branch[len('refs/heads/'):]
  25. else:
  26. raise Exception("can't handle " + tracking_branch)
  27. tracking_remote = oneliner('git', 'config', 'branch.%s.remote' % local_branch)
  28. remote_url = oneliner('git', 'config', 'remote.%s.url' % tracking_remote)
  29. except subprocess.CalledProcessError:
  30. remote_url = oneliner('git', 'config', 'remote.origin.url')
  31. relpath = oneliner('git', 'rev-parse', '--show-prefix')[:-1]
  32. remote_url = re.sub(r'^(.+@.+):(.+)$', r'ssh://\1/\2', remote_url)
  33. if remote_url.endswith('.git'):
  34. remote_url = remote_url[:-len('.git')]
  35. parsed = urllib.parse.urlparse(remote_url)
  36. hostname = parsed.hostname
  37. if hostname.startswith('github.com-'):
  38. hostname = 'github.com'
  39. obj_type = 'tree'
  40. if len(sys.argv) == 2:
  41. obj_type = 'blob'
  42. repo_url = urllib.parse.urlunparse(('https', hostname, parsed.path, '', '', ''))
  43. def url_for(treeish: str) -> str:
  44. dir_url = '%s/%s/%s' % (repo_url, obj_type, treeish)
  45. if relpath:
  46. dir_url = '%s/%s' % (dir_url, relpath)
  47. if len(sys.argv) == 2:
  48. return '%s/%s' % (dir_url, sys.argv[1])
  49. else:
  50. return dir_url
  51. if tag is not None:
  52. print(url_for(tag))
  53. elif tracking_branch is not None:
  54. print(url_for(tracking_branch))
  55. print(url_for(commit))
  56. for remote in oneliner('git', 'remote').splitlines():
  57. try:
  58. default_branch = oneliner('git', 'rev-parse', '--abbrev-ref', remote + '/HEAD').removeprefix(remote + '/')
  59. except subprocess.CalledProcessError as e:
  60. if e.returncode == 128: # not a cloned repo
  61. continue
  62. else:
  63. raise
  64. if default_branch != tracking_branch:
  65. print(url_for(default_branch))