What does git add --intent-to-add or -N do and when should it be used?

后端 未结 4 1137
再見小時候
再見小時候 2021-01-01 10:58

On git add -h I can see the following option:

-N, --intent-to-add   record only the fact that the path will be added later

But

4条回答
  •  感动是毒
    2021-01-01 11:30

    user456814's answer explains very well what git add -N is useful for. I just want to give a more detailed explanation of what's going on in the background.

    You can think of git as maintaining a history of file changes like creations, modifications, and deletions (let's call them deltas). The deltas are pretty self-explanatory up until the most recent commit, but things get more complicated when you're working in your project to prepare a new commit to go on top. There are three different types of deltas when you're in this situation.

    (Side note: most people, when first introduced to git, are taught something like "committing in git takes two steps; first you do git add and then you can do git commit". While this is true, it only brings attention to the Changes to be committed type of delta. Understanding git add -N requires understanding the other types of deltas as well.)

    #1: Changes to be committed

    Commonly called "staged changes", these deltas appear at the top when you run git status, if there are any. If your shell supports color, they will be green.

    When you git add a file, it gets promoted into this category. These are the changes that will actually be included if you run git commit without any flags.

    #2: Changes not staged for commit

    These deltas appear second when you run git status, if there are any. If your shell supports color, they will be red.

    These are changes that you have made to files in the git repository that have not yet been committed AND have not been moved to #1. When you edit a file and then save, it appears in this category by default.

    For a file to show up in this category, it has to EITHER already exist in the most recent commit, OR be added if the changes in #1 were to be committed. Otherwise, it will show up in category #3.

    (Note: because you choose when to "promote" a file into category #1, it's possible to have the same file show up in both #1 and #2. For example, I could see

    modified:   abc.txt
    

    in green in #1, and

    modified:   abc.txt
    

    in red in #2 at the same time. This can happen if I promote a file to #1, then later make some more changes to it. The entry in #1 references the delta that I made before promoting the file, maybe adding a new line of code, and the entry in #2 references the delta that I made afterwards, maybe adding a comment at the top. If I were more organized, I would have made all the changes before promoting the file to #1, in order to avoid this situation altogether.)

    #3: Untracked files

    These deltas appear last when you run git status, if there are any. If your shell supports color, they will be red.

    These are all the files that are not in the most recent commit AND not in #1. While technically a delta in the sense that adding it would change the commit, it's possible that the file has just always been there and people just don't want git to record any information about it. (In this case, you should add the file to .gitignore, and it will stop showing up in git status.)

    When you create a brand-new file, it shows up in this category.

    So what does all this have to do with git add -N?

    git add -N is all about making it easier to work with #3 deltas. As referenced in the accepted answer above, git diff lets you see what deltas you have actually prepared. Here is a good set of commands that work with git diff.

    git diff only shows the differences between #1 and #2 (i.e. the deltas in #2).

    git diff --staged only shows the deltas in #1.

    git diff HEAD only shows the deltas in #1 and #2, put together.

    Notice that none of these commands even look at #3. However, by running git add -N, you basically do the following:

    • Split a new file into two deltas: just file creation, and filling the file with text/content
    • git add the "file creation" delta into #1

    This has the effect of making the second delta appear in #2. Now the new file is completely out of #3, and you can use git diff commands with it.

    As for git commit -a, essentially what it does is:

    • git add everything in #2 so that it is also staged with everything in #1
    • git commit (which takes everything in #1, including the stuff that was just added, and creates an actual commit from it)

    Without git add -N, this command misses your new file in #3; however, you can see that after running git add -N, your new file is spread across #1 and #2, and will get included in the commit.


    Well, I've explained everything I want to explain. If you want to check your understanding, you can follow along with the example below:

    1. I make a new git repo.

      sh-4.1$ cd ~/Desktop
      sh-4.1$ mkdir git-demo
      sh-4.1$ cd git-demo
      sh-4.1$ git init
      Initialized empty Git repository in /local/home/Michael/Desktop/git-demo/.git/
      
    2. git status shows me this repo is empty.

      sh-4.1$ git status
      On branch master
      
      Initial commit
      
      nothing to commit (create/copy files and use "git add" to track)
      
    3. I make some new files.

      sh-4.1$ echo "this is the abc file" > abc.txt
      sh-4.1$ echo "this is the def file" > def.txt
      sh-4.1$ echo "this is the ghi file" > ghi.txt
      
    4. git status shows me all the new files are currently in category #3.

      sh-4.1$ git status
      On branch master
      
      Initial commit
      
      Untracked files:
        (use "git add ..." to include in what will be committed)
      
          abc.txt
          def.txt
          ghi.txt
      
      nothing added to commit but untracked files present (use "git add" to track)
      
    5. git diff does nothing, since it doesn't operate on #3.

      sh-4.1$ git diff
      
    6. I commit one file, add another one, and add -N the third one.

      sh-4.1$ git add abc.txt && git commit -m "some commit message"
      [master (root-commit) 442c173] some commit message
       1 file changed, 1 insertion(+)
       create mode 100644 abc.txt
      sh-4.1$ git add def.txt
      sh-4.1$ git add -N ghi.txt
      
    7. In git status, abc.txt no longer shows up, since it has already been committed. def.txt only shows up in category #1 since the whole file has been added. ghi.txt shows up in categories #1 and #2.

      sh-4.1$ git status
      On branch master
      Changes to be committed:
        (use "git reset HEAD ..." to unstage)
      
          new file:   def.txt
          new file:   ghi.txt
      
      Changes not staged for commit:
        (use "git add ..." to update what will be committed)
        (use "git checkout -- ..." to discard changes in working directory)
      
          modified:   ghi.txt
      
    8. By running git diff, I can show all the deltas listed in #2. The only delta that shows up is that I added a line to ghi.txt.

      sh-4.1$ git diff
      diff --git a/ghi.txt b/ghi.txt
      index e69de29..8a8dee2 100644
      --- a/ghi.txt
      +++ b/ghi.txt
      @@ -0,0 +1 @@
      +this is the ghi file
      
    9. By running git diff --staged, I can show all the deltas listed in #1. Three of them show up: creating a new file def.txt, adding a line in def.txt, and creating a new file ghi.txt. Even though there are 2 deltas for def.txt, the file name itself is only output one time in example 7 above, to avoid clutter.

      sh-4.1$ git diff --staged
      diff --git a/def.txt b/def.txt
      new file mode 100644
      index 0000000..48baf27
      --- /dev/null
      +++ b/def.txt
      @@ -0,0 +1 @@
      +this is the def file
      diff --git a/ghi.txt b/ghi.txt
      new file mode 100644
      index 0000000..e69de29
      

提交回复
热议问题