Skip to content

Instantly share code, notes, and snippets.

@orez-
Last active October 2, 2021 16:18
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save orez-/8e119eb09aeb43019881 to your computer and use it in GitHub Desktop.
Save orez-/8e119eb09aeb43019881 to your computer and use it in GitHub Desktop.
Run through your git stashes, either committing them to branches or deleting them.
#!/usr/bin/env python2.7
import collections
import os
import re
import readline
import subprocess
tty_bold = '\x1b[1m'
tty_af = '\x1b[{}m'.format
tty_clear = '\x1b[0m'
tty_bold_blue = tty_bold + tty_af(34)
tty_bold_red = tty_bold + tty_af(31)
stash_ref = 'stash@{{{}}}'.format
class AdvanceStashNum(Exception):
pass
class Quit(Exception):
pass
def has_local_changes():
return bool(subprocess.check_output(['git', 'status', '--porcelain']))
def option_drop(stash_num, **_):
"""drop this stash"""
if subprocess.call(['git', 'stash-applied', stash_ref(stash_num)]) != 0:
if raw_input("Stash may not be applied. Drop anyway? [y/N] ") != 'y':
return
subprocess.call(['git', 'stash', 'drop', stash_ref(stash_num)])
def option_branch(stash_num, can_save_branch, **_):
"""commit this stash to a separate branch and delete it"""
stash_name = stash_ref(stash_num)
if not can_save_branch:
print(tty_bold_red + "ERROR - Can't commit branches with unstaged files!." + tty_clear)
return
commit_msg_file = '.git/COMMIT_EDITMSG'
branch_name = 'stash/__TEMP_STASH__'
subprocess.call(['git', 'checkout', '-b', branch_name])
subprocess.call(['git', 'stash', 'apply', stash_name])
subprocess.call(['git', 'add', '.'])
if subprocess.call(['git', 'commit', '-n']):
subprocess.call(['git', 'reset', 'HEAD'])
subprocess.call(['git', 'checkout', '.'])
subprocess.call(['git', 'clean', '-f'])
subprocess.call(['git', 'checkout', '-'])
subprocess.call(['git', 'branch', '-d', branch_name])
return
# Change the branch name to the first line of the commit message.
with open(commit_msg_file, 'r') as f:
subject = next(line for line in f if line and line[0] != '#')
subject = re.sub(r'\s+', '_', subject.strip())
new_branch_name = 'stash/' + re.sub(r'\W', '', subject).lower()
subprocess.call(['git', 'branch', '-m', new_branch_name])
subprocess.call(['git', 'checkout', '-'])
subprocess.call(['git', 'stash', 'drop', stash_name])
def option_skip(**_):
"""take no action on this stash"""
raise AdvanceStashNum
def option_help(**_):
"""print help"""
message = getattr(option_help, 'help_message', None)
if not message:
message = option_help.help_message = (
tty_bold_red +
'\n'.join(
'{} - {}'.format(k, v.__doc__)
for k, v in options.iteritems()
if k
) + tty_clear
)
print(message)
return True
def option_apply(stash_num, **_):
"""apply; apply the stash and take no further action"""
subprocess.call(['git', 'stash', 'apply', stash_ref(stash_num)])
raise Quit
def option_quit(**_):
"""quit; take no further action on remaining stashes"""
raise Quit
options = collections.OrderedDict([
('d', option_drop),
('b', option_branch),
('s', option_skip),
('a', option_apply),
('q', option_quit),
('?', option_help),
('', option_help),
])
def git_stash_inbox():
can_save_branch = not has_local_changes()
if not can_save_branch:
print(
tty_bold_red +
"WARNING - Can't backup stashes as branches with local changes.\n"
"Resolve local changes to backup stashes as branches." +
tty_clear
)
action_display = "{}Action on this stash [{}]? {}".format(
tty_bold_blue,
','.join(o for o in options if o),
tty_clear,
)
stash_num = 0
once = False
while True:
with open(os.devnull, 'w') as fnull:
output = subprocess.call(
['git', 'stash', 'show', '-p', stash_ref(stash_num)],
stderr=fnull,
)
if output not in {0, 141}:
# Reached end of stash list.
if not once:
print("No stashes found.")
break
once = True
action = raw_input(action_display)
try:
options.get(action, lambda **_: None)(
can_save_branch=can_save_branch,
stash_num=stash_num,
)
except AdvanceStashNum:
stash_num += 1
except Quit:
break
if __name__ == '__main__':
try:
git_stash_inbox()
except (KeyboardInterrupt, EOFError):
print()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment