Skip to content

Instantly share code, notes, and snippets.

@mccutchen
Created June 14, 2018 16:49
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mccutchen/10268ede9bb594d68436906709830ff8 to your computer and use it in GitHub Desktop.
Save mccutchen/10268ede9bb594d68436906709830ff8 to your computer and use it in GitHub Desktop.
#!/usr/bin/env python
import subprocess
import sys
def get_current_branch():
cmd = 'git name-rev --name-only HEAD'.split()
return subprocess.check_output(cmd).strip()
def delete_local_branch(name):
cmd = 'git branch -d {}'.format(name).split()
return subprocess.check_output(cmd).strip()
def delete_remote_branch(name):
remote, name = name.split('/', 1)
cmd = 'git push {} :{}'.format(remote, name).split()
return subprocess.check_output(cmd).strip()
def list_branches(opts=''):
"""
Return a list of branch names that should be safe to delete. The given opts
will be passed into `git branch` and should determine whether to list local
or remote branches.
Local branch output:
$ git branch --merged
INFRA-658-user-cleanup
* master
master-new
new-master
Remote branch output:
$ git branch -r --merged
origin/DISTR-609-scaffold-components-and-fetch-artifacts
origin/HEAD -> origin/master
origin/INFRA-658-user-cleanup
origin/VIDPUB-136-API-GET-project-by-ID-precommit
origin/feeder-worker
origin/master
"""
assert isinstance(opts, basestring)
cmd = 'git branch {} --merged'.format(opts).split()
lines = subprocess.check_output(cmd).splitlines()
names = map(parse_branch_name, lines)
return filter(safe_to_delete, names)
def list_local_branches():
return list_branches()
def list_remote_branches():
return list_branches('-r')
def parse_branch_name(line):
return line.lstrip(' *').split()[0]
def safe_to_delete(name):
return name.split('/')[-1] not in ('master', 'develop', 'HEAD')
def pluralize(xs, suffix='s'):
return suffix if len(xs) > 0 else ''
def get_answer(base_prompt):
answer_map = { 'y': True, 'yes': True, 'n': False, 'no': False}
prompt = '{} [Y/n]: '.format(base_prompt)
while True:
answer = raw_input(prompt).strip().lower() or 'y'
if answer in answer_map:
return answer_map[answer]
def main():
if get_current_branch() != 'master':
print 'Error: git-cleanup must be run from the `master` branch'
return 1
local_branches = list_local_branches()
remote_branches = list_remote_branches()
if local_branches:
count = len(local_branches)
branch_word = 'branch{}'.format(pluralize(local_branches, 'es'))
print 'Found {} fully merged local {}:'.format(count, branch_word)
for branch in local_branches:
print ' - {}'.format(branch)
do_delete = get_answer('Delete {} local {}?'.format(count, branch_word))
if do_delete:
for branch in local_branches:
delete_local_branch(branch)
print
if remote_branches:
count = len(remote_branches)
branch_word = 'branch{}'.format(pluralize(remote_branches, 'es'))
print 'Found {} fully merged remote {}:'.format(count, branch_word)
for branch in remote_branches:
print ' - {}'.format(branch)
do_delete = get_answer('Delete {} remote {}?'.format(count, branch_word))
if do_delete:
for branch in remote_branches:
delete_remote_branch(branch)
if __name__ == '__main__':
sys.exit(main())
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment