简体   繁体   中英

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

在此处输入图片说明

在此处输入图片说明

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?

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, ie, "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


1 Internally, 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.

2 As 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.

The answer is in the documentation fragment you listed:

--only

Make a commit by taking the updated working tree contents of the paths specified on the command line, disregarding any contents that have been staged for other paths. This is the default mode of operation of git commit if any paths are given on the command line, in which case this option can be omitted.

When you add file paths in the command line, --only doesn't add anything to the command and the semantics described for the second option you described in the question applies.

On the other hand, as the rest of the description of --only explains, if it is used together with --amend of --allow-empty , the content of the index (the staged files) is ignored.

git commit --amend modifies the current commit by adding the currently staged files and changing its commit message. With --only , the content of the commit is not modified (the staged files are ignored), only the commit message is updated.

It is not clear for me how --only changes the behaviour of git commit --allow-empty .

However, --allow-empty is not of any use in the daily working routine with Git. It was added to help automatic conversion of repositories of other VCS-es to Git. Fe when a branch is created, Subversion creates a new commit that is empty from the Git's perspective. --allow-empty allows git-svn to create such an empty commit when it converts a Subversion repository to Git.

git commit -- takes the commit message from the given file. In the parameter you should enter the name of the file you want from your repository.

git commit --only is the default mode of operation of git commit.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM