Ken Williams
Ken Williams

Reputation: 24015

Git partial commit of staged changes

My current git status looks like this:

# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#       modified:   docs/file3.org
#       modified:   src/main/R/lib.R
#
# Changes not staged for commit:
#   (use "git add <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#       modified:   docs/file3.org
#       modified:   src/main/R/lib.R

I'd like to commit the staged changes to docs/file3.org first, then the other staged changes. But if I do git commit -m '...' docs/file3.org, it will commit both the staged & unstaged changes to that file.

Is there an easy way to do this? Or do I need to stash my unstaged changes, unstage one of the files, commit the other, restage, commit, and stash pop?

Upvotes: 20

Views: 21000

Answers (4)

alexy2k
alexy2k

Reputation: 560

What's really weird, is that git-scm book explains staging exactly how you described:

unstaged changes of the same file don't go with commit, only staged changes.

Check it out: http://git-scm.com/book/en/Git-Basics-Recording-Changes-to-the-Repository#Staging-Modified-Files

I started testing and regardless of what changes of the same file I had staged, all changes went with the commit.

Upvotes: 5

Sailesh
Sailesh

Reputation: 26207

Since you only need to commit the staged changes to a file, you can just stash, keeping the indexed changes intact, and committing the file after that.

git stash --keep-index #Note that the staged changes also become part of the stash
git commit -m "Commit only one file" docs/file3.org

git commit -m "Commit other staged changed"
git stash pop 
# This may raise CONFLICTS since the stash includes the earlier staged changes.
# In case of conflict, the stash is not popped, so you need to drop it explicitly.

Upvotes: 15

Jake Wharton
Jake Wharton

Reputation: 76115

I'm going to assume you're on master for all this...

git branch whatever
git commit # commit currently staged changes as 'commit1'
git add docs/file3.org
git commit # commit unstaged changes as 'commit2'

You'll now have this

master
  \
   \--commit1--commit2

Now, run git log and record the SHAs for 'commit1' and 'commit2'.

git checkout master
git cherry-pick commit2SHA
git cherry-pick commit1SHA

Which will give you

master--newcommit2--newcommit1
   \
    \--commit1--commit2

And then you can destroy the 'whatever' branch

git branch -D whatever

edit:

You could also do something similar using the stash:

git stash --keep-index  # Save unstaged changes
git stash               # Save staged changes
git stash pop stash@{1} # Pop unstanged changes
git commit ...          # Commit unstanged changes
git stash pop stash@{0} # Pop staged changes
git commit ...          # Commit staged changes

Upvotes: 2

VonC
VonC

Reputation: 1329822

git stash is the best option, combined with.

git add --interactive

This should allow you to 'revert' (unstage) some files already added to the index.
Then git stash would allow you to save those files and restore them.

(See "git add --patch and --interactive" article for an example)

Upvotes: 1

Related Questions