本地 .git 目录中的哪些项目被复制 to/from 远程到 pushing/fetching?
Which items from the local .git directory get copied to/from the remote upon pushing/fetching?
当您 push
到远程存储库时,本地 .git
目录中的哪些项目会被复制到远程 .git
目录?
同题反方向,当你执行fetch
.
当我们创建(使用 git init)或从上游克隆一个 git 回购时,git 创建 .git
目录,其中包含关于我们本地回购的大量信息使用我们的远程仓库帮助我们对代码进行版本控制。如果我们删除 .git
目录,那么我们将得到 fatal: Not a git repository (or any of the parent directories): .git
我认为我们在远程仓库中没有 .git
目录,我认为 .git
目录仅在我们的本地仓库中找到,用于跟踪分支、提交、用户配置、添加remotes repos ... 我们在远程 repo 中只有 .gitignore
文件,如果我们添加它是为了忽略文件...
如果您查看 .git
目录,您可以看到以下文件和文件夹...
├── HEAD
├── branches
├── config
├── description
├── hooks
│ ├── pre-commit.sample
│ ├── pre-push.sample
│ └── ...
├── info
│ └── exclude
├── objects
│ ├── info
│ └── pack
└── refs
├── heads
└── tags
很多都是实现细节——如何 Git存储信息,而不是什么信息 Git 商店。因此它是无关紧要的:关于其他一些 Git 存储库,你所知道的只是 它会向你展示它有什么 ,而不是 它把它放在哪里 .
尽管如此,在典型的设置中,远程的是另一个 Git 存储库,就像您的存储库一样。事实上,从他们的角度来看,你是遥控器。 他们是真人,你是克隆人。从您的角度来看,他们是克隆人而您是真人。从外部观察者的角度来看,您的克隆和他们的克隆只是两个独立的 Git 存储库。
当您 运行 git push
时,您的 Git 会呼叫他们的 Git 并且您的 Git 和他们的 Git 会进行对话。这次谈话有一个最终目标,如果我们从那里开始并向后工作,它会更有意义。最终目标是您希望 他们的 Git 设置 他们的 名称之一(他们存储库中的分支或标签名称)记住一个特定的提交。因此,您要向他们发送以下形式的礼貌请求或强制命令:set your refs/heads/master
to commit hash ID a123456...
.
(这个名字可以是任何东西,包括他们已经有的名字,或者他们还没有的名字。accept/reject规则取决于名字的拼写和他们是否有yet, but up to the receiving Git。通常接收者不喜欢不为人知的名字,例如,refs/heads/*
、refs/tags/*
、refs/notes/*
。GitHub 将拒绝任何设置 refs/pull/*
名称的尝试,例如。)
他们可能会拒绝——即使你发送命令而不是礼貌的请求——但如果他们不这样做,他们必须在他们的存储库中提交a123456...
.因此,作为此对话的一部分,您的 Git 将向他们提供该承诺。他们会说 我已经有了那个 或 是的,我想要那个。如果提交 a123456...
依赖于 任何较早的提交,通过将它们作为其父项,您的 Git 现在必须提供他们的 Git 那些提交(s) 还有。如果这些提交依赖于之前的提交,你的 Git 必须继续向他们提供更多的提交,直到你达到你提供的提交是他们已经拥有的提交,或者你已经提供了你拥有的每一个提交(以哪个为准)首先)。
要给另一个 Git 提交,您还必须给它每个 file 提交,除非 in ——再次——他们已经有了那个版本的文件。所以其中一些也进入了对话,尽管其中很多是暗示的:如果他们没有提交 X 但确实有提交 W 那么他们肯定拥有 W 中的所有文件,其中许多文件可能与 X 中的文件完全相同,因此无需发送它们即使我们发送提交 X.
所以这是对话的最早部分,在将您的两个 Git 连接起来之后:您通过哈希 ID 向他们提供一些提交和文件。他们选择他们想要的和他们已经拥有的,然后你的 Git 打包这些对象。
这些对象——提交、树、文件和注释标签的内部 Git 存储形式——可能存在于你的 .git/objects/
目录中,或者可能 打包 到 .git/objects/pack/
子目录中的 包文件 中。但是您通常不会向他们提供 文件 。 (确切的细节取决于传输协议。)相反,通常你的 Git 制作一个 new 包,一种特殊的 "thinned out" 类型称为 薄包。您的 Git 向他们的 Git 发送了薄包装。他们的 Git 把它养肥成一个普通的包。如果他们最后接受了你所有的提交和文件,他们可能只是把这个包放到他们的 .git/objects/pack/
子目录中,尽管你永远无法确定,因为在未来与他们的对话中,你的 Git 和他们的 Git 只会通过他们的哈希 ID 谈论 对象 。
(他们也可能完全拆除肥大的包并为自己构建新的包,这比仅仅推入瘦包的肥大版本更有效。同样,你永远不会真正知道,除非你可以登录到另一台机器并在其 .git
目录中查找。)
最后,一旦他们接受了他们将要接受的任何东西,并服从了你的请求或命令,他们设置了他们的 refs/heads/<em>whatever</em>
,他们将更新该引用的副本,但他们将其存储:可能作为 .git/refs/heads/
中的文件,或者作为某个参考数据库中的数据库条目,或者可能完全是其他东西。
那么,简短的回答是,通常没有 .git
文件会直接复制。相反,信息被重新打包成合适的 "wire format",以这种方式传递,然后重新打包成合适的 "storage format".
与 git fetch
相同,除了在对话开始时,您的 Git 让他们的 Git 列出他们的姓名和哈希 ID;他们的 Git 通过哈希 ID 提供你的对象,而你的对象对这些 ID 说 want/have ;最后,他们永远不会向您发送任何请求或命令。相反,在获得他们的对象后,您的 Git 更新您的 远程跟踪名称 ,这就是您的 Git 记住他们的 Git 所说的内容的方式 [= =11=] 是:那成为你的 refs/remotes/origin/master
,你对他们 refs/heads/master
的记忆。
基本上在 fetch
或 push
操作期间共享两件事:对象和引用。请注意,接收存储库可能将给定的信息存储在与发送服务器存储该信息的文件不同的文件中。
对象包括提交、"tree" 对象(表示内容目录)和 "blob" 对象(表示单个内容文件)等。这些共同构成了您的项目的历史。它们以 "loose" 形式存储在 .git/objects 下(每个对象一个文件,在 directories/files 中,其名称源自对象的 ID - 这是对象数据的 SHA 哈希)或“打包”形式(在 .git/objects/packs 下的文件中)。回购协议之间的传输使用打包形式,如果认为合适,则由接收回购协议重新组织其包。
引用是分支、标签和其他东西;它们提供进入历史的“入口点”。它们以 "loose" 形式存储在 .git/refs 下,或以压缩形式存储在 packed-refs 文件中。接收端,但根据用于共享 ref 的 refspec,远程可能会更新完全不同的 ref(例如,在获取时,您通常会更新跟踪 ref 以匹配远程的分支)
当您
push
到远程存储库时,本地.git
目录中的哪些项目会被复制到远程.git
目录?同题反方向,当你执行
fetch
.
当我们创建(使用 git init)或从上游克隆一个 git 回购时,git 创建 .git
目录,其中包含关于我们本地回购的大量信息使用我们的远程仓库帮助我们对代码进行版本控制。如果我们删除 .git
目录,那么我们将得到 fatal: Not a git repository (or any of the parent directories): .git
我认为我们在远程仓库中没有 .git
目录,我认为 .git
目录仅在我们的本地仓库中找到,用于跟踪分支、提交、用户配置、添加remotes repos ... 我们在远程 repo 中只有 .gitignore
文件,如果我们添加它是为了忽略文件...
如果您查看 .git
目录,您可以看到以下文件和文件夹...
├── HEAD
├── branches
├── config
├── description
├── hooks
│ ├── pre-commit.sample
│ ├── pre-push.sample
│ └── ...
├── info
│ └── exclude
├── objects
│ ├── info
│ └── pack
└── refs
├── heads
└── tags
很多都是实现细节——如何 Git存储信息,而不是什么信息 Git 商店。因此它是无关紧要的:关于其他一些 Git 存储库,你所知道的只是 它会向你展示它有什么 ,而不是 它把它放在哪里 .
尽管如此,在典型的设置中,远程的是另一个 Git 存储库,就像您的存储库一样。事实上,从他们的角度来看,你是遥控器。 他们是真人,你是克隆人。从您的角度来看,他们是克隆人而您是真人。从外部观察者的角度来看,您的克隆和他们的克隆只是两个独立的 Git 存储库。
当您 运行 git push
时,您的 Git 会呼叫他们的 Git 并且您的 Git 和他们的 Git 会进行对话。这次谈话有一个最终目标,如果我们从那里开始并向后工作,它会更有意义。最终目标是您希望 他们的 Git 设置 他们的 名称之一(他们存储库中的分支或标签名称)记住一个特定的提交。因此,您要向他们发送以下形式的礼貌请求或强制命令:set your refs/heads/master
to commit hash ID a123456...
.
(这个名字可以是任何东西,包括他们已经有的名字,或者他们还没有的名字。accept/reject规则取决于名字的拼写和他们是否有yet, but up to the receiving Git。通常接收者不喜欢不为人知的名字,例如,refs/heads/*
、refs/tags/*
、refs/notes/*
。GitHub 将拒绝任何设置 refs/pull/*
名称的尝试,例如。)
他们可能会拒绝——即使你发送命令而不是礼貌的请求——但如果他们不这样做,他们必须在他们的存储库中提交a123456...
.因此,作为此对话的一部分,您的 Git 将向他们提供该承诺。他们会说 我已经有了那个 或 是的,我想要那个。如果提交 a123456...
依赖于 任何较早的提交,通过将它们作为其父项,您的 Git 现在必须提供他们的 Git 那些提交(s) 还有。如果这些提交依赖于之前的提交,你的 Git 必须继续向他们提供更多的提交,直到你达到你提供的提交是他们已经拥有的提交,或者你已经提供了你拥有的每一个提交(以哪个为准)首先)。
要给另一个 Git 提交,您还必须给它每个 file 提交,除非 in ——再次——他们已经有了那个版本的文件。所以其中一些也进入了对话,尽管其中很多是暗示的:如果他们没有提交 X 但确实有提交 W 那么他们肯定拥有 W 中的所有文件,其中许多文件可能与 X 中的文件完全相同,因此无需发送它们即使我们发送提交 X.
所以这是对话的最早部分,在将您的两个 Git 连接起来之后:您通过哈希 ID 向他们提供一些提交和文件。他们选择他们想要的和他们已经拥有的,然后你的 Git 打包这些对象。
这些对象——提交、树、文件和注释标签的内部 Git 存储形式——可能存在于你的 .git/objects/
目录中,或者可能 打包 到 .git/objects/pack/
子目录中的 包文件 中。但是您通常不会向他们提供 文件 。 (确切的细节取决于传输协议。)相反,通常你的 Git 制作一个 new 包,一种特殊的 "thinned out" 类型称为 薄包。您的 Git 向他们的 Git 发送了薄包装。他们的 Git 把它养肥成一个普通的包。如果他们最后接受了你所有的提交和文件,他们可能只是把这个包放到他们的 .git/objects/pack/
子目录中,尽管你永远无法确定,因为在未来与他们的对话中,你的 Git 和他们的 Git 只会通过他们的哈希 ID 谈论 对象 。
(他们也可能完全拆除肥大的包并为自己构建新的包,这比仅仅推入瘦包的肥大版本更有效。同样,你永远不会真正知道,除非你可以登录到另一台机器并在其 .git
目录中查找。)
最后,一旦他们接受了他们将要接受的任何东西,并服从了你的请求或命令,他们设置了他们的 refs/heads/<em>whatever</em>
,他们将更新该引用的副本,但他们将其存储:可能作为 .git/refs/heads/
中的文件,或者作为某个参考数据库中的数据库条目,或者可能完全是其他东西。
那么,简短的回答是,通常没有 .git
文件会直接复制。相反,信息被重新打包成合适的 "wire format",以这种方式传递,然后重新打包成合适的 "storage format".
与 git fetch
相同,除了在对话开始时,您的 Git 让他们的 Git 列出他们的姓名和哈希 ID;他们的 Git 通过哈希 ID 提供你的对象,而你的对象对这些 ID 说 want/have ;最后,他们永远不会向您发送任何请求或命令。相反,在获得他们的对象后,您的 Git 更新您的 远程跟踪名称 ,这就是您的 Git 记住他们的 Git 所说的内容的方式 [= =11=] 是:那成为你的 refs/remotes/origin/master
,你对他们 refs/heads/master
的记忆。
基本上在 fetch
或 push
操作期间共享两件事:对象和引用。请注意,接收存储库可能将给定的信息存储在与发送服务器存储该信息的文件不同的文件中。
对象包括提交、"tree" 对象(表示内容目录)和 "blob" 对象(表示单个内容文件)等。这些共同构成了您的项目的历史。它们以 "loose" 形式存储在 .git/objects 下(每个对象一个文件,在 directories/files 中,其名称源自对象的 ID - 这是对象数据的 SHA 哈希)或“打包”形式(在 .git/objects/packs 下的文件中)。回购协议之间的传输使用打包形式,如果认为合适,则由接收回购协议重新组织其包。
引用是分支、标签和其他东西;它们提供进入历史的“入口点”。它们以 "loose" 形式存储在 .git/refs 下,或以压缩形式存储在 packed-refs 文件中。接收端,但根据用于共享 ref 的 refspec,远程可能会更新完全不同的 ref(例如,在获取时,您通常会更新跟踪 ref 以匹配远程的分支)