git 存储库中的 Composer 包冲突;如何在推送到远程时取消跟踪文件但避免删除文件

Composer package conflict in git repository; how to untrack files but avoid deletion of files when pushing to remote

我通过 composer 在我的网络应用程序上安装了一个包。 并将包文件夹添加到 .gitignore,同时提交 composer.jsoncomposer.lock

为了部署到我们的服务器,我们推送到服务器上的裸机 Git 远程服务器,后者又将修改后的文件推送到服务器上的相关位置。

这个工作流程一切正常。

稍后,在存储库中工作的其他人将包文件添加到存储库并从 git忽略中删除了包。

我们希望包版本完全由作曲家管理,而不是像以前那样由 git 存储库管理。

到目前为止我唯一的想法是执行以下操作:

  1. 从存储库中删除文件并将包文件夹添加回git忽略。提交这个。
  2. 推送到远程(显然会推送删除的文件)
  3. 运行composer update赶紧在服务器上推送一次,重新安装删除的包。

但是这里的问题是 从服务器上删除包几秒钟,我们希望尽可能避免这种情况,因为它是地点。我们不想破坏某些东西。

有什么方法可以让包文件夹不被跟踪,同时推送提交时不会导致包从远程删除

我已在此处阅读了 assume-unchangedskip-worktree (Git - Difference Between 'assume-unchanged' and 'skip-worktree'),但我不确定使用哪个以及这些命令中的任何一个(如果有)具体会产生什么影响在遥控器上?

Is there any way I can remove the package folder from being tracked, whilst NOT causing the package to be deleted from the remote when the commit is pushed?

没有

幸运的是,您可能不需要。

不幸的是,无论您在这里做什么,使用起来都会有些难看和痛苦。

I have read about assume-unchanged and skip-worktree ... but I am unsure which to use and what effect either of these commands will have (if any) specifically on the remote?

两者都可以,但 --skip-worktree 是您应该在此处使用的那个。两者都不会对任何其他 Git 存储库产生任何影响。


要理解所有这些,您需要一个关于 Git 实际作用的正确模型。

首先要记住Git中的基本存储单元是commit。每个提交都有一个唯一的、又大又丑的哈希 ID,例如 083378cc35c4dbcc607e4cdd24a5fca440163d17。该哈希 ID 是提交的 "true name"。每个 Git 存储库都同意 that 哈希 ID 保留用于 that 提交,即使 Git 存储库有问题 尚未提交。 (这就是 Git 中所有真正魔法的来源:这些 seemingly-random 的唯一性,但实际上 totally-not-random,散列 ID。)

提交存储的内容分为两部分:数据,它包含所有文件的快照;加上 元数据 ,其中 Git 存储诸如提交人、时间(date-and-time 标记)和原因(日志消息)等信息。作为元数据的重要部分,每个提交还存储一些 previous 提交哈希 ID,作为文本中的原始哈希 ID。这让 Git 从任何给定的提交,向后,到之前的某个提交。

任何 Git 提交的实际哈希 ID 只是其所有数据的校验和。 (从技术上讲,它只是元数据的校验和,因为快照本身作为单独的 Git object 存储,其哈希 ID 进入提交 object。但是,这个单独的 object 的哈希 ID 也是一个校验和,因此通过 Merkle trees 的数学计算,一切都解决了。)这就是 为什么 提交中的所有内容完全 read-only,永远冻结。如果您尝试更改提交内的任何内容,您实际上并没有更改 提交。相反,你会得到一个 new 提交,带有一个新的不同的哈希 ID。旧提交仍然存在,其哈希 ID 未更改。

所以:Git 是关于提交的,Git 通过哈希 ID 查找提交。但是我们人类无法处理哈希 ID(快点,是 08337-something 还是 03887-something?)。我们想要 names,例如 master。同时,Git 想要一种快速的方法来找到在某个点结束的某个提交链中的 last 提交。所以 Git 通过让我们创建 分支名称 .

来为我们提供名称

分支名称只包含某个链中last提交的哈希ID。该提交持有链中 parentprevious 提交的哈希 ID。 parent 提交持有,因为它的 parent——我们最后一次提交的总 parent——提交的哈希 ID 更进一步,依此类推:

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

如果提交哈希 ID 是像 H 这样的单个字母,这可能是一个准确的绘图:名称 master 将包含哈希 ID H,提交 H 将持有哈希 ID G 作为其 parent,提交 G 将持有哈希 ID F 作为其 parent,依此类推。

