【问题标题】:Git equivalent of hg add + hg diff?Git相当于hg add + hg diff?
【发布时间】:2017-01-24 10:37:17
【问题描述】:

在习惯了 mercurial 之后,我最近开始使用 git。

在 mercurial 中,如果我 hg add 一些文件,然后是 hg diff,我会得到一个补丁,然后理论上我可以应用一个简单的 patch -p1 并获得完全相同的本地副本。

现在,有了 git,事情就不同了:你 git diff 在你 git add 之前。但是我怎样才能让git diff 也覆盖所有未跟踪的文件,比如hg diffhg adding 之后?

【问题讨论】:

  • 我觉得你在这里要求多件事,但 Git 中的差异只涉及从一个提交到另一个提交的跟踪文件之间的更改。未跟踪的文件不属于图片的一部分,除非您先跟踪它们。

标签: git mercurial diff


【解决方案1】:

TL;DR:添加所有内容,然后运行git diff --cached

Mercurial 和 Git 在这里有不同的理念。 Git 显式地公开了 Git 所称的索引。 Mercurial 没有索引(它在内部有类似的东西但没有公开它,所以你甚至不需要知道它的存在)。许多喜欢 Git 的人认为暴露的索引很棒,而许多诅咒 Git 的人认为它很糟糕。 :-) 尽管如此,这是阻碍你前进的原因,如果你使用 Git,你就在使用索引,所以是时候了解它是什么以及如何处理它了。

所以,让我们定义“索引”。 Git 的 index——也称为 staging area,有时也称为 cache——是一个复杂的小野兽,有很多隐藏的方面Git 通常不会公开。但是,它确实有一个您需要知道的简单定义:它是您构建下一个要进行的提交的地方。

这里值得一提的是 Git 和 Mercurial 之间的另一个区别。 Mercurial 存储变更——变更集,是技术性的——而 Git 存储快照。大多数情况下,这并没有真正的区别。快照很容易转换为变更集:只需将快照与其父级进行比较。给定 parent-as-snapshot,变更集很容易转换为新快照:只需应用变更集。不过,应用很长的变更集链很慢,因此 Mercurial 会定期存储快照。它在幕后完成所有这一切,您永远不必意识到它。像往常一样,Git 会暴露一切(这有点像 flasherstreaker 那样,赤身裸体地跑来跑去,暴露出没有人真正想看到的笨拙的部分)。

当您运行git commit 时,Git 会将索引中的任何内容转换为提交快照。所以git add 将一个文件放入索引中。如果文件已经存在,git add 将现有副本替换为从工作树中获取的新版本。如果文件在那里,git add 将工作树版本作为新文件放入索引中。无论哪种方式,索引版本现在都已更新——暂存——并准备好进入下一个快照。

要从索引中取出文件,您可以运行git rm。这会将文件从 both 索引 工作树中删除。或者,您可以运行 git rm --cached,它只将其从索引中取出,将其留在工作树中(但要小心,因为这可能是一个未来的陷阱)。

现在,因为索引/暂存区/缓存是这样暴露的,你可以git diff它。为此,请使用git diff --cachedgit diff --staged(它们的含义完全相同;我通常坚持使用--cached,因为git rm--cached,但没有--staged)。

问题在于,这区分已在索引中更新的文件。更准确地说,它运行相当于git diff HEAD <index>,即,它将当前提交与索引的内容进行比较。这意味着您在工作树中修改但尚未暂存的任何文件都不会进行差异化处理。解决方案很简单:只需 git add 那些文件。

除此之外:.gitignore 和未跟踪与忽略

一次添加一堆文件很痛苦,因此您可能想使用git add .git add -A(这些略有不同;请参阅其他 StackOverflow 问题和答案,并注意周围发生了很大变化Git 版本 2.0 会影响此处的 -A 选项)。但是,您的工作树通常包含您不想添加的文件,这就是我们进入未跟踪文件与未跟踪和忽略文件的时间。

