git add --intent-to-add 或 -N 有啥作用,啥时候应该使用?

Posted

技术标签:

【中文标题】git add --intent-to-add 或 -N 有啥作用,啥时候应该使用?【英文标题】:What does git add --intent-to-add or -N do and when should it be used?git add --intent-to-add 或 -N 有什么作用,什么时候应该使用? 【发布时间】:2014-08-11 07:36:08 【问题描述】:

git add -h 上我可以看到以下选项:

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

但我不明白什么时候应该使用这个选项。这个选项的真正作用是什么,应该如何使用它?

【问题讨论】:

"只记录稍后会添加路径的事实"。在典型的 Git 方式中,帮助文本不是很清楚,尽管 official documentation 解释它有点更好(但不是更好)。 【参考方案1】:

启用未跟踪文件的差异

Blue112's answer部分正确。 git add --intent-to-add 确实为工作副本中每个指定的未跟踪文件添加了一个空文件到暂存区/索引,但这样做的主要目的之一是让您能够使用 git diff 包含尚未添加到 Git 存储库的文件,方法是将未跟踪的文件与暂存区域中的空版本进行比较:

$ 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

对文件进行 diff 后,您可以通过简单地执行普通的git add 将非空版本添加到暂存区/索引:

$ git add foo.txt

启用git commit -a 的未跟踪文件

同样,由于 --intent-to-add 通过将这些文件的空版本添加到暂存区/索引,使 Git “知道”未跟踪的文件,它还允许您使用 git commit --allgit commit -a 提交这些文件以及您已知的已修改文件,否则您将无法做到这一点。

如official Linux Kernel documentation for git commit中所述:

-a [或--all] 开关与commit 命令一起使用[将] 自动“添加”所有已知文件(即已在索引中列出的所有文件)的更改...然后执行实际的提交

文档

来自official Linux Kernel git add documentation:

-N
--intent-to-add

仅记录稍后将添加路径的事实。路径的条目被放置在没有内容的索引中。除其他外,这对于使用 git diff 显示此类文件的未暂存内容并使用 git commit -a 提交它们很有用。

【讨论】:

您的回答是正确且有道理的,所以 +1。但是--intent-to-add 对我来说似乎没有用。假设我有一个包含 100 行代码的新文件 foo.txt。当我 git add -N foo.txt 并运行 git diff foo.txt 时,我看到了 100 行添加的代码。 (似乎这相当于剪切所有文件的内容,添加文件,然后将其粘贴回文件中。)现在,当我进行更改时——比如添加一行——然后再次 git diff,我现在看到添加了 101 行代码。因此,向舞台添加空文件路径对查看差异没有帮助。 @chharvey 这取决于您的工作流程。只需暂存文件并忽略此新选项即可获得您想要的行为。我喜欢在输入 git diff 时看到我编写的所有代码,这样我就可以在提交和打开 PR 之前在一个地方自我审查所有内容。 我经常在迭代模式下打补丁,没有-N就不可能打补丁新添加的文件。【参考方案2】:

user456814's answer 很好地解释了git add -N 的用途。我只是想更详细地解释一下后台发生的事情。

您可以将 git 视为维护文件更改的历史记录,例如创建、修改和删除(我们称它们为 deltas)。在最近的提交之前,这些增量是不言自明的,但是当您在项目中工作以准备一个新的提交 时,事情会变得更加复杂。当您处于这种情况时,有三种不同类型的增量。

(旁注:大多数人在第一次接触 git 时,都会被教导类似“在 git 中提交需要两个步骤;首先你执行git add,然后你可以执行git commit”。虽然这是真的,但它只关注要提交的更改类型的增量。了解git add -N还需要了解其他类型的增量。)

#1:要提交的更改

通常称为“分阶段更改”,当您运行 git status(如果有)时,这些增量会出现在顶部。如果你的 shell 支持颜色,它们将是绿色的。

当你git add 一个文件时,它会被提升到这个类别中。如果您在没有任何标志的情况下运行 git commit,这些更改将实际包含在内。

#2:更改未暂存以提交

当您运行git status(如果有)时,这些增量会出现。如果您的外壳支持颜色,它们将是红色的。

这些是您对 git 存储库中的文件所做的更改尚未提交且尚未移至 #1。当您编辑文件然后保存时,它默认显示在此类别中。

要显示在此类别中的文件,它必须已经存在于最近的提交中,或者如果要提交 #1 中的更改则添加强>。否则,它将显示在类别 #3 中。

