如何以编程方式检查本地副本是否位于远程副本后面



目前我正在获取最新版本,然后运行git status并解析Your branch is up to date with 'origin/master'的输出,但这感觉像是一个黑客。

我已经研究过使用git status --porcelain但这只包括在系统上所做的文件更改,而不包括在远程上进行的更改。我不关心实际进行了哪些更改,我只想知道是否存在任何更改(在本地或远程)。

我怎样才能干净利落地做到这一点?

要以编程方式获取当前分支与其上游不同的提交计数,请使用git rev-list --count --left-right HEAD...@{upstream}git rev-list --count master...master@{upstream}例如。 请注意这里的三个点,它们将分支名称或HEADbranch@{upstream}这就是git statusgit branch -vv打印ahead 1behind 2up to date或其他任何东西的方式。

请注意,这假设您首先在分支上,并且分支有一个上游在前面和/或后面。 如果上游是远程跟踪名称,如origin/master,则假定存储在远程跟踪名称中的值是您希望存储在其中的值。

还有很多要知道的

如果您正在编写这些内容的脚本,请务必准确了解(或定义)最新的含义。

纯粹在本地(即在一个存储库 + 工作树组合中),需要考虑三个实体:

  • 当前提交,又名HEAD

    这可能是一个分离的 HEAD,其中HEAD包含原始哈希 ID,或者相反,在分支上,HEAD包含分支本身的名称。 在分支上时,分支名称,例如master,包含当前提交的原始哈希 ID。 无论哪种方式,HEAD始终引用当前提交。1

    当前提交本身是只读的(完全)和永久的(大多数情况下,您可以故意放弃提交,之后它们最终会被删除)。 您可以更改哪个提交是当前提交(例如,git checkoutdifferent-commit),但不能更改提交本身。 由于提交不能更改,因此根据定义,它永远不会"过时":它就是它是什么。 与任何提交一样,当前提交有一些元数据(谁做的,何时完成的等)以及每个文件的完整快照。

    提交中的文件存储采用特殊的仅 Git 格式(当然是只读的)。

  • 工作树,只是你工作的地方。

    在这里,您可以读取和写入每个文件。 这些文件采用普通格式,未压缩且特定于 Git。 你也可以在这里拥有 Git 不知道的文件,但在我们正确讨论这个问题之前,我们需要涵盖第三个实体。

  • 索引,也称为
  • 暂存区域,有时也称为缓存

    该索引有多种用途(因此有多个名称),但我认为如果您现在进行了提交,最好将其描述为您将进行的下一次提交。 也就是说,索引(实际上只是一个文件)包含 Git 制作新快照所需的所有信息,以放入新提交中。 因此,索引保存将进入您下次提交的所有文件

    索引中的文件经过压缩,并且采用仅限 Git 的格式,就像提交中的文件一样。 但是,对于我们这里的目的来说,关键区别在于索引中的文件可以更改。 可以将新文件放入索引中,也可以从索引中删除现有文件。

    git addfile真正要做的只是将文件从工作树复制到索引。 这将替换索引中的先前版本,以便索引现在与工作树匹配。 或者,如果要删除文件,git rmfile从索引工作树

    中删除该文件。

1新仓库根本没有提交,因此此规则有一个例外:HEAD可以引用根本不存在的分支名称。 在一个全新的存储库中就是这种情况:HEAD说当前分支是master的,但在您进行第一次提交之前master实际上并不存在。

(git checkout --orphan命令可以为另一个分支重新创建此特殊的"在尚不存在的分支上"状态。 这不是大多数人大多数时候都会做的事情,但它可以在检查状态的程序中出现。


git status做什么

由于索引和工作树都是可写的,因此两者都可能是"脏的"或以某种方式导致某些内容"过时"。 如果您认为工作树文件是最新的,则可能是索引副本已过期,因为它与工作树副本不匹配。 将工作树文件复制到索引中后,索引不再与提交HEAD匹配,并且在某些时候需要新的提交。

除了对分支及其上游运行git rev-list --count --left-right并获取这些数字之外,git status所做的2是它实际上运行了两个git diffs(--name-status,因为它对详细的补丁不感兴趣):

  1. HEAD与索引进行比较。 无论这里有什么不同,这些都是为提交而暂存的更改,因为如果您现在进行提交,Git 将创建整个索引的快照,并且该快照将与这些文件的当前提交不同。

  2. 将索引与工作树进行比较。 无论这里有什么不同,这些更改都不会为提交而暂存。 对这些文件运行git add后,索引副本将与工作树副本匹配,但不再与HEAD副本匹配,因此现在这些将是暂存的更改以进行提交。


2请注意,git status首先检查您是否在分支,如果是,则检查分支是否具有上游设置。 此外,这一切都内置在其中,因此它不必运行单独的程序,但原理是相同的。


未跟踪,可能被忽略

现在,我们也可以正确定义取消跟踪文件的含义。 简单地说,未跟踪的文件是不在索引中的文件。 也就是说,如果我们使用git rm --cached从索引中删除文件(仅),或者如果我们在工作树中创建文件而不在索引中创建相应的文件,则我们有一个在索引中没有同的工作树文件。 这是一个未跟踪的文件

如果一个文件取消跟踪,git status通常会抱怨它:它运行的将索引与工作树进行比较的差异说啊,这是工作树中不在索引中的文件,Git 会告诉你它是未跟踪的。 如果故意取消跟踪,您可以通过在.gitignore文件中列出该文件或与之匹配的路径名模式来git status闭嘴。 本质上,在抱怨某些文件未被跟踪之前,Git 会查看忽略指令。3但是,如果文件索引中,Git 从不在任何.gitignore中查找其名称。


3ignore 指令还告诉git add,如果该文件当前未被跟踪,则任何集体"添加所有内容"都应避免添加该文件。


上游和远程

分支的上游可以是远程跟踪名称,例如origin/master。 这些名称是 Git 记住其他一些 Git 分支的方式。 要更新远程origin的远程跟踪名称,只需运行git fetch origin

请注意,您可以拥有多个遥控器! 如果在第二个 URL 上添加第二个远程fredgit fetch fred将在URL 处调用 Git,并更新您的fred/master等等。 因此,将git fetch运行到正确的遥控器非常重要。

运行没有额外名称的git fetch将获取当前分支上游的远程,或者从origin当前分支没有上游,或者没有当前分支,因此这通常只是运行git fetch的问题。

子模块

子模块实际上只是对另一个 Git 存储库的引用,但这给总体计划带来了全新的皱纹。 每个 Git 存储库都有自己的HEAD、工作树和索引。 这些可以像以前一样干净或脏,如果子模块未处于分离 HEAD 状态,则子模块的分支可以位于上游的前面和/或后面。

但是,子模块存储库通常处于分离的 HEAD 状态。超级项目中的每个提交都列出了 Git 应将该子模块 Git 分离到的特定提交。 当超级项目 Git 签出提交时,超级项目 Git 会将子模块的哈希 ID 存储到超级项目的索引中。 这样,每个新的超级项目提交都会记录正确的哈希ID。

更改哈希 ID,git add在超级项目中将实际签出的子模块的当前哈希 ID 复制到超级项目存储库中的索引中(呜呜! 因此,如果您已将子模块(通过git checkout移动到那里),则导航回超级项目,在子模块路径上运行git add,现在超级项目的索引记录了正确的哈希ID,为下一个超级项目提交做好准备。

(测试子模块是否在超级项目索引所需的提交上更加困难。

最新更新