I have two working branches, master and forum and I\'ve just made some modifications in forum branch, that I\'d like to ch
You should use the index. After doing a mixed reset ("git reset HEAD^"), add the first set of changes into the index, then commit them. Then commit the rest.
You can use "git add" to put all changes made in a file to the index. If you don't want to stage every modification made in a file, only some of them, you can use "git add -p".
Let's see an example. Let's suppose I had a file called myfile, which contains the following text:
something
something else
something again
I modified it in my last commit so that now it looks like this:
1
something
something else
something again
2
Now I decide that I want to split it into two, and I want the insertion of the first line to be in the first commit, and the insertion of the last line to be in the second commit.
First I go back to HEAD's parent, but I want to keep the modifications in file system, so I use "git reset" without argument (which will do a so-called "mixed" reset):
$ git reset HEAD^
myfile: locally modified
$ cat myfile
1
something
something else
something again
2
Now I use "git add -p" to add the changes I want to commit to the index (=I stage them). "git add -p" is an interactive tool that asks you about what changes to the file should it add to the index.
$ git add -p myfile
diff --git a/myfile b/myfile
index 93db4cb..2f113ce 100644
--- a/myfile
+++ b/myfile
@@ -1,3 +1,5 @@
+1
something
something else
something again
+2
Stage this hunk [y,n,a,d,/,s,e,?]? s # split this section into two!
Split into 2 hunks.
@@ -1,3 +1,4 @@
+1
something
something else
something again
Stage this hunk [y,n,a,d,/,j,J,g,e,?]? y # yes, I want to stage this
@@ -1,3 +2,4 @@
something
something else
something again
+2
Stage this hunk [y,n,a,d,/,K,g,e,?]? n # no, I don't want to stage this
Then I commit this first change:
$ git commit -m "Added first line"
[master cef3d4e] Added first line
1 files changed, 1 insertions(+), 0 deletions(-)
Now I can commit all the other changes (namely the numeral "2" put in the last line):
$ git commit -am "Added last line"
[master 5e284e6] Added last line
1 files changed, 1 insertions(+), 0 deletions(-)
Let's check the log to see what commits we have:
$ git log -p -n2 | cat
Commit 5e284e652f5e05a47ad8883d9f59ed9817be59d8
Author: ...
Date: ...
Added last line
Diff --git a/myfile b/myfile
Index f9e1a67..2f113ce 100644
--- a/myfile
+++ b/myfile
@@ -2,3 +2,4 @@
something
something else
something again
+2
Commit cef3d4e0298dd5d279a911440bb72d39410e7898
Author: ...
Date: ...
Added first line
Diff --git a/myfile b/myfile
Index 93db4cb..f9e1a67 100644
--- a/myfile
+++ b/myfile
@@ -1,3 +1,4 @@
+1
something
something else
something again
To change the current commit into two commits, you can do something like the following.
Either:
git reset --soft HEAD^
This undoes the last commit but leaves everything staged. You can then unstage certain files:
git reset -- file.file
Optionally restage parts of those files:
git add -p file.file
Make a new first commit:
git commit
The stage and commit the rest of the changes in a second commit:
git commit -a
Or:
Undo and unstage all of the changes from the last commit:
git reset HEAD^
Selectively stage the first round of changes:
git add -p
Commit:
git commit
Commit the rest of the changes:
git commit -a
(In either step, if you undid a commit that added a brand new file and want to add this to the second commit you'll have to manually add it as commit -a
only stages changes to already tracked files.)
You can use git rebase -i <commit>
, where <commit>
is the latest commit you want to keep as-is. Add a break
at each point where you would like to insert a new split-out commit. Then at each break, use git checkout -p <commit containing parts you want>
to pull in the parts you want to split out, and commit them. Then git rebase --continue
to remove those parts from later commits.
For the simple case of splitting the most recent commit, this looks like:
$ git rebase -i HEAD^
# add 'break' at the start
$ git checkout -p master # or whatever your branch is called
# choose the parts you want to split out
$ git commit
# commit the newly-split-out parts
$ git rebase --continue
# rebase the remaining parts of the change onto the split-out parts
This assumes you want the later commit to retain the original commit message; that's the situation I usually find myself in (factoring out some preparatory change).
I'm surprised nobody suggested git cherry-pick -n forum
. This will stage the changes from the latest forum
commit but not commit them - you can then reset
away the changes you don't need and commit what you want to keep.
splitme
) into two.splitme
. splitme
. The rebase steps (1 & 7) can be skipped if the splitme
is the most recent commit.
git rebase -i splitme^
# mark splitme commit with 'e'
git reset HEAD^ -- $files
git commit --amend
git add $files
git commit -m "commit with just some files"
git rebase --continue
If I wanted the split files to be committed first, I'd then rebase -i again and switch the order
git rebase -i splitme^
# swap order of splitme and 'just some files'
git checkout HEAD~1 -- files with unwanted changes
and git commit
. If not, files with mixed changes can be partially staged git reset file
and git add -p file
as an intermediate step.) Call this the revert.git revert HEAD
– Make yet another commit, that adds back the unwanted changes. This is the double-revertgit rebase -i HEAD~3
). This commit now becomes free of the unwanted changes, for those are in the second commit.