(注意:因为您选择何时将文件“提升”到类别 #1,所以可能会在 #1 和 #2 中显示相同的 file。例如,我可以见

modified:   abc.txt

#1 中的绿色,并且

modified:   abc.txt

同时在#2 中显示为红色。如果我将文件提升到 #1,然后对其进行更多更改,就会发生这种情况。 #1 中的条目引用了我在提升文件之前创建的增量,可能添加了新的代码行,而 #2 中的条目引用了我之后创建的增量,可能在顶部添加了注释。如果我更有条理,我会在将文件提升到 #1 之前进行所有更改,以完全避免这种情况。)

#3:未跟踪的文件

当您运行git status(如果有)时,这些增量最后出现。如果您的外壳支持颜色,它们将是红色的。

这些是所有不在最近提交中且不在 #1 中的文件。虽然从技术上讲,添加它会更改提交,但从技术上讲,该文件可能一直存在,人们只是不希望 git 记录有关它的任何信息。 (在这种情况下,您应该将该文件添加到 .gitignore,它会停止在 git status 中显示。)

当您创建一个全新的文件时,它会显示在此类别中。

那么这一切与git add -N 有什么关系?

git add -N 旨在让#3 deltas 的使用变得更容易。正如上面接受的答案中提到的,git diff 让您可以看到您实际准备了哪些增量。 Here 是一组很好的命令,可以与 git diff 一起使用。

git diff 仅显示 #1 和 #2 之间的差异(即 #2 中的增量)。

git diff --staged 仅显示 #1 中的增量。

git diff HEAD 仅显示 #1 和 #2 中的增量,放在一起。

请注意,这些命令都没有查看#3。但是,通过运行git add -N,您基本上可以执行以下操作:

将新文件拆分为两个增量:仅创建文件,并使用文本/内容填充文件 git add #1 中的“文件创建”增量

这具有使第二个增量出现在#2 中的效果。现在新文件已经完全脱离了#3,你可以使用git diff命令来处理它。

至于git commit -a,本质上它的作用是:

git add #2 中的所有内容,因此它也与 #1 中的所有内容一起上演 git commit(它获取 #1 中的所有内容,包括刚刚添加的内容,并从中创建实际提交)

没有git add -N,这个命令会丢失你在#3 中的新文件;但是,您可以看到在运行 git add -N 之后,您的新文件分布在 #1 和 #2 中,并将包含在提交中。


好吧,我已经解释了我想解释的一切。如果您想检查您的理解,您可以按照以下示例进行操作:

    我创建了一个新的 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 告诉我这个仓库是空的。

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

    我制作了一些新文件。

    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 显示所有新文件当前都在类别 #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 什么都不做,因为它不在#3 上运行。

    sh-4.1$ git diff
    

    我提交一个文件,添加另一个,add -N 第三个。

    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
    

    git status 中,abc.txt 不再出现,因为它已经被提交了。 def.txt 仅显示在类别 #1 中,因为已添加整个文件。 ghi.txt 出现在 #1 和 #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
    

    通过运行git diff,我可以显示#2 中列出的所有增量。唯一出现的增量是我在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
    

    通过运行git diff --staged,我可以显示#1 中列出的所有增量。其中三个出现:创建一个新文件def.txt,在def.txt 中添加一行,并创建一个新文件ghi.txt。尽管def.txt 有 2 个增量,但文件名本身在上面的示例 7 中只输出一次,以避免混乱。

    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
    

【讨论】:

很好的例子和解释。 +1【参考方案3】:

主要用于为你的提交添加一个空文件。

更多信息请访问Git with intent to add!。

【讨论】:

【参考方案4】:

请注意,在 git 2.10(2016 年第三季度)之前,git add -N 有时会跳过一些条目。

参见Nguyễn Thái Ngọc Duy (pclouds)commit 6d6a782、commit c041d54、commit 378932d、commit f9e7d9f(2016 年 7 月 16 日)。(由 Junio C Hamano -- gitster -- 合并到 commit 3cc75c1,2016 年 7 月 25 日)

如果你有:

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

而你add -N 一切...然后subdir 文件记录为 i-t-a(“打算添加”)条目。

cache-tree.c:修复 i-t-a 条目有时会跳过目录更新

Commit 3cf773e(cache-tree:修复在存在CE_REMOVE 时写入缓存树 - 2012-12-16 - Git v1.8.1.1)在从索引构建树对象时跳过 i-t-a 条目。不幸的是,它可能会跳过太多。

如果subdir/file1是一个ita,由于这段代码中的条件不正确,我们仍然认为“subdir”是一个ita文件,而不是写下“subdir”并跳转到最后一个文件。 结果树现在只有两个项目:a-filethe-last-filesubdir 也应该在那里(即使它只记录两个子条目,file2file3)。


git status 已经改进,使用 Git 2.17(2018 年第二季度,四年后):在工作树中移动路径后(因此 它显示为“已删除”),然后使用 -N 选项添加(因此 使它看起来“添加”),git status 将其检测为重命名,但没有 正确报告新旧路径名。

请参阅commit 176ea74、commit 5134ccd、commit ea56f97、commit 98bc94e、commit 06dba2b、commit 6de5aaf(2017 年 12 月 27 日)Nguyễn Thái Ngọc Duy (pclouds)。 帮助者:Igor Djordjevic (boogisha)。(由 Junio C Hamano -- gitster -- 合并于 commit 12accdc,2018 年 2 月 27 日)

提醒:itai-t-a 代表“意图添加”,-N 的作用。

wt-status.c: 处理工作树重命名

425a28e 之前(diff-lib:允许将 ita 条目视为“尚不存在” in index" - 2016-10-24, Git 2.11.0-rc0) 索引中从来没有“新文件”,这实际上禁用了重命名检测,因为我们只在 diff 对中出现新文件时检测重命名。

在那次提交之后,一个 i-t-a 条目可以作为一个新文件出现在“git diff-files”中。但是wt-status.c中的diff回调函数没有 处理这种情况并产生不正确的状态输出。

【讨论】:

以上是关于git add --intent-to-add 或 -N 有啥作用,啥时候应该使用?的主要内容,如果未能解决你的问题,请参考以下文章

Git-撤销(回退)已经add,commit或push的提交

gitadd文件名

git add提交后撤销 ,出现git add Untracked files

怎么修改Git remote add时使用的远程仓库

`Git add --patch` 多个文件:从上一个文件转到大块

是否有任何用于“git add -p”的 GUI 工具?