现在我们知道索引是什么,对于 untracked 文件有一个非常(对于 Git 而言)简短而优美的定义。未跟踪的文件是不在索引中的文件。就是这样——这就是它的全部。如果它在索引中,它就会被跟踪。如果没有,那就不是。

但当然有一个复杂的问题(在 Mercurial 中也有):如果你有一堆未跟踪的文件,你会从版本控制系统中得到很多关于它们的抱怨。要关闭它,您可以将文件名或 glob 模式添加到 .gitignore。请注意,与 Mercurial 不同,您不能将正则表达式添加到 .gitignore,只能添加 glob 模式。这既好(glob 模式更容易正确)也不好(glob 模式不如完整的正则表达式强大),但无论如何,它就是这样。 1

.gitignore 中列出的文件不会自动添加到git add -Agit add .。但是,.gitignore 中列出文件并不会使其无法跟踪。唯一会使文件无法跟踪的是它不在索引中。如果您不小心将不应跟踪的文件放入索引中,则必须从索引中git rm

顺便说一句:索引给你一些强大的东西

从 Mercurial 迁移到 Git 的人一开始通常非常讨厌索引。让许多人更喜欢它的一件事是git add -p。有的人根本用不上,但有用的人,其实挺好的。

Git 将“添加到索引中的内容,以及将在下一次提交中的内容”与“工作树中的内容”分开意味着您可以签出分支,修改一些项目以进行调试目的,修改其他项(在相同或单独的文件中)以修复问题或添加功能,然后选择性地添加仅错误修复或新功能,而不是调试更改。

当您git commit 结果时,您会得到一个只有错误修复或新功能的提交,而不是额外的调试。

像往常一样,这既有优点也有缺点。例如,很难确定您刚刚提交的内容是否真的有效。也许只有额外的调试才能使它工作。也许你忘了git add 其中的一部分。然而,因为 Git 有点鼓励 “修改”和重写提交,2 并使提交和分支真正变得便宜,您在 Git 中的工作方式与在 Mercurial 中不同。 Mercurial 分支更重,它的提交和变基以及hg histedit 明显变慢,这不鼓励这种快速和松散的 commit-recommit-rebase-fixup-squash 工作。 Git 强烈en鼓励这样做。您应该以不同的方式使用 Git,在大量临时分支上进行大量临时提交。您没有必须这样做,但尝试一下是个好主意。


1Mercurial 支持.hgignore 中的全局模式 正则表达式。不幸的是,正则表达式——那些很难做到的——在实践中比 glob 模式快得多。我曾让同事将 glob 更改为正则表达式以提高速度,但后来弄错了。如果您要将 glob 模式转换为正则表达式,请记住锚定它们,并注意.

2在 Mercurial 和 Git 中,提交几乎是永久性的。但是,两者都提供历史编辑和commit --amend。它们以非常不同的方式到达那里:Git 通过复制旧提交来生成新提交,并将分支名称移动到指向新提交的位置。这会在存储库中创建“废弃”对象。 Git 使用它所谓的 reflogs 将它们保留一段时间,以便您可以根据需要恢复它们,然后最终使 reflog 条目过期并“垃圾收集”剩余的垃圾来获取彻底摆脱它。

Mercurial 确实不能这样做,因此它“剥离”变更集,将它们放入剥离备份导出的变更集文件中。然后,如果您想要它们,您可以重新导入它们。这比 Git 的“重写历史”的松散的“提交、重新提交、移动分支指针、放弃旧对象”方法要慢得多。由于 Git 的方法在时间和空间方面的成本更低——您将重写的临时提交通常非常接近免费,尽管这确实取决于“松散对象”文件的大小——它对 在 Git 中做这个。

【讨论】:

    猜你喜欢
    • 2011-12-07
    • 2013-03-18
    • 1970-01-01
    • 2012-04-17
    • 2012-06-24
    • 2016-12-11
    • 2019-12-29
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多