【问题标题】:How to check if a folder exists in a git tag如何检查git标签中是否存在文件夹
【发布时间】:2020-03-15 03:54:37
【问题描述】:

我需要在发布期间将各种版本的代码推送到 git,并在出现问题时回滚到以前的版本。

我们使用标签来发布。

为了回滚,我想检查代码文件夹是否存在于之前的标签中。

我知道我可以使用 git-cat 文件,但这对我的情况没有帮助,因为代码在我们的流程中部署之前已经在分支中。

有没有办法检查git标签中是否存在文件夹? 然后我想使用 mt 脚本的返回值。

【问题讨论】:

  • git 标签可以指向任何 git 对象、blob、树、提交等,所以我认为没有可靠的方法来做到这一点。如果标签引用了提交或树,您可以这样做,但同样不可靠。你应该能够在没有这种检查的情况下回滚到最后一个可靠的版本
  • 而不是寻找特定的git命令,如果标签是树状的,你为什么不检查一下并统计目录??

标签: git tags


【解决方案1】:

对于您没有提出的问题,有一个相对直接和简单的答案,即:运行git cat-file -t <em>commit-specifier</em>:<em>path</em>。如果它说它存在并且是一个 tree 对象,则您指定的提交包含将进入具有该名称的文件夹的文件。如果它说这是某种其他类型的对象,则您指定的提交不包含此类文件:它具有此对象。如果这完全失败,则您指定的提交不包含任何具有该名称的对象。因此:

objid="${tagname}:${path}"
objtype=$(git cat-file -t $objid 2>/dev/null) || objtype=none
case $objtype in
none) echo no such folder would be created;;
blob) echo the specified path holds a file rather than a folder;;
tree) echo a folder would be created and contain some files;;
commit) echo the specified path refers to a submodule;;
*) echo something went terribly wrong;;
esac

您可以将这些不同的echos 转换为您喜欢的任何退出状态。这可能就是你想要的答案。

回答你的问题,而不是我认为你想问的问题

不过,您实际提出的问题是:

有没有办法检查git标签中是否存在文件夹?

tag 只是一个名称——通常类似于v2.1 或类似的行——它指的是某个 Git 对象。该 Git 对象通常是一个 带注释的标记对象 或一个 commit。如果标签名称直接指向一个提交,Git 将其称为 轻量级标签。如果标签名称引用了带注释的标签对象,Git 将其称为 带注释的标签。带注释的标签对象然后引用另一个对象。

在某些情况下,一个带注释的标签对象的目标对象是另一个带注释的标签。如果是这样,Git 可以读取这个 second 标签对象,它引用另一个对象。该对象可以是另一个带注释的标记对象:如果是这样,Git 会继续读取和跟踪,直到最终某个带注释的标记对象引用了一个带注释的标记对象以外的东西。换句话说,虽然一个带注释的标签对象可以通过指向另一个标签对象来“继续飞行”,但最终所有这些链都“掉到地上”,就像它一样。 Git 调用此过程,跟踪标记对象,直到 Git 到达非标记对象,剥离标记。剥离标签最终会导致剩余可能的对象类型之一:committreeblob

因此,从技术上讲,没有任何标签存储任何文件文件夹。它只是命名一个提交。而且,不幸的是,也没有 commit 存储文件夹。幸运的是,这并不重要!

为什么我认为你想问别的问题

Git 不存储文件夹的技术原因是它从其 index 进行新的提交,并且索引不能存储文件夹名称。1 但是,它确实需要索引的文件的名称,可以很长并且可以包含正斜杠(例如,dir/sub/file.ext),并将它们分成组成部分。所以当index保存了一个名为dir/sub/file.ext的文件时,Git会创建一个名为dir的树对象,引用一个名为sub的树对象,引用一个名为file.ext的blob对象。我们稍后会看到它是如何工作的。

回到提交,一个提交对象持有一个提交。提交代表您的源的完整且完整的快照。在内部,它通过指向(保存哈希 ID)tree 对象来实现。例如,这里是 Git 存储库中 Git 的实际提交:

$ git rev-parse HEAD
51ebf55b9309824346a6589c9f3b130c6f371b8f
$ git cat-file -p HEAD | sed 's/@/ /'
tree 7db7271b4def298424e57b0a04129f6a929955d0
parent f97741f6e9c46a75b4322760d77322e53c4322d7
author Junio C Hamano <gitster pobox.com> 1581974501 -0800
committer Junio C Hamano <gitster pobox.com> 1581974539 -0800

The sixth batch for 2.26

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

注意第一行,tree 7db7271b4def298424e57b0a04129f6a929955d0。这是提交51ebf55b9309824346a6589c9f3b130c6f371b8f中保存的快照。

一个树对象包含一个三元组数组:。组件名称是路径名称的一部分。该模式是一小组允许的八进制数之一,代表子树、提交对象 (gitlinks) 或 blob 对象(文件和符号链接内容)。如果模式是树或 blob 对象的模式,则条目中的哈希 ID 必须分别是持有树或 blob 对象的 Git 对象。

这是tree对象7db7271b4def298424e57b0a04129f6a929955d0中的部分内容:

$ git cat-file -p 7db7271b4def298424e57b0a04129f6a929955d0
[snip]
100644 blob 536e55524db72bd2acf175208aef4f3dfc148d42    COPYING
040000 tree d12f433f975c365f2516022fd6ba597548a12a55    Documentation
100755 blob 616d5a6404e4f1a6550b2aca89488889dbe6d34f    GIT-VERSION-GEN
100644 blob 22c364f34f573c615d968a85374ba2c429b7fabd    INSTALL
100644 blob d38b1b92bdb2893eb4505667375563f2d6d4086b    LGPL-2.1
[snip]

注意COPYINGGIT-VERSION-GEN 等普通文件是如何作为blob 对象引用存储在这个顶级树中的。但是,Documentation 不是任何文件的名称。提取此提交后,某些文件的名称将 Documentation/ 开头。这是树d12f433f975c365f2516022fd6ba597548a12a55 中的一些内容:

$ git cat-file -p d12f433f975c365f2516022fd6ba597548a12a55
[snip]
100644 blob aa828dfdc44a856c8bdd8b0826defeb398113bcc    MyFirstObjectWalk.txt
040000 tree 467d9eb934bfbb8ff63f5d239b2d4c975ab8e9b9    RelNotes
100644 blob 4515cab5193ddf354a461aafb1b68dcc1ef2932e    SubmittingPatches
[snip]

这向我们表明,当提取此提交时,您可以使用一些文件,其中一个文件将命名为 Documentation/MyFirstObjectWalk.txt,另一个文件将命名为 Documentation/SubmittingPatches。由于还有一个名为RelNotes 的子树,因此必须有名称以Documentation/RelNotes/ 开头的文件。


1索引与此最接近的是它可以存储gitlink名称。 Gitlinks 有模式160000,这是通过将符号链接模式120000 与树模式040000 组合在一起获得的模式。很遗憾您无法获得模式 040000 索引条目,因为让 Git 存储空文件夹。


这一切意味着什么

如果我们找到一个提交对象哈希ID,例如,通过git rev-parse v2.1 如果有一个名为v2.1 的标签,我们可以检查提交对象。它将有一个tree 条目。

然后我们可以检查那个 tree 对象。如果它有一个子树,就像上面那个有Documentation,那么我们可以假设在那个提交中必须有文件,其名称​​以Documentation/开头.为了提取这样的提交,Git 必须创建一个名为 Documentation 的文件夹。

提交不包含这个文件夹2但是提取提交确实产生这样一个文件夹你的工作树。这可能是您关心的:例如,提取提交(使用git checkout)或移动到它(使用git reset --hard)会创建或删除, 3 你的工作树中的这样一个文件夹。

在任何给定的提交 this 方式中测试此树对象的存在,通过一次检查每个级别,是很痛苦的。但是git rev-parse 命令可以将任何 有效名称转换为Git 对象哈希ID,使这变得简单。因此,虽然您可以运行 git rev-parse <em>hash</em> 来查看 hash 是否是有效的 Git 对象 ID,但您也可以运行 git rev-parse <em>hash</em>:<em>path</em> 来查看 hash 是否可以变成一个 commit 对象,然后从中解析出一个路径名。该路径名将自动引用某个 Git 对象; git rev-parse 将产生其哈希 ID。

我们并不真正想要 散列 ID。这只是告诉我们某个对象存在,而没有告诉我们该对象有什么type。要找到对象的类型,我们需要git cat-file -t。这会获取任何对象的名称或 ID,并告诉我们类型——如果 ID 无效,则会产生错误:

$ git cat-file -t d12f433f975c365f2516022fd6ba597548a12a55
tree
$ git cat-file -t 616d5a6404e4f1a6550b2aca89488889dbe6d34f
blob
$ git cat-file -t a123456
fatal: Not a valid object name a123456

如果我们提供带注释的标签对象的标签名称,我们会得到:

$ git cat-file -t v2.23.0
tag

告诉Git 剥离标签,使用^{},我们找到目标对象:

$ git cat-file -t v2.23.0^{}
commit

并告诉 Git 在此提交中查找路径名,我们得到:

$ git cat-file -t v2.23.0:Documentation
tree

这比:

$ git rev-parse v2.23.0:Documentation
b55461e880f78ae8253c8be287476cbbddd44957

所以这是要走的路。


2如果您将 tree 对象 称为“文件夹”,您可以声称提交确实包含一个文件夹——但这并不是 Git 的实际工作方式。如果子树对象中存在 files,则子树对象只能存在于提交中。这种情况有时很烦人,但事实就是如此。

3git checkout 从包含例如 sub/onesub/two 的干净签出提交移动到具有 no 的提交时操作系统需要的文件放在名为sub/ 的文件夹中,Git 当然会从工作树中删除这两个文件。他们现在索引和工作树中,但他们不在在目标提交中,所以他们必须离开。删除这两个文件后,Git 也会从工作树中删除空的 sub/ 文件夹。如果sub/ 文件夹为空,由于存在未跟踪的文件,Git 将不会删除它。这就是现在所有这些工作:Git 创建文件夹 根据操作系统的要求它们存在以便文件进入它们。 Git在清空它们时删除它们,但如果不清空它们则不会。否则,Git 根本不会碰它们。

【讨论】:

    猜你喜欢
    • 2013-03-12
    • 2011-11-15
    • 2013-08-24
    • 1970-01-01
    • 2010-12-10
    • 1970-01-01
    • 2012-02-05
    • 2022-07-01
    • 2011-02-21
    相关资源
    最近更新 更多