要以编程方式获取当前分支与其上游分支上不同的提交的计数,例如使用git rev-list --count --left-right HEAD...@{upstream} 或git rev-list --count master...master@{upstream}。注意这里的三个点,它们将分支名称或 HEAD 与 <em>branch</em>@{upstream} 分开,这就是 git status 或 git branch -vv 打印 ahead 1 或 behind 2 或 up to date 或其他什么的方式。
请注意,这假设您首先在一个分支上,并且该分支有一个上游可以领先和/或落后。如果上游是一个远程跟踪名称,例如origin/master,则假定存储在远程跟踪名称中的值就是您想要存储在其中的值。
还有很多要了解的
如果您正在编写这些内容的脚本,请务必准确了解(或定义)最新的含义。
纯粹在本地——即在一个存储库 + 工作树组合中——需要考虑三个实体:
-
当前提交,又名HEAD。
这可能是一个分离的 HEAD,其中 HEAD 包含原始哈希 ID,或者相反,在一个分支上,其中 HEAD 包含名称分支本身。在分支上时,分支名称,例如master,包含当前提交的原始哈希 ID。无论哪种方式,HEAD 始终引用当前提交。1
当前提交本身是只读的(完全)和永久的(大多数情况下——您可以故意放弃提交,之后它们最终会被删除)。您可以更改哪个提交是当前提交(例如,git checkout <em>different-commit</em>),但您不能更改提交本身。由于提交无法更改,因此根据定义,它永远不会“过时”:它就是它。与任何提交一样,当前提交具有一些元数据(谁提交、何时提交等)以及每个文件的完整快照。
在提交中存储的文件是一种特殊的、仅限 Git 的格式(当然是只读的)。
-
工作树,就是你工作的地方。
在这里,您可以读取和写入每个文件。这些文件采用普通格式,未压缩且特定于 Git。你也可以在这里有 Git 不知道的文件,但在我们正确谈论这个之前,我们需要涵盖第三个实体。
-
索引,也称为暂存区,有时也称为缓存。
索引有多种用途(因此有多个名称),但我认为最好将其描述为如果您现在提交,您将进行的下一次提交。也就是说,索引(实际上只是一个文件)包含 Git 制作新快照以放入新提交所需的所有信息。因此,索引包含将进入您所做的 next 提交的所有 文件。
索引中的文件是压缩的,并且是仅 Git 格式,就像提交中的文件一样。但是,对于我们的目的而言,关键的区别在于索引中的文件 可以更改。您可以将新文件放入索引中,也可以从索引中删除现有文件。
git add <em>file</em> 真正所做的只是将文件从 工作树复制到 索引。这将替换索引中的先前版本,以便索引现在与工作树匹配。或者,如果您希望删除一个文件,git rm <em>file</em> 会从索引和工作树中删除该文件。
1一个新的存储库根本没有提交,所以这个规则有一个例外:HEAD 可以引用一个根本没有的分支名称存在。在一个全新的存储库中就是这种情况:HEAD 表示当前分支是 master,但 master 在您进行第一次提交之前实际上并不存在。
(git checkout --orphan 命令可以为另一个分支重新创建这种特殊的“在尚不存在的分支上”状态。这不是大多数人大部分时间会做的事情,但它可以出现在程序中检查状态。)
git status 做了什么
由于索引和工作树都是可写的,因此两者都可能是“脏的”或导致某些内容以某种方式“过时”。如果您认为工作树文件是最新的,则可能是 index 副本已过期,因为它与工作树副本不匹配。一旦工作树文件被复制到索引中,索引就不再匹配 HEAD 提交,并且在某个时候需要一个新的提交。
git status 的作用是,除了在分支及其上游运行 git rev-list --count --left-right 并获取这些数字之外,2 是它实际上运行了两个 git diffs(与 @987654349 @因为它对详细的补丁不感兴趣):
将HEAD 与索引进行比较。无论这里有什么不同,这些都是为提交暂存的更改,因为如果您现在提交,Git 将对整个索引进行快照,而该快照将不同于current 正是这些文件中的提交。
将索引与工作树进行比较。无论这里有什么不同,这些都是不为提交暂存的更改。在这些文件上运行 git add 后,索引副本将匹配工作树副本,但不再匹配 HEAD 副本,因此现在这些更改将是 暂存以进行提交的更改。
2请注意,git status 首先检查您是否在一个分支,如果是,则该分支有一个上游环境。还有,这都是内置的,所以不用单独运行程序,但是原理是一样的。
未跟踪,可能被忽略
我们现在也可以正确定义文件不被跟踪意味着什么。未跟踪的文件很简单,就是不在索引中的文件。也就是说,如果我们(仅)使用git rm --cached 从索引中删除一个文件,或者如果我们在工作树中创建一个文件而不在索引中创建相应的文件,我们将得到一个没有任何内容的工作树文件索引中相同的 name。这是一个未跟踪的文件。
如果一个文件 未被跟踪,git status 通常会抱怨它:它运行的比较索引与工作树的差异显示 啊,这是工作中的一个文件- 不在索引中的树,Git 会告诉你它未被跟踪。如果它故意未被跟踪,您可以通过在.gitignore 文件中列出该文件(或与之匹配的路径名模式)来让git status 闭嘴。本质上,就在抱怨某个文件未被跟踪之前,Git 会查看忽略指令。3 但是如果文件 在索引中,Git 永远不会在任何文件中查找它的名称.gitignore.
3忽略指令还告诉git add,任何集体“添加所有内容”都应该避免添加该文件,如果它当前未被跟踪的话。
上游和远程
分支的上游可以是远程跟踪名称,例如origin/master。这些名称是您的 Git 记住其他 Git 分支的方式。要更新远程origin 的远程跟踪名称,您只需运行git fetch origin。
请注意,您可以拥有多个遥控器!如果你在第二个 URL 添加第二个远程 fred,git fetch fred 将在 那个 URL 调用 Git,并更新你的 fred/master 等等。所以将git fetch 运行到right 遥控器很重要。
运行git fetch不带附加名称将获取当前分支上游的远程,或者从origin当前分支没有上游,或者没有当前分支,所以这通常只是运行@的问题987654369@.
子模块
子模块实际上只是对另一个 Git 存储库的引用,但这给总体计划带来了一个全新的问题。每个 Git 存储库都有自己的 HEAD、工作树和索引。这些可以像以前一样是干净的或脏的,如果子模块不处于 detached-HEAD 状态,则子模块的分支可以在 其上游之前和/或之后。
但是,子模块存储库通常处于 detached-HEAD 状态。 superproject 中的每个提交都列出了您的 Git 应该将该子模块 Git 分离到的 特定提交。当超级项目 Git 签出提交时,超级项目 Git 将子模块的哈希 ID 存储到超级项目的索引中。这样,每个新的超级项目提交都会记录正确的哈希 ID。
为了更改哈希ID,超级项目中的git add将实际签出子模块的当前哈希ID复制到存储库中的索引中超级项目(哇!)。因此,如果您已经移动了子模块(通过git checkout 那里),您导航回超级项目,在子模块路径上运行git add,现在超级项目的索引记录了正确的哈希 ID,为下一次超级项目提交做好了准备。
(测试子模块是否在超级项目索引所需的提交上更困难。)