关于github:如何撤消git add –all后跟多次提交

how to un-do git add --all followed by multiple commits

我之前不小心使用了git add --all,然后进行了几次提交,它试图添加几个应该忽略的大文件。

现在,一旦提交,就会显示"这超过了Github的文件大小限制100.00MB"。我尝试了git-reset,但它显示了您的分支在2次提交之前领先于'origin/master'。如何让Git恢复正常?非常感谢。


DR:

在Git中如何撤消最后一次提交时看到了各种各样的答案?或者,跳到最后一节,"最后一个例子"。好的。解释

你的问题是你没有正确地描述你的处境。您不仅添加了各种文件,还提交了它们。这意味着它们现在永久存储在您的存储库中。好的。

单词permanent在git中有点奇怪。的确,无论您做什么,提交都是固定的、不变的,并且在您的存储库中保持很长一段时间。默认情况是他们永远留在那里,这是任何人所做的每一个承诺的历史的一部分。Git从不删除任何提交:每个新提交都保留其以前提交的名为其父提交的标识,而从新提交到旧提交的向后链是存储在存储库中的历史记录。好的。

提交本身实际上是由散列ID存储的,Git向您显示的那些大的丑陋的东西(有时缩写),比如a9b307c等等。这些散列ID是每个提交的"真名"。它们看起来是随机的,基本上是不可能让人记住的,所以我们要做的是在某些特定的任务上附加一个名字,比如master。我们称之为提交分支的提示。该提交本身包含上一个分支提示的哈希ID。好的。

考虑一下这个仅包含三个提交的存储库绘图,所有提交都在master上。我将对每个提交使用一个字母名称,而不是哈希ID:好的。

1
A <--B <--C   <--master

名称master存储哈希ID badf00d或其他内容,这是commit C的实际哈希ID。我们说master指向C。同时,C存储B的散列ID,我们说C指向B。commit B存储A的ID,所以B指向A。好的。

由于A是第一次提交,它不能指向任何更早的提交。所以它只是没有。我们称A为根提交。任何Git存储库都必须有第一次提交,任何Git存储库都必须有任何提交,所以通常都有一个根提交。根提交是Git如何停止向后遍历的。好的。

要添加新的提交,Git只需编写提交,其中包含所有文件,每个提交都存储与该提交对应的每个文件,并使其指向当前分支的提示:好的。

1
A <--B <--C <--D

commit D得到一个新的、唯一的哈希,它基于D中的所有内容(包括C的哈希ID、您的姓名、电子邮件地址和当前时间)。但现在Git中分支名称的秘密是在分支名称中写入新提交的哈希ID,因此名称master现在指向提交D:好的。

1
A <--B <--C <--D   <--master

这就是树枝生长的方式。提交完成后,名称指向它,提交本身将永久存储在您的存储库中。好的。坏消息

这听起来像个坏消息:你已经提交了那些文件,所以现在坚持住了。而且,这是个坏消息,但也不是致命的坏消息。好的。好消息

然而,如果你努力工作,承诺最终会被遗忘。此外,当您要将您的提交转移到其他人的存储库(即git push的存储库)时,您的新提交git将只推送那些可以从您正在推送的分支或分支1访问的提交。好的。

因此,在这里你需要做的是"忘记"你的一些承诺。通过告诉Git重新指向分支名称,可以做到这一点。例如,假设commit D本身就是问题,我们只是想摆脱它。假设我们可以告诉Git:"嘿,让master再次指向C"—如下:好的。

1
2
3
A--B--C   <-- master
       \
        D

承诺书1〔1〕还在那里(这是永久的!)但它不再可以从名称master访问,因为git从master命名的提交哈希ID开始,并向后工作。这意味着Git没有"看到"提交D:它不再位于master分支上。好的。

1您可以同时推送多个分支名称。这曾经是git push的默认操作,事实上,尽管事实证明这很容易出错,但现在的默认操作是只推送当前的分支名称。好的。

在git中,有时很重要的一点是要仔细区分名称,比如masterdevelop,以及我所说的"匕首":提交图的一部分,就像我们上面画的那样。提交图是一个有向非循环图或DAG。git fetchgit push都采用分支名称或任何其他类型的名称。他们在网络电话的另一端调用另一个git,并与之交谈:他们给对方一些这些名称,然后将它们转换成适当的哈希ID。然后,他们根据散列ID和我前面提到的父链接来决定发送或接收哪个提交(和其他git对象)。因为散列ID仅基于每个提交的内容,所以如果您的Git和它们的Git具有相同的提交,那么这两个提交具有相同的散列ID。好的。关于git reset的了解

在git中,移动分支指针的主要方法是使用git reset。不幸的是,git reset是一个复杂的命令。好的。

Git还有另一个重要的特性:当您使用git commit进行提交时,您可以使用某些东西进行提交。"某物"本身就是Git的索引。索引主要是构建下一个提交的地方。好的。

当你运行git add时,git会从你的工作树(你工作的地方)中获取文件,文件的格式你可以实际使用它们,并将它们复制到索引中。运行git add --all时,git会获取所有工作树文件2并将它们添加到索引中。好的。

