1

我阅读了有关git patch命令的内容,但所有示例都显示了如何为所有未暂存或缓存的文件,甚至是某些提交包含的所有文件(从一个提交到另一个提交)获取补丁。但是假设我有10 个未暂存的文件,我需要为其中的 6 个创建一个适当的补丁。我怎样才能做到这一点?如果有办法创建这样的补丁,我该如何应用它? 很抱歉,如果很明显。

4

1 回答 1

1

您必须首先定义短语适当的补丁。什么使补丁正确?是什么让一个人不合适?就此而言,补丁差异有什么区别? 没有一个固定的答案,但请参阅补丁和差异文件之间的区别

也就是说,git diff会产生差异,并且非常灵活。它是供人类使用的,而不是供机器使用的,因此它的输出可能是也可能不是您想要的(特别是因为您没有定义任何术语)。该git format-patch程序不太灵活,更适合机器使用:它产生的输出很容易被 消化git apply,这意味着应用单个补丁而不提交它,或者git am意味着应用存储在您的整个系列的补丁以“邮箱格式”的计算机,提交每一个。(am这里或多或少是应用邮箱的缩写。)

因为你还没有定义你的术语,所以你的问题没有一个正确的答案。如果我们假设您的意思是生成一个git apply可以应用的文件,我们会得到一个可能的答案。如果我们假设您的意思是生成一个git am可以应用和提交的邮箱格式补丁,我们会得到不同的答案。

git format-patch命令将从一些提交或提交中生成一个邮箱格式的补丁(或一系列补丁) 。所以要使用它,你必须做出一个 commit。如果您愿意,您可以简单地在新分支上提交您希望在补丁中包含的那些特定文件。(请参阅下面的详细信息。)

