我调查了一个棘手的问题,最后决定通过逐步减少我的闭源项目来创建一个SSCCE。在每个步骤中,我都验证了问题发生并且git
与git commit --amend
一起提交,即只有一个提交。现在,我想在像gitlab.com或github.com这样的FLOSS代码托管平台上发布SSCCE作为公共项目。
我是否有可能暴露任何已被git commit --amend
覆盖的文件内容?将推送的当前提交可以视为保存以发布。
我知道我可以删除存储库并在本地重新创建它。这是一个值得学习的问题。
答案 0 :(得分:1)
(警告:这是一个很长的答案;我没有时间写一个较短的答案。部分是在做其他事情之间写的,文本中可能存在一些语义空白。)
这个答案有很多部分,但简短的版本是git push
通常会推送一组最小的提交,这会带来最小的树和文件集。这意味着在正常情况下,您将在这里安全。
可能存在与您有关的异常案例的可能性,这些可能是创建没有其他历史记录的新存储库的充分理由。
让我们先看看git commit --amend
真正做了什么,因为它实际上并没有修改任何提交。为实现这一目标,我们必须从git commit
如何在低级别工作开始,这意味着查看实际的提交。
这是来自Git的Git存储库中的一个:
$ git rev-parse HEAD
468165c1d8a442994a825f3684528361727cd8c0
$ git cat-file -p 468165c1d8a442994a825f3684528361727cd8c0 | sed 's/@/ /'
tree 6a54cb7c68d97e863a28478d728c58a1e47f0b4f
parent 1614dd0fbc8a14f488016b7855de9f0566706244
author Junio C Hamano <gitster pobox.com> 1522689215 -0700
committer Junio C Hamano <gitster pobox.com> 1522689215 -0700
Git 2.17
Signed-off-by: Junio C Hamano <gitster pobox.com>
这是一个典型的正常提交:与所有提交一样,它有一个tree
,并且像大多数但不是所有提交一样,它有一个parent
行。它有一个作者和提交者 - 每个提供三个信息:名称,电子邮件地址和时间戳 - 它有一条日志消息,它包含第一个空白行之后的所有行。
父母或父母告诉Git在此提交之前提交。其他提交可能有两个或多个父项 - 这些是合并提交 - 并且至少有一个提交,这是您在存储库中创建的第一个提交,其中没有父项,因为没有来到它之前。 parent
行允许Git将提交链接在一起。没有父项的提交是 root 提交,其中链结束。
提交的文件存储在tree
对象下面,这有点大 - 这里只是其中的一部分:
100644 blob 536e55524db72bd2acf175208aef4f3dfc148d42 COPYING
040000 tree ccab6cfb14e8e198eb4981fbfbee7ac091478119 Documentation
100755 blob 1b4624c876dae8f38f7c9e13f82d11b6ead39c9b GIT-VERSION-GEN
100644 blob c39006e8e7e5c5be2114b79d50135dc08e3d1aaa INSTALL
100644 blob d38b1b92bdb2893eb4505667375563f2d6d4086b LGPL-2.1
100644 blob a1d8775adb4b38a0340cd7d04184915f0ee65d28 Makefile
每个树条目提供模式,对象类型(从模式和底层对象隐式),底层对象的散列ID和路径名组件。对于某个目录中的文件,该组件是文件的名称;对于子树,组件是目录的名称。递归遍历所有tree
对象让Git为每个文件构建完整路径名,当检查 out 提交时,Git会执行此递归行程以构建索引。 (我们通常可以忽略索引,但它本质上是递归扩展且因此扁平化的树,Git可以使用它来获取文件的完整路径名。)
实际文件内容存储在 blob 对象中,因此要查看名为COPYING
的文件的内容:
$ git cat-file -p 536e55524db72bd2acf175208aef4f3dfc148d42 | head -3
Note that the only valid version of the GPL as far as this project
is concerned is _this_ particular version of the license (ie v2, not
所以,总结一下上面的内容:
master
= 468165c1d8a442994a825f3684528361727cd8c0
(目前)。因此,我们可以看到提交通常如何增长。我们从单个根提交开始,没有父节点,以及标识该提交的名称master
:
A <--master
然后我们进行新的提交:在{{1}新的blob到索引之后,我们将Git打包当前索引作为新的树对象,将根提交的哈希ID写为{{ 1}}行,写出自己作为作者和提交者&#34;现在&#34;作为两个时间戳,写出我们的日志消息,并将整个事件转换为新的提交对象,该对象获取新的唯一哈希ID。我们的新提交git add
已将parent
作为其父级,因此,如果我们将新提交的哈希ID写入B
,我们会得到:
A
重复足够长的时间,你有一长串的提交,以提交到master
之类的分支名称结束。您现在可以A <-B <--master
这些提交到另一个Git存储库,如果您愿意:您的Git向他们发送所有您不具备的提交,例如,如果他们的{{ 1}}在提交master
结束,并且您通过git push
添加了master
,您的Git会发送这五个提交,以及他们使用的任何树和文件对象在B
或C
中找到。
G
A
如果我们已经有了一个链:
B
我们运行--amend
,Git像往常一样创建一个新的提交--amend
,但有一个例外:新提交的父项是/是父项当前提交。也就是说,新提交A--B--C <-- master
指向git commit --amend
使用的相同提交:
D
提交对象D
本身,以及树和任何子树以及提交C
的文件blob,都仍在存储库中。他们将留在那里,直到有东西来移除它们。 (一旦没有名称,垃圾收集器 C
/
A--B--D <-- master
将最终删除它们。但是我们的Git将有 reflog条目提供隐藏名称,因此保护他们一个月左右,所以他们至少会坚持这么久。)
假设服务器无论身在何处,都会提交C
和C
。如果我们现在git gc
,我们的Git将调用服务器,实际上, 1 从中获取其A
所代表的哈希ID。这告诉我们的Git什么提交,因此树和blob,他们有,它告诉我们的Git我们的Git应该发送什么。在这种情况下,那只是提交B
以及它(我们认为)服务器Git缺乏的任何相关对象。
即使我们正在修改&#34;这仍然适用。根提交。修改只是意味着使用当前提交的父级进行新提交,所以如果我们只有一个提交git push
并且我们&#34;修改&#34;它,我们得到:
master
在确定要发送哪些提交以及任何必要的支持对象之后,我们的Git将它们打包成包文件,这会获得额外的压缩 - 这就是所有< em>计算对象和压缩对象输出是关于并发送包文件,接收Git会根据需要将其扩展回来。
这里的关键是包文件只必要的对象 - 或者更确切地说,我们的Git 假设是必要的,基于提交和其他对象找到从我们专门告诉我们Git推送的提交中遍历我们的提交图,不包括他们Git告诉我们他们拥有的那些提交。
1 在这个例子中,我们的Git可以真正做到这一点。但是,在更复杂的情况下,我们的Git不一定会拥有他们的分支的提示。在这种情况下,通常的协议使用has / want交换,通过提交哈希ID来确定要发送的提交以及另一端已经拥有的提交。
以上都适用于确定要发送的对象。为了使它工作,我们的Git必须与服务器Git交谈,以找出服务器有什么。这是正常的(基于https或ssh的)推送系统,但并非所有传输都使用这样的系统。 The Pro Git Book has a chapter on the standard "dumb" and "smart" protocols,并提到push始终使用智能协议。因此,只要您的Git使用智能协议并且您不小心要求它发送不在单一提交D
分支顶端的对象,您将只发送您关心的对象约。
但是,如果有人自上次进行此分析以来已经优化了什么呢?如果新的协议意识到他们的 Git什么都没有,那么你的 Git已经将所有对象打包成一个好的包文件,并且有大量的网络带宽,所以只是发送那个包文件?他们可能会所有你的对象,包括你(相信你)修改过的对象。它今天不会发生,但明天呢?
你不 担心这一点。它几乎肯定是安全的,甚至未来的优化可能会小心不要交出像这样的延迟对象。但是你愿意依靠它多少钱?