在这一点上,它们在索引中,但没有被提交,所以它们不是永久的。你可以把它们从索引中去掉。这是git reset的工作之一:重新设置索引。好的。

但是这些文件也在您的工作树中。工作树版本也没有提交,因此它们不是永久的。你也可以用git reset来代替它们,这是git reset的另一项工作。好的。

当然,git reset可以移动一个分支名称,这正是我们在这个特定案例中需要的,因为您提交了文件,所以现在它们作为新提交的一部分永久存储。移动分支名称是git reset三个主要任务中的第三个。3这三个任务的重要性排序如下:好的。

  • git reset总是移动(重新设置)分支名称;
  • git reset有时重置索引;以及
  • git reset偶尔会重置工作树。
  • 你可以用--soft(只做作业1)、--mixed(只做作业1和2)和--hard(所有三个作业)。好的。

    在移动分支时,必须选择是否保留索引和/或工作树。如果你使用git reset --hard,git会做这三件事。只要您准备好丢失索引和工作树中的临时内容,这就可以了。好的。

    提交是永久的,尽管一旦您忘记了它们的散列ID,它们将很难找到。所以把它们重置掉没关系:你可以把它们再拿回来。此外,您可以用一个新名称保存散列ID,例如一个新的分支名称,然后您可以很容易地将它们全部返回。让我们来看最后一个例子。好的。

    2所有,也就是说,除了(a)不在索引中的文件和(b)列在.gitignore或类似的"不自动添加"文件中。好的。

    3.git reset命令还可以做一些更具体的事情,在这种情况下,它可以停止做一些主要的工作,但在这里我们不会提到它们。好的。最后一个例子

    假设你在master上,并且做出了许多承诺,而不仅仅是一个,其中有太多的文件。您希望记住(保存)您的工作,但也要扔掉索引和工作树,并使master与您的"上游"存储库(您克隆的存储库)恢复同步,您的Git称之为origin。好的。

    您的提交图中包含如下内容:好的。

    1
    2
    3
    ...--o--o--o   <-- origin/master
                \
                 X--o--o--o--Y   <-- master (HEAD)

    你在你的master分支上,它上面有所有的底行承诺,加上你和其他git在origin共享的所有顶行承诺。好的。

    你在第一次犯错误的时候,就犯了一个标记为X的错误。因此,您希望将master重置为指向与origin/master相同的提交,并抛出当前的索引和工作树,因为它们是"干净的"(提交并匹配master的尖端,即commit Y)。您可以运行git reset --hard来执行此操作,但随后您将忘记从XY的所有哈希ID。好的。

    所以,您可以简单地创建一个新的分支,指向commit Y:好的。

    1
    git branch save-my-mistake

    现在您的图片如下:好的。

    1
    2
    3
    ...--o--o--o   <-- origin/master
                \
                 X--o--o--o--Y   <-- master (HEAD), save-my-mistake

    现在是时候运行了:好的。

    1
    git reset --hard origin/master

    它移动当前分支仍为master指向与origin/master相同的提交,并重新设置索引和工作树以匹配该提交:好的。

    1
    2
    3
    ...--o--o--o   <-- master (HEAD), origin/master
                \
                 X--o--o--o--Y   <-- save-my-mistake

    现在你可以随时从save-my-mistake分支机构得到你想要的任何东西,因为这个分支机构的名字会记住你对你的承诺。好的。

    最后,当您完成了它之后,您可以简单地删除该分支。这些提交迟早会被"垃圾收集",并从您的存储库中消失。在那之前,你不会再看到它们了,因为你(和Git)可以通过它找到无法理解的散列ID的名称已经不复存在了。好的。

    4到期时间比较复杂。它部分取决于reflog条目,其中"可到达"提交在90天内到期,"不可到达"提交在30天内到期,可到达和不可到达是根据引用的当前值定义的。一旦reflog条目本身过期,如果底层git对象全局不可访问(即从任何名称不可访问),那么它们就有资格进行垃圾收集。虽然在大多数情况下,如果30天或90天的时间段已经到期,14天是很久以前的事了,但从创建之日起,它们仍然有14天的宽限期。但是,删除一个分支名称会删除它的所有reflog条目,这可能会暴露较年轻的提交,然后获取它们14天的剩余时间。好的。

    在任何情况下,所有这些都是由git gc --auto驱动的,当其他git命令认为有充分的理由执行这些命令时,它们就会运行这些命令。因此,在其中一个git命令运行git gc --auto之前,这些过期的对象可能会一直存在。这与Git的非正式名称"版本控制的Borg"非常吻合:它将所有东西都集中在它的集体中,但它可能会破坏你的世界。-)好的。好啊。


    如果尚未提交,请运行:

    git stash -u


    你可以通过运行EDOCX1[1]来返回2个提交。


    如果已经提交,一个选项是删除文件夹并再次克隆。新更改将丢失!只有当你一个人在回购协议上工作,而且变化不大的时候,才这么做。