了解 git 修订列表
Understanding git rev-list
在寻找 git 钩子示例时,我遇到了以下 post: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 值?
意思是:
- 列出可以通过给定提交的父链接访问的提交,此处
$new_list
,新的、修改的或删除的提交
- 但不包括前面带有
^
的提交可访问的提交,此处为“all”,即所有 HEADS 提交或标记的提交。
这将 rev-list 限制为仅接收到的新提交,而不是所有提交(已接收并已存在于接收存储库中)
git rev-list
命令是非常复杂的,非常重要命令在Git中,正如它做的是走图。这里的 graph 这个词指的是提交图本身,在某些情况下,指的是下一层(Git objects reachable from提交)。
I figured that rev-list shows commits in reverse chronological order.
不完全是,但接近:
- 顺序可变。 默认是reverse-chronological。
- 默认是遍历一些提交,但您可以
rev-list
更深入以包括树和 blob 对象,甚至标记对象。这适用于像 git fetch
和 git push
(调用 git pack-objects
)和 git pack-objects
这样的程序。我打算在这里完全忽略这种可能性,但我觉得我至少应该提一下。
所以默认是按照倒序列出一些提交。准确指定 图表的哪些部分 我们将 git rev-list
遍历既重要又有点棘手: some 在 一些提交中 .
But, can someone share more insight on what --not
and --all
options are meant for?
与一样,这里的效果是列出接收仓库的新提交。这取决于这个 git rev-list
命令在 pre-receive 钩子 中的 运行。除了这个特定的钩子之外,它通常不会做任何有用的事情。因此,如您所见,在 Git 中的钩子 run-time 环境通常至少有一点特殊。 (这不仅适用于 pre-receive 挂钩:必须考虑每个挂钩的激活上下文。)
更多关于 --not --all
--all
选项执行您从文档中引用的内容:
Pretend as if all the refs in refs/
are listed on the command line ...
所以这相当于 git for-each-ref refs
:它遍历每个引用。这包括 b运行ch 名称(master
或 main
、develop
、feature/tall
等等,所有这些实际上都在 refs/heads/
),标签名称(v1.2
,实际上是 refs/tags/v1.2
),remote-tracking 名称(origin/develop
,实际上是 refs/remotes/origin/develop
),替换引用(在 refs/replace/
)、存储 (refs/stash
)、二分引用、Gerrit 引用(如果您使用的是 Gerrit)等等。请注意,它不会循环引用日志条目。
--not
前缀是一个简单的布尔运算。在 gitrevisions 语法中——参见 the gitrevisions documentation——我们可以写类似 develop
的东西,意思是 I tell you to start from develop
and work backwards and include 这些提交,还有像 ^develop
这样的事情,意思是 我告诉你从 develop
开始,然后倒退 排除这些提交。所以如果我写:
git rev-list feature1 feature2 ^main
我要求 Git 走提交 可从 由名称 feature1
和 feature2
标识的提交,但是到 exclude 可从 main
标识的提交中访问的提交。有关 可达性 和 graph-walking 的(更多)一般概念,请参阅 Think Like (a) Git。
--not
运算符有效地翻转每个 ref:
上的 ^
git rev-list --not feature1 feature2 ^main
是shorthand,可以说是:
git rev-list ^feature1 ^feature2 main
这会遍历可从 main
访问的提交列表,但不包括可从 feature1
或 feature2
.
访问的提交列表
通常 所有 提交都可以通过 --all
找到
如果您以日常方式使用 Git, 和 目前没有“分离的 HEAD”——分离的 HEAD 模式不完全是异常 但这不是通常的工作方式——git rev-list
的 --all
选项告诉它包含 all 提交,因为所有提交都可以从所有引用中访问。1所以--not --all
有效地排除所有提交.因此,将 --not --all
添加到任何本来会列出一些提交的 git rev-list
具有抑制列表的效果。输出为空:我们为什么要打扰?
如果您处于分离的 HEAD 模式并进行了几次新提交——例如,当您处于交互或冲突的 rebase 中间时可能会发生这种情况——然后 git rev-list HEAD --not --all
将列出那些提交 可从 HEAD
访问,但 不能 从任何 b运行ch 名称访问。例如,在那个 rebase 中,那只是你到目前为止复制的那些提交。
所以“分离的 HEAD”模式曾经是 git rev-list --not --all
在命令行中有用的地方。但是对于您正在检查的情况——pre-receive 挂钩——我们并不是真正在命令行上。
Pre-receive 挂钩
当有人使用 git push
将提交 发送到 你自己的 Git 时,你的 Git:
- 设置一个 qua运行tine 区域来保存任何新对象(新提交和 blob 等等);1
- 与发件人协商决定发件人应该发送什么;
- 收到这些对象;和
- 获取 ref 更新请求 的列表。这些更新请求基本上只是说 让这个名字持有这个哈希 ID.2[=260=
在实际执行任何请求的更新之前,您的Git:
- 将整个列表提供给 pre-receive 挂钩。那个钩子可以说“不”;如果是,则整个推送被拒绝。
- 如果显示“确定”,则将列表(一次一个请求)提供给更新挂钩。当那个钩子说“ok”时,更新。如果挂钩显示“否”,则您的 Git 会拒绝一次更新,但会继续检查其他更新。
- 在步骤 2 中接受或拒绝所有更新后,将接受的列表提供给 post-receive 挂钩。
在第 2 步中添加到某些 ref 中的所需对象已从 qua运行tine 移至 Git 的对象数据库。那些被拒绝的不是。
现在,想想一个典型的 git push
。我们得到一些新的提交和一个请求:create a new b运行ch name feature/short
,或者我们得到一些新的提交和一个请求:更新现有的 b运行ch 名称 develop
以包括这些新提交以及旧提交。
在上面的第 1 步中,我们有一个新的哈希 ID。我们 运行 一个循环来读取所有的 ref 名称,以及它们的当前和 proposed-new 哈希 ID,并且循环 运行 只有一次,因为只有一个 name 正在 git push
-ed。该哈希 ID 指的是 new 提交或提交,它们将被添加到现有的 b运行ch 中,或者是提示和新提交独有的其他提交b运行ch.
我们现在想要检查这些提交,而不是从任何现有 b运行ch 可以访问的任何现有提交。为简单起见,而不是 $new_list
在我的另一个答案中,假设我们只是一个新的哈希 ID,$new
,和 b运行ch 名称的旧哈希 ID,$old
: all-zeros 如果 b运行ch 是 all-new,或者一些有效的现有提交,如果它是现有的 b运行ch 名称。
如果新提交在一个全新的 b运行ch 上,那么:
git rev-list $new ^master ^develop ^feature/short ^feature/tall
将覆盖它们,例如,如果我们知道唯一存在的 b运行 是这四个(并且没有标签等需要担心)。但是,如果将它们添加到 develop
中怎么办?然后我们想排除 develop
上 当前 的提交。我们可以使用 $old
哈希 ID 来做到这一点:
git rev-list $new ^master ^$old ^feature/short ^feature/tall
这将再次只列出 运行 git push origin develop
想要添加到我们的 develop
.
的新提交
但是想想$old
。这是一个哈希 ID。 Git 从哪里弄来的? Git 从 name develop
得到了 这个哈希 ID。这是一个pre-receive挂钩; 名称 develop
尚未更新。所以名称 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
的错误在于它没有获得任何标签或其他引用。我们可以使用 --not --branches --tags
但 --not --all
更短并且还得到所有其他参考。
所以这就是 --not --all
的来源:它取决于 pre-receive 钩子的特殊情况。我们列出了新的哈希 ID,正如 运行 和 git push
所提议的那样,我们的 Git 已作为行列表传递给我们。我们 git rev-list
遍历 proposed-to-be-updated 提交图,查看 qua运行tine 区域中的新提交,但不包括我们存储库中已有的所有提交。 rev-list 命令生成这些哈希 ID,每行一个,然后我们在 shell 循环中读取这些 ID,并在每次提交时做任何我们想做的事情 检查 。
1qua运行tine 区域是 Git 2.11 中的新区域。在此之前,新对象可能会在存储库中保留一段时间,即使推送被拒绝也是如此。 qua运行tine 区域对大多数人来说并不是什么大问题,但对于像 GitHub 这样的大型服务器,它可以为他们节省 lot磁盘 space.
2请求可以是强制的,也可以是not-forced,如果是强制的,可以是force-with-lease,也可以不是。此信息在 pre-receive 挂钩(也不在更新挂钩)中不可用,也就是说,嗯,我们只是说 不太好 ,但是添加存在兼容性问题它。不过,这主要是宜居的。钩子可以判断它是 create new ref 还是 delete existing ref 请求,因为如果是这样,两个哈希 ID 之一——旧的或新的——将是 all-zeros“空哈希”(保留;不允许哈希 ID 为 all-zeros)。
在寻找 git 钩子示例时,我遇到了以下 post: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 值?
意思是:
- 列出可以通过给定提交的父链接访问的提交,此处
$new_list
,新的、修改的或删除的提交 - 但不包括前面带有
^
的提交可访问的提交,此处为“all”,即所有 HEADS 提交或标记的提交。
这将 rev-list 限制为仅接收到的新提交,而不是所有提交(已接收并已存在于接收存储库中)
git rev-list
命令是非常复杂的,非常重要命令在Git中,正如它做的是走图。这里的 graph 这个词指的是提交图本身,在某些情况下,指的是下一层(Git objects reachable from提交)。
I figured that rev-list shows commits in reverse chronological order.
不完全是,但接近:
- 顺序可变。 默认是reverse-chronological。
- 默认是遍历一些提交,但您可以
rev-list
更深入以包括树和 blob 对象,甚至标记对象。这适用于像git fetch
和git push
(调用git pack-objects
)和git pack-objects
这样的程序。我打算在这里完全忽略这种可能性,但我觉得我至少应该提一下。
所以默认是按照倒序列出一些提交。准确指定 图表的哪些部分 我们将 git rev-list
遍历既重要又有点棘手: some 在 一些提交中 .
But, can someone share more insight on what
--not
and--all
options are meant for?
与git rev-list
命令在 pre-receive 钩子 中的 运行。除了这个特定的钩子之外,它通常不会做任何有用的事情。因此,如您所见,在 Git 中的钩子 run-time 环境通常至少有一点特殊。 (这不仅适用于 pre-receive 挂钩:必须考虑每个挂钩的激活上下文。)
更多关于 --not --all
--all
选项执行您从文档中引用的内容:
Pretend as if all the refs in
refs/
are listed on the command line ...
所以这相当于 git for-each-ref refs
:它遍历每个引用。这包括 b运行ch 名称(master
或 main
、develop
、feature/tall
等等,所有这些实际上都在 refs/heads/
),标签名称(v1.2
,实际上是 refs/tags/v1.2
),remote-tracking 名称(origin/develop
,实际上是 refs/remotes/origin/develop
),替换引用(在 refs/replace/
)、存储 (refs/stash
)、二分引用、Gerrit 引用(如果您使用的是 Gerrit)等等。请注意,它不会循环引用日志条目。
--not
前缀是一个简单的布尔运算。在 gitrevisions 语法中——参见 the gitrevisions documentation——我们可以写类似 develop
的东西,意思是 I tell you to start from develop
and work backwards and include 这些提交,还有像 ^develop
这样的事情,意思是 我告诉你从 develop
开始,然后倒退 排除这些提交。所以如果我写:
git rev-list feature1 feature2 ^main
我要求 Git 走提交 可从 由名称 feature1
和 feature2
标识的提交,但是到 exclude 可从 main
标识的提交中访问的提交。有关 可达性 和 graph-walking 的(更多)一般概念,请参阅 Think Like (a) Git。
--not
运算符有效地翻转每个 ref:
^
git rev-list --not feature1 feature2 ^main
是shorthand,可以说是:
git rev-list ^feature1 ^feature2 main
这会遍历可从 main
访问的提交列表,但不包括可从 feature1
或 feature2
.
通常 所有 提交都可以通过 --all
找到
如果您以日常方式使用 Git, 和 目前没有“分离的 HEAD”——分离的 HEAD 模式不完全是异常 但这不是通常的工作方式——git rev-list
的 --all
选项告诉它包含 all 提交,因为所有提交都可以从所有引用中访问。1所以--not --all
有效地排除所有提交.因此,将 --not --all
添加到任何本来会列出一些提交的 git rev-list
具有抑制列表的效果。输出为空:我们为什么要打扰?
如果您处于分离的 HEAD 模式并进行了几次新提交——例如,当您处于交互或冲突的 rebase 中间时可能会发生这种情况——然后 git rev-list HEAD --not --all
将列出那些提交 可从 HEAD
访问,但 不能 从任何 b运行ch 名称访问。例如,在那个 rebase 中,那只是你到目前为止复制的那些提交。
所以“分离的 HEAD”模式曾经是 git rev-list --not --all
在命令行中有用的地方。但是对于您正在检查的情况——pre-receive 挂钩——我们并不是真正在命令行上。
Pre-receive 挂钩
当有人使用 git push
将提交 发送到 你自己的 Git 时,你的 Git:
- 设置一个 qua运行tine 区域来保存任何新对象(新提交和 blob 等等);1
- 与发件人协商决定发件人应该发送什么;
- 收到这些对象;和
- 获取 ref 更新请求 的列表。这些更新请求基本上只是说 让这个名字持有这个哈希 ID.2[=260=
在实际执行任何请求的更新之前,您的Git:
- 将整个列表提供给 pre-receive 挂钩。那个钩子可以说“不”;如果是,则整个推送被拒绝。
- 如果显示“确定”,则将列表(一次一个请求)提供给更新挂钩。当那个钩子说“ok”时,更新。如果挂钩显示“否”,则您的 Git 会拒绝一次更新,但会继续检查其他更新。
- 在步骤 2 中接受或拒绝所有更新后,将接受的列表提供给 post-receive 挂钩。
在第 2 步中添加到某些 ref 中的所需对象已从 qua运行tine 移至 Git 的对象数据库。那些被拒绝的不是。
现在,想想一个典型的 git push
。我们得到一些新的提交和一个请求:create a new b运行ch name feature/short
,或者我们得到一些新的提交和一个请求:更新现有的 b运行ch 名称 develop
以包括这些新提交以及旧提交。
在上面的第 1 步中,我们有一个新的哈希 ID。我们 运行 一个循环来读取所有的 ref 名称,以及它们的当前和 proposed-new 哈希 ID,并且循环 运行 只有一次,因为只有一个 name 正在 git push
-ed。该哈希 ID 指的是 new 提交或提交,它们将被添加到现有的 b运行ch 中,或者是提示和新提交独有的其他提交b运行ch.
我们现在想要检查这些提交,而不是从任何现有 b运行ch 可以访问的任何现有提交。为简单起见,而不是 $new_list
在我的另一个答案中,假设我们只是一个新的哈希 ID,$new
,和 b运行ch 名称的旧哈希 ID,$old
: all-zeros 如果 b运行ch 是 all-new,或者一些有效的现有提交,如果它是现有的 b运行ch 名称。
如果新提交在一个全新的 b运行ch 上,那么:
git rev-list $new ^master ^develop ^feature/short ^feature/tall
将覆盖它们,例如,如果我们知道唯一存在的 b运行 是这四个(并且没有标签等需要担心)。但是,如果将它们添加到 develop
中怎么办?然后我们想排除 develop
上 当前 的提交。我们可以使用 $old
哈希 ID 来做到这一点:
git rev-list $new ^master ^$old ^feature/short ^feature/tall
这将再次只列出 运行 git push origin develop
想要添加到我们的 develop
.
但是想想$old
。这是一个哈希 ID。 Git 从哪里弄来的? Git 从 name develop
得到了 这个哈希 ID。这是一个pre-receive挂钩; 名称 develop
尚未更新。所以名称 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
的错误在于它没有获得任何标签或其他引用。我们可以使用 --not --branches --tags
但 --not --all
更短并且还得到所有其他参考。
所以这就是 --not --all
的来源:它取决于 pre-receive 钩子的特殊情况。我们列出了新的哈希 ID,正如 运行 和 git push
所提议的那样,我们的 Git 已作为行列表传递给我们。我们 git rev-list
遍历 proposed-to-be-updated 提交图,查看 qua运行tine 区域中的新提交,但不包括我们存储库中已有的所有提交。 rev-list 命令生成这些哈希 ID,每行一个,然后我们在 shell 循环中读取这些 ID,并在每次提交时做任何我们想做的事情 检查 。
1qua运行tine 区域是 Git 2.11 中的新区域。在此之前,新对象可能会在存储库中保留一段时间,即使推送被拒绝也是如此。 qua运行tine 区域对大多数人来说并不是什么大问题,但对于像 GitHub 这样的大型服务器,它可以为他们节省 lot磁盘 space.
2请求可以是强制的,也可以是not-forced,如果是强制的,可以是force-with-lease,也可以不是。此信息在 pre-receive 挂钩(也不在更新挂钩)中不可用,也就是说,嗯,我们只是说 不太好 ,但是添加存在兼容性问题它。不过,这主要是宜居的。钩子可以判断它是 create new ref 还是 delete existing ref 请求,因为如果是这样,两个哈希 ID 之一——旧的或新的——将是 all-zeros“空哈希”(保留;不允许哈希 ID 为 all-zeros)。