git diff程序或其任何更面向机器的相关命令(git diff-tree、、、git diff-filesgit diff-index将产生人类可读的差异。如果它没有上色,它将适合与git apply. 要使用这些,您无需提交。

正如LeGEC 所指出的,您可以git diff在特定文件上使用。请注意,默认情况下,将每个指定路径的索引副本与同一路径的工作树副本进行比较。这可能就是你想要的。如果您已配置为始终生成彩色输出,请在一个操作期间将其关闭。将输出保存在某处:git diff -- pathsgit diffgit diff

git diff -- file1 ... fileN > /tmp/patch

或者:

git diff --color=never -- file1 ... fileN > /tmp/patch

如果您需要禁用着色。

长:关于提交

如果以上内容足够,则无需阅读其余部分。

如果您对正确补丁的定义意味着git am可以变成一个提交,那么您将需要进行新的提交。这就是了解stagednot staged以及分支名称和提交如何工作变得非常重要的地方。

Git 本质上是关于提交的。分支——或者更具体地说,分支名称——很有用,特别是如果你是一个人,但它们并不是 Git 的真正意义所在。Git 是关于提交的。

每个提交都有编号,但这些数字不是简单的顺序计数。我们找不到提交#1,然后提交#2,依此类推。相反,每个提交都会获得一个唯一的哈希 ID。这个东西看起来是随机的,但实际上完全是非随机的,并且是经过仔细计算的,因此每个Git 都会以相同的方式对 100% 相同的、逐位相同的提交进行编号。这样,每个地方的每个 Git 都会同意这个提交获得这个哈希 ID,并且没有其他提交获得这个哈希 ID。

提交的内容分为两部分:数据元数据:

  • 提交中的数据非常简单:它是Git 知道的每个文件的完整快照。

    这显然使 Git 存储库变得非常庞大,因为每个提交都存储每个文件。但他们不会(变得非常肥胖)。原因是文件以特殊的、只读的、仅限 Git 的、压缩的和去重复的格式存储。如果您进行一千次提交,每个提交有 1000 个文件,但每次重用1000 个文件中的999 个,那么所有一千次提交与其他提交共享999 个文件。

    (除此之外还有更多,但重复数据删除是第一步,而且是非常大的一步。)

  • 提交中的元数据是有关提交的信息。例如,这是 Git 存储提交人的姓名和电子邮件地址的地方。不过,在这个元数据中,Git 存储了 Git 自己需要的一组特定信息。每个提交都存储其父提交的提交编号——哈希 ID 。这就是历史在存储库中的存在方式。

由于提交中的文件是只读的——而且只有Git,永远冻结,压缩成这种特殊的冻干格式,只有 Git 本身可以使用——使用的文件必须从提交中提取. 这就是git checkout(或者,从 Git 2.23 开始,git switch)所做的。你选择一个提交并告诉 Git:从这个提交中提取所有文件,以便我可以看到它们并使用 / 处理它们。

由于每个提交都会记住它之前的提交,所以要使用Git,您需要做的就是让 Git 为您记住最后一次提交的唯一哈希 ID 。这就是分支名称的来源。 分支名称仅包含要被视为该分支一部分的最后一次提交的哈希 ID 。

这意味着我们可以像这样绘制一个分支:

... <-F <-G <-H   <--master

名称 master包含最后一次提交的实际哈希 ID,此时为哈希 ID H。该提交保存了所有文件的快照,还保存了之前提交的哈希 ID G

Git 可以通过其哈希 ID 查找任何提交(实际上是任何内部对象),因此 Git 可以查找 commit H,提取其所有文件,然后让您处理它。或者,Git 可以查找 commit H,找到其父哈希 IDG并查找 commit G。然后 Git 可以提取G的文件,或查找其父哈希 ID F。这是 Git 的第一个大秘密。

提交、你的工作树和索引

鉴于上述情况——只读提交和读/写文件——我们已经看到 Git 必须将提交提取到您可以查看和处理文件的区域中。该区域是您的工作树工作树。因此,每个感兴趣的文件都有两个副本:一个在当前提交中,它一直被冻结,另一个在您的工作树中,您可以使用它。这非常简单,但有一个转折点:您可以在这个区域创建 Git知道的文件。

不过,这里真正棘手的部分是 Git 如何进行新的提交,以及 Git知道哪些文件。例如,您可能认为 Git 只会保留一个文件名列表,并使用您的工作树文件进行新的提交……但事实并非如此。

取而代之的是,Git 保留了第三份副本——好吧,一份去重的副本,采用冻干格式——在中间位置。在当前提交和您的工作树之间,Git 拥有Git 在结帐时从提交中取出的每个文件的另一个“副本”(已经去重,所以不完全是副本)。

每个文件的这些中间“副本”位于 Git 所称的不同位置,即indexstaging area,或(这些天很少)cache。请注意,这些副本已准备好进入新的提交,因为它们已经是仅 Git 的冻干格式。与提交本身的副本不同,它们可以被替换。

这就是git add全部。该git add命令意味着使某些文件的索引副本与工作树副本匹配。 如果您更改了工作树中的文件,则必须告诉 Git 将更新后的文件复制回 Git 的索引中。

这就是暂存文件。在任何时候,Git 的索引都有 Git 知道的每个文件的副本。如果它在索引中,它就可以提交了——但它可能与当前提交中的文件相同!如果是这样,它已经被删除了,Git 可以很容易地判断它是相同的。

如果文件的索引副本与当前提交副本不同,或者是全新的,则进入下一次提交的内容与当前提交中的内容不同。Git 调用了暂存的 . 但是,如果索引副本与提交的副本相同,Git 什么也不说。

同时,文件的索引副本可能与工作树副本匹配,也可能不匹配。如果索引副本确实与工作树文件匹配,Git 不会说任何关于它的内容。如果不是,Git 会说该文件是unstaged

这意味着一个文件可以同时为提交暂存和不为提交暂存!如果提交的副本(无法更改)与索引副本不匹配,并且索引副本与工作树副本不匹配,则您有一个文件既为提交暂存,又不为提交暂存。您可以通过以下方式获得此状态:

git checkout somebranch
edit file.ext             # change something in a file
git add file.ext          # copy the updated file back into Git's index
edit file.ext             # change something else in the same file

当你运行git commit时,Git 所做的就是从当时 Git 索引中的任何内容进行新的提交。因此,如果您现在有 10 个未暂存的文件,其中有git add6 个然后运行git commit,您将获得一个新的提交,其中:

  • 六个文件与上一个提交(运行之前的当前提交)不匹配git commit,但是
  • 所有其他文件与之前的提交匹配。

现在您已经做出了新的提交,新的提交就是当前的提交。您是索引中的文件创建的,因此新的当前提交的所有文件都与索引中的所有文件匹配。没有文件被“暂存以进行提交”,但是您没有 git add的四个文件仍然存在于您的工作树中,与索引中相应的四个文件和当前提交中的这四个文件仍然不同。所以这四个文件仍然是“未暂存提交”。

如果您愿意,您现在可以git add将这四个文件复制回 Git 的索引,然后复制git commit结果。您现在有两个以前没有的新提交。最后一个与您的工作树匹配,因此当前提交、Git 的索引和您的工作树都匹配:没有文件被暂存,也没有文件被取消暂存。

更多关于分支名称

请注意,每次您进行新提交时,Git 都必须更新当前分支名称。假设您master最初在您的分支上,如下所示:

...--F--G--H   <-- master

现在您创建一个的分支名称,例如feature. 这个新名称标识了 commit H。我们将 name HEAD(全部大写)添加到分支名称之一,以显示我们正在使用的分支名称:

...--F--G--H   <-- feature (HEAD), master

现在我们将处理git add一些文件并进行新的提交。它将获得一个看起来随机的新哈希 ID;我们就这样称呼它I

...--F--G--H   <-- master
            \
             I

诀窍在于,Git 现在将I的哈希 ID 写入名称 feature(附加到的名称)中HEAD,以便名称指向I现在:

...--F--G--H   <-- master
            \
             I   <-- feature (HEAD)

如果我们再次添加更多文件git commit,我们会得到另一个新的提交J

...--F--G--H   <-- master
            \
             I--J   <-- feature (HEAD)

请注意,每个提交都有每个文件的完整快照,因为它在您运行时出现在 Git 的索引中git commit。当您使用git format-patch将提交转换为补丁时,Git:

  • 从提交的父级中提取文件(例如Hfor I);
  • 从提交 ( ) 中提取文件I
  • 比较提取的文件;和
  • 告诉您哪些文件不同,为您提供将旧版本更改为新版本的秘诀。

由于这是一个可提交的补丁,Git 添加了一个标头,给出了提交者的姓名和电子邮件地址、适当的日期和时间戳,以及提交者解释原因的日志消息他们做出了承诺。补丁本身位于此标头之后。

于 2020-08-10T17:00:28.530 回答