为什么这行不通:'git log branchname:filepath'?

时间:2018-10-06 11:43:58

标签: git git-log

为什么这不起作用:

git log someBranch:path/to/myfile.txt       
echo $?  # shows 0/success but no output from git log

(这确实有效:)

git rev-parse someBranch:path/to/myfile.txt # this works and returns the SHA

还有另一种方法来获取与特定分支上的特定文件相关的日志/提交(无需检出该分支)吗?

2 个答案:

答案 0 :(得分:1)

git log --branches=<regex> -- <filename>:将所有修改过filename的提交记录在名称与regex匹配的分支中。

答案 1 :(得分:0)

之所以无法正常工作,是因为git log是关于 commits 的,而someBranch:path/to/myfile.txt是一个 blob 的名字。 Git会用“ blob”一词来表示“提交中存储的文件”。

要完全理解这一点,请通读网站Think Like (a) Git上的说明。但是,为了大致了解它,请阅读以下内容:

Git主要与 commits 有关。毕竟,这与支行无关。这意味着您需要确切地知道什么是提交 ,以及提交和分支之间的区别。这有点粘性,因为Git和使用Git的人们将“ branch”一词的几种不同含义混合在一起。要弄清分支的这两种含义,有助于非常具体地讨论每个分支名称。但是,让我们从提交开始。

任何提交的真实名称都是一个丑陋的哈希ID。您将在git log输出中看到这些哈希ID:

$ git log
commit c05048d43925ab8edcb36663752c2b4541911231
Author: Junio C Hamano <gitster@pobox.com>
Date:   Tue Sep 4 14:33:27 2018 -0700

    Git 2.19-rc2

    Signed-off-by: Junio C Hamano <gitster@pobox.com>

c0504...1231是提交的实际名称,正如您所看到的,提交会记住其作者和时间戳。 (还有更多,但我们会在这里停留以保持此空头。)

提交的主要功能有两个:提交使Git永久保留,或者只要提交继续存在(可能也是永久的)就可以保留其中的所有内容的完整快照。存储库。好吧,更准确地说,提交保留了提交中所有内容的快照,但这似乎有点多余:提交具有提交所具有的内容。尽管如此,这是最准确的表达方式。之所以说它具有所有内容的完整快照,是因为其他版本控制系统通常将提交存储为更改,但是Git将它们存储为整个快照。

提交的另一个主要功能是记住其 parent 或先前的提交。您在上方看不到的是c05048d43925ab8edcb36663752c2b4541911231的父级是e9983f8965c0875cc6727e9644c84ff1dfc99372。要直接查看,可以使用git cat-file -p c05048d43925ab8edcb36663752c2b4541911231。但是git log会看到的,这对我们来说很重要:

向我们展示了一次提交后,git log继续向我们展示了先前的/父提交。它使用父母的真实姓名(其丑陋的哈希ID大)找到了先前的/父提交。 —存储在提交中。只要我们掌握了提交的丑陋ID,就可以使用它查找实际的提交。我们通常说这个ID 指向提交:它可以让我们找到它。

因此,每个提交都指向其父级。这使得链条向后看。如果我们使用单个大写字母来表示较大的丑陋哈希ID,那么我们会得到更人性化的内容,尽管在仅仅提交26次左右(取决于字母)之后,我们的字母就用光了:

A  <-B  <-C  ...  <-H

使用这个单字母系统,很容易找到 last 提交:我们只找到最后一个字母。但是这些代表了真正的,显然是随机的(根本不是随机的,但也不是可预测的)哈希。因此,我们需要以某种方式知道H last 哈希,这就是分支 names 进入的地方。

分支名称,例如master last 提交的哈希ID存储在分支中。 Git将此称为 tip 分支的提交。 名称总是指向尖端提交,所以我们应该这样写:

A  <-B  <-C  ...  <-H   <--master

如果我们向分支添加一个 new 提交,则Git这样做的方法是使所有现有的提交保持不变,而完全不触及它们。它会产生一些新的丑陋的哈希ID I,并将I的父级设置为H,然后更改名称 master,因此master存储I的哈希ID而不是H的哈希ID:

A  <-B  <-C  ...  <-H  <-I   <--master

