Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
59 views
in Technique[技术] by (71.8m points)

what"s the difference between git commit <file> and git commit --only?

enter image description here

enter image description here

It seems that the two options function the same way.

Maybe there is a difference between them which I can't see, cause of my poor English reading skills.

Could you tell me the truth?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

There are three ways to use git commit (without --amend anyway):

  • git commit, without naming any files: this means commit the current index.

  • git commit --only file1 ... fileN, where you name some number of files and use the --only option. We'll describe this in a moment.

  • git commit --include file1 ... fileN, where you name some number of files and use the --include option. We'll describe this in a moment as well.

If you choose to list some nonzero number of files, but omit the --only option, Git assumes the --only option. If you don't want the --only option, you must specify the --include option. If you choose to list some files on the command line, you must choose one of these two options, and if you fail to choose, Git will choose for you. So --only is merely the opposite of --include.

The index and its role in commits

To properly understand all three modes of operation, you need to understand that Git builds commits from an index. Normally it uses the index. That's the first mode listed above: git commit, without any file names, uses whatever is in the index right now.

The index itself is a somewhat hidden data structure. It has three names, reflecting either its great importance, or perhaps the rather bad initial name, i.e., "index". The second name is the staging area, because the role of the index is to act as the place in which you build—or stage—the next commit you will make. The third name of this thing—this index or staging-area—is the cache.

The index starts out holding copies of all the files in the current commit, as extracted when you ran git checkout branch. These files were copied from the branch-tip commit into the index / staging-area, and also into the work-tree where you can work on them. The name index comes from the fact that the index keeps track of the work-tree: it indexes it. (See the verb definitions here.)

How a regular commit works

When you run git commit in the normal mode, Git packages up all the files that are in the index right then—all the files that were in the commit you checked out, with files that you git add-ed overwritten with files from the work-tree. These files become the new snapshot. Git writes out the snapshot itself, your name and email address as the author of the commit, and your other metadata such as your log message. It sets the parent of the new commit to the previous tip of the branch, and then writes the new commit's hash ID into the current branch name, so that the new commit is now the current commit.

Since Git just built the new commit from the index, the new, now-current, commit and the index match. Hence everything is all ready for the next commit that you might make, by modifying work-tree files and copying them into the index.

How --only and --include work

To build a new commit with --include, Git in essence just adds the files to the index and commits.1 That's very straightforward: it looks as though you ran git add file1 ... fileN and then ran git commit. The new commit is built from this modified index, which is now the index, since it now matches the new current commit.

For --only, however, Git has a problem. The index—the regular one—has quite possibly been modified and may no longer match the current commit. So what Git does is, in effect, to extract the current commit into a temporary index, use git add to copy work-tree files into that temporary index, and then build the commit from the temporary index. This part is not that complicated: the new commit is built from an index, it's just not the index. But once this is done, Git is right back to the problem: the real index—the one you probably modified—doesn't match the new commit Git just made, and probably doesn't match the previous commit. It doesn't match anything, and this is a big mess.

What Git does at this point, following the successful commit, is to copy those same files to the real index, so that file1 ... fileN in the index are updated to match what you just committed. Meanwhile all remaining files in the real index remain however they looked before you ran git commit --only.

Note that files that you had carefully staged in the real index—using git add --patch, for instance—that neither matched the work-tree nor matched the old (no longer current) commit may still be the way you staged them. However, if you named one of those files as one of the --only files, the carefully-staged version is now gone! It has been replaced with the work-tree version because of the post-commit git add.2


1Internally, it's more complicated: Git still builds a temporary index, the way it does for --only. However, if the commit succeeds, the temporary index becomes the regular index. So this is less complicated than --only. Note that this "in essence" phrase papers over a multitude of sins: for instance, if the file is totally new to git, the --include part fails in at least some versions of Git, while a direct git add would work.

I say at least some here because I just tested it in Git 2.24.0, which is pretty current, which probably means it fails in all versions; but this seems like a bug, so it might get fixed in a future version of Git. What's particularly scary is that it just silently ignores new files! You can get a commit that does not include a file you named. That seems like not just a bug, but a bad one.

2As with git commit --include, this has rough edges, especially around the case of files that aren't currently in the index. However, the behavior in Git 2.24, at least, is slightly better in that Git produces an error message here, instead of silently ignoring the file.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...