{"thread":{"id":"7496","subject":"Commit cherry-picking","startedAt":"2007-04-03T03:42:37Z","lastAt":"2007-04-03T05:45:02Z","messageCount":4,"participants":["Alberto Bertogli","Shawn O. Pearce"],"isPatch":false,"patchVersion":null,"patchTotal":null},"messages":[{"id":"38490","messageId":"20070403034234.GB24722@gmail.com","threadId":"7496","inReplyTo":null,"subject":"Commit cherry-picking","fromName":"Alberto Bertogli","fromEmail":"albertito@gmail.com","sentAt":"2007-04-03T03:42:37Z","receivedAt":"2007-04-03T03:42:37Z","isPatch":false,"sender":{"key":"albertito@gmail.com","avatar":null},"body":"\nHi!\n\nI often use darcs, and one feature I miss when I use git is the ability\nto do cherry-picking on what I'm about to commit.\n\nIt allows me to do small changes to the code when I'm working on\nsomething else, and don't do ugly commits.\n\nI know the proper way to do this would be to have different branches and\nall. But that means I have to switch between branches to do quick fixes,\nwhich is an expensive operation in human terms, because I have to stop\nthinking about the code and switch branches and so on.\n\nSo I wrote two small scripts to do that: git-pcp and git-commit-cp. The\nformer acts as a helper to the later. Both are attached.\n\ngit-pcp takes a diff file, and produces two files: one with the hunks to\napply, and another one with the ones to skip. It asks the user to\nselect, for each hunk, where to put it.\n\ngit-commit-cp is the command to use, which calls git-pcp to do the\ncherrypicking, and then applies the corresponding patches.\n\n\nThe implementation of git-pcp should be better (the diff parsing is not\nas strong as it should be, although it works for most cases; and the\nuser interaction sucks, because I don't like UI =), so it's working but\nit needs some improvements.\n\n\nI wanted to ask you if this was an acceptable command to add to git, and\nif you had any recommendations or thoughts about the implementation.\n\nThanks a lot,\n\t\tAlberto\n\n\nPS: Is there a way of telling git-diff how many context lines to use?\n\n\n\n#!/usr/bin/env python\n\n\"\"\"\nPatch Cherry-Picking\n\nThis scripts takes a patch and asks you which chunks you want to apply. Then\nit creates two output files: one with the ones to apply, and one with the ones\nto skip.\n\nNote that the output patches are NOT fixed, you might want to run them through\nrediff (although git-commit-cp does not need to).\n\"\"\"\n\nimport sys\n\n\n#\n# Classes and main data structures\n#\n\nclass Diff:\n\tdef __init__(self):\n\t\tself.parts = []\n\n\tdef append(self, part):\n\t\tself.parts.append(part)\n\n\tdef __str__(self):\n\t\ts = ''.join((str(i) for i in self.parts))\n\t\treturn s\n\n\tdef __eq__(self, other):\n\t\tif self.parts == other.parts:\n\t\t\treturn True\n\t\treturn False\n\n\nclass File:\n\tdef __init__(self, fname):\n\t\tself.fname = fname\n\t\tself.parts = []\n\n\tdef append(self, part):\n\t\tself.parts.append(part)\n\n\tdef __str__(self):\n\t\ts = ''.join((str(i) for i in self.parts))\n\t\treturn s\n\n\tdef __eq__(self, other):\n\t\tif self.parts == other.parts:\n\t\t\treturn True\n\t\treturn False\n\n\nclass Hunk:\n\tdef __init__(self):\n\t\tself.parts = []\n\n\tdef append(self, line):\n\t\tself.parts.append(line)\n\n\tdef __str__(self):\n\t\ts = ''.join(self.parts)\n\t\treturn s\n\n\tdef __eq__(self, other):\n\t\tif self.parts == other.parts:\n\t\t\treturn True\n\t\treturn False\n\n\n#\n# Diff parsing\n#\n\ndef startswithany(l, *starts):\n\tfor s in starts:\n\t\tif l.startswith(s):\n\t\t\treturn True\n\treturn False\n\n\ndef parse(fd):\n\tdiff = Diff()\n\tfile = None\n\thunk = None\n\tcurrent = diff\n\ttrailing = []\n\n\tfor line in fd:\n\t\tif line.startswith('+++ '):\n\t\t\tunused, fname = line.strip().split(' ', 1)\n\t\t\tif hunk:\n\t\t\t\tfile.append(hunk)\n\t\t\t\thunk = Hunk()\n\t\t\tif file:\n\t\t\t\tdiff.append(file)\n\t\t\tfile = File(fname)\n\t\t\tfor i in trailing:\n\t\t\t\tfile.append(i)\n\t\t\tfile.append(line)\n\t\t\ttrailing = []\n\n\t\telif line.startswith('@@ '):\n\t\t\tif hunk:\n\t\t\t\tfile.append(hunk)\n\t\t\thunk = Hunk()\n\t\t\thunk.append(line)\n\n\t\telif startswithany(line, '+', '-', ' ') \\\n\t\t\t\tand not line.startswith('---'):\n\t\t\tif hunk:\n\t\t\t\thunk.append(line)\n\t\t\telse:\n\t\t\t\ttrailing.append(line)\n\n\t\telse:\n\t\t\tif hunk:\n\t\t\t\tfile.append(hunk)\n\t\t\t\thunk = None\n\t\t\ttrailing.append(line)\n\n\tif hunk:\n\t\tfile.append(hunk)\n\tif file:\n\t\tif trailing:\n\t\t\tfor i in trailing:\n\t\t\t\tfile.append(i)\n\t\ttrailing = []\n\t\tdiff.append(file)\n\tif trailing:\n\t\tfor i in trailing:\n\t\t\tdiff.append(i)\n\treturn diff\n\n\n#\n# Cherry-picking\n#\n\ndef cherrypick(original, toapply, toskip):\n\tfor part in original.parts:\n\t\tif isinstance(part, File):\n\t\t\tprint '+++ ', part.fname\n\t\t\thapply, hskip = select_parts(part.parts)\n\t\t\tprint\n\n\t\t\tif happly:\n\t\t\t\tf = File(part.fname)\n\t\t\t\tfor i in happly:\n\t\t\t\t\tf.append(i)\n\t\t\t\ttoapply.append(f)\n\n\t\t\tif hskip:\n\t\t\t\tf = File(part.fname)\n\t\t\t\tfor i in hskip:\n\t\t\t\t\tf.append(i)\n\t\t\t\ttoskip.append(f)\n\t\telse:\n\t\t\ttoapply.append(part)\n\t\t\ttoskip.append(part)\n\n\ndef ask(prompt, valid_options, default = None, help = ()):\n\n\twhile True:\n\t\tr = raw_input(prompt)\n\t\tif not r:\n\t\t\tif default:\n\t\t\t\tr = default\n\t\t\telse:\n\t\t\t\tcontinue\n\n\t\tif r in valid_options:\n\t\t\treturn r\n\t\telif help and r == help[0]:\n\t\t\tprint help[1]\n\t\telse:\n\t\t\tprint \" -- Unknown option\"\n\n\ndef select_parts(parts):\n\thapply = []\n\thskip = []\n\n\tfor h in parts:\n\t\tif not isinstance(h, Hunk):\n\t\t\t# we don't care about lines\n\t\t\thapply.append(h)\n\t\t\thskip.append(h)\n\t\t\tcontinue\n\n\t\tsys.stdout.write(str(h))\n\t\tprint\n\t\tr = ask(\"* Include in commit? [y/n/Y/N/?] \", 'ynYN',\n\t\t\thelp = ('?', 'Help not implemented') )\n\n\t\tif r == 'y':\n\t\t\thapply.append(h)\n\t\telif r == 'n':\n\t\t\thskip.append(h)\n\t\telif r == 'Y':\n\t\t\treturn (parts, [])\n\t\telif r == 'N':\n\t\t\treturn ([], parts)\n\n\t# if we don't have any chunks, skip everything\n\tif len( [h for h in happly if isinstance(h, Hunk)] ) == 0:\n\t\thskip = []\n\tif len( [h for h in hskip if isinstance(h, Hunk)] ) == 0:\n\t\thskip = []\n\n\treturn (happly, hskip)\n\n\n#\n# Main\n#\n\nif __name__ == '__main__':\n\tif len(sys.argv) != 4:\n\t\tprint \"Usage: pccp diff_file to_apply to_skip\"\n\t\tsys.exit(1)\n\n\tfin, fapply, fskip = sys.argv[1:4]\n\n\toriginal = parse(open(fin))\n\ttoapply = Diff()\n\ttoskip = Diff()\n\tcherrypick(original, toapply, toskip)\n\n\topen(fapply, 'w').write(str(toapply))\n\topen(fskip, 'w').write(str(toskip))\n\n\n\n\n#!/bin/bash\n\nset -e\nSUBDIRECTORY_OK=Yes\n. git-sh-setup\nrequire_work_tree\ncd_to_toplevel\n\n\nTMPB=`mktemp -t git-commit-cp.XXXXXX` || exit 1\nORIGINAL=\"$TMPB-original\"\nTOAPPLY=\"$TMPB-apply\"\nTOSKIP=\"$TMPB-skip\"\n\nfunction cleanup() {\n\trm $TMPB $ORIGINAL $TOAPPLY $TOSKIP\n}\n\n\ngit-diff --full-index HEAD > $ORIGINAL\n\ngit-pcp $ORIGINAL $TOAPPLY $TOSKIP\n\nif [ ! -s $TOAPPLY ]; then\n\techo \"* Nothing to commit!\"\n\tcleanup\n\texit 1\nfi\n\nif [ -s $TOSKIP ]; then\n\tgit-apply -R $TOSKIP;\nfi\n\ngit-commit -a \"$@\"\n\nif [ -s $TOSKIP ]; then\n\tgit-apply --index $TOSKIP;\nfi\n\ncleanup\nexit 0\n"},{"id":"38491","messageId":"20070403051947.GE15922@spearce.org","threadId":"7496","inReplyTo":"20070403034234.GB24722@gmail.com","subject":"Re: Commit cherry-picking","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2007-04-03T05:19:47Z","receivedAt":"2007-04-03T05:19:47Z","isPatch":false,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"Alberto Bertogli <albertito@gmail.com> wrote:\n> I often use darcs, and one feature I miss when I use git is the ability\n> to do cherry-picking on what I'm about to commit.\n\nHave you tried:\n\n\tgit add -i\n\tgit commit\n\n?\n\nThe `git add -i` flag starts up an interactive tool that you can use\nto add patch hunks to the index, staging them for the next commit.\nRunning commit with no arguments will then commit exactly what is\nin the index, leaving the other hunks beind in the working directory.\n\nOr did I miss something?  Note that `git add -i` was added as a\nnew feature in Git 1.5.0 (and later).\n\n-- \nShawn.\n"},{"id":"38493","messageId":"20070403053344.GG15922@spearce.org","threadId":"7496","inReplyTo":"20070403051947.GE15922@spearce.org","subject":"Re: Commit cherry-picking","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2007-04-03T05:33:44Z","receivedAt":"2007-04-03T05:33:44Z","isPatch":false,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"\"Shawn O. Pearce\" <spearce@spearce.org> wrote:\n> Alberto Bertogli <albertito@gmail.com> wrote:\n> > I often use darcs, and one feature I miss when I use git is the ability\n> > to do cherry-picking on what I'm about to commit.\n> \n> Have you tried:\n> \n> \tgit add -i\n> \tgit commit\n> \n> ?\n> \n> The `git add -i` flag starts up an interactive tool that you can use\n> to add patch hunks to the index, staging them for the next commit.\n> Running commit with no arguments will then commit exactly what is\n> in the index, leaving the other hunks beind in the working directory.\n\nAlso `git gui` (or `git citool`) offers this hunk selection feature,\nbut in a Tcl/Tk based GUI format.  The hunk selection isn't as\npowerful as I'd like it to be, but it works well enough that I\nhaven't bothered to improve upon it.\n \n> Or did I miss something?  Note that `git add -i` was added as a\n> new feature in Git 1.5.0 (and later).\n\ngit-gui also shipped in 1.5.0...\n\n-- \nShawn.\n"},{"id":"38496","messageId":"20070403054502.GD24722@gmail.com","threadId":"7496","inReplyTo":"20070403053344.GG15922@spearce.org","subject":"Re: Commit cherry-picking","fromName":"Alberto Bertogli","fromEmail":"albertito@gmail.com","sentAt":"2007-04-03T05:45:02Z","receivedAt":"2007-04-03T05:45:02Z","isPatch":false,"sender":{"key":"albertito@gmail.com","avatar":null},"body":"On Tue, Apr 03, 2007 at 01:33:44AM -0400, Shawn O. Pearce wrote:\n> \"Shawn O. Pearce\" <spearce@spearce.org> wrote:\n> > Alberto Bertogli <albertito@gmail.com> wrote:\n> > > I often use darcs, and one feature I miss when I use git is the ability\n> > > to do cherry-picking on what I'm about to commit.\n> > \n> > Have you tried:\n> > \n> > \tgit add -i\n> > \tgit commit\n[...]\n> \n> Also `git gui` (or `git citool`) offers this hunk selection feature,\n\nArgh, how can I have missed all that!\n\nSorry for the noise, and thanks for the suggestions.\n\n\t\tAlberto\n"}]}