现在,从结尾开始-使用名称master查找I-我们可以使用提交I。提交I会记住H的哈希值,因此我们可以使用它来获取HH记住G的哈希值,因此我们可以使用 that 到达G,这使我们到达F,依此类推,向后。最终,这导致BA,并且由于A是有史以来的第一个提交,因此它没有父级,我们可以停止。 >

所有这些,我们最终了解git log的工作方式

git log的主要任务是显示一些从某个起点开始的提交(或者我们可以将其称为终点因为Git总是向后工作)。我们可以从 any 提交开始,只要我们具有其哈希ID。但是我们拥有的哈希ID是存储在分支名称中的ID。

上面的图显示了只有一个分支名称时该图的外观。如果分支名称更多,则每个名称都将存储一些 tip (在我们的图形中最右边)提交的哈希ID。每次提交都照常指向其父级。如果我们开始绘制内部箭头只是为了使其更容易连接,这将为我们提供例如

...--F--G--H--I   <-- master
         \
          J--K   <-- develop

名称master表示I开始并向后。名称develop表示K开始并向后工作

(请注意,无论我们是从G还是从I开始,随着向后工作,我们最终都会到达提交K。此问题供您​​将来探索:哪个分支包含提交G我们暂时不在这里解决。)

限制git log的输出

在任何情况下,git log都是从结尾开始,然后沿着提交链向后移动,一次向您展示每个提交。在一个大项目中,这可能是很多提交。

每个提交都具有文件 all 的完整快照。如果您在存储库中已经有path/to/myfile.txt很长时间了,那么path/to/myfile.txt的副本中会有很多。它们中的许多可能完全相同,如果是这样,Git会 share ,对具有该特定版本的所有提交仅使用一个副本。 Git之所以可以这样做是因为已提交的版本已冻结:没有任何东西可以更改它们。

但是,如果它们与先前的每个提交相同,那么从您的角度来看,这会使每个提交都变得很无聊!如果所有这些提交都具有相同的符号,为什么要从提交Z开始,然后显示YXW,依此类推? 1}}中?因此,path/to/myfile.txt可以显示提交。

您可以要求git log显示您的提交,从某个起点(结束点)开始并向后进行,但是跳过以显示其中的大部分。您可以说:仅当在比较提交及其父项之后,文件git log在这两个提交中不同时,才显示每个提交。

也就是说,path/to/myfile.txt将从结尾开始-提交git log-并查找Z的父Z。如果Ypath/to/myfile.txt中的Z 相同,则Y 不会显示git log 。如果不同,则Z 显示git log。然后,无论如何,Git将移回Z。现在,它正在Y上运行,Git将查找Y的父Y。如果Xpath/to/myfile.txt中的Y是相同的,则X将继续前进到git log。这一直持续到您对X的输出感到厌倦,或者git log一直回到最初的提交为止。

要使此工作有效,您必须给git log正确的开始(结束?)提交。这就是git log的用处。{{1 }}是从您的当前提交开始的,但是您要从其他特定的提交开始:一些分支名称的提示。 然后,您还必须提供git log branchname个限制文件。这就是git log的来源。路径名位于git log之后,以将其与分支名称分开。如果您有一个名为-- path的文件,--将向您显示从master开始的提交,但是git log master将向您显示从当前提交开始的提交,但仅限于那些更改了文件master

请注意,git log -- master之后的文件名是否存在并不重要:master只是检查在回溯历史时提交之间的文件名是否存在差异。如果该文件不在两个提交中,那么它将随时进行比较,这意味着“对该文件没有更改”。如果文件恰好在两个提交中的一个 中,则意味着文件 did 发生了更改(已添加或删除)。如果文件同时在两个快照中,则文件当然也只有在文件更改的情况下才更改。

--失败的原因现在应该清除

使用git log使git log someBranch:path/to/myfile.txt查找原始Blob哈希ID。那不是一次提交,因此someBranch:path/to/myfile.txt无法显示,也不会显示。然后git log没有 commit 开头,因此也不能向后工作。没有提交显示,它什么也不做,然后退出。 git log至少应警告您,您的起点(终点?)根本不是一个提交,但实际上不是。