Jeff
Jeff

Reputation: 1591

Can I specify multiple files in a git commit command that have already been staged?

I have many files that are already added to the index but I would like to commit only some of them AT FIRST because the review tool works on commits. If I simply do a git commit, I think I will get ALL the files. I think I can do: git commit file1 file2 file3 and if this is true, I would like to be able to store a very long list of files in file and specify this as input to the commit command. Alternatively, I would like to be able to commit many times with a single commit id. I do not want to unstage some files because there are hundreds of files in the index, only some of which are important to be reviewed.

Is there a safe way to force this to happen? Once that is done, I will then do a commit to get the remaining files, if possible.

EDIT: Bottom line, yes you can specify some subset of staged files, a partial commit. But in the case of a merge which caused many files to change and be staged and only some of which I wanted to be committed for review (the rest later) you can't do this; Git will not allow a partial commit during a merge. If I am wrong, I am sure someone will let me know.

EDIT: I attempted the "xargs" (as suggested by Edmundo) solution and got the error: fatal: cannot do a partial commit during a merge.

Upvotes: 0

Views: 517

Answers (3)

torek
torek

Reputation: 488183

As both phd and Edmundo answered, yes, you can do this, but you need to be aware of how Git actually makes commits.

The normal, standard setup for Git is that there are three copies of every file associated with the current commit:

HEAD     index    work-tree
-----    -----    ---------
file1    file1    file1
file2    file2    file2
  :        :        :
fileN    fileN    fileN

The copy in the commit (HEAD) is read-only: it can never be changed. The copy in the index is read/write, but otherwise stored in a special Git-only format. The copy in the work-tree is read/write and is stored in the normal everyday form that your computer uses for files.

When you run git add, Git copies the work-tree version of a file into the index. Let's say you changed file2 somewhat:

HEAD     index    work-tree
-----    -----    ---------
file1    file1    file1
file2    file2    file2*
  :        :        :
fileN    fileN    fileN

The asterisk here means that the contents of this version of file2 are different.

Running git add file2 copies file2 from the work-tree into the index:

HEAD     index    work-tree
-----    -----    ---------
file1    file1    file1
file2    file2*   file2*
  :        :        :
fileN    fileN    fileN

(Aside: note that running git reset file2 copies file2 from HEAD into the index, removing the change from the index while leaving it in the work-tree.)

When you run an ordinary git commit, Git packages up whatever is in the index right now and makes the new commit using those files. So if you have modified file2, then copied the new file into the index, then run git commit, you get the modified file2 in the new commit. The unmodified file1, file3, ..., fileN also go into the new commit.

When you run, e.g., git commit file1 file2, though, Git does something special.

Committing with file arguments makes a temporary extra index

Let's make a change to file file3 but not run git add yet.

HEAD     index    work-tree
-----    -----    ---------
file1    file1    file1
file2    file2*   file2*
file3    file3    file3*
  :        :        :
fileN    fileN    fileN

If you run git commit file3 now, for instance, Git starts by copying something into a temporary index.

There are several things Git could sensibly copy here, to get the starting point for the temporary index. What it actually uses depends on flag arguments:

  • git commit --include file3: Git copies the existing index to the temporary index. This gets you file1, file2*, file3, and so on. Note that we get the new file2 here, not the old one.

  • git commit --only file3: Git copies the HEAD commit to the temporary index. This gets you file1, file2, file3, and so on. Note that we get the old (HEAD-version) file2 here.

Next, Git adds the files listed on the command line. This replaces the temporary index's file3 with file3*.

Now Git makes the commit, using the temporary index.

Last, Git copies some things from the temporary index back into the regular (ordinary) index. Specifically, it copies back all the files you listed on the command line—so file3* goes into the regular index.

Partially staged files

What phd is referring to is the use of git add -p, git reset -p, or the ability to do:

$ make-change-to file2
$ git add file2
$ make-different-change-to file2

Now you have something we might describe as:

HEAD     index    work-tree
-----    -----    ---------
file1    file1    file1
file2    file2*   file2†
  :        :        :
fileN    fileN    fileN

That is, there are now three different versions of file2 active all at the same time: the original unmodified HEAD version, the first modified version stored in the index, and the second modified version stored in the work-tree.

If you git commit file2, Git will make a new temporary index, copy the work-tree file2† version into that temporary index, commit from the temporary index, and then copy the temporary index file2† file over the regular index file2* file. The effect is that you lose the middle version entirely.

Upvotes: 0

phd
phd

Reputation: 94483

I think I can do: git commit file1 file2 file3

Yes, but you must understand that git will ignore staged hunks and commit these files wholly. This is important if you staged parts of files using git add -p.

Alternatively, I would like to be able to commit many times with a single commit id.

Impossible. Every time you amend a commit its SHA-1 id changes. But if you use gerrit for review it doesn't matter: gerrit appends its own id to commit message on the first commit to link amended commit to the same review; do not remove this id when ammending the commit.

Upvotes: 2

eftshift0
eftshift0

Reputation: 30212

If you have the list on a file, you could use xargs to get them to be passed as the parameters of the git commit: cat file-of-files.txt | xargs git commit -m "Here's a few files to be committed"

Upvotes: 2

Related Questions