Reputation: 187
As is said in
git - How do I commit only some files? - Stack Overflow
we can use
git commit [--only] a b c -m "only part of files"
However in the following example:
$ mkdir t
$ cd t
$ git init
Initialized empty Git repository in /mnt/c/test/git-test/t/.git/
$ touch a b
$ git add .
$ git commit a -m a
[master (root-commit) c7939f9] a
1 file changed, 0 insertions(+), 0 deletions(-)
create mode 100644 a
$ git commit b -m b
[master cf4514a] b
1 file changed, 0 insertions(+), 0 deletions(-)
create mode 100644 b
$ git status
On branch master
nothing to commit, working tree clean
$ ls
a b
I tried to commit only the file b into the second commit but failed. (With a, b in working tree and working tree clean. This implies the two files are both committed.)
So how to truly commit part of files?
Even git add
a single file doesn't work:
$ mkdir t
$ cd t
$ git init
Initialized empty Git repository in /mnt/c/test/git-test/t/.git/
$ touch a b
$ git add a
$ git commit --only a -m "a"
[master (root-commit) 04383c9] a
1 file changed, 0 insertions(+), 0 deletions(-)
create mode 100644 a
$ git rm --cached -r .
rm 'a'
$ git add b
$ git commit --only b -m "b"
[master d518916] b
1 file changed, 0 insertions(+), 0 deletions(-)
create mode 100644 b
$ git checkout -f head~
Note: switching to 'head~'.
...
HEAD is now at 04383c9 a
$ ls
a
$ git checkout -f master
Previous HEAD position was 04383c9 a
Switched to branch 'master'
$ ls
a b
File a is still in the second commit.
Background: Say I have a folder with many many files, and I want to commit file set A into the first commit (i.e. The first commit contains only file set A), set B into the second commit,... Why I do this: Just for curiosity.
Upvotes: 0
Views: 1499
Reputation: 487755
To add to Mark Adelsberger's answer and address your comment here:
I used to take commit as a snapshot but not diff content. So I expect the commit command just takes a snapshot of the index and stores it.
This is correct. However, when you use git commit --only
, the way Git achieves this is complicated. (It's also not well documented.)
I normally talk about "the" index / staging-area / cache. Git does have one particular distinguished index, "the" index, although it is actually per-work-tree: if you run git worktree add
, you not only get a new work-tree, but also a new index (and new HEAD
, and other work-tree-specific refs such as those for git bisect
). But Git is capable of working with additional temporary index files, and this is what git commit --only
and git commit --include
do.
Let's look at your setup again:
$ mkdir t $ cd t $ git init Initialized empty Git repository in /mnt/c/test/git-test/t/.git/ $ touch a b $ git add .
At this point, "the" index (the main one in .git/index
) contains two files. Here they are:
$ git ls-files --stage
100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0 a
100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0 b
Now, however, you run git commit a -m a
, creating the initial commit (a root commit, with no parents). This command—git commit --only a
, more or less—works by:
.git/indexdigits
;GIT_INDEX_FILE=.git/indexdigits add a
;cp .git/index .git/index.moredigits
to create a second temporary index;GIT_INDEX_FILE=.git/index.moredigits add a
;2git commit
normally builds a commit from the main index;3 and.git/index
, so that it becomes the primary index.What this does is:
HEAD
commit plus the --only
files. The main index is undisturbed in case the new commit fails (though in your case it succeeds).If the commit succeeds, the first temporary index is discarded and the second temporary index becomes the main index (via a rename
operation so that it's all atomic). If the commit fails, both temporary index files are removed.
This means that after a successful git commit --only
, the main index is updated as if you had run git add
on the --only
files. After a failed one—the commit can fail due to pre-commit hooks, or you erasing the commit message, for instance—all is as if you had never run git commit --only
at all.
(In your case, since you didn't modify the file a
before running git commit --only a
, you can't tell some of these cases apart.)
When you went on to run git commit --only b
, these steps repeated but with file b
instead of file a
.
1There is no current commit, as you haven't created any yet, so this is treated as a special case: Git creates this as an empty index.
2This git add
winds up having no effect, since the file named a
is still empty. Had you modified the file named a
in your work-tree at this point, though, it would have updated the second temporary index.
3Since Git is not using the file .git/index
to build this new commit, any pre-commit hook that assumes that the index is named .git/index
will do the wrong thing. Note that with added work-trees, the main index for that added work-tree has a different name as well (.git/worktrees/<name>/index
, if I remember correctly offhand).
Upvotes: 2
Reputation: 45659
git commit [--only] path
commits changes only to the specified files.
So you started with an empty repo. You staged two new files: a
and b
. Now you have two things staged (as you could confirm with git status
):
a
b
You said
git commit a -m a
If you ran git status
at this time, you would see that indeed only the change to a
was committed; a
is now committed and b
is still staged as a "new file". Then you said
git commit b -m b
which committed only b
, leaving a
unchanged from the previous commit.
Again you can confirm that each commit only affected the files you specified with
git log --name-status
which will show you that the first commit only added a
and the 2nd only added b
.
It sounds like you want a command to create a commit that only contains specified files. To do that, you need to commit changes to not only any new files you're adding, but also any old files you want to remove. That's why your second attempt failed: you successfully created a staging area that only contains the file you wanted, but then you told git
not to apply changes to anything but the new file. If instead of
git commit --only b -m "b"
you had simply said
git commit -m "b"
it would've done what you're trying to do.
Upvotes: 1
Reputation: 39
The solution is pretty simple: add to stage only file that you are interested in.
For example add .
means - add to stage all files from current directory. Instead
go to folder with a
and type add a
.
Upvotes: 0