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
It is mainly used to add an empty file for your commits.
More informations at Git with intent to add!.
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 sometimesCommit 3cf773e (
cache-tree
: fix writing cache-tree whenCE_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
andthe-last-file
.
subdir
should be there too (even though it only records two sub-entries,file2
andfile3
).
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 renamesBefore 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 inwt-status.c
does not handle this case and produces incorrect status output.
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
git commit -a
of untracked filesLikewise, 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
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 withgit commit -a
.
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.)
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.
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.)
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.
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:
git add
the "file creation" delta into #1This 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 #1git 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:
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/
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)
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
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)
git diff
does nothing, since it doesn't operate on #3.
sh-4.1$ git diff
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
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
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
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