git am/format-patch: 控制行尾格式

14

我使用以下方法从三个提交创建了一个补丁:

git format-patch <revision_three_commits_ago>

这将创建三个补丁文件,我从笔记本电脑发送邮件,并在我的台式电脑(两者均为Windows系统)上阅读邮件。

现在我要做的是

git am --3way --ignore-space-change *.patch
补丁已经应用了,但我得到的提交SHA1 ID不同。在修改后的文件中搜索一下,我发现我的台式电脑上修改的行以LF结尾,而笔记本电脑上(我创建补丁的地方)修改的行以CR LF结尾。
所以,我的第一个想法是在没有--ignore-space-change的情况下调用git am,但这会给我一个错误(patch does not apply)。
我怎样才能告诉git format-patchgit am如何处理行结尾(msysgit 1.7.4)?
我真的需要用VIM将文件格式从UNIX改为DOS,然后再应用补丁吗?
编辑:甚至使用VIM修改补丁文件也没有帮助:我以为set ff=dos:%s/^M//g会有帮助,但事实并非如此!
在我看来,应用补丁应该得到完全相同的内容,并且与我从创建补丁的其他仓库拉取的内容具有相同的提交哈希值。我是否对此想错了?

注意:现在你有另一个选项,使用 Git 2.3.0(2015年2月):而不是依赖于 --keep-cr,现在可以使用 --transfer-encoding 指定要使用的传输编码 (quoted-printable, 8bit, base64)。参见下面我的答案 - VonC
4个回答

21

在尝试了各种选项(core.autocrlfcore.eol)之后,我发现使用

git am --keep-cr

这个技巧可以解决问题(但会导致警告出现尾随空格)。

不需要手动编辑补丁文件或其他污物。

但是,(当然)哈希值与nikai的答案中描述的不同... 感谢nikai指向哈希内容。

在我的笔记本-台式机方案中,我想将一些更改离线从笔记本传输到台式机,但仓库不应分歧,也不应该在桌面上应用补丁并执行git pull desktop时发生相同的提交两次。

为了实现这一点,我采取了以下措施:

  1. 在桌面上,按照上述方法使用git am --keep-cr ...应用补丁
  2. 在笔记本上,执行git pull desktop,这会导致每个由补丁引入的提交都出现两次(一次是原始笔记本提交,一次是打过补丁并拉入桌面的提交)
  3. 现在(在笔记本的master分支上),发出git rebase desktop/master命令会导致No changes -- Patch already applied消息,并将原始笔记本提交替换为桌面提交

8
Git 2.3.0(2015年2月)将提供另一个新选项:--transfer-encoding,以便指定要使用的传输编码(quoted-printable、8位、base64),而不仅仅依赖于--keep-crgit send-email man page.
git am man page.
请参见commit 8d81408Paolo Bonzini (bonzini)添加:

git-send-email: 添加--transfer-encoding选项

邮件列表线程详细介绍了在具有CRLF行结尾的存储库中使用“git am”应用补丁时出现的问题。
在线程示例中,存储库源自“git-svn”,因此无法在其上使用core.eol和其他设置。
目前,最好的选择是使用“git am --keep-cr”。
但是,当补丁创建新文件时,补丁应用过程将拒绝新文件,因为它发现了一个“/dev/null\r”字符串而不是“/dev/null”。
问题在于SMTP传输是不安全的
通过电子邮件发送补丁与通过“dos2unix | unix2dos”传递相同。
通常情况下,新引入的CRLF是透明的,因为git-am会将其剥离。 keepcr=true设置保留它们,但这主要是偶然的,并且在具有混合LF和CRLF行结尾的存储库中具有“git am”工作流程将非常棘手。
MIME解决方案是引用可打印的传输编码
这不是我们想要默认启用的内容,因为它使接收到的电子邮件难以查看。
但是,对于将CRLF行结尾存储在存储库中的项目来说,这非常匹配。
引用可打印唯一的缺点是引用可打印的补丁无法应用,如果维护者使用“git am --keep-cr”,因为解码的补丁将在行末具有两个回车符。
因此,还需要添加对base64传输编码的支持,这使得接收到的电子邮件在MUA(邮件用户代理)之外几乎无法查看,但确实有效。
该补丁涵盖了所有方面,包括仍然生活在80年代末期的用户,通过提供一个7位内容传输编码,拒绝发送其中包含非ASCII字符的电子邮件。
最后,“8bit”将添加Content-Transfer-Encoding标头,但除此之外什么也不做。

git send-email的文档现在将包括:

--transfer-encoding=(7bit|8bit|quoted-printable|base64)

Specify the transfer encoding to be used to send the message over SMTP.
7bit will fail upon encountering a non-ASCII message.

Quoted-printable can be useful when the repository contains files that contain carriage returns, but makes the raw patch email file (as saved from a MUA) much harder to inspect manually.

Default is the value of the 'sendemail.transferEncoding' configuration value; if that is unspecified, git will use 8bit and not add a Content-Transfer-Encoding header.


在Git 2.32(2021年第二季度)中, "git mailinfo"(man)(因此 "git am"(man))学会了 "--quoted-cr" 选项,以控制以CRLF结尾的行如何处理包含base64或qp编码的文本。

请查看由 Đoàn Trần Công Danh (sgn) 提交的提交 59b519a, 提交 133a4fd, 提交 f1aa299, 提交 0b68956(2021年5月10日)以及提交 dd9323b, 提交 d582992(2021年5月6日)。
(合并于 提交 483932a,2021年5月16日,由Junio C Hamano -- gitster --完成)

mailinfo: 如果在解码base64/QP电子邮件时发现CRLF,则发出警告

签名:Đoàn Trần Công Danh

当SMTP服务器接收到8位电子邮件消息时,可能仅使用LF作为行尾符,其中一些决定将该LF更改为CRLF。当一些邮件列表软件接收到8位电子邮件消息时,决定对这些消息进行base64或quoted-printable编码。如果电子邮件通过上述邮件服务器传输,然后由此类邮件列表软件分发,则收件人将收到一个包含在另一种编码中编码的CRLF编码的补丁的电子邮件。因此,"mailsplit"无法删除此类CRLF中的CR。因此,无法干净地应用该发送的补丁。已经观察到了这样的意外情况have been observed in the wild。我们不要默默地拒绝这些消息,而是在找到此类CR(作为CRLF的一部分)时向用户发出警告。警告内容如下:
warning: quoted CRLF detected

3

这里有一个类似的问题:为什么明显相同的提交会产生不同的sha1值?

简单回顾一下,git cat-file commit <sha>应该能够缩小范围,确定树、父级、电子邮件、日期、作者或提交者名称是否不同,或者提交消息中是否引入了额外的'\n'。


2
我忘记了日期也包含在提交哈希中。所以,同样的内容为什么不会产生相同的哈希值就可以理解了。但是这仍然无法回答为什么会有不同的行结尾符。 - eckes

2
简短回答:使用命令git am --committer-date-is-author-date 详细解释:我在尝试在两个代码库之间传递提交时发现了同样的问题。在尝试使用git am的选项后,我发现文件ID总是匹配的,但是对于相同的选项,连续运行git am会产生不同的提交ID。原来有两个时间戳 - 你通常看到的“作者”时间戳和创建提交时的“提交者”时间戳。后者默认情况下设置为当前时间。你需要使用--committer-date-is-author-date选项来保持日期同步,这将使提交ID同步。
因此,如果您的环境允许,我建议使用git bundle,它更可靠。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接