What is the meaning of :/ (colon, forward slash) in the 2.0 version of git add --update syntax?

后端 未结 4 539
粉色の甜心
粉色の甜心 2020-12-16 19:02

I upgraded Git a couple months ago and since then I\'ve been receiving the following deprecation notice upon attempting git add --update:

4条回答
  •  野趣味
    野趣味 (楼主)
    2020-12-16 19:49

    Edit, Nov 2019: I've replaced this answer as the original was more wrong than right, really.

    The :/ syntax for git add uses what Git calls a pathspec. Pathspecs are defined in the gitglossary. It's worth mentioning that : and :/ have an entirely different meaning in gitrevisions syntax, which is used by many other commands, but not by git add. For these other uses, see VonC's answer.

    In pathspecs, the colon character : is supposed to be followed by either the long form or the short form of various modifiers. After the modifiers, whatever is left over is either a pattern, or a simple string that gets treated as a file name.

    :/ uses the short form. In the short form, each recognized character after the colon has some special meaning. The / character means at the top of the work-tree.1

    Any unrecognized characters after the colon are part of the left-over stuff. In this case, the entire sequence is colon-and-slash, so nothing is left over. However, if you wrote :/README, that would mean the file README in the top of the work-tree.

    That is, suppose you're in the sub-directory (sub-folder) named sub/, and it has a README file as well. Then:

    git add README
    

    adds sub/README (because you're in sub), but:

    git add :/README
    

    or:

    git add ../README
    

    adds the top-level file.

    For each short-form expression, there's a long-form variant. There are long-form variants that do not have short-forms, though. To use the long-form variant, write a colon, then an open parenthesis (, then as many long-form names as you like, separated by commas. End the sequence with a clone parenthesis ).2 For instance, you can write :(top,icase)readme to mean a file named readme, or ReadMe, or README, or any other crazy mixed case, at the top of the work-tree. Some command-line interpreters may require quotes around parenthesized expressions: git add ":(top,icase)readme".

    The empty string, in this particular case, has the same meaning as . to Git. So :/: or :(top) means the same as :/:. or :(top).. This names every file in the work-tree.3 Leaving off the terminating : in the pathspec syntax is OK, so :/ also means every file in the work-tree.


    1There are—at least currently—only two other special characters, ! and ^, which both mean "exclude". So with the colon form, you can write :/ or :^ or :!, or combine them with :/!.

    You can terminate the short form with a second colon, i.e., you can write :/:! if you want to add the file named ! that is at the top of the work-tree. The pathspec :/!:! means do not add the file ! that is at the top of the tree; this would only be meaningful if you said to add multiple top-of-tree files, e.g., git add :/!:! :/:. would add all files except the top-level file !.

    2Do not add another colon at the end of the long form. That is, :/: is correct, and :(top) is correct, but :(top): is wrong! This trips me up now and then.

    3The usual .gitignore rules apply, of course. Remember that any tracked file—any file that is in the index right now—is never ignored!


    Sidebar: removed files

    As I recall, Git 1.x and 2.0 differ in treatment of files that were in the previous commit, are currently tracked (i.e., are present in the index, visible with git ls-files --stage for instance), but—for whatever reason—are not in the work-tree right now.

    The behavior in Git 2.0 is that files removed from the work-tree tend to get removed from the index. For instance, suppose we run:

    $ git checkout master
    

    and get a file named file in the work-tree as a result of the tip commit of master containing the file named file. Then we do:

    $ rm file
    

    so that file is still in the index but is no longer in the work-tree. A git status at this point will say:

    On branch master
    Changes not staged for commit:
      (use "git add/rm ..." to update what will be committed)
      (use "git checkout -- ..." to discard changes in working directory)
    
            deleted:    file
    
    no changes added to commit (use "git add" and/or "git commit -a")
    

    Various forms of git add will now remove the file file from the index. That includes git add :/ as well as git add file:

    $ git add :/
    $ git status
    On branch master
    Changes to be committed:
      (use "git reset HEAD ..." to unstage)
    
            deleted:    file
    

    If you don't want this removal "added" to the index, you have a number of options, including the --ignore-removal option:

    $ git reset --hard
    HEAD is now at 29c2e68 initial
    $ git add --ignore-removal :/
    $ git status
    On branch master
    nothing to commit, working tree clean
    

    So if you are using :/ but want to skip updating (i.e., removing) removed files, use --ignore-removal. The default in Git 2.x is to "add the removal" as well.

提交回复
热议问题