git - “git push”和“git exile push”有什么区别?

标签 git

我有一个git存储库,并被指示执行以下操作序列:


将给定的文件集从文件夹复制到上述git存储库(“源文件夹”不是存储库的一部分)。
执行git add .
执行git exile push folder_name/
执行git commit -m 'Commit message'


现在,我想了解我在做什么。更具体地说,前两个步骤对我来说很清楚(我在存储库中更改了一些内容,然后将此更改添加到“临时区域”,因此已准备好用于git commit)。但是,最后两个步骤(3和4)令人困惑,我对它们有以下问题:


通常我们先commit然后是push。为什么我们在这里做不同的操作(先按first,然后按commit)?
我们使用git push代替git exile push。两者有什么区别?它推到哪里?它推动什么?


我听说这与大文件有关。我们不是“明确地”使用它们,而是使用它们的“引用”(或指向它们的“链接”)。但这到底是什么意思?

添加

我假设git exile push接收大文件,将其内容复制到适合保存较大文件的位置,然后通过指向其副本的链接替换原始文件的内容。因此,换句话说,文件的内容将由指向其内容副本的链接替换。之后,git exile push执行git add。因此,它更改了文件,将它们添加到临时区域,而我唯一需要做的就是git commit

我的解释正确和完整吗?

最佳答案

git exile不属于Git。从ElpieKay's link很明显,它在某些方面类似于Git-LFS(它也不是Git的一部分),并且在“添加”部分中进行了描述:


我假设git exile push接收大文件,将其内容复制到适合保存较大文件的位置,然后通过指向其副本的链接替换原始文件的内容。因此,换句话说,文件的内容将由指向其内容副本的链接替换。


就目标而言,这是正确的,但就其潜在机制而言,这是不正确的。

对于Git-LFS,目标是基于文件的大小,并且Git-LFS中包含许多使之有效的代码。对于Git-Exile(我没有使用过,也没有做过详尽的检查-我对代码进行了快速的了解),目标是基于“二进制性质”而不是大小,并且您必须选择要声明为二进制的文件按名称模式。也就是说,您可能会说*.jpg和/或*.exe被视为二进制。

现在让我们来详细说明。

您的工作树,提交和分支名称

您已经知道Git提交了存储文件(“快照”)。如果您还不了解这一点,请阅读一些说明该部分如何工作的内容。为了使内容更小巧,Git以特殊的,仅Git的格式存储文件,只有Git可以处理。您需要使用非Gitty格式的文件,以便可以使用它们。因此,Git将文件从快照中复制到工作树中,这是您进行工作的区域。

但是,现在考虑一个相当明显的事实:提交完全是只读的。您永远不能更改任何现有提交的内容。您可以随时阅读它们。您可以进行新的(和不同的)提交,而将现有的提交保留下来。您永远都无法更改提交。

每个提交都由一个大的,难看的,显然是随机的哈希ID标识,例如e3a80781f5932f5fea12a49eb06f3ade4ed8945c(这是Git存储库Git本身中的一个提交)。这些ID基本上是人类无法使用的,因此我们选择一些重要的提交,例如分支上的最新提交,并为其命名,例如master。提交哈希的名称会随着时间而变化:每次我们向存储库添加新的提交时,Git都会为其分配一个新的唯一哈希ID。如果我们只是将新提交添加到master分支,Git会将新ID存储到名称master中,以便该名称始终标识最新提交!

每次提交后,都会永久修复。它还存储先前提交的哈希ID(并永久存储该ID,因为没有东西可以更改提交)。因此,使用我们以名称master找到的最新提交,我们可以向后查找较早的提交:

      <-C   <--master


我们只需要遵循提交C中的箭头(哈希ID)来查找先前的提交:

  <-B <-C   <--master


现在也有一个箭头(实际上是父哈希ID)从B冒出来,所以我们找到了更早的提交:

A <-B <-C   <--master


在我们小的示例存储库中,只有三个提交:A是有史以来的第一个提交,因此它没有父箭头/哈希ID,并且我们知道我们可以停止追逐父链接。

工作树非常简单,但这不是提交,提交也不是工作树。 Git可以将提交提取到工作树中,并最终(某种程度上)将工作树保存到新的提交中,但是Git坚持要遍历其索引。其他版本控制系统没有索引,或者如果它们确实具有类似于索引的功能,则它们会将其完全隐藏,而您不必了解它。 Git的方向相反。

指标

这一切都意味着,无论何时使用Git,您都必须了解并使用Gi​​t调用的索引,或者(取决于谁在进行调用以及他们想强调什么)索引区域或缓存。这是一件事情的三个名字。这件事非常重要,以至于用这三个名字来结束!好吧,那个或第一个“索引”是一个如此糟糕的名字... :-)但是,认真地说,索引一直在您的面前,让您了解它位于您和提交之间。

简而言之,Git的索引包含将进入您下一次提交的文件。这意味着索引开始时将保存当前提交中的所有文件。

当您运行git commit时,Git立即打包索引中的所有内容,并从这些文件中进行新的提交,但是它们现在会出现在索引中。索引稍后可能包含不同的内容,但是在您运行git commit时,Git会获取其中的内容,将其打包,然后进行新的提交。

