为什么“从”是“从”? “git pull”中的行响应未列出裸存储库的名称?
我是Git的新手,我正在尝试了解
...
From /tvm
f8322345..9837f82f master -> origin/master
...
Git对Git branch dev
的响应中的响应。
我有一个裸露的仓库,还有两个将/拉到该酒吧仓库的存储库。全部在单个服务器上。配置如下:
/
|
+-- 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的网站。随着人们的撰写新文章等,数据库会发生变化,因此生产存储库中的主分支随之而来。
我需要将主分支合并到开发存储库中的开发分支机构中,以使开发人员保持最新状态。当在/htdocs/weblote
中(始终具有分支Master
检查)时,我首先卸载DB,然后git commit
和git推
。 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 < /code < /code>),这是分支
。主
<代码>主
接下来,我更改为DevLopment目录,该目录具有分支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拉拉
从裸露中拉出。响应是(再次被删除的一些行):
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为什么要告诉我来自 /tvm < /code>而不是< /code> < /tvm.git < /code> < /code>?这只是不一致吗?其次,为什么git Writing
Master-&GT; Origin/Master
?为什么主
而不是dev
?第三,为什么git写作已经是最新的。
?主分支是不是最新的;我刚刚承诺并推动了裸露的存储库中的主人分支。
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
- 在Bare Repo_中:
[core]
repositoryformatversion = 0
filemode = true
bare = true
I'm rather new to git, and I'm trying to understand the lines
...
From /tvm
f8322345..9837f82f master -> origin/master
...
in git's response to a git pull
with branch dev
checked out.
I've got a git bare repo, and two repos that push/pull to that bar repo; all on a single server. The configuration is as follows:
/
|
+-- 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.
This setup is used to manage a Joomla based web site. The database changes as people are writing new articles, etc., so the master branch in the production repo changes with this.
I need to merge the master branch into the dev branch in the development repo to bring dev up to date. While in /htdocs/website
(which always has branch master
checked out), I first unload the DB, then git commit
, and git push
. Git's response is (some lines deleted for brevity):
Enumerating objects: 5, done.
...
Total 3 (delta 2), reused 0 (delta 0), pack-reused 0
To /tvm.git
5f08eef0..9837f82f master -> master
I understand the last two lines to mean: Git has pushed the changes to the bare repo (To /tvm.git
) and this was branch master
to master
.
Next, I change to the devlopment directory, which has branch dev
checked out.
/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
Just to be sure, I pull from the bare with git pull
. The response is (again some lines deleted):
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.
I don't understand, firstly, why does git tell me From /tvm
and not From /tvm.git
? Is this just inconsistency? Secondly, why is git writing master -> origin/master
? Why master
and not dev
? And thirdly, why is git writing Already up to date.
? The master branch is not up to date; I had just commited and pushed changes to the master branch in the bare repo.
The git config files contain ...
- In the development repo:
[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
- In the production repo:
[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
- In the bare repo_:
[core]
repositoryformatversion = 0
filemode = true
bare = true
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
Git 从
origin
读取url =
设置(在本例中)并使用它,但它确实倾向于从origin
中剥离.git
此类 URL 的末尾。这没有特别好的或坏的原因。 Git 就是这么做的。git pull
命令包含运行另外两个 Git 命令:1git pull
运行git fetch
。(如果您确实提供了任何参数,那么您传递给 git pull 的大多数但不是全部参数都会直接转到 git fetch 。您没有提供,因此我们可以绕过所有参数血淋淋的细节在这里。)
git fetch
命令是产生这些输出行的命令。 fetch 命令:调用其他一些 Git 存储库:无论提供的 URL 上有什么答案。通常,URL 以
https://
或ssh://
开头,以便 Git 通过网络进行传输,有点像拨打网络电话。 Web 或 ssh 服务器将应答并将“电话呼叫”转移到其他 Git 软件,或者至少是其他使用 Git 协议的软件,这样现在就有两个单独的 Git 命令正在运行:您的、您的存储库上的和他们的,在他们的存储库上。在你的例子中,URL 是一个文件,所以你自己的 Git 软件扮演了其他 Git 软件的角色,而不是打电话给某人看谁回答。 (您的 Git 可能会也可能不会生成第二轮 Git 软件,具体取决于您所使用的所有内容的版本。传统的 C Git 确实(或至少确实)必须生成另一个版本,因为存在或至少曾经是,太多的全局变量,以至于只有一个存储库可以“发挥作用”,要么被读取(Git 服务于获取)要么被写入;随着时间的推移,这一问题至少大部分已经修复,因此细节可能会因 Git 版本而异。 )无论哪种方式原理都是一样的:你的 Git 服务器“调用”读取他们的存储库,而您的 Git写入您的 Git 存储库。
他们的 Git(他们的存储库上的软件)列出了他们的分支和标签以及其他此类名称以及与这些名称相关的提交哈希 ID。你的 Git 会挑选出它“喜欢”的那些。对于默认
git fetch
,您的 Git 喜欢一切,因此您的 Git 看到的任何提交哈希 ID 您的 Git 还没有拥有,您的 Git请求他们的 Git 将其发送过来。这迫使他们提供该提交的父级;你的 Git 检查你是否有这些提交,如果没有,你的 Git 会要求这些提交,这使得他们的 Git 提供更多的父项,等等。此阶段对话的最终结果是,您的 Git 了解了他们拥有而您没有的每个提交,并且他们了解了重叠部分:你们都有哪些提交。然后,他们使用该信息打包您的 Git 需要的所有提交和支持对象,以便您将拥有他们拥有的一切以及您自己从未发送过的任何提交。 p>
这涵盖了计数和枚举以及压缩和接收阶段。您的 Git 现在拥有所有必要的提交和支持对象,以便您拥有每个提交。你的 Git 会保存这些内容,并在必要时使用他们在拥有/想要对话阶段了解到的你已经拥有的对象来扩展(“解包”和“验证”等)内容。所有通信现已完成,您的 Git 软件可以与它们断开连接。
现在您已经拥有了他们的所有提交,您的 Git 将获取他们的每个分支名称并将其更改为您的远程跟踪名称:他们的
main
成为您的origin/main
,他们的dev
成为您的origin/dev
,依此类推。请记住,在开始时,他们列出了所有他们的分支名称以及所有这些名称所代表的提交哈希 ID。您的 Git 现在会检查您的哪些
origin/*
名称需要创建或更新。这就是:风格线条出来了。这意味着他们的
master
(您的origin/master
)曾经将提交命名为f8322345
,但现在他们的master
名称为 commit9837f82f
。因此,您的 Git 需要更新您自己的origin/master
。为了完全理解这一点,您还需要一个事实:任何提交的哈希 ID 都是普遍唯一的。如果您提交了
9837f82f
,您的 Git 会调用9837f82f
,他们的 Git 会调用9837f82f,宇宙中所有其他拥有该提交的 Git 都将其称为
9837f82f
。 (这个9837f82f
实际上是从完整的哈希ID缩短的,它必须很大,这样它可以是普遍唯一的。)所以你的Git和他们的Git可以告诉哪些提交只要检查一下数字,你们就都知道了。 (哈希 ID 是提交内容的大型加密校验和的十六进制表示形式。2)关于该行还有其他一些需要注意的事项:
首先,请注意这两个点,以及该行并非以
(forced update)
结尾的事实。这意味着提交f8322345
是提交9837f82f
的祖先:父母、祖父母、曾祖父母、伟大 n-n > 的祖父母1。如果master
被倒回并重写,您会看到一个加号、三个点,以及添加的强制更新
备注。如果您自己的 Git 还没有
origin/master
,那么它们的master
就存在(您的origin/master< /code>)对于您的存储库来说是新的,您会得到:
并且如果您在启用了修剪选项的情况下运行 git fetch 并且他们删除了一些分支名称,他们曾经有但不再这样做,你会得到:
这些因此,这些行会告诉您自上次从给定命名远程(如
origin
)收集更新以来发生的情况。我们现在进入
git pull
将运行的第二命令。您可以选择此命令,但它(当前或之前至少3)默认为git merge
。您的另一个选择是git rebase
,但您将获得git merge
。与
git fetch
(获取所有其他 Git 分支名称)不同,4第二个 Git 命令 — 无论是哪一个您选择的一个 - 将仅在当前分支上运行。您当前的分支是dev
,其上游是origin/dev
。git status
和gitbranch -vv
命令将向您显示上游设置:这里我在我的 Git 存储库的
master
上,并且它与origin/master
同步。由于我刚刚运行了git fetch
,这意味着origin
的 master 标识了相同的提交:我们在这里看到两个名称都标识了提交
ab1f2765f78e75ee51dface57e1071b3b7f42b09
,即 Git 版本 2.36.0-rc1。您的
git fetch
更新了您的origin/master
— 这就是您在git pull
的git fetch
输出中看到的内容 —但没有更新您的origin/dev
。您现有的dev
分支的提示提交与您的origin/dev
提交相同或早于您的提交。这意味着git merge
在他们方面没有任何工作可以与您可能已经完成的任何工作结合起来。您的dev
已与您的origin/dev
保持同步。这就是 Git 在这里告诉你的。1过去,
git pull
实际上只是一个实际运行git fetch
的 shell 脚本,然后实际运行第二个 Git 命令。如今,它是一个庞大的 C 程序,在编译时,包含与git fetch
和其他两个程序相同的代码,因此不必将其他程序作为命令调用,它以子例程调用的方式调用它们。不过原理是一样的,为了保持“向后兼容性”中的“向后”,C 代码顽强地经历了过去需要的每一个愚蠢的小问题,因为这些是单独的程序。2目前这是一个 160 位 SHA-1 哈希,但事实证明 160 位 SHA-1 的加密强度毕竟不强,Git 正在向 256 位 SHA-256 迈进。另请参阅 Git 和 SHA-256。
3这个默认值对许多人来说是坏/错误的,Git 现在开始要求您配置默认值。如果您收到关于需要配置
pull.ff
和/或pull.rebase
的抱怨,那么您拥有的 Git 版本会促使您有意识地选择某些内容,而不仅仅是选择接受一些可能对您来说错误的默认设置。4这实际上取决于多个细节,但默认设置是您获取所有分支,前提是您运行不带选项的
git fetch
。请注意有关使用git pull --all
的答案:这会将--all
传递给git fetch
,这并不意味着 < em>所有分支,因为情况已经如此。相反,它意味着所有遥控器。这并不是有害,但它并没有起到人们认为的作用,因此推荐--all
的答案应该谨慎对待,以免编写它们的人有其他有害的误解。结论
git pull 是一个执行很多操作的大命令。它运行另外两个 Git 命令,每个命令也是一个执行很多操作的大命令:
git fetch
,然后是git merge
或git rebase
。 首先研究所有这三个由git pull
运行的命令是一个非常好的主意,并且在我看来,避免是一个好主意>git pull
倾向于运行各个命令,直到您对每个命令的作用有了很好的了解。一旦您知道每个命令的作用,您可能会发现 git pull 的便捷捷径是运行 git fetch ,然后立即运行,而无需让您有机会观察什么
git fetch
已获取 - 运行第二个命令就是您想要的。但在您知道自己想要什么之前,您实际上不会知道这一点,而在您知道其中每一个的作用之前,您是无法知道的。更糟糕的是,当其中一个命令失败时,您将不知道如何恢复。精通任何事物(软件、烹饪、木工、核反应堆操作等)的很大一部分在于知道当某些事情不起作用时该怎么做。当一切都按照设计进行时,任何傻瓜都可以做到;专业人士知道在不知道的情况下该怎么做。Git reads the
url =
setting fromorigin
(in this case) and uses that, but it does have a tendency to strip.git
from the end of such URLs. There's no particularly good or bad reason for this. Git just does it.The
git pull
command consists of running two other Git commands:1git pull
runsgit fetch
.(Most but not all of the arguments you pass to
git pull
go directly togit fetch
, if you do give any. You didn't, so we get to bypass all the gory details here.)The
git fetch
command is the one producing these lines of output. The fetch command:Calls up some other Git repository: whatever answers at the URL provided. Typically the URL starts with
https://
orssh://
so that Git goes over the network, sort of like making an Internet phone call. A web or ssh server will answer and transfer the "phone call" to other Git software, or at least, other software that speaks the Git protocol, so that there are now two separate Git commands running: yours, on your repository, and theirs, on their repository.In your case the URL is for a file, so instead of calling someone to see who answers, your own Git software plays the role of the other Git software. (Your Git may or may not spawn a second round of Git software, depending on what version of everything you're using. Traditional C Git does, or at least did, have to spawn another one because there are, or at least were, too many global variables so that only one repository could be "in play", being either read—the Git serving the fetch—or written; this has at-least-mostly-fixed over time so the details may vary depending on Git version.) The principle is the same either way: the server your Git "calls up" reads their repository, and your Git writes to your Git repository.
Their Git (their software on their repository) lists out their branches and tags and other such names and the commit hash IDs that go with these. Your Git picks out the ones it "likes". For a default
git fetch
, your Git likes everything, so any commit hash IDs your Git sees that your Git does not already have, your Git asks their Git to please send that over. This obliges them to offer that commit's parent(s); your Git checks to see if you have these commits, and if not, your Git asks for those, which makes their Git offer more parents, and so on.The end result of this phase of the conversation is that your Git learns about every commit they have that you don't, and they learn about the overlap: which commits you both have. They then use that information to package up all the commits and supporting objects that your Git will need, so that you will have everything they have plus any commits of your own that you have never sent out.
This covers the counting and enumerating and compressing and receiving phases. Your Git now has all the necessary commits and supporting objects so that you have every commit. Your Git saves these away, expanding ("unpacking" and "verifying" and so on) things if/as necessary, using the objects you already have, that they learned about during the have/want conversation phase. All the communications are done now, and your Git software can disconnect from theirs.
Now that you have all of their commits, your Git takes each of their branch names and changes those into your remote-tracking names: their
main
becomes yourorigin/main
, theirdev
becomes yourorigin/dev
, and so on. Remember, at the start of this, they listed out all their branch names and all the commit hash IDs that those names represented.Your Git now checks to see which of your
origin/*
names need creating or updating. This is where the:style lines come out. This means their
master
—yourorigin/master
—used to name commitf8322345
, but now theirmaster
names commit9837f82f
. Your Git therefore needs to update your ownorigin/master
.To make complete sense of this, you need one more fact: the hash ID of any commit is universally unique. If you have commit
9837f82f
, your Git calls that9837f82f
, their Git calls that9837f82f
, and every other Git in the universe that has that commit calls it9837f82f
. (This9837f82f
is actually shortened from the full hash ID, which has to be huge so that it can be universally unique.) So your Git and their Git can tell which commits you both have, just by examining the number. (The hash ID is a hexadecimal representation of a large cryptographic checksum over the commit's content.2)There are a few other things to observe about the line:
First, note the two dots, and the fact that the line does not end with
(forced update)
. That means that commitf8322345
is an ancestor of commit9837f82f
: parent, or grandparent, or great-grand-parent, or greatn-grand-parent for n > 1. Ifmaster
had been rewound and rewritten, you'd see a plus sign, three dots, and the addedforced update
remark.If your own Git did not have an
origin/master
yet, so that the existence of theirmaster
(yourorigin/master
) were new to your repository, you would get:and if you run
git fetch
with the prune option enabled and they have deleted some branch name they used to have but no longer do, you would get:These lines therefore tell you a lot about what has happened since the last time you collected updates from a given named remote like
origin
.We now get into the second command that
git pull
will run. You get to choose this command, but it (currently, or previously at least3) defaults to beinggit merge
. Your other option isgit rebase
, but you're gettinggit merge
.Unlike
git fetch
, which fetches all the other Git's branch names,4 the second Git command—regardless of which one you choose—will only operate on the current branch. Your current branch isdev
and its upstream isorigin/dev
. Thegit status
andgit branch -vv
commands will show you the upstream setting:Here I'm on my
master
for my Git repository for Git, and it's in sync withorigin/master
. Since I just rangit fetch
, that meansorigin
's master identifies the same commit:We see here that both names identify commit
ab1f2765f78e75ee51dface57e1071b3b7f42b09
, which is Git version 2.36.0-rc1.Your
git fetch
updated yourorigin/master
—that's what you saw in thegit fetch
output fromgit pull
—but did not update yourorigin/dev
. Your existingdev
branch's tip commit is either the same as, or ahead of, yourorigin/dev
commit. This meansgit merge
has no work on their side to combine with any work you may have done on your side. Yourdev
is already up to date with yourorigin/dev
. And that's what Git told you here.1In the past,
git pull
was literally just a shell script that actually rangit fetch
, then actually ran a second Git command. These days it's a big hairy C program that at compile time, includes the same code asgit fetch
and both other programs, so that instead of invoking the other programs as commands, it invokes them as subroutine calls. The principle is the same, though, and to keep the "backwards" in "backwards compatibility", the C code doggedly goes through every last silly little wrinkle that used to be required because these were separate programs.2This is currently a 160-bit SHA-1 hash, but it turns out that 160 bits of SHA-1 is not cryptographically strong after all, and Git is moving towards 256-bit SHA-256. See also Git and SHA-256.
3This default turns out to be bad/wrong for many, and Git is now starting to require that you configure a default. If you get a complaint about needing to configure
pull.ff
and/orpull.rebase
, you have a version of Git that's pushing you to pick something consciously, rather than just accepting some default that might be wrong for you.4This actually depends on multiple details, but the default setup is that you get all branches, provided you run
git fetch
without options. Watch out for answers that talk about usinggit pull --all
: this passes--all
togit fetch
, where it does not mean all branches since that's already the case. Instead, it means all remotes. This is not harmful, but it does not do what people think it does, so answers recommending--all
should be treated with caution, lest the person who wrote them have other misunderstandings that are harmful.Conclusion
git pull
is a big command that does a lot. It runs two other Git commands, each of which is also a big command that does a lot:git fetch
, then one ofgit merge
orgit rebase
. It's a very good idea to study all three of these run-by-git pull
commands first, and in my opinion, a good idea to avoidgit pull
in favor of running the individual commands, until you have a good working knowledge of what each one does.Once you know what each command does, you may find that
git pull
's convenience short-cut of runninggit fetch
and then immediately—without giving you a chance to observe whatgit fetch
fetched—running a second command is what you want. But you won't actually know that until you know what you want, which you can't know until you know what each of these does. Worse, when one of the commands fails, you won't know how to recover. A huge part of being skilled with anything (software, cooking, woodworking, nuclear reactor operation, and so on) lies in knowing what to do when something didn't work. When everything works as designed, any fool can do it; the pros know what to do when it doesn't.