33

I'm looking for exactly the same behavior as

git add -i -p

But instead of composing a commit from my working directory, I'd like to compose my working directory from (parts of) a commit. How can I do that ?

My use case is that I have several distinct features that are grouped together in a single commit and I'd like to test them one by one

Using cherry-pick -n is not really satisfactory, since it leaves me with the dirty job of removing all the unrequired code. I'd really just like to pick the selected changes I want to test.

Pierre Arnaud
  • 10,212
  • 11
  • 77
  • 108
krosenvold
  • 75,535
  • 32
  • 152
  • 208
  • 1
    Well, the real solution is to split commits, so you won't be stuck with 'all the unrequired code' to remove in the first place. My money is on cherry-pick, not git reset (always a bit dangerous) – sehe May 03 '11 at 05:45
  • Of course I will split the commits, I just need to find out *what* to split (and what to discard) – krosenvold May 03 '11 at 05:56

4 Answers4

39

Using cherry-pick -n is not really satisfactory, since it leaves me with the dirty job of removing all the unrequired code. I'd really just like to pick the selected changes I want to test.

The job may have been a dirty one before, but with the advent of git checkout --patch, you can now selectively discard changes, similar to git add -p for adding.

Community
  • 1
  • 1
Marc Mutz - mmutz
  • 24,485
  • 12
  • 80
  • 90
  • Oh, that's a sweet one. Exactly what I needed. Note that it might be wise to rebase/cherry pick the commit on top of your target commit if your target is not an ancestor of the cherry – krosenvold May 03 '11 at 19:52
  • Note: this doesn't really pick changes from a commit consistently, but pieces from a certain file state ! (possibly overriding / loosing other later changes) – kxr Jan 28 '21 at 12:51
11

You could use git reset --mixed HEAD^1 to revert the index, then pick the hunks you want with git add -i.

The reset will roll back the index to the previous commit (essentially un-committing whatever was the HEAD), but it won't touch the working tree. You can now stage the hunks you want, commit them and throw away the rest with a git reset --hard HEAD.

Cameron Skinner
  • 51,692
  • 2
  • 65
  • 86
3

Building on the great answer from Cameron Skinner, I will expand this a bit to include the case when you have a branch with multiple commits, perhaps merge commits, perhaps a lot of "noise" and you want to cherry-pick only very specific hunks from it. (which was exactly what happened to me today)

Follow these steps:

git checkout source-branch
git checkout -b cherry-pick-branch # Gives you a new branch based on source-branch
git reset --mixed master

This will leave you with a branch where all changes are in the filesystem, but nothing has been commited/staged. Then use git add -p to selectively add which parts you want to stage, and then commit & push as normal.

Per Lundberg
  • 3,837
  • 1
  • 36
  • 46
0

dirty job of removing all the unrequired code

If this is not acceptable,

git cherry-pick -n ...         # Starting from clean status
git reset                      # unstage
git add -p / -i [<pathspec>…​]  # add hunks interactively
git restore .                  # wipe the rest (at root dir)

, for example if you want to merge into uncommited changes or don't want to spoil time stamps, then using git apply like this may be a cleaner option:

git show <commit> | tee _.patch
edit _.patch                    # strip unwanted hunks
git apply -3 _.patch

(As long as git cherry-pick and git apply do not support -p directly)

Note: Methods like git checkout / restore -p ... do not consistently revert hunks from a specific commit but pick parts from a certain file state - possibly loosing changes in other later commits.

kxr
  • 4,841
  • 1
  • 49
  • 32