了解git rev-list

wfypjpf4  于 2023-08-01  发布在  Git
关注(0)|答案(2)|浏览(125)

在寻找git hook示例时,我看到了以下帖子:https://github.com/Movidone/git-hooks/blob/master/pre-receive和我想了解下面的命令:

git rev-list $new_list --not --all

字符串
其中new_list是从:

NULL_SHA1="0000000000000000000000000000000000000000" # 40 0's
new_list=
any_deleted=false
while read oldsha newsha refname; do
    case $oldsha,$newsha in
        *,$NULL_SHA1) # it's a delete
            any_deleted=true;;
        $NULL_SHA1,*) # it's a create
            new_list="$new_list $newsha";;
        *,*) # it's an update
            new_list="$new_list $newsha";;
    esac
done


我认为rev-list显示提交的时间顺序相反。
但是,有人可以分享更多关于-not-all选项的含义吗?
根据文件:

--not
Reverses the meaning of the ^ prefix (or lack thereof) for all following revision specifiers, up to the next --not.
--all
Pretend as if all the refs in refs/ are listed on the command line as <commit>.


我不能完全理解这些选项。
[更新]在做了一些测试提交后,我发现如果我不使用--not--all选项,那么git rev-list列出了分支上的所有提交,而不是我打算推送的那一个。
然而,想了解为什么当--all选项被传递时,它不打印sha值在终端上?

mwyxok5s

mwyxok5s1#

git rev-list命令是Git中一个 * 非常 * 复杂**中心 * 的命令,因为它所做的是 * 遍历图 。这里的 graph 指的是提交图本身,在某些情况下,指的是下一层(Git objects * reached from commits)。
我认为rev-list显示提交的时间顺序相反。
不完全是,但很接近:

  • 顺序是可变的。default 是逆时间顺序的。
  • 默认情况是遍历一些提交,但你可以让rev-list更深入,以便包括树和blob对象,甚至标签对象。这适用于git fetchgit push(调用git pack-objects)和git pack-objects等程序。我打算在这里完全忽略这种可能性,但我觉得我至少应该提一下。😀

所以 default 是以逆时间顺序列出一些提交。精确指定x 1 m6n1x遍历图的哪些部分是很重要的,也有点棘手:在 some commits 中的 some
但是,有人可以分享更多关于--not--all选项的含义吗?
作为VonC notes,这里的 effect 是列出接收存储库的新提交。这取决于这个git rev-list命令运行在 pre-receive hook 中。它通常不会在这个特定钩子之外做任何有用的事情。因此,正如您所看到的,在Git中,钩子的运行时环境通常至少有点特殊。(这不仅仅适用于预接收钩子:必须考虑每个钩子的激活上下文。)

更多关于--not --all

--all选项的功能正是您在文档中引用的功能:
假装refs/中的所有引用都列在命令行上...
这相当于git for-each-ref refs:它在每个引用上循环。包括分支名称的(mastermaindevelopfeature/tall等等,所有这些都是在refs/heads/中)、标记名(v1.2,实际上是refs/tags/v1.2),远程跟踪名称(origin/develop,实际上是refs/remotes/origin/develop),替换引用(refs/replace/),stash(refs/stash),平分引用,Gerrit引用,如果你使用Gerrit,等等。请注意,它 * 不 * 在reflog条目上循环。
--not前缀是一个简单的布尔运算。在gitrevision语法中-参见the gitrevisions documentation-我们可以写像develop这样的东西,意思是 * 我告诉你从develop开始并向后工作,* 包括 * 这些提交 ,但也可以写像^develop这样的东西,意思是 * 我告诉你从develop开始并向后工作, 排除 * 这些提交 *。所以如果我写:

git rev-list feature1 feature2 ^main

字符串
我要求Git从feature1feature2标识的提交中遍历可达的提交,但从main标识的提交中排除可达的提交。更多关于 * 可达性 * 和图形行走的一般概念,请参阅Think Like (a) Git
--not操作符有效地翻转每个ref上的^

git rev-list --not feature1 feature2 ^main


可以说是简写

git rev-list ^feature1 ^feature2 main


这遍历了main可访问的提交列表,但排除了feature1feature2可访问的提交。

通常 * 所有 * 提交都可以通过--all找到

如果你以正常的日常方式使用Git,* 和 * 目前还没有“分离的HEAD”--分离的HEAD模式并不完全 * 异常 *,但它不是通常的工作方式--git rev-list--all选项告诉它包含 all 提交,因为所有提交 * 都可以 * 从所有引用中获得。因此,将--not --all添加到任何git rev-list中,否则将列出一些提交,这将产生禁止列表的效果。输出为空:我们为什么要麻烦
如果你在分离HEAD模式下,并且已经做了几个新的提交-这可能发生在你处于交互式或冲突的rebase中间,例如-那么git rev-list HEAD --not --all会列出那些 * 可以 * 从HEAD * 到达但 * 不能 * 从任何分支名称。例如,在这个rebase中,这将只是您目前为止复制的那些提交。
因此,“分离HEAD”模式将是一次在命令行中使用git rev-list --not --all的地方。但是对于您正在研究的情况-一个 * 预接收钩子 *-我们并不真正使用命令行。

预收挂钩

当有人使用git push发送commit * 到 * 你自己的Git时,你的Git:

  • 设置隔离区来保存任何新对象(新提交和blob等); 1
  • 与发送者协商以决定发送者应该发送什么;
  • 接收这些对象;和/或
  • 获取 *ref更新请求 * 的列表。这些更新请求基本上只是说 make this name hold this hash ID。2

在实际执行所请求的任何更新之前,您的Git:
1.将整个列表馈送到预接收挂钩。那个钩子可以说“不”;如果是,则整个推送作为一个整体被拒绝。
1.如果显示“ok”,则将列表一次一个请求馈送到update钩子。当钩子说“ok”时,就更新了。如果钩子说“否”,你的Git拒绝一个更新,但继续检查其他更新。

1.在步骤2中接受或拒绝所有更新之后,将接受的列表馈送到后接收挂钩。
在步骤2中添加到ref中的所需对象将从隔离区移到Git的对象数据库中。那些被拒绝的人不是。
现在,考虑一个典型的git push。我们得到了一些新的commit(s)和一个请求:* 创建一个新的分支名称feature/short,或者我们得到一些新的提交和请求: 更新现有分支名称develop,以包括这些新提交,沿着旧提交 *。
在上面的步骤1中,我们有一个新的哈希ID。我们运行了一个循环来读取所有的ref名称,以及它们的当前和新的hash ID,循环只运行了一次,因为只有一个 namegit push-ed。该哈希ID指的是 new commit或commits,它们要么将被添加到这个现有分支,要么是tip和其他对新分支独占的commits。
我们现在要检查这些提交,而不是从任何现有分支可访问的任何现有提交。为了简单起见,而不是我的另一个答案中的$new_list,让我们假设我们只有一个新的哈希ID $new和旧的哈希ID $old:如果分支是全新的,则为全零;如果分支是现有分支名称,则为某个有效的现有提交。
如果新的提交是在一个全新的分支上,那么:

git rev-list $new ^master ^develop ^feature/short ^feature/tall


例如,如果我们知道唯一存在的分支是这四个(并且没有标记等需要担心),那么我们将覆盖它们。但是如果它们被添加到develop中呢?然后我们想排除develop上 * 当前 * 的提交。我们可以使用$old哈希ID来实现这一点:

git rev-list $new ^master ^$old ^feature/short ^feature/tall


这将再次列出运行git push origin develop的任何人想要添加到develop中的新提交。
但是想想$old。这是哈希ID。Git从哪里得到的?Git * 从 namedevelop获取 * 这个哈希ID。这是一个 * 预接收钩子 *;namedevelop尚未更新。因此,名称develop * 是旧哈希ID $old的名称。这意味着:

git rev-list $new ^master ^develop ^feature/short ^feature/tall


我也会做好这份工作。
如果git rev-list $new后面跟着“and not all existing”就可以完成任务,那么:

git rev-list $new --not --branches


会做这份工作。这几乎就是我们在这里拥有的。
只使用--branches的bug是它没有得到任何标签或其他引用。我们可以使用--not --branches --tags,但--not --all更短,也可以获得所有其他参考。
这就是--not --all的来源:这取决于预接收钩的特殊情况。我们列出了新的哈希ID,这是运行git push的人提出的,Git已经作为行列表传递给我们的。我们让git rev-list遍历建议更新的提交图,查看隔离区中的新提交,但排除了存储库中已经存在的所有提交。rev-list命令生成这些散列ID,每行一个,然后我们在shell循环中读取这些ID,并做任何我们喜欢的检查 * 每个提交。
1隔离区在Git 2.11中是新的。在此之前,新对象可以在存储库中保留一段时间,即使推送被拒绝。隔离区对大多数人来说并不是什么大问题,但对于像GitHub这样的大型服务器来说,它可以为他们节省大量磁盘空间。
2请求可以是强制的或非强制的,如果是强制的,可以是强制的,也可以不是强制的。这个信息在pre-receive hook中是不可用的(在update hook中也是不可用的),也就是说,嗯,让我们这么说吧,不是很好,但是添加它会有兼容性的问题。不过,大多数都是宜居的。钩子可以判断它是一个 * createnewref * 还是 delete existing ref 请求,因为如果是的话,两个hash ID中的一个-old或new-将是全零的“null hash”(这是保留的;不允许散列ID为全零)。

siotufzp

siotufzp2#

意思是:

  • 列出通过从给定提交(这里是$new_list,新的、修改的或删除的提交)的父链接可到达的提交
  • 但排除了前面有^的提交中可到达的提交,这里是“all”,即所有HEADS提交或标记提交。

这将rev-list限制为仅接收到的新提交,而不是所有提交(接收到的并且已经存在于接收存储库中)
请注意,现在可以将相同的限制应用到带有pseudo-opts的标准din:
在Git 2.42(2023年第3季度)中,get_revision() API的设置代码现在允许在--stdin模式下提供--all--not等馈送选项。
参见commit c40f0b7commit af37a20commit cc80450(2023年6月15日)由Patrick Steinhardt ( pks-t )
(由Junio C Hamano -- gitster --合并到commit 812907d,2023年7月4日)

revision:我的天啊在--stdin模式下处理伪选项

签收人:帕特里克·斯坦哈特
虽然git-rev-listgit-log都支持--stdin,但它只接受提交和文件。
最值得注意的是,不可能通过stdin传递任何伪选项,如--all--glob=或其他。
这使得在某些脚本化的场景中很难使用这个函数,比如当人们希望支持针对特定修订版本的查询时,也希望支持针对引用模式的查询时。
虽然这在理论上是可以通过使用参数实现的,但一旦我们使用足够大的查询达到平台限制,这可能会遇到问题。
而且因为--stdin不能处理伪opts,唯一的替代方法就是混合使用参数和标准输入,这很麻烦。
在这两个命令中实现对伪选项的处理支持,以更好地支持此用例。
这里一个值得注意的限制是--stdin只支持--glob=foo形式的“卡住”参数。
这是因为“unstuck”参数还需要我们阅读下一行,这会给代码增加相当多的复杂性。
这个限制对于脚本化的使用应该是很好的。
rev-list-options现在在其手册页中包括:
除了从命令行获取参数外,还可以读取它们作为标准输入。
它接受提交和伪选项,如--all--glob=。当看到--分隔符时,以下输入被视为路径并用于限制结果。
现在这是可能的:

git rev-list --stdin < --all --not --branches

字符串

相关问题