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

后端 未结 4 1108
再見小時候
再見小時候 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:11

    It is mainly used to add an empty file for your commits.

    More informations at Git with intent to add!.

    0 讨论(0)
  • 2021-01-01 11:15

    Note that before git 2.10 (Q3 2016), git add -N can sometime skips some entries.

    See commit 6d6a782, commit c041d54, commit 378932d, commit f9e7d9f (16 Jul 2016) by Nguyễn Thái Ngọc Duy (pclouds).
    (Merged by Junio C Hamano -- gitster -- in commit 3cc75c1, 25 Jul 2016)

    If you have:

    a-file
    subdir/file1
    subdir/file2
    subdir/file3
    the-last-file
    

    And you add -N everything... then subdir files are not recorded as i-t-a ("intended to add") entries.

    cache-tree.c: fix i-t-a entry skipping directory updates sometimes

    Commit 3cf773e (cache-tree: fix writing cache-tree when CE_REMOVE is present - 2012-12-16 - Git v1.8.1.1) skips i-t-a entries when building trees objects from the index. Unfortunately it may skip too much.

    If subdir/file1 is an i-t-a, because of the broken condition in this code, we still think "subdir" is an i-t-a file and not writing "subdir" down and jump to the-last-file.
    The result tree now only has two items: a-file and the-last-file.
    subdir should be there too (even though it only records two sub-entries, file2 and file3).


    git status has improved, with Git 2.17 (Q2 2018, four years laters): after moving a path in the working tree (hence making it appear "removed") and then adding with the -N option (hence making that appear "added"), git status detected it as a rename, but did not report the old and new pathnames correctly.

    See commit 176ea74, commit 5134ccd, commit ea56f97, commit 98bc94e, commit 06dba2b, commit 6de5aaf (27 Dec 2017) by Nguyễn Thái Ngọc Duy (pclouds).
    Helped-by: Igor Djordjevic (boogisha).
    (Merged by Junio C Hamano -- gitster -- in commit 12accdc, 27 Feb 2018).

    Reminder: ita or i-t-a stands for "intended-to-add", what -N does.

    wt-status.c: handle worktree renames

    Before 425a28e (diff-lib: allow ita entries treated as "not yet exist in index" - 2016-10-24, Git 2.11.0-rc0) there are never "new files" in the index, which essentially disables rename detection because we only detect renames when a new file appears in a diff pair.

    After that commit, an i-t-a entry can appear as a new file in "git diff-files". But the diff callback function in wt-status.c does not handle this case and produces incorrect status output.

    0 讨论(0)
  • 2021-01-01 11:21

    Enable diffing of untracked files

    Blue112's answer is partially correct. git add --intent-to-add does indeed add an empty file to the staging area/index for each specified untracked file in your working copy, but one of the main purposes of this is to enable you to use git diff with files that haven't been added to the Git repository yet by diffing your untracked file against its empty version in the staging area:

    $ echo foo > foo.txt
    $ git diff foo.txt
    
    $ git add --intent-to-add foo.txt
    $ git status
    On branch master
    Your branch is up-to-date with 'origin/master'.
    
    Changes to be committed:
      (use "git reset HEAD <file>..." to unstage)
    
            new file:   foo.txt
    
    Changes not staged for commit:
      (use "git add <file>..." to update what will be committed)
      (use "git checkout -- <file>..." to discard changes in working directory)
    
            modified:   foo.txt
    
    $ git diff --staged foo.txt
    diff --git a/foo.txt b/foo.txt
    new file mode 100644
    index 0000000..e69de29
    
    $ git diff foo.txt
    diff --git a/foo.txt b/foo.txt
    index e69de29..257cc56 100644
    --- a/foo.txt
    +++ b/foo.txt
    @@ -0,0 +1 @@
    +foo
    

    Once you've diffed the file, you can add the non-empty version to the staging area/index by simply doing a normal git add:

    $ git add foo.txt
    

    Enable git commit -a of untracked files

    Likewise, since --intent-to-add makes untracked files "known" to Git by adding empty versions of those files to the staging area/index, it also allows you to use git commit --all or git commit -a to commit those files along with your known modified files, which is something that you wouldn't be able to do otherwise.

    As explained in the official Linux Kernel documentation for git commit:

    using the -a [or --all] switch with the commit command [will] automatically "add" changes from all known files (i.e. all files that are already listed in the index)...and then perform the actual commit

    Documentation

    From the official Linux Kernel git add documentation:

    -N
    --intent-to-add
    

    Record only the fact that the path will be added later. An entry for the path is placed in the index with no content. This is useful for, among other things, showing the unstaged content of such files with git diff and committing them with git commit -a.

    0 讨论(0)
  • 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 <file>..." 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 <file>..." to unstage)
      
          new file:   def.txt
          new file:   ghi.txt
      
      Changes not staged for commit:
        (use "git add <file>..." to update what will be committed)
        (use "git checkout -- <file>..." 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
      
    0 讨论(0)
提交回复
热议问题