新的提交指向当前的提交。因此,如果我们有如上所述的简单三提交存储库:

A--B--C   <-- master (HEAD)


并且当我们将D附加到分支HEAD时,我们进行一次新的提交master,以便当前的提交为提交C,新的提交将指向C,而Git将使名称为< cc>指向master

A--B--C--D   <-- master (HEAD)


分支就是这样发展的。

那么如何使文件进入索引呢?

由于此索引又称为暂存区域非常重要,因此您需要知道如何将文件放入索引。当然,它是从当前提交的文件开始的,该文件来自D,但是那又如何呢?

什么部分主要是git checkout。运行:

git add README.txt


告诉Git从您的工作树中打包git add的内容,将其转换为仅Git的特殊格式,并将其填充到索引中,并命名为README.txt

这意味着Git中的文件流如下所示:

提交->索引<->工作树

使用README.txt,可以将文件从某些提交(通常是当前提交)复制到索引中,在那里它们保留特殊的仅Git格式,但现在变为可写状态。然后从索引到工作树,然后它们变成普通格式。使用git checkout,将文件从工作树复制到索引中,覆盖之前的副本,然后将文件转回特殊的仅Git格式。

最终,您运行git add将索引打包成一个提交。提交将保存索引中的所有内容,该索引已转换为仅Git格式,因此这部分非常容易。 Git只是确保文件作为提交的一部分永久存在,即,将来覆盖索引版本的git commit不会覆盖或丢弃提交的版本。用于仅Git格式的基本机制(使用“垃圾回收”进行哈希处理)使这一工作变得微不足道。

涂抹并清洁滤网

以上所有内容中隐藏了一个有趣的观点:Git必须将文件从索引复制到工作树中,从而将仅Git格式扩展为普通格式。而且,Git必须将文件从工作树复制到索引中,然后将其压缩为仅Git格式。如果我们在复制过程中偷偷摸摸怎么办?

Git在这里提供了自己的内部过滤器,例如执行CR-LF行尾而不是仅LF行尾,或者扩展git add以包含哈希ID。这些过滤器意味着索引中的内容和工作树中的内容实际上不再匹配。该文件的索引版本不仅是工作树文件的压缩版本。它是修改版本或替换版本。

这就是Git-LFS和Git-Exile的工作方式。它们添加了在“从索引到工作树的提取”步骤中运行的过滤器,以及在“从工作树到索引的压缩”中运行的过滤器。这些过滤器实际上不交换整个文件内容,而不仅仅是交换CRLF和仅LF的结尾或扩展或压缩$Id$字符串。

$Id$期间,大文件或二进制文件根本不会进入索引。 LFS或Exile过滤器会将实际文件保存在其他位置,然后将链接放入Git。 Git将此称为干净过滤器:它将icky工作树文件清理成一个不错的干净索引版本。

git add期间,大型或二进制过滤器不在索引中,但是LFS或Exile过滤器获取链接并从其他位置查找实际文件,然后将该文件放入您的工作树中。 Git称它为污迹过滤器:它从索引中删除了漂亮的干净提交版本,并对其进行了污垢处理,从而生成了邪恶的工作树文件。

调用污点和清除过滤器的机制是将文件名全局模式放入git checkout文件中,并带有.gitattributes指令。 the filter= documentation under the gitattributes section中对此进行了描述。 Git-LFS通过过滤每个文件,检查文件大小,使用Git长期运行的过滤过程技巧来减少开销,从而工作。 Git-Exile通过使用简单得多的每文件过滤器方法来匹配感兴趣的文件。

应在何时何地保存移动的文件?


通常我们先提交然后推送。为什么我们在这里做不同的操作(先推送然后提交)?


使用Git-LFS,不在索引中的大文件会立即发送到大文件服务器。使用Git-Exile,大文件将被填充到辅助存储库中(如果我正确阅读了代码和描述)。

filter步骤将移动的文件推送到关联的辅助存储库。您不必一定要先执行此操作,这是一个好主意,以防有人在您有机会进行操作之前抓住您的链接对象。 (甚至有人可能是您。工作树文件仍然存在,但是如果您在仅具有链接的索引条目上调用污迹过滤器,它将查找已移动的文件。)

摘要

现在,我们可以看到这在思想上是正确的,但在执行方面是错误的:


我假设git exile push接收大文件,将其内容复制到适合保存较大文件的位置,然后通过指向其副本的链接替换原始文件的内容。因此,换句话说,文件的内容将由指向其内容副本的链接替换。


替换实际上发生在git exile push时间!另一方面,仅链接版本的替换发生在git add期间。

关于git - “git push”和“git exile push”有什么区别?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49334624/

相关文章:

git - 如何在没有ssh auth的情况下设置git服务器

在我创建提交消息之前,Git 提交中止,我使用 "--wait"

git - v2.0 从零开始。我应该继续使用现有的 git repo 还是开始一个新的?

git - Smartgit 打不开

c# - 推荐用于 Windows 的 Git 工具

java - Jenkins - 在每次构建之前检查 Git

Git推送多个账户

git - 无法推送到 GitHub

linux - 如何在 linux 中传入读取的 bash 变量并将其作为消息发送到 git commit 中?

git - 如何附加分支名称并提交到 git 存档输出文件?