Git 相当于 hg add + hg diff?
Git equivalent of hg add + hg diff?
在习惯了 mercurial 之后,我最近开始使用 git。
在 mercurial 中,如果我 hg add
一些文件,然后 hg diff
,我会得到一个补丁,然后我可以应用,理论上,使用简单的 patch -p1
并获得完全相同的本地副本。
现在,有了 git,情况就不同了:你 git diff
在 git add
之前。但是我怎样才能让 git diff
也覆盖所有未跟踪的文件,比如 hg add
ing 之后的 hg diff
?
TL;DR:添加 所有内容,然后添加 运行 git diff --cached
Mercurial 和 Git 在这里有不同的理念。 Git 明确公开了 Git 调用的 索引 。 Mercurial 没有索引(它在内部有类似的东西但不公开它,所以你甚至不需要知道它的存在)。很多喜欢Git的人认为暴露指数很棒,很多骂Git的人认为它很可怕。 :-) 尽管如此,这就是您在这里遇到的问题,如果您正在使用 Git,那么您正在使用索引,所以是时候了解它是什么以及如何处理它了。
所以,让我们定义"the index"。 Git 的 index——也被称为 staging area,有时也称为 cache — 是一个复杂的小野兽,有很多 Git 通常不会 暴露的大部分隐藏方面。但是,它确实有一个您需要知道的简单定义:它是您构建 下一次提交以进行 .
的地方
这里值得一提的是 Git 和 Mercurial 之间的另一个区别。 Mercurial 存储变更——变更集,技术性的——而Git存储快照。大多数时候,这没有什么区别。快照很容易转换为变更集:只需将快照与其父级进行比较即可。鉴于 parent-as-snapshot,变更集很容易转换为新的快照:只需应用变更集。但是,应用很长的变更集链很慢,因此 Mercurial 会定期存储快照。它在幕后完成所有这一切,您永远不必意识到这一点。 Git,像往常一样,暴露一切(这有点像 flasher or streaker 那种方式,运行 赤身裸体,暴露没有人真正想看到的悬垂位)。
当您 运行 git commit
时,Git 将索引中的任何内容转换为提交快照。所以 git add
将一个文件放入索引中。如果文件已经存在,git add
会用从 work-tree 中获取的新版本替换现有副本。如果文件还没有,git add
将work-tree 版本作为新文件放入索引中。无论哪种方式,索引版本现在都已更新——暂存——并准备好进入下一个快照。
要从索引中取出一个文件out,可以运行git rm
。这将从 索引 和 work-tree 中删除文件。或者,您可以 运行 git rm --cached
,它只将它从索引中取出,将其留在 work-tree 中(但要小心,因为这可能是一个未来的陷阱)。
现在,因为索引/staging-area/缓存是这样暴露的,你可以git diff
它。为此,请使用 git diff --cached
或 git diff --staged
(它们具有完全相同的含义;我通常坚持使用 --cached
,因为 git rm
具有 --cached
而不是 --staged
).
问题是这个 仅 比较索引中已更新的文件。更准确地说,它 运行 相当于 git diff HEAD <index>
,即将当前提交与索引的内容进行比较。这意味着您在 work-tree 中修改但 尚未 暂存的任何文件都不是 diff-ed。虽然解决方案很简单:只是 git add
那些文件。
旁白:.gitignore
未跟踪与忽略
一次添加一堆文件很痛苦,因此您可能想使用 git add .
或 git add -A
(它们略有不同;请参阅其他 Whosebug 问题和答案,并注意Git 2.0 版有一个很大的变化,影响了这里的 -A
选项)。然而,您的 work-tree 经常有您 不想 添加的文件,这就是我们进入未跟踪文件与 untracked-and-ignored 文件的时候。
现在我们知道了索引是什么,untracked 文件有一个非常(对于 Git)简短而有趣的定义。未跟踪文件是指不在索引中的文件。仅此而已——仅此而已。如果它在索引中,它就会被跟踪。如果不是,那就不是。
但是当然有一个复杂的问题(在 Mercurial 中也有):如果你有一堆未跟踪的文件,你会从版本控制系统那里得到很多关于它们的抱怨。要闭嘴,您可以将文件名或 glob 模式添加到 .gitignore
。请注意,与 Mercurial 不同,您不能将正则表达式添加到 .gitignore
,只能添加 glob 模式。这既好(glob 模式 far 更容易正确)也不好(glob 模式不如完整的正则表达式那么强大),但无论如何,它就是这样。 1
.gitignore
中列出的文件不会自动添加 git add -A
或 git add .
。但是,在 .gitignore
中列出文件不会使其无法跟踪。 唯一使文件无法跟踪的 是它不在索引。如果您不小心将不应跟踪的文件放入索引,则必须从索引中git rm
它。
旁白:索引给你带来了一些强大的东西
从 Mercurial 迁移到 Git 的人一开始通常非常讨厌索引。 git add -p
让许多人更喜欢它的一件事是。有些人根本用不着这个,但对那些用过的人来说,其实还是很不错的。
Git 在 "what is added to the index, and will be in the next commit" 和 "what is in the work-tree" 之间的分离意味着您可以签出一个分支,修改一些项目以进行调试,修改 其他 项(在相同或不同的文件中)来解决问题或添加功能,然后 选择性地添加 错误修复或新功能,而不是调试更改.
当您 git commit
结果时,您得到的提交只有错误修复或新功能,没有额外的调试。
和往常一样,这既有优点也有缺点。例如,很难确定您刚刚提交的内容是否真的有效。也许只有额外的调试才能让它工作。也许你忘记了 git add
其中的一部分。但是,因为 Git 有点鼓励
"amending" 和重写提交,2 并使提交和分支 真的 便宜,你可以在 Git 中以不同的方式工作水银。 Mercurial 分支更重,它的提交和变基以及 hg histedit
明显更慢,这阻碍了这种快速和松散的 commit-recommit-rebase-fixup-squash 工作。 Git 强烈 en 鼓励这样做。您应该以不同的方式使用 Git,在很多临时分支上进行大量临时提交。您没有,但尝试一下是个好主意。
1Mercurial 在 .hgignore
中支持 glob 模式 和 正则表达式。不幸的是,正则表达式——那些很难正确使用的表达式——在实践中比 glob 模式快得多。我已经 co-workers 将 globs 更改为 regexps 以提高速度,但后来弄错了。如果您要将 glob 模式转换为正则表达式,请记住锚定它们,并注意 .
!
2在 Mercurial 和 Git 中,提交几乎都是永久性的。但是,两者都提供历史编辑和 commit --amend
。他们以非常不同的方式到达那里:Git 通过复制旧的提交来进行新的提交,并移动分支名称以指向新的提交。这会在存储库中创建 "abandoned" 个对象。 Git 使用它所谓的 reflogs 将它们保留一段时间,以便您可以在需要时恢复它们,然后最终使 reflog 条目和 "garbage collects" left-over 垃圾以完全摆脱它。
Mercurial 从字面上看 不能 那样做,因此它 "strips" 变更集,将它们放入 strip-backup 导出的变更集文件中。如果你想让他们回来,你可以 re-import 他们。这比 Git 的 loosey-goosey "commit, recommit, move branch pointer, abandon old objects" 方法 "rewriting history" 慢得多。由于 Git 的方法在时间和 space 方面成本较低——您将重写的临时提交通常非常接近免费,尽管这确实取决于 "loose object" 文件大小——在 Git.
中 执行 更有价值
在习惯了 mercurial 之后,我最近开始使用 git。
在 mercurial 中,如果我 hg add
一些文件,然后 hg diff
,我会得到一个补丁,然后我可以应用,理论上,使用简单的 patch -p1
并获得完全相同的本地副本。
现在,有了 git,情况就不同了:你 git diff
在 git add
之前。但是我怎样才能让 git diff
也覆盖所有未跟踪的文件,比如 hg add
ing 之后的 hg diff
?
TL;DR:添加 所有内容,然后添加 运行 git diff --cached
Mercurial 和 Git 在这里有不同的理念。 Git 明确公开了 Git 调用的 索引 。 Mercurial 没有索引(它在内部有类似的东西但不公开它,所以你甚至不需要知道它的存在)。很多喜欢Git的人认为暴露指数很棒,很多骂Git的人认为它很可怕。 :-) 尽管如此,这就是您在这里遇到的问题,如果您正在使用 Git,那么您正在使用索引,所以是时候了解它是什么以及如何处理它了。
所以,让我们定义"the index"。 Git 的 index——也被称为 staging area,有时也称为 cache — 是一个复杂的小野兽,有很多 Git 通常不会 暴露的大部分隐藏方面。但是,它确实有一个您需要知道的简单定义:它是您构建 下一次提交以进行 .
的地方这里值得一提的是 Git 和 Mercurial 之间的另一个区别。 Mercurial 存储变更——变更集,技术性的——而Git存储快照。大多数时候,这没有什么区别。快照很容易转换为变更集:只需将快照与其父级进行比较即可。鉴于 parent-as-snapshot,变更集很容易转换为新的快照:只需应用变更集。但是,应用很长的变更集链很慢,因此 Mercurial 会定期存储快照。它在幕后完成所有这一切,您永远不必意识到这一点。 Git,像往常一样,暴露一切(这有点像 flasher or streaker 那种方式,运行 赤身裸体,暴露没有人真正想看到的悬垂位)。
当您 运行 git commit
时,Git 将索引中的任何内容转换为提交快照。所以 git add
将一个文件放入索引中。如果文件已经存在,git add
会用从 work-tree 中获取的新版本替换现有副本。如果文件还没有,git add
将work-tree 版本作为新文件放入索引中。无论哪种方式,索引版本现在都已更新——暂存——并准备好进入下一个快照。
要从索引中取出一个文件out,可以运行git rm
。这将从 索引 和 work-tree 中删除文件。或者,您可以 运行 git rm --cached
,它只将它从索引中取出,将其留在 work-tree 中(但要小心,因为这可能是一个未来的陷阱)。
现在,因为索引/staging-area/缓存是这样暴露的,你可以git diff
它。为此,请使用 git diff --cached
或 git diff --staged
(它们具有完全相同的含义;我通常坚持使用 --cached
,因为 git rm
具有 --cached
而不是 --staged
).
问题是这个 仅 比较索引中已更新的文件。更准确地说,它 运行 相当于 git diff HEAD <index>
,即将当前提交与索引的内容进行比较。这意味着您在 work-tree 中修改但 尚未 暂存的任何文件都不是 diff-ed。虽然解决方案很简单:只是 git add
那些文件。
旁白:.gitignore
未跟踪与忽略
一次添加一堆文件很痛苦,因此您可能想使用 git add .
或 git add -A
(它们略有不同;请参阅其他 Whosebug 问题和答案,并注意Git 2.0 版有一个很大的变化,影响了这里的 -A
选项)。然而,您的 work-tree 经常有您 不想 添加的文件,这就是我们进入未跟踪文件与 untracked-and-ignored 文件的时候。
现在我们知道了索引是什么,untracked 文件有一个非常(对于 Git)简短而有趣的定义。未跟踪文件是指不在索引中的文件。仅此而已——仅此而已。如果它在索引中,它就会被跟踪。如果不是,那就不是。
但是当然有一个复杂的问题(在 Mercurial 中也有):如果你有一堆未跟踪的文件,你会从版本控制系统那里得到很多关于它们的抱怨。要闭嘴,您可以将文件名或 glob 模式添加到 .gitignore
。请注意,与 Mercurial 不同,您不能将正则表达式添加到 .gitignore
,只能添加 glob 模式。这既好(glob 模式 far 更容易正确)也不好(glob 模式不如完整的正则表达式那么强大),但无论如何,它就是这样。 1
.gitignore
中列出的文件不会自动添加 git add -A
或 git add .
。但是,在 .gitignore
中列出文件不会使其无法跟踪。 唯一使文件无法跟踪的 是它不在索引。如果您不小心将不应跟踪的文件放入索引,则必须从索引中git rm
它。
旁白:索引给你带来了一些强大的东西
从 Mercurial 迁移到 Git 的人一开始通常非常讨厌索引。 git add -p
让许多人更喜欢它的一件事是。有些人根本用不着这个,但对那些用过的人来说,其实还是很不错的。
Git 在 "what is added to the index, and will be in the next commit" 和 "what is in the work-tree" 之间的分离意味着您可以签出一个分支,修改一些项目以进行调试,修改 其他 项(在相同或不同的文件中)来解决问题或添加功能,然后 选择性地添加 错误修复或新功能,而不是调试更改.
当您 git commit
结果时,您得到的提交只有错误修复或新功能,没有额外的调试。
和往常一样,这既有优点也有缺点。例如,很难确定您刚刚提交的内容是否真的有效。也许只有额外的调试才能让它工作。也许你忘记了 git add
其中的一部分。但是,因为 Git 有点鼓励
"amending" 和重写提交,2 并使提交和分支 真的 便宜,你可以在 Git 中以不同的方式工作水银。 Mercurial 分支更重,它的提交和变基以及 hg histedit
明显更慢,这阻碍了这种快速和松散的 commit-recommit-rebase-fixup-squash 工作。 Git 强烈 en 鼓励这样做。您应该以不同的方式使用 Git,在很多临时分支上进行大量临时提交。您没有,但尝试一下是个好主意。
1Mercurial 在 .hgignore
中支持 glob 模式 和 正则表达式。不幸的是,正则表达式——那些很难正确使用的表达式——在实践中比 glob 模式快得多。我已经 co-workers 将 globs 更改为 regexps 以提高速度,但后来弄错了。如果您要将 glob 模式转换为正则表达式,请记住锚定它们,并注意 .
!
2在 Mercurial 和 Git 中,提交几乎都是永久性的。但是,两者都提供历史编辑和 commit --amend
。他们以非常不同的方式到达那里:Git 通过复制旧的提交来进行新的提交,并移动分支名称以指向新的提交。这会在存储库中创建 "abandoned" 个对象。 Git 使用它所谓的 reflogs 将它们保留一段时间,以便您可以在需要时恢复它们,然后最终使 reflog 条目和 "garbage collects" left-over 垃圾以完全摆脱它。
Mercurial 从字面上看 不能 那样做,因此它 "strips" 变更集,将它们放入 strip-backup 导出的变更集文件中。如果你想让他们回来,你可以 re-import 他们。这比 Git 的 loosey-goosey "commit, recommit, move branch pointer, abandon old objects" 方法 "rewriting history" 慢得多。由于 Git 的方法在时间和 space 方面成本较低——您将重写的临时提交通常非常接近免费,尽管这确实取决于 "loose object" 文件大小——在 Git.
中 执行 更有价值