为什么 "git pull" 响应中的 "From" 行没有列出裸仓库的名称?

Why does the "From" line in a "git pull" response not list the bare repo's name?

我是 git 的新手,我正在努力理解这些台词

...
From /tvm
   f8322345..9837f82f  master     -> origin/master
...

在 git 对带有分支 devgit pull 的回复中。

我有一个 git 裸仓库,还有两个 push/pull 到那个 bar 仓库的仓库;全部在一台服务器上。配置如下:

/
|
+-- tvm.git           The bare repo
|
+-- htdocs
      |
      +-- dev         The development repo. Has both master and dev branches
      |
      +-- website     The production repo. A clone of the master branch.

此设置用于管理基于 Joomla 的网站。数据库随着人们写新文章等而变化,因此生产仓库中的 master 分支也随之变化。

我需要将 master 分支合并到开发仓库中的 dev 分支以使 dev 保持最新。在 /htdocs/website 中(总是检查出分支 master),我首先卸载数据库,然后 git commit,然后 git push。 Git 的响应是(为简洁起见删除了一些行):

Enumerating objects: 5, done.
...
Total 3 (delta 2), reused 0 (delta 0), pack-reused 0
To /tvm.git
   5f08eef0..9837f82f  master -> master

我理解最后两行的意思是:Git 已将更改推送到裸仓库 (To /tvm.git),这是 mastermaster 的分支。

接下来,我转到开发目录,其中检查了分支 dev

/htdocs/website $ cd ../dev/
/htdocs/dev $ git status
On branch dev
Your branch is up to date with 'origin/dev'.

nothing to commit, working tree clean

为了保险起见,我用 git pull 从裸机中拉出来。响应是(再次删除了一些行):

remote: Enumerating objects: 17, done.
...
Unpacking objects: 100% (13/13), 32.46 KiB | 21.00 KiB/s, done.
From /tvm
   f8322345..9837f82f  master     -> origin/master
Already up to date.

我不明白,第一,为什么git告诉我From /tvm而不是From /tvm.git?这只是不一致吗?其次,为什么git写成master -> origin/master?为什么是 master 而不是 dev?第三,为什么 git 写成 Already up to date.?主分支 不是 最新的;我刚刚提交并将更改推送到裸仓库中的主分支。

git 配置文件包含...

[core]
        repositoryformatversion = 0
        filemode = true
        bare = false
        logallrefupdates = true
