Skip to content

Instantly share code, notes, and snippets.

@balazsbotond
Last active June 16, 2024 11:01
Show Gist options
  • Save balazsbotond/31da6b3b07414ed31cd86c910689c5df to your computer and use it in GitHub Desktop.
Save balazsbotond/31da6b3b07414ed31cd86c910689c5df to your computer and use it in GitHub Desktop.
Git Branch Cleanup - Asks if you want to delete local git branches that don't exist on any remote
#!/usr/bin/env python
import re
import os
import sys
from subprocess import check_output, check_call, call, STDOUT
BLUE = '\033[94m'
GRAY = '\033[90m'
ENDC = '\033[0m'
"""asks a yes/no question and returns a boolean indicating the user's answer"""
def yes_or_no(question):
while True:
reply = str(raw_input(question + ' [yN]: ')).strip().lower()
if reply[:1] == 'y':
return True
return False
"""log a gray message to the console"""
def log(message):
print '%s%s%s' % (GRAY, message, ENDC)
# check if we are inside a git repo
if call(['git', 'rev-parse', '--is-inside-work-tree'], stderr=STDOUT, stdout=open(os.devnull, 'w')) != 0:
print 'This is not a git repository.'
sys.exit(1)
# fetch and delete orphaned remote tracking branches
# TODO: do this for all remotes
prune_output = check_output(['git', 'fetch', '--prune', 'origin'], stderr=STDOUT).splitlines()
deleted_branch_lines = [line for line in prune_output if line.startswith(' x [deleted]')]
to_prune = [re.search(r' x [deleted].*?-> (.*)').group(1) for line in deleted_branch_lines]
for branch in to_prune:
log('Pruned %s.' % branch)
# get local and remote branches
local_output = check_output(['git', 'branch']).splitlines()
remote_output = check_output(['git', 'branch', '--remote']).splitlines()
# get branch names from command output
local_branches = {re.search(r'[\*\s]*(.*)', branch).group(1) for branch in local_output}
remote_branches = {re.search(r'[\*\s]*.*?/(.*)', branch).group(1) for branch in remote_output}
candidates = local_branches - remote_branches # local branches that do not exist on any remote
# no branches found to clean up
if not candidates and not to_prune:
print 'Nothing to do.'
# ask if the user wants to delete each branch
for branch in candidates:
if yes_or_no('Delete %s%s%s?' % (BLUE, branch, ENDC)):
check_call(['git', 'branch', '-d', branch], stdout=open(os.devnull, 'w'))
log('Deleted %s.' % branch)
else:
log('Skipped %s.' % branch)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment