From: Alberto Bertogli <albertito@gmail.com>
To: git@vger.kernel.org
Subject: Commit cherry-picking
Date: Tue, 3 Apr 2007 00:42:37 -0300 [thread overview]
Message-ID: <20070403034234.GB24722@gmail.com> (raw)
[-- Attachment #1: Type: text/plain, Size: 1432 bytes --]
Hi!
I often use darcs, and one feature I miss when I use git is the ability
to do cherry-picking on what I'm about to commit.
It allows me to do small changes to the code when I'm working on
something else, and don't do ugly commits.
I know the proper way to do this would be to have different branches and
all. But that means I have to switch between branches to do quick fixes,
which is an expensive operation in human terms, because I have to stop
thinking about the code and switch branches and so on.
So I wrote two small scripts to do that: git-pcp and git-commit-cp. The
former acts as a helper to the later. Both are attached.
git-pcp takes a diff file, and produces two files: one with the hunks to
apply, and another one with the ones to skip. It asks the user to
select, for each hunk, where to put it.
git-commit-cp is the command to use, which calls git-pcp to do the
cherrypicking, and then applies the corresponding patches.
The implementation of git-pcp should be better (the diff parsing is not
as strong as it should be, although it works for most cases; and the
user interaction sucks, because I don't like UI =), so it's working but
it needs some improvements.
I wanted to ask you if this was an acceptable command to add to git, and
if you had any recommendations or thoughts about the implementation.
Thanks a lot,
Alberto
PS: Is there a way of telling git-diff how many context lines to use?
[-- Attachment #2: git-pcp --]
[-- Type: text/plain, Size: 4026 bytes --]
#!/usr/bin/env python
"""
Patch Cherry-Picking
This scripts takes a patch and asks you which chunks you want to apply. Then
it creates two output files: one with the ones to apply, and one with the ones
to skip.
Note that the output patches are NOT fixed, you might want to run them through
rediff (although git-commit-cp does not need to).
"""
import sys
#
# Classes and main data structures
#
class Diff:
def __init__(self):
self.parts = []
def append(self, part):
self.parts.append(part)
def __str__(self):
s = ''.join((str(i) for i in self.parts))
return s
def __eq__(self, other):
if self.parts == other.parts:
return True
return False
class File:
def __init__(self, fname):
self.fname = fname
self.parts = []
def append(self, part):
self.parts.append(part)
def __str__(self):
s = ''.join((str(i) for i in self.parts))
return s
def __eq__(self, other):
if self.parts == other.parts:
return True
return False
class Hunk:
def __init__(self):
self.parts = []
def append(self, line):
self.parts.append(line)
def __str__(self):
s = ''.join(self.parts)
return s
def __eq__(self, other):
if self.parts == other.parts:
return True
return False
#
# Diff parsing
#
def startswithany(l, *starts):
for s in starts:
if l.startswith(s):
return True
return False
def parse(fd):
diff = Diff()
file = None
hunk = None
current = diff
trailing = []
for line in fd:
if line.startswith('+++ '):
unused, fname = line.strip().split(' ', 1)
if hunk:
file.append(hunk)
hunk = Hunk()
if file:
diff.append(file)
file = File(fname)
for i in trailing:
file.append(i)
file.append(line)
trailing = []
elif line.startswith('@@ '):
if hunk:
file.append(hunk)
hunk = Hunk()
hunk.append(line)
elif startswithany(line, '+', '-', ' ') \
and not line.startswith('---'):
if hunk:
hunk.append(line)
else:
trailing.append(line)
else:
if hunk:
file.append(hunk)
hunk = None
trailing.append(line)
if hunk:
file.append(hunk)
if file:
if trailing:
for i in trailing:
file.append(i)
trailing = []
diff.append(file)
if trailing:
for i in trailing:
diff.append(i)
return diff
#
# Cherry-picking
#
def cherrypick(original, toapply, toskip):
for part in original.parts:
if isinstance(part, File):
print '+++ ', part.fname
happly, hskip = select_parts(part.parts)
print
if happly:
f = File(part.fname)
for i in happly:
f.append(i)
toapply.append(f)
if hskip:
f = File(part.fname)
for i in hskip:
f.append(i)
toskip.append(f)
else:
toapply.append(part)
toskip.append(part)
def ask(prompt, valid_options, default = None, help = ()):
while True:
r = raw_input(prompt)
if not r:
if default:
r = default
else:
continue
if r in valid_options:
return r
elif help and r == help[0]:
print help[1]
else:
print " -- Unknown option"
def select_parts(parts):
happly = []
hskip = []
for h in parts:
if not isinstance(h, Hunk):
# we don't care about lines
happly.append(h)
hskip.append(h)
continue
sys.stdout.write(str(h))
print
r = ask("* Include in commit? [y/n/Y/N/?] ", 'ynYN',
help = ('?', 'Help not implemented') )
if r == 'y':
happly.append(h)
elif r == 'n':
hskip.append(h)
elif r == 'Y':
return (parts, [])
elif r == 'N':
return ([], parts)
# if we don't have any chunks, skip everything
if len( [h for h in happly if isinstance(h, Hunk)] ) == 0:
hskip = []
if len( [h for h in hskip if isinstance(h, Hunk)] ) == 0:
hskip = []
return (happly, hskip)
#
# Main
#
if __name__ == '__main__':
if len(sys.argv) != 4:
print "Usage: pccp diff_file to_apply to_skip"
sys.exit(1)
fin, fapply, fskip = sys.argv[1:4]
original = parse(open(fin))
toapply = Diff()
toskip = Diff()
cherrypick(original, toapply, toskip)
open(fapply, 'w').write(str(toapply))
open(fskip, 'w').write(str(toskip))
[-- Attachment #3: git-commit-cp --]
[-- Type: text/plain, Size: 564 bytes --]
#!/bin/bash
set -e
SUBDIRECTORY_OK=Yes
. git-sh-setup
require_work_tree
cd_to_toplevel
TMPB=`mktemp -t git-commit-cp.XXXXXX` || exit 1
ORIGINAL="$TMPB-original"
TOAPPLY="$TMPB-apply"
TOSKIP="$TMPB-skip"
function cleanup() {
rm $TMPB $ORIGINAL $TOAPPLY $TOSKIP
}
git-diff --full-index HEAD > $ORIGINAL
git-pcp $ORIGINAL $TOAPPLY $TOSKIP
if [ ! -s $TOAPPLY ]; then
echo "* Nothing to commit!"
cleanup
exit 1
fi
if [ -s $TOSKIP ]; then
git-apply -R $TOSKIP;
fi
git-commit -a "$@"
if [ -s $TOSKIP ]; then
git-apply --index $TOSKIP;
fi
cleanup
exit 0
next reply other threads:[~2007-04-03 3:50 UTC|newest]
Thread overview: 4+ messages / expand[flat|nested] mbox.gz Atom feed top
2007-04-03 3:42 Alberto Bertogli [this message]
2007-04-03 5:19 ` Commit cherry-picking Shawn O. Pearce
2007-04-03 5:33 ` Shawn O. Pearce
2007-04-03 5:45 ` Alberto Bertogli
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20070403034234.GB24722@gmail.com \
--to=albertito@gmail.com \
--cc=git@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).