gpt4 book ai didi

git - git-如何为git prune创建无法访问的提交?

转载 作者:行者123 更新时间:2023-12-02 09:10:42 24 4
gpt4 key购买 nike

我正在为git写教学 Material ,我需要演示git prune删除“分离的对象”。我以为可以通过使用git reset将提交与分支历史记录分离而将提交置于分离状态。

这将触发git checkout将提交视为已分离,但是git prune不会在意它。

我当前的分离提交模拟的设置如下:

~ $ mkdir git-prune-demo
~ $ cd git-prune-demo/
~/git-prune-demo $ git init .
Initialized empty Git repository in /Users/kev/Dropbox/git-prune-demo/.git/
~/git-prune-demo $ echo "hello git prune" > hello.txt
~/git-prune-demo $ git add hello.txt
~/git-prune-demo $ git commit -am "added hello.txt"
[master (root-commit) 994b122] added hello.txt
1 file changed, 1 insertion(+)
create mode 100644 hello.txt
~/git-prune-demo $ echo "this is second line txt" >> hello.txt
~/git-prune-demo $ git commit -am "added another line to hello.txt"
[master 5178bec] added another line to hello.txt
1 file changed, 1 insertion(+)
~/git-prune-demo $ git reset --hard 994b122045cf4bf0b97139231b4dd52ea2643c7e
HEAD is now at 994b122 added hello.txt
~/git-prune-demo $ git prune -n
~/git-prune-demo $ nothing

是的,我知道 git prune通常不用作独立命令,实际上是 git gc的子级。

最佳答案

TL; DR

您将需要先运行git reflog expire --expire-unreachable=now,然后运行git prune --expire now。即使那样,事情还是可能出错,尽管对于这个特定的简单示例来说,这可能就足够了。



I am writing educational material for git and I need to demonstrate git prune removing a detached commit.



但这不是 git prune所做的。它的作用可以产生这种效果,但仅在特定条件下才可以。重要的是,分离式提交在Git中不是一个定义明确的短语:Git为分离式HEAD定义了一个概念,我们稍后再讨论,但是提交本身是可到达的还是不可到达的。我认为您的意思是在这里谈论无法实现的提交。

重要的是, git prune处理的对象比提交的对象更通用。 Git有四种类型的对象:提交,树,blob和带注释的标签。只要满足其他几个条件,Git的 git prune可以删除任何无法访问的对象。不过,在到达那里之前,让我们先看一些其他项目。

纠正误解

I thought I could put a commit in detached state by using git reset to detach it from the branch history.



根据定义,如果有某个外部名称直接命名该提交(或对象)本身,或者使用其他一些对象可以到达给定的提交,则该提交(或任何其他Git对象)是可以到达的。 (有关此内容的更多信息,请参见 Think Like (a) Git。)使用 git reset,我们可以使仅通过当前分支名称可访问的提交变为不可访问。例如,如果仅通过当前分支名称(即,不能通过任何其他分支名称,也不能通过任何标签名称或其他非分支名称引用)访问 a123456...,则可以使用 git reset调整当前分支,以便排除 a123456...使提交不可到达。

This will trigger git checkout to see the commit as detached ...



我认为您在这里谈论的是Git所谓的独立头。

分离的HEAD只是意味着Git的特殊HEAD引用存储为名为 .git/HEAD的文件,其中包含提交的原始哈希ID。当 .git/HEAD包含分支名称时,就会发生相反的情况(我们可以称其为附加的HEAD,因为这是明显的detached的反义词)。在这两种情况下, HEAD都引用当前提交;当 HEAD包含分支名称时, HEAD也引用当前分支名称。 Git内部处理此问题的方式是,它具有不同的功能和程序来象征性地解析 HEAD:
$ git symbolic-ref HEAD
refs/heads/master

或哈希ID:
$ git rev-parse HEAD
c05048d43925ab8edcb36663752c2b4541911231

(对于独立的HEAD情况,由于没有分支名称,因此 git symbolic-ref会产生错误。)

在以下情况下, git checkout命令将HEAD附加到某些指定的分支名称:
  • 您给它命名是分支名称,或
  • 使用
  • 创建并附加到新的分支名称。

  • 在以下情况下,它会分离HEAD:
  • 您提供了可以解析为哈希ID而不解析为分支名称的名称(例如,原始哈希ID或类似origin/master的远程跟踪名称)或
  • 您可以使用--detach标志强制分离的HEAD,即使它通常已附加HEAD。

  • 分离式HEAD模式并不意味着您正在使用不可到达的提交。实际上,将HEAD分离到否则无法到达的提交中,会使该提交突然达到,因为现在是HEAD提交。换句话说,将HEAD分离到任何提交都增加了另一种到达提交的方式,但是就修剪而言,有趣的问题不是到达该对象的名称是多少,而是数字是否为非零。一个名称,两个名称,十个名称或数百万个名称:所有这些与 git prune相同。当我在这里说名称时,我的意思不仅仅是参考名称加上可能分离的 HEAD,但在添加下一个复杂功能之前,我们将从这些名称开始。

    Git的对象模型以及对对象的引用

    Think Like (a) Git很好地描述了引用如何使提交可到达。但是,它并没有提到引用通常可以指定任何对象的哈希ID,而不仅仅是提交。这是因为它与分支有关,而不仅仅是任何旧对象,并且分支名称( refs/heads/*)和远程跟踪名称( refs/remotes/*)都被约束为仅指向提交。它还没有涉及提交内部内容的详细信息,即Git如何存储文件和文件名。这是树和斑点对象进入的地方。

    每个提交都包含单个树对象的哈希ID。树对象包含一系列三值项:模式,名称和哈希ID。该模式指定此树条目是用于文件,子树还是其他奇特项之一(符号链接(symbolic link)和gitlinks)。该名称给出了要表示的实体的名称,例如 README.txtsubdirfile.ext。哈希ID通常是blob对象或另一个树对象的ID:如果条目是针对像 README.txt这样的文件,则是blob哈希,如果是针对像 subdir这样的子树,则是子树的hash ID。 。

    如果我们以一次提交的方式从所有最上面一行的分支名称开始提取所有这些内容,则会得到如下所示的内容:
    ... <-  commit a1234...   <-- branchname
    |
    v
    tree 07f39...: (100644, README.txt, 531c2...); (040000, subdir, ...)
    | |
    v |
    blob 531c2...: data for README.txt |
    |
    v
    tree ...: ...

    允许带注释的标记对象指向任何其他对象(包括其他带注释的标记对象),尽管大多数情况下,它们仅指向提交对象。因此,在此图片上添加带注释的标签,我们通常只会看到诸如 refs/tags/v1.0的标签引用,该标签引用指向带有一些哈希ID的带注释的标签对象,然后带注释的标签对象继续指向例如commit a1234...。这将为该提交提供另一个参考。如果我们还没有创建任何标签,则无需担心这些标签,但是它们对于完整图片很重要。

    与提交一样,如果存在从某个外部名称引出的路径(对于Blob,是Git索引中存储的内部引用)导致了这些对象,则将引用任何对象。索引只能引用blob,因此,当我们仅对提交感兴趣时,可以忽略索引的引用,但是像标签一样,它们对于整个 View 很重要。

    无论如何,在上图中,我们可以看到名称 branchname使提交 a1234...可以访问。提交 a1234...使树 07f39...可以访问,从而使blob和另一个子树可以访问,依此类推。由于这些都是可以访问的,因此 git prune绝对不会修剪它们。

    重要的是,每个引用名称加上特殊的 HEAD名称,都有一个可选的reflog,该引用日志存储该引用的先前值。这些保存的值将有效一段时间,直到过期。 Git用于使过期的保存值失效的命令是 git reflog expire,它使用两个不同的命令行选项 --expire=when--expire-unreachable=when

    如果要显示 git prune删除对象,则需要确保该对象完全未被引用。这意味着您将需要删除任何直接记住(提交)或间接记住(树和Blob)其哈希ID的reflog条目。这样做的简单方法(尽管具有破坏性)是使用:
    git reflog expire --expire-unreachable=now --all

    (我们可以添加 --expire=now,但可以假定引用的当前值未达到reflog值,因此 --expire-unreachable设置将适用。)

    这就建立了必要的条件;现在该回到 git prune本身了。

    一切都结束了,让我们回到 git prune git prune命令处理所有四种类型的对象。它的工作是删除未引用的对象。从上面的内容中我们知道,在使用 git branch -fgit branch -Dgit reset这样的命令来确保没有分支名称也记住它之后,我们必须通过使可能会记住它的所有reflog条目过期来确保提交未被引用。

    但是现在我们需要了解有关Git对象的另外两件事:
  • 它们可以是散装的或打包的,而
  • 他们有年龄,就像reflog条目一样。

  • 松散的对象存储在文件系统中的单独文件中。这使得Git易于操作它,但是意味着它已被最小压缩。 Git将根据命令(或通过 git gc自动)将许多单独的对象打包到一个打包文件中。在这一点上,文件系统中的一个文件包含许多对象:几十个(也许是几百万个)或介于两者之间的某个对象。
    prune命令永远不会修剪打包的对象,因为这太难了。打包对象可能是其打包文件中增量压缩链的一部分。因此, git prune只会查看松散的对象。一个单独的程序 git repack将重新打包对象,并将未引用的打包对象变回松散的对象(或完全丢弃它们)。

    通常,对象不会立即打包,因此最近创建的对象可能会松动。但是,如果对象已经打包,并且现在未引用,则需要运行 git repack

    同时,作为防止竞争性Git进程的保护, git prune还会检查松散对象的时间戳。此时间戳必须足够旧,以允许 git prune删除该对象。这样做的原因是,当Git创建新对象(包括新提交)时,它将一次将这些对象写入一个(或几个)对象到存储库数据库。 Git必须编写带有其Blob哈希值的最深子树,然后使用这些子树及其哈希值以及这些树中存在的任何Blob哈希值来编写下一层的树。一旦Git写出所有树并获得顶级树哈希以进行新的提交,则Git才可以编写提交对象。在此之前,所有这些树都是未引用的。即使提交已写入,也不会被引用,直到更新当前分支名称(或分离的 HEAD)以指向新创建的提交。

    此过程需要时间。默认情况下,Git会给自己14天的时间来完成该过程。如果 git commit需要14天以上才能完成,则 git prune可能会删除其某些对象-但是14天应该是足够的时间。

    如果您知道没有运行任何其他Git命令,则可以手动覆盖默认值:
    git prune --expire now

    意味着无论它们是多么新,都应删除所有未引用的松散对象。因此,您需要做的就是确保您的提交未被引用,然后使用“现在”的到期时间进行修剪。

    关于git - git-如何为git prune创建无法访问的提交?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52582651/

    24 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com