进行 new 提交的行为包括:

  • 写出所有文件的快照;和
  • 添加适当的元数据:您作为作者和提交者,"now" 作为 date-and-time-邮票,等等。这个新提交的 parent 应该是 current 提交的任何内容,如当前分支名称中记录的那样。如果 master 指向 H 那么新提交的 parent——我们称之为 I——将是 H,因此 I points back toH`.

实际进行了此提交(并在此过程中找到了它的哈希 ID),Git 只需将新的哈希 ID I 写入分支名称 master:

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

我们有一个新的提交。

要查看 发生了什么 在诸如 I 之类的提交中,Git 将提交及其所有文件提取到一个临时区域,然后提取之前commit H的文件到一个临时区,然后对比。对于那些相同的,Git 什么也没说。对于那些不同的,Git 显示了差异。对于那些新的,Git 表示它们是 "added",而对于那些在上一次提交中但不在本次提交中的,git 表示它们是 "deleted"。

现在,对某些特定提交执行 git checkout 意味着以您可以使用的形式编写该提交的 内容 ——即数据。提交中的 frozen-for-all-time 文件副本采用 Git-only 格式,这对于存档来说很好,但对于完成新工作毫无用处。所以 Git 必须将提交提取到一个工作区,您可以在其中查看和使用您的文件。 Git 将此工作区称为您的 work-treeworking tree(或这些的某些变体艾姆斯)。除了在您询问时将文件写入其中之外,Git 主要是此工作区的 hands-off:那是 您的 游乐场,而不是 Git 的。

但是新提交中的新快照从何而来?在某些版本控制系统中,新快照来自 work-tree 中的文件。 不是Git中的情况。相反,Git 从 Git 的 index 中的任何内容进行新提交。你不能看到这些文件——至少,不容易——但是当Git第一次提取一些提交时,它有效地将所有该提交的保存、冻结的文件复制到Git的索引。只有当它们进入索引后,Git 才会将它们复制(并解冻/再水化)到您的 work-tree 中,以便您可以使用它们。

提交中的冻结副本与索引中的 "soft-frozen" 副本之间的关键区别在于,您 可以 覆盖索引副本。1 你不能覆盖 committed 副本,但这没关系:提交不能更改,但你可以做出新的更好的提交,这就是版本控制无论如何。

每当您 运行 git commit 时,Git 在第一步(制作快照)中所做的就是简单地打包所有 pre-frozen index 每个文件的副本。因此我们可以将索引视为 提议的下一次提交 。这也是为什么你必须一直 git add 文件,即使它们已经在之前的提交中。 git add 正在做的是 复制 work-tree 文件到该文件索引中的任何内容之上(尽管技术细节再次参见脚注 1)。

这意味着 每个文件始终有三个 "live" 副本。一个被冻结在 当前提交 中。一个是semi-frozen,在index,Git也叫暂存区。最后一个是你的副本,在你的 work-tree 中,你可以用它做任何你想做的事:它是一个普通文件,而不是特殊的 Git-only 格式。

当你运行 git status, Git 运行s 两个单独的比较:

  • 首先,git status将当前(HEAD)提交中的所有文件与索引中的所有文件进行比较。对于每个 相同 的文件,Git 什么都不说。对于每个 不同 的文件,Git 表示此文件 暂存以提交 。如果索引中的文件是新的——不在 HEAD 中——Git 称它为新的;如果文件从索引中消失,Git 表示它已删除

  • 然后,git status 将索引中的所有文件与 work-tree 中的所有文件进行比较。对于每个 相同 的文件,Git 什么都不说。对于每个 不同 的文件,Git 表示此文件 未暂存提交 。如果 work-tree 中的文件是新文件——不在索引中——Git 会抱怨该文件 未跟踪 。如果文件从 work-tree消失,Git 表示它已删除。

最后一个案例是 未跟踪 文件的来源。它还为我们提供了未跟踪的定义:如果 work-tree 中存在的文件不存在于索引中,则该文件未被跟踪。由于我们无法 查看 索引,因此我们只能在 git status 抱怨这些未跟踪的文件时看到这种情况。

.gitignore 文件中列出未跟踪的文件会使 Git 闭嘴:git status 不会再发牢骚了。它还使 git add 添加 文件到索引,如果它不存在的话,但它对 的文件没有影响在索引中。如果文件在索引中,根据定义,它会被跟踪,git add 会很乐意添加它。

这终于是 --assume-unchanged--skip-worktree 发挥作用的地方。 这些是您可以在 在索引中。设置任何一个标志都会告诉 Git:嘿,当你要考虑这个文件的 work-tree 副本时......你现在可以跳过它。也就是说,git add 查看索引和 work-tree,并检查 .gitignore 文件,以查看已跟踪的内容、未跟踪的内容、work-tree 中更新的内容以及需要更新的内容建议下一次提交,等等。如果某些文件 未跟踪 并列在 .gitignore 中,git add 将跳过它。如果它被 tracked,如果 work-tree 副本不同,Git 将添加它... 除非 跳过标志是放。如果设置了 --assume-unchanged 标志,Git 将 假定 它没有改变,并且不会添加它。如果设置了--skip-worktree标志,Git知道绝对不应该添加它,即使文件实际上被更改了。

所以 --skip-worktree 意味着我们在这里想要的:不要 git add 这个文件,即使它被改变了。 --assume-unchanged 标志也有效,因为 Git 假设它是 nt 改变了,因此 git add 它也没有改变。今天的实际操作没有区别,但是"skip worktree"表达了正确的意图

请注意,因为这些标志是在文件的 index(又名 staging-area)副本上设置的,所以它们仅适用于 tracked 文件。跟踪的文件是索引 / staging-area 中的文件。该文件必须在索引中才能设置标志。而且,如果该文件在索引中,该文件的副本——现在在索引中的那个——将在下一个 你做出的承诺。

但是这个文件副本是从哪里来的呢?答案在我们之前的 git checkout 中:git checkout 将我们选择的提交中的所有文件复制到索引中。它得到 索引,然后通过我们的第一个 git checkout 进入我们的 work-tree。如果我们从那时起就对 work-tree 副本大惊小怪,那么,我们设置的 标志 意味着 git add 从未将 work-tree 副本复制回索引副本,因此它仍然与旧提交相同。我们一直在使用保存在索引中的文件副本进行新的提交,可能持续数天或数月或其他时间。

如果我们git checkout一些其他提交,而另一个提交有一个不同 其中的文件副本,Git 将要用我们尝试切换到的提交中的副本替换我们的索引副本。将其复制到索引不会删除我们设置的标志,但它 覆盖 work-tree 副本。如果我们更改了 work-tree 副本,Git 将不经询问就覆盖它(这可能很糟糕)或者说:我无法检查该提交,它将覆盖您的(assumed/skipped,但我不会提及)work-tree 该文件的副本。 实际上,Git 采用后一种方法。

要解决这个问题,每次您 git checkout 提交 覆盖您标记的文件时,您必须移动或复制您的 work-tree 复制, git checkout 覆盖索引和 work-tree 副本,然后移动或复制你的 work-tree 副本回到原位。显然最好不要一开始就陷入这种情况。

但是,如果您 git rm 这些文件,那么 else 从有文件的提交转移到没有文件的提交会怎样?例如,也许您 pushing-to 的遥控器现在已经检出该文件,然后他们将 git checkout a new commit you make that 没有这些文件。当然,他们的 Git 会尽职尽责地从 他们的 Git 的索引和 他们的 [=389] 的索引中删除这些文件=] 的用户 work-tree。那是你不想要的,所以现在你不得不在 你的 Git 的 他们的 中保留该文件的副本索引,以便它进入您的新提交。

这就是这个复杂舞蹈的意义所在。 每次提交都是一个快照,并且在您的新提交中,您希望您的快照具有某些特定文件的它们的副本。所以你必须把他们的复制到你的Git的索引中。你从一些提交中得到它,将它复制到你的索引中。然后你将它保存在 你的 Git 的索引 / staging-area 中,即使你不在自己的 work-tree 中使用它.在处理这三个副本时,您将 正确的 副本保留在您自己的 Git 索引中。


1从技术上讲,索引中的内容是对冻结副本的引用。更新索引副本包括制作 new 冻结副本、准备提交以及将新引用写入索引。如果您开始直接使用 git update-index 来放入新文件,或者使用 git ls-files --stage 查看索引,这些细节很重要:您将看到 Git 的内部 blob object 在这里散列 ID。但是您可以将索引视为以内部冻结格式保存每个文件的完整副本:该心智模型对于您通常使用 Git.

的级别来说已经足够好了