[remote "origin"]
        url = /tvm.git
        fetch = +refs/heads/*:refs/remotes/origin/*
[branch "master"]
        remote = origin
        merge = refs/heads/master
[branch "dev"]
        remote = origin
        merge = refs/heads/dev
[core]
        repositoryformatversion = 0
        filemode = true
        bare = false
        logallrefupdates = true
[remote "origin"]
        url = /tvm.git
        fetch = +refs/heads/*:refs/remotes/origin/*
[branch "master"]
        remote = origin
        merge = refs/heads/master
[core]
        repositoryformatversion = 0
        filemode = true
        bare = true

I don't understand, firstly, why does git tell me From /tvm and not From /tvm.git?

Git 从 origin 读取 url = 设置(在本例中)并使用它,但它确实有从这样 URLs。这没有特别好的或坏的理由。 Git 做到了。

Secondly, why is git writing master -> origin/master? Why master and not [the branch I am on, i.e.,] dev?

git pull 命令由 运行 两个其他 Git 命令组成:1

  • 首先,git pull 运行s git fetch.
  • 然后是 运行 另一个命令,我们将暂时忽略它。

(大多数但不是所有你传递给 git pull 的参数都会直接传递给 git fetch,如果你提供的话。你没有,所以我们可以绕过所有血腥的细节在这里。)

git fetch 命令是产生这些输出行的命令。获取命令:

  1. 调用其他一些 Git 存储库:URL 提供的任何答案。通常 URL 以 https://ssh:// 开头,这样 Git 通过网络传输,有点像拨打 Internet phone 电话。 Web 或 ssh 服务器将应答并 运行 将“phone 调用”传递给其他 Git 软件,或者至少是其他使用 Git 协议的软件,因此现在有两个单独的 Git 命令 运行ning:你的,在你的存储库上,他们的,在他们的存储库上。

    在您的情况下,URL 用于文件,因此您自己的 Git 软件扮演其他 Git 软件的角色,而不是打电话给某人看谁接听。 (您的 Git 可能会或可能不会产生第二轮 Git 软件,这取决于您使用的所有软件的版本。传统的 C Git 确实,或者至少是,必须产生另一个,因为有,或者至少是,太多的全局变量,所以只有一个存储库可以“在玩”,要么被读取——Git 服务于获取——要么被写入;这有 at-least-mostly-fixed 随着时间的推移,因此详细信息可能会因 Git 版本而异。)两种方式的原理都是相同的:您的 Git 服务器“调用” 读取 他们的存储库,以及您的 Git 写入 您的 Git 存储库。

  2. 他们的 Git(他们的存储库中的软件)列出了 他们的 b运行ches 和标签以及其他类似的名称和与这些一起使用的提交哈希 ID。您的 Git 挑选出它“喜欢”的那些。对于 default git fetch,您的 Git 喜欢所有内容,因此您的 Git 的任何提交哈希 ID 都可以看到您的 Git 还没有,您的 Git 要求他们的 Git 请将其发送过来。这迫使他们提供该提交的 parent(s);您的 Git 检查您是否有这些提交,如果没有,您的 Git 会询问这些提交,这使得他们的 Git 提供更多 parent,依此类推。

    这个对话阶段的最终结果是您的 Git 了解了他们拥有而您没有的每一个提交,并且他们了解了重叠:你们都有哪些提交。然后他们使用该信息打包所有提交并支持您的 Git 需要的 objects,这样您将拥有 他们拥有的一切 加上任何提交你自己的,你从未发送过。

    这涵盖了计数和枚举以及压缩和接收阶段。您的 Git 现在拥有所有必要的提交和支持 objects,因此您拥有 every 提交。你的 Git 将这些保存起来,扩展(“解包”和“验证”等)if/as 必要的东西,使用你已经拥有的 objects,他们在 have/want对话阶段。所有 通信 现已完成,您的 Git 软件可以与他们的断开连接。

  3. 现在您拥有了他们所有的 提交 ,您的 Git 获得了他们的每个 b运行 ch 名称并将其更改为您的 remote-tracking 名称:他们的 main 变为您的 origin/main,他们的 dev 变为你的origin/dev,等等。请记住,在开始时,他们列出了 all 他们的 b运行ch 名称和 all 这些名称的提交哈希 ID代表。

    您的 Git 现在会检查您的哪些 origin/* 名称需要创建或更新。这是:

       f8322345..9837f82f  master     -> origin/master
    

    样式线条出来了。这意味着他们的 master——你的 origin/master——曾经命名为提交 f8322345,但现在他们的 master 命名为提交 9837f82f。因此,您的 Git 需要更新您自己的 origin/master

要完全理解这一点,您还需要一个事实:任何提交的哈希 ID 都是 普遍唯一的 。如果你有提交9837f82f你的 Git调用9837f82f他们的 Git称之为 9837f82f,宇宙中所有其他 Git 拥有该提交的都称之为 9837f82f。 (这个 9837f82f 实际上是从完整的哈希 ID 缩短而来的,它必须很大才能可以 是普遍唯一的。)所以你的 Git 和他们的 Git 可以通过检查 number[= 来判断你们都有哪些提交239=]。 (哈希 ID 是对提交内容的大型加密校验和的 hexadecimal 表示。2

关于这条线还有一些其他的事情需要注意:

   f8322345..9837f82f  master     -> origin/master

首先,请注意两个点,以及该行不以 (forced update) 结尾的事实。这意味着提交 f8322345 是提交 9837f82f 祖先 :parent,或 g运行dparent, or great-grand-parent, or greatn-grand-parent for n > 1 .如果 master 被倒带和重写,您会看到一个加号、三个 点和添加的 forced update 注释。

如果你自己的Git还没有一个origin/master,那么他们master的存在(你的origin/master) 是您的存储库的新手,您将获得:

 * [new branch]            master     -> origin/master

并且如果您 运行 git fetch 启用了 p运行e 选项并且他们 删除了 一些 b运行 ch name 他们 曾经有 但不再有,你会得到:

 - [deleted]               (none)     -> origin/foo

因此,这些行告诉您很多关于自上次您从给定的命名远程收集更新以来发生的事情,例如 origin

And thirdly, why is git writing Already up to date.?

我们现在进入 second 命令,git pull 将 运行。您可以选择此命令,但它(当前或以前至少 3)默认为 git merge。您的另一个选项是 git rebase,但您得到的是 git merge.

不像git fetch,它获取所有其他Git的b运行ch名称,4 second Git 命令——无论你选择哪一个——将只在 current b运行ch.您当前的 b运行ch 是 dev,它的 upstreamorigin/devgit statusgit branch -vv 命令将显示上游设置:

$ git status
On branch master
Your branch is up to date with 'origin/master'.

这里是我的 master Git 存储库 Git,它与 origin/master 同步。由于我只是 运行 git fetch,这意味着 origin 的主人识别相同的提交:

$ git rev-parse master origin/master
ab1f2765f78e75ee51dface57e1071b3b7f42b09
ab1f2765f78e75ee51dface57e1071b3b7f42b09

我们在这里看到两个名称都标识了提交 ab1f2765f78e75ee51dface57e1071b3b7f42b09,即 Git version 2.36.0-rc1.

您的 git fetch 更新了您的 origin/master——这就是您在 git pullgit fetch 输出中看到的内容——但是 没有 更新你的 origin/dev。您现有的 dev b运行ch 的提示提交与您的 origin/dev 提交相同或领先。这意味着 git merge 他们的 方面没有任何工作可以与您可能在您方面完成的任何工作相结合。您的 dev 已经与您的 origin/dev 保持同步。这就是 Git 在这里告诉你的。


1过去,git pull字面上只是一个shell脚本,实际上运行git fetch,然后实际上运行 第二个 Git 命令。如今,它是一个毛茸茸的大 C 程序,在编译时包含相同的代码 as git fetch 和其他两个程序,因此它不是将其他程序作为命令调用,而是调用它们作为子程序调用。不过,原理是一样的,为了在“向后兼容性”中保持“向后”,C 代码顽强地经历了过去需要的每一个愚蠢的小皱纹因为这些是单独的程序。

2目前这是一个160位的SHA-1散列,但事实证明160位的SHA-1毕竟密码强度不高,而且Git 正在转向 256 位 SHA-256。另见

3这个默认值对许多人来说是 bad/wrong,并且 Git 现在开始要求您 配置 一个默认值。如果您收到关于需要配置 pull.ff and/or pull.rebase 的投诉,您有一个 Git 版本促使您有意识地选择一些东西,而不是仅仅接受一些默认值对你来说可能是错误的。

4这实际上取决于多个细节,但默认设置是您获得所有 b运行ches,前提是您 运行 git fetch 没有选项。注意谈论使用 git pull --all 的答案:这将 --all 传递给 git fetch,它并不意味着 all b运行ches 因为已经是这样了。相反,它意味着 所有遥控器 。这不是有害的,但它并没有像人们认为的那样,所以推荐--all的答案应该谨慎对待,以免写它们的人有其他误解 有害。


结论

git pull 是一个可以做很多事情的大命令。它 运行 是另外两个 Git 命令,每个都是 也是 一个做很多事情的大命令:git fetch,然后是 git mergegit rebase。研究所有这三个 run-by-git pull 命令 first 是个好主意,在我看来,avoi 是个好主意 git pull 支持 运行 单独的命令,直到您对每个命令的作用有很好的应用知识。

一旦你知道每个命令的作用,你可能会发现 git pull 的便利性 short-cut 的 运行 宁 git fetch 然后立即 - 没有给你一个有机会观察 git fetch 获取了什么——运行 宁第二个命令 你想要的。但是,您实际上 知道 直到您知道自己想要什么,而您 不能 知道,直到您知道它们各自的作用。更糟糕的是,当其中一个命令 失败 时,您将不知道如何恢复。精通任何事物(软件、烹饪、木工、核反应堆操作等)的很大一部分在于知道在某些事情 没有 工作时该怎么做。当一切都按设计运行时,任何傻瓜都能做到; .

专业人士知道该怎么做