Skip to content

Instantly share code, notes, and snippets.

@netfeed
Last active December 13, 2022 13:05
Show Gist options
  • Select an option

  • Save netfeed/16bbf23c908226008313c2161a2362df to your computer and use it in GitHub Desktop.

Select an option

Save netfeed/16bbf23c908226008313c2161a2362df to your computer and use it in GitHub Desktop.
#!/usr/bin/env python
import argparse
import sys
import shlex
import subprocess
import os
import os.path
def get_command(branch=None, grep=None):
b = ''
g = ''
if grep:
g = "--grep '%s'" %(grep) if grep else ''
b = branch if branch else ''
else:
b = '--no-merges %s ^develop' %(branch)
s = "git log %s --pretty='format:%%h' --reverse --no-merges %s" %(g, b)
return shlex.split(s)
def get_hashes(branch=None, grep=None):
command = get_command(branch, grep)
process = subprocess.Popen(
command,
universal_newlines=True,
stdout=subprocess.PIPE,
stdin=subprocess.PIPE)
stdout, stderr = process.communicate()
if stderr:
raise OSError(stderr)
return stdout.splitlines()
def get_options():
parser = argparse.ArgumentParser(
prog='Recursive Cherry Pick')
parser.add_argument('--grep')
parser.add_argument('--continue', action='store_true', dest='cont')
parser.add_argument('--abort', action='store_true')
parser.add_argument('--dry-run', action='store_true')
parser.add_argument('branch', nargs='?')
args = parser.parse_args()
if args.cont or args.abort:
return (None, None, args.dry_run, args.cont, args.abort)
if args.grep is None and args.branch is None:
raise OSError("at least one of branch and grep is needed")
return (args.branch, args.grep, args.dry_run, False, False)
def write_hashes(filename, hashes):
with open(filename, 'w') as f:
f.writelines(l + '\n' for l in hashes)
def read_hashes(filename):
with open(filename, 'r') as f:
lines = f.readlines()
return [l.strip() for l in lines]
def start(branch, grep, filename):
if branch is None and grep is None:
raise OSError("at least one of branch and grep is needed")
hashes = get_hashes(branch, grep)
write_hashes(filename, hashes)
def abort_prog(filename):
os.remove(filename)
def continue_cherry_pick():
process = subprocess.Popen(['git', 'status'], stdout=subprocess.PIPE, stdin=subprocess.PIPE)
stdout, stderr = process.communicate()
if 'You are currently cherry-picking' not in str(stdout):
return
cont = subprocess.Popen(['git', 'cherry-pick', '--continue'], stdout=subprocess.PIPE, stdin=subprocess.PIPE)
stdout, stderr = cont.communicate()
if cont.returncode != 0:
raise OSError(str(stdout))
def cherry_pick(filename, dry_run):
hashes = read_hashes(filename)
if not hashes:
abort_prog(filename)
return
current = hashes.pop(0)
dr = '-n' if dry_run else ''
s = 'git cherry-pick %s %s' %(dr, current)
write_hashes(filename, hashes)
command = shlex.split(s)
process = subprocess.Popen(
command,
stdout=subprocess.PIPE,
stdin=subprocess.PIPE)
stdout, stderr = process.communicate()
if stdout:
print(stdout)
cherry_pick(filename, dry_run)
else:
print(stderr)
sys.exit(1)
filename = '/tmp/rec-cherry-pick'
branch, grep, dry_run, cont, abort = get_options()
if not os.path.isfile(filename):
start(branch, grep, filename)
cont = True
elif not cont and not abort:
raise OSError("--continue or --abort needed as there's an ongoing process")
if abort:
abort_prog(filename)
elif cont:
continue_cherry_pick()
cherry_pick(filename, dry_run)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment