git add文件位于何处

时间:2018-04-15 15:35:07

标签: git

我正在使用linux ubuntu服务器。我不小心在一个非常大的目录中运行了git add并耗尽了系统上的所有存储空间。

我需要删除使用git add命令创建的文件。是否有删除此文件的命令?或者,这些文件存储在哪里,以便我可以手动定位和删除它。

3 个答案:

答案 0 :(得分:2)

您可以将git reset <filepath>用于非舞台文件

git reset HEAD&lt;文件&gt; unstages自上次提交以来对文件所做的任何修改

或者可以提醒它会改变上次提交的文件内容

To unstage one file :
$ git checkout <path-to-file>

Remember to replace <path-to-file> with the actual file name.

To unstage all files:
$ git checkout -- .

有用的评论 - 斯蒂芬纽厄尔

  

@Paul - 你可能需要做git gc --prune = all。那将删除任何   对象git add created,但它有一些风险(检查文档)。

答案 1 :(得分:2)

为了避免git gc&#34;意外重新包装,您还可以运行&#34; git prune&#34;它只删除新对象。可能它还需要降低当前指数。所以,整体而言:

rm .git/index
git prune --expire now
# to restore the index
git reset

答案 2 :(得分:1)

TL; DR

注意:这基本上是max630's answer,有一些补充和注释,加上下面的详细解释。

  1. 腾出足够的空间来完成一些工作,或者如果你对它完全正确的话,请完全删除索引。
  2. 如果您未在步骤1中完全删除索引,请删除或替换错误的索引条目。
  3. 运行git prune --expire now
  4. 如果您在步骤1中删除了索引,请运行git reset进行重建。
  5. 对于后来遇到这种情况的人来说,这是一个试图彻底解决的答案。虽然通常的描述是git add将文件复制到索引中 - 在特定情况下属于高级别 - 在文件系统上磁盘空间不足的情况下,这不是足够。此外,我们可能希望使用像BFG这样的东西来清理这里的错误,但如果我们真的没有空间,任何实际创建 new 文件的东西都会出现问题。

    首先,让我们注意&#34;索引&#34;实际上是一个名为.git/index 1 的文件,但它包含的内容不是其他文件的完整副本。相反,它有一个小的&#34;条目&#34;每个文件, 2 给出文件的名称,一些重要的缓存数据和 blob哈希ID 。索引开始为签出提交中的每个文件保留一个条目。当您运行git add path时,您可以替换现有条目或添加新条目,具体取决于之前 path 是否在索引中。

    这意味着只需删除.git/index或修复git add添加或覆盖的条目,就无法解决问题。然而,这是一个重要的第一步第二个步骤是删除 blob本身,这有点棘手。这里的问题是blob作为对象存储在Git对象数据库中,这是Git存储所有存储库数据的地方:每个提交中的每个文件。

    1 现代Git有它所谓的&#34;拆分索引&#34;,其中索引内容分布在两个单独的文件中,但原则是相同的。

    2 从技术上讲,每个文件最多有4个条目,使用&#34; staging slot&#34;数字,但git add仅使用插槽零。插槽1,2和3仅在合并时使用。添加文件会删除较高级的插槽,因此在git add之后,您可以保证仅使用舞台插槽零。

    我们的主要目标是摆脱这些不需要的blob对象

    直接回答您的子问题:

      

    这些文件存储在哪里,以便我可以手动找到并删除它

    是每个blob对象存储(当前!)作为松散对象.git/objects/ab/cdef0123...或其他一些。我们希望摆脱这些对象,但它们与许多我们 想要删除的对象混合在一起,因此不小心删除对象是不明智的。而且,即使我们只删除了正确的blob,如果我们这样做而不清理索引,我们的Git也会变得不快乐。

    如果只有一个或两个这样的文件,我们可以相对手动完成所有这些操作。如果有很多这样的文件,我们会想要自动完成这项工作。让我们从最手动的方法开始,然后看看如何在以后自动化它。

    手动修复两个简单的错误

    我们故意在这里犯两个不同的错误。为此,我们需要一个存储库,其中包含一些提交签出,以及一些非常大的文件:

    $ git checkout something     # check out some commit or branch
    $ cp /external/hugefile existingfile    # copy very big file
    $ cp /external/hugefile newfile         # into our work-tree
    

    现在我们已经准备好犯下两个错误了:

    $ git add existingfile       # mistake type #1
    $ git add newfile            # mistake type #2
    

    错误类型#1,我们用一个巨大的文件覆盖一个小文件。这个小文件已经在索引和工作树中,因为它在提交中;我们只是覆盖了工作树文件,然后告诉Git将该文件复制到索引中。错误类型#2,我们创建了一个新的巨大文件。它之前不在工作树中,之前它不在索引中,现在它位于索引中的工作树中。

    获取blob哈希ID

    我们可以获得大文件&#39;散列或散列 - 使用上面列出的过程,只有一个散列 - 使用git ls-files --stage

    $ git ls-files --stage existingfile newfile
    100644 <hash> 0    existingfile
    100644 <hash> 0    newfile
    

    哈希ID取决于blob内容,但看起来像1b4624c876dae8f38f7c9e13f82d11b6ead39c9b。这告诉我们Git存储实际对象的位置。我们在修复索引之后需要

    修复索引

    在删除不需要的blob之前,我们必须先删除引用。换句话说,我们希望我们的索引不再告诉Git Git应该在寻找那个blob,或者那些blob。

    这里有一个循环问题:我们可能是完全没有空间。 Git无法在不创建新索引的情况下更新索引。 Git更新.git/index的过程是创建一个新的空.git/index.lock文件,以防止其他Git命令运行,然后写出 new (更新) )索引到锁文件。锁定文件安全地写入永久存储后,Git 重命名 .git/index.lock以使其替换.git/index,以便解锁和切换到新索引同时发生。这意味着我们需要一点空间。幸运的是,我们可以获得很多空间:我们只创建了两个我们不想要的大文件。让我们只讨论其中一个或两个,例如:

    $ cp /dev/null existingfile
    

    (假设是Unix / Linux-ish命令行)。您可以完全删除该文件,但如果是这样,您将从后续的Git命令中获得一些小的抱怨。

    从错误类型#1恢复的下一步是切换回索引和工作树中的正确文件:

    $ git checkout HEAD -- existingfile
    

    这让Git从当前提交中将正确的文件提取到索引中,然后将该索引文件提取到工作树中。从错误类型#2中恢复的步骤是完全删除大文件:

    $ git rm -- newfile
    

    将其从索引和工作树中删除,或者:

    $ git rm --cached newfile
    

    将其从索引(仅)中删除,将其保留在工作树中(假设您有理由保留它,在这种情况下,您最好不要先将其破坏以获得空间)。

    手动删除blob对象

    如果上面的哈希ID是1b4624c876dae8f38f7c9e13f82d11b6ead39c9b,我们现在可以通过简单的文件删除删除对象:

    $ rm .git/objects/1b/4624c876dae8f38f7c9e13f82d11b6ead39c9b
    

    此文件名只是.git/objects/,后跟哈希ID的前两个字符,后跟/,后跟 rest 哈希ID。实际文件是只读的,因此您的删除命令可能会询问您是否确定。

    重复此操作直到你删除了所有添加的巨型blob,然后你就完成了。

    自动化

    在您的特定情况下,您添加了许多大文件,所有文件都位于某个子目录中。所以你只想:

    $ git rm -r --cached subdir/*
    

    或等效物。和以前一样,你首先必须做某事以释放一些空间。从技术上讲,可以删除其中一个大对象,就像我们上面手动执行的那样(使用git ls-files --stage查找哈希ID),前提是您确定 哈希ID不会出现在存储库的其他位置。 git rm -r --cached没有费心去寻找对象。

    使用git prune

    现在所有这些都已从索引中删除,我们想要让Git 修剪未引用的对象。这里的 prune 动词有一个非常专业的含义,但我们先谈谈未引用的对象

    Git对象引用,如果它可以通过其哈希ID找到。那就是它的所有内容,但实际上它是相当复杂的陈述:Git如何做到这一点发现?我不会在这里详细介绍完整的细节,但是请注意参考名称< / em>,像分支或标记名称一样,包含一个哈希ID,通常是提交的ID。提交包含其他提交和对象的哈希ID,对象具有 blob 对象的名称和哈希ID。这意味着Git可以使用该名称来查找提交以查找树以查找blob。当Git这样做时,该blob被引用。同时,提交和树也是如此:Git必须使用找到 blob。

    索引也充当blob引用!每个索引条目 - 每个文件名及其临时插槽和blob哈希ID - 计为该blob的引用。因此,如果文件在某个提交中并且Git可以找到该提交,则该文件 - 更确切地说,保存文件内容的blob对象引用。同样,引用当前在索引中的每个blob对象。有些对象被多次引用:例如,如果你的README.md文件在一千次提交中是相同的,那么它至少有一千个引用。 Git并不关心对象有多少引用,只要它至少有一个

    git prune所做的是找到松散的对象 3 - 提交,带注释的标签,树和blob - 引用,删除它们。这几乎就是它的全部,但这也很重要,因为这可以让Git随时生成对象。如果Git最终没有使用这个对象,那么它就会自行消失。所以git add可以创建大量的松散物体。如果他们得到承诺,他们就会永远活着;如果没有,他们最终会被修剪掉。

    但是有一个障碍:git gc有时为你自动运行git prune,所有这些都在后台无形中运行,而你正在做其他工作。为确保git prune不会销毁仅暂时未引用的对象,git prune的默认宽限期为14天在此期间,一个未引用的松散物体无论如何都会四处乱窜。这意味着每个Git命令都有两周的时间来完成并确保引用对象。

    在这里,我们要特别打破默认的两周宽限期,因此我们使用git prune --expire now

    3 形容词 loose 这里的意思是一个尚未打包的对象。换句话说, loose 的反面不是,而是压缩 Packing 是一种进一步压缩Git对象的方法。打包代码只打包引用的对象,以避免干扰修剪代码,在git gc编排的相当复杂的舞蹈中。我们在这里没有使用完整的git gc,因为我们只想处理git add创建的松散对象。

    删除并重建索引

    如果我们完全删除索引作为我们的第一步,那自动意味着索引中没有blob引用。如果缺少索引,Git本身就可以了:这是裸存储库中的正常状态。该索引有多种用途,但其中一个关键是在您工作时缓存有关工作树的数据,而裸存储库没有工作树(根据定义)。

    因此,删除索引本身也会删除所有索引对当前提交中所有blob的引用。这具有&#34;不添加&#34;的副作用。所有内容:如果您要进行新的提交,则与当前提交相比,现在计划删除所有文件。换句话说,缺少索引与索引相同。

    我们可以在git prune操作期间以这种方式保留所有内容,然后使用HEADgit reset --mixed HEAD提交重新生成索引。由于--mixed是默认设置,HEAD是默认设置git reset,完全没有选项,所以可以完成工作。

    因此,如果可以&#34;取消添加&#34; 所有,我们最终得到了顶部三步命令序列:删除索引,修剪,恢复索引。