I love git
on the command line and I actually never use a GUI to navigate git
branches. But sometimes, I need scripting to make abstractions that make life more convenient. What often happens is that I need to go back to the "main" branch. I write main
in quotation marks because it's not always called main
. Sometimes it's called master
. And it's tedious to have to remember which one is the default.
So I wrote a script called Gcm
:
#!/usr/bin/env python3
import subprocess
def run(*args):
default_branch = get_default_branch()
current_branch = get_current_branch()
if default_branch != current_branch:
checkout_branch(default_branch)
else:
print(f"Already on {default_branch}")
return 1
def checkout_branch(branch_name):
subprocess.run(f"git checkout {branch_name}".split())
def get_default_branch():
res = subprocess.run(
"git config --worktree --get git-checkout-default.default-branch".split(),
check=False,
capture_output=True,
)
if res.returncode == 0:
return res.stdout.decode("utf-8").strip()
origin_name = "origin"
res = subprocess.run(
f"git remote show {origin_name}".split(),
check=True,
capture_output=True,
)
for line in res.stdout.decode("utf-8").splitlines():
if line.strip().startswith("HEAD branch:"):
default_branch = line.replace("HEAD branch:", "").strip()
subprocess.run(
f"git config --worktree git-checkout-default.default-branch "
f"{default_branch}".split(),
check=True,
)
return default_branch
raise NotImplementedError(f"No remote called {origin_name!r}")
def get_current_branch():
res = subprocess.run("git branch --show-current".split(), capture_output=True)
for line in res.stdout.decode("utf-8").splitlines():
return line.strip()
res = subprocess.run("git show -s --pretty=%d HEAD".split(), capture_output=True)
for line in res.stdout.decode("utf-8").splitlines():
return line.strip()
raise NotImplementedError("Don't know what to do!")
if __name__ == "__main__":
import sys
sys.exit(run(*sys.argv[1:]))
It ain't pretty or a spiffy one-liner, but it works. It assumes that the repo has a remote called origin
which doesn't matter if it's the upstream or your fork. Put this script into a file called ~/bin/Gcm
and run chmox +x ~/bin/Gcm
.
Now, whenever I want to go back to the main branch I type Gcm
and it takes me there.
It might seem silly, and it might not be for you, but I love it and use it many times per day. Perhaps by sharing this tip, it'll inspire someone else to set up something similar for themselves.
Why it's spelled with an uppercase G
I have a pattern (or rule?) that all scripts that I write myself are always capitalized like that. It avoids clashes with stuff I install with brew
or other bash/zsh aliases.
For example:
ls -l ~/bin/RemoteVSCodePeterbecom.sh ls -l ~/bin/Cleanupfiles ls -l ~/bin/RandomString.py
UPDATE: July 2021
Jason @silverjam Mobarak on Twitter suggested an optimization. I've incorporated his suggestion into the original blog post above. See this twitter thread.
Essentially, it now uses git config --worktree
to set and get the outcome of the default branch name so the next time it's needed it does not depend on network.
But watch out, if you, like me, change the default branch of some existing repos from master
to main
; then you'll need to run: git config --worktree --unset git-checkout-default.default-branch
.
Comments
Thank you for sharing this cool tool.
I had to adapt it a little bit because I am using German language settings where `HEAD branch` is called `Hauptbranch`.
It's a shame that git makes it so complicated to find out about the head branch.