推送后存储库的一致性

时间:2016-11-14 02:49:31

标签: git push

我对git push的作用感到困惑。

  1. 它会将所有已提交的文件仅推送到中央存储库吗?那些也由Git管理但未被更改的其余文件呢?它们也被推送到中央存储库,或者只是推送它们。

  2. 如果只推送了已提交的文件,为什么git可以检测到冲突,如果另一个文件发生了变化 - 在我拉出之后但在我推动之前由其他团队成员制作的hello.html。 (我从不更改那个hello.html文件)。

  3. git push之后,工作目录,本地存储库,中央存储库是否一致?是否拥有相同的内容?

3 个答案:

答案 0 :(得分:1)

  1. Git push将提交发送到远程存储库,而不是文件。所以它发送的内容取决于你之前传递给git commit的内容。未提交的文件不会发送到远程存储库。

  2. Git会检测远程存储库何时发生更改,并且即使您尚未提交更改,也会进行更改。

  3. 工作目录与git push无关。只有本地和远程存储库的已提交内容才相关。这两个存储库通常会在推送后提交相同的提交,但是您的本地工作目录可能还有其他您尚未提交的内容。

答案 1 :(得分:1)

(在重新阅读你的问题时,我可能已经回答了你实际上不打算提出的问题!这些都是关于其他 Git的工作树会发生什么,如果有的话。)

John Zwinck's answer是正确的。关于git push如何推送提交,而不是文件(这使得你的工作树无关紧要),我对What does GIT PUSH do exactly?的回答(更多)。请阅读以查看遥控器上的其他Git对您的git push执行的操作(并记住其他Git有权接受您的推送参考更新请求,或者拒绝一个,或多个或全部)。

第3部分 - 接收Git的工作目录会发生什么 - 是最棘手的。这取决于在其他Git上设置的配置。它还取决于其他Git的版本,因为有一个配置只适用于较新版本。

首先,让我们假设远程Git 接受你的推送引用更新,因为如果它拒绝它,那么,没有任何反应。 1 如果没有发生了,"发生了什么"很无聊!所以其他Git已接受你的推送,并在存储库, 2 中有一些新的提交,并有一个引用 - 一个分支或标记名称 - 指向其中一个新的提交。如果这是 new 分支或 new 标记,则接收Git存储库只有一个新的分支或标记,并且任何现有的分支或标记都不会受到影响,并且再一次,这个问题有点无聊:显然没有必要改变工作树中的任何内容。

接下来,我们需要讨论一个所谓的存储库。

您已经知道Git存储库在这里有两个感兴趣的部分,提到第三个部分非常重要:

  1. 存储库本身,所有提交及其所需的一切都将永久存储,只能由Git本身使用,以便git checkout任何特定的分支,或git checkout任何较旧的提交,以查看其中的内容 - 这为您提供了可怕的"分离的HEAD"在此之后,您必须再次git checkout某个分支重新连接您的HEAD。您还可以git checkout一个特定的标签,它也会为您提供一个分离的HEAD。 3 Git的存储库对象(提交,以及其他项目提交在存储库中使用存储文件是永久的,不变的,Git只能添加到它们。 4

  2. 工作树工作树(同一事物的两个名称):普通,计算机和人类可访问的文件,名称正常,可以查看和编辑文件,运行程序或应用程序,使用您的网页,或者使用Git进行的任何操作。

  3. index staging-area (同样,两个名称相同 - 虽然有第三个名称,缓存,与git diff --cached一样,与git diff --staged完全相同。索引主要是"什么将进入 next 提交":在工作树中编辑文件后,您git add该文件。这会将其新内容复制到索引 5 中,以便它可以进入下一次提交。否则,您进行的下一次提交将按照当前提交中的方式保存文件,即它现在在索引中的方式。

    (索引在合并中也扮演着重要的角色,但这是另一个话题。它在使Git像Git一样快的时候扮演的角色较小,这就是"缓存"方面来了。)

  4. 现在, bare 存储库非常简单,只是一个没有工作树的Git存储库。由于它没有工作树,所以在这里无需担心。您可以推送到裸存储库中的任何分支(前提是您满足Git为接受推送而设置的任何要求)。

    这样我们就可以使用非正常("常规"?)Git存储库来接受推送。它的工作树会发生什么?那么,现在我们回到:"这取决于。"

    所有Git存储库 - 包括裸存储库 - 仍然具有当前分支的概念。当前分支是您使用git checkout branchname设置的分支。在大多数存储库中,它以master开头,而您git checkout可以根据需要更改其他分支。

    所有Git存储库也都有配置。其中一个配置项是receive.denyCurrentBranch。这可以设置为truerefusefalsewarnignore或更新的Git版本 - updateInstead。如果您未设置任何内容,则默认设置为refuse。每个设置都会导致不同的行为:

    • 如果设置为refusetrue,则接收Git只会自动拒绝任何推送到其当前分支的尝试。拒绝此一个分支的更新请求。这使问题消失了。在相同的推送中更新请求,但对于其他分支,当然可能仍会被接受。

    • 如果设置为falseignorewarn,则接收Git会接受更新,但不会更新工作树 (也不是指数)。它们变得不同步,这通常很糟糕;但这取决于管理Git处理它的人。如果设置是warn,那么你(推送的那个)会得到一个关于这个的警告信息,这使得他们(运行接收Git的人)没有一点好。 6

    • 如果设置为updateInstead - 仅在Git版本2.3之后可用 - 那么接收Git实际上会检查:"是否所有签入的分支都很好?"如果是这样,它允许更新进行全面检出,以便索引和工作树得到同步。如果不是 - 如果索引或工作树有未提交的更改 - 它会拒绝推送。

    此处还有另一个棘手的案例,可以在2.5或更高版本的Git版本中使用:您现在可以拥有与存储库关联的多个工作树。每个工作树都有自己独立的索引和自己的当前分支。接收Git如何对待这些,我不知道。从逻辑上讲,"拒绝"如果任何关联的工作树将该分支作为其当前分支,并且" updateInstead"则模式应该拒绝更新。模式应该更新哪个工作树有该分支,但我没有测试过。

    我认为,这涵盖了今天所有可用的案例。大多数中央存储库都是裸的,因此该存储库上的当前分支是无关紧要的。 (另请注意,这些都不会影响任何post-receive部署脚本,其中一些会自动检出特定备用工作树的某些分支,从而覆盖"裸露"。但是这个过程与Git本身是分开的,完全取决于后接收钩子。)

    1 像往常一样,完整的事实更加复杂:另一端可能通过预接收或更新挂钩拒绝您的推送,同时 - 通过挂钩制造事情发生了。但这不太可能,所以让我们忽略它。

    2 在少数情况下 - 经典的例子是强制推动"撤回"提交或简单的轻量级标记推送来标记一些现有提交 - 没有提交,只是更改分支名称或添加新标记。

    3 一个工作树,带有一个"分离的HEAD"在没有分支上。但是,您仍然可以在其中进行新的提交。当你这样做时,他们会有效地进入匿名(未命名)分支。因为该分支没有名称,所以它不会进入本讨论的其余部分:成功更新某些引用名称的推送请求,以及此非分支/匿名分支"分离的HEAD" work-tree没有名称,因此无法更新。 (您可以说"它的名称为HEAD":这是真的,但HEAD不是分支名称。此外,因为它的存在在refs/名称空间之外,它永远不会被推送到。)

    4 除此之外还有一个例外"只有添加新东西"规则。有些对象意味着是临时的,所以虽然没有任何对象可以更改,但Git确实有一种摆脱过时的,未使用的对象的机制。那个Git"垃圾收集器"或git gc。您还可以使用此方法通过倒回分支来丢弃不需要的提交,以便它不再引用它们。这样的承诺变得“放弃”#34;并且最终 - 不会立即扫除。

    (请注意,他们永远不会更改,只会幻影,然后最终完全删除。" ghosts"可以通过reflog找到,直到reflog条目到期为止,然后直到git gc清除它们,git fsck更加痛苦地找到它们,这与git gc非常相似,除了它检查所有内容仔细,并可选择恢复丢失的物品,而不是丢弃它们。)

    5 实际上,不是将整个文件复制到索引中,而是Git所做的是存储文件的哈希ID 在索引中。运行git add实际上将文件粘贴到存储库中,作为" blob"宾语。然后Git将blob的ID放入索引中。如果你更新并git add一个文件,然后再次更新git add,你会得到一个未引用的("悬空",在git fsck条款中)blob,这是垃圾收集器最终收获;但在那之前,它实际上可以恢复增加的版本。

    6 warn模式实际上适用于控制接收存储库的情况。它会提醒您在必要时登录到其他计算机,然后转到该存储库并处理问题。

答案 2 :(得分:1)

Git使用4种类型的对象管理项目历史记录:

  • blob:表示一个文件,包含文件的压缩全部内容。
  • tree:表示一个目录,包含对blob和树的引用。
  • commit:表示提交,包含提交消息,日期,对表示根目录的树的引用等。
  • tag:表示一个标记,包含标记的注释,对提交的引用等。

当您提交一些更改时,Git会为每个已更改的文件和提交对象创建全新的blob。

当推送时,Git会发送不在中央存储库但位于存储库中的对象。

所以,

  1. 是的,只发送已提交的文件(blob)。 在进行一些提交之后,您的存储库包含新的blob和提交不在中央存储库中的对象。 他们都被送到中央,但没有其他人被送去。

  2. Git比较中央和你之间的提交对象。它会在推送之前检查您的提交是否位于中心的提交历史记录的提示中,如果不是则拒绝推送。

  3. 没有。如果其他人将某些对象推送到中央,那么在获取之前,您的存储库将永远不会有对象。 并且您的工作目录可能包含甚至不在您的存储库中的更改(即未提交的更改。)