【问题标题】:Will doing a hard reset to an old commit on new branch will reset the master as well?对新分支上的旧提交进行硬重置是否也会重置 master?
【发布时间】:2016-07-13 14:36:01
【问题描述】:

我想看看设计在之前的提交中是什么样子的,所以我想签出到一个新的分支,然后恢复/硬重置到那个旧的提交。

我对提交很陌生,不希望通过随机试验破坏任何东西,因此在新分支上硬重置为较旧的提交将硬重置主分支以及之前的提交。

我想知道上述问题的答案以及正确的做法(对于 git 初学者)。

谢谢 dk

【问题讨论】:

    标签: git git-branch git-reset


    【解决方案1】:

    在我深入了解以下内容之前(您应该阅读并了解它,因为您最终会需要它),我想提一下,您可以使用git checkout -b <commit-id> 以更简单的方式做到这一点。这具有检查现有(较旧)提交并创建指向该提交的 new 分支名称的效果。这就像在一个步骤中完成git branch,然后是git checkout,然后是下面的git reset

    使用git reset

    git reset 所做的有点难以解释,但很容易说明,特别是如果我们有颜色......我们没有。所以,让我们分多个部分来做吧。

    绘制 DAG

    首先,我们在存储库中有一组实际的提交。这些提交形成了一个图——特别是一个 Directed Acyclic Graph 或 DAG,尽管现在我们需要的只是名称,并且“ DAG”是一个nice short name。这里要知道的主要事情是每个提交都“指向”它的父提交,并且这些提交是非常可靠和永久的。无论我们做什么,这些提交都将保留在图表中,至少会保留一段时间。稍后我们将讨论异常(“一段时间”部分)。

    如果我们用较早的提交向左绘制,后来(较新的)提交向右侧绘制,它看起来像这样:

           o <- o
          /
    o <- o
          \
           o <- o
    

    (斜线/ 代表向下和向左箭头,并非在所有字体中都可用;反斜线\ 代表向上和向左箭头)。每个o 节点代表一个提交,箭头代表父指针。这个特定的图表有六个提交,没有合并。

    每个提交都有一个唯一的 SHA-1 哈希名称,例如 a123456...。这些名称永远不会改变:每个名称都特定于它的一个特定提交。您可以随时使用这些名称,但它们当然很难输入。 (有时我会用鼠标来复制粘贴这些长长的数字串。)

    标记 DAG

    由于我们知道早期的提交是向左的,后来的提交是向右的,并且提交指向它们的父节点,所以我通常把它画得更紧凑。另外,我们通常想知道这些提交在哪些分支上,所以让我们添加一些分支名称:

          master
            |
            v
    
         o--o
        /
    o--o
        \
         o--o
    
            ^
            |
         develop
    

    这是相同的六个提交,但现在我们知道它们在哪个分支上:前两个提交——在左侧,在垂直中间——在两个分支上,然后是是两个仅在master(顶部)上的提交和两个仅在develop(底部)上的提交。请注意,这里的分支名称指向最尖端的提交

    移动分支名称

    接下来要了解的关于分支标签的事情是它们移动。提交是固定的和永久的,但标签就像小鸟,在提交之间飞来飞去。让我们通过向develop 添加一个提交来使图表更大一点,看看标签会发生什么:

          master
            |
            v
    
         o--o
        /
    o--o
        \
         o--o--o
    
               ^
               |
            develop
    

    我们走了:它动了!它仍然指向提示提交,但提示提交是我们刚刚添加的。

    添加新标签(新分支)

    现在让我们添加一个 new 标签——一个新的分支——到这个图上。我也会将新标签指向develop 的尖端。为了腾出空间,我会将现有标签绘制到右侧,而不是上方和下方,但请记住,这些是可移动的标签。

         o--o      <-- master
        /
    o--o
        \
         o--o--o   <-- develop
    
               ^
               |
              new
    

    使用git reset

    现在,假设我已经完成了git checkout new 以便我在分支new 上,让我们看看git reset 对标签做了什么。具体来说,让我们获取一个旧的提交并获取它的编号,例如,fdeee3df9f54372c31506eb24f2b7f2339ba21ec(这个特定的编号是 Git 版本 2.8.1):

    $ git branch new
    $ git checkout new
    Switched to branch 'new'
    $ git reset --hard fdeee3df9f54372c31506eb24f2b7f2339ba21ec
    HEAD is now at d95553a Git 2.8.1
    

    Git 本身的存储库图表太大而无法绘制,但假设我在一个较小的存储库中执行此操作,例如现在只有 7 次提交的存储库(当然哈希不再是 fdeee3d...)。那么我现在可能有这个:

         o--o      <-- master
        /
    o--o
        \
         o--o--o   <-- develop
    
         ^
         |
        new
    

    (假设我给了 Git 正确的哈希值)。如果我给 Git 存储库中第一次提交的哈希值,我会得到:

         o--o      <-- master
        /
    o--o
        \
    ^    o--o--o   <-- develop
    |
    new
    

    git reset 移动分支标签

    这里的重点是git reset正在做的是移动分支标签

    由于我在分支newgit reset 移动的标签是new

    注意git reset

    如果我现在要git checkout develop,然后git reset 指向后退一步怎么办?也就是说,假设我使图表看起来像这样:

         o--o      <-- master
        /
    o--o
        \
    ^    o--o--o
    |       ^
    new  develop
    

    请注意不再有任何箭头指向过去分支develop 的最尖端提交?

    当这种情况发生时,该提交现在被“放弃”或“不受保护”(更准确的术语是未引用)。1不受保护的提交符合垃圾收集git gc 命令(根据需要自动为您调用,因此您通常不必运行它)将找到这些废弃的剩余部分并回收它们以恢复您的磁盘空间。

    最终,在git reset 删除develop 上的额外提交之后,Git 将真正删除它,我们将拥有这个:

         o--o      <-- master
        /
    o--o
        \
    ^    o--o
    |       ^
    new  develop
    

    也就是说,我们将回到六次提交。

    分支名称当然会保护其分支的尖端提交,但它也保护所有 在尖端的提交,因为在 Git 中,我们总是可以跟随箭头,并且提交指向它们的父提交(在这些图中,向左)。我们可以通过以名称开头、遵循箭头指向提交、遵循提交的箭头指向其他提交的任何提交——所有这些提交都是可达,因此受到保护并永远保留在 DAG 中。

    因此,您必须至少对git reset 小心一点,以确保您有 some 标签(通常是一些分支名称)仍然指向您想要保留的提交。创建一个新的 分支名称,然后移动它,保证你没问题。

    注意git reset --hard

    all git resets 移动分支标签时,--hard 重置也做了其他事情:它通过重置那些删除索引/暂存区域和工作树中的工作到 same 提交,你有 git reset 移动分支标签。通常,就像您的情况一样,这正是您想要的。

    事实上,有时您想要移动分支标签,但您确实想要重置索引和工作树。在这种情况下,您可以直接运行 git reset --hard 而无需命名特定的提交。 Git 会将分支标签从其旧提交“移动”到 ... 现在的任何位置。也就是说,它实际上停留在它所在的位置。然后 Git 将继续重置索引和工作树,即清除您所做的任何更改。如果您在代码上工作了一段时间并决定重新开始,这就是您想要的:“将所有内容设置回最近提交中的方式。”使用git reset --hard 可以做到这一点。它仍然会移动分支标签,但是将其 from 移动到它所在的位置,to 它所在的位置,意味着您只能看到“重置索引和工作树”效果。


    1幸运的是,Git 通常使用 Git 所谓的 reflogs 将每个正常提交保持半保护至少 30 天。每个分支都有自己的 reflog,HEAD 也有一个大的 reflog,默认情况下会记住 30 到 90 天的提交 ID。只要 reflog 记住——引用——提交 ID,提交就会受到保护,不会被垃圾回收。

    【讨论】:

    【解决方案2】:

    所以我想签出到一个新的分支,然后恢复/硬重置到那个旧的提交。

    其实我觉得你想按照相反的顺序来做,即:

    • 通过git checkout &lt;SHA-1 hash of earlier commit&gt;签出之前的提交
    • 通过git checkout -b new_branch 从那里签出一个新分支

    当您在较早的提交中签出分支时,您将处于分离的HEAD 状态。从那里创建一个新分支应该会给你留下你想要的东西。

    【讨论】:

    • 好的,我会试试的。我也想知道我的问题的答案?你能回答一下吗?这将有助于更好地理解 git。
    • 检查一个旧的提交并从那个点AFAIK创建一个分支没有错。
    • 好的,我通过检查倒数第二次提交尝试了你所说的,但 git 显示:error: Your local changes to the following files would be overwritten by checkout: css/.sass-cache/bacc3ae0ceb52dd23ae7e6fc6d3812f0ace1bc3c/base.scssc css/base.css css/base.css.map css/base.scss elements/footer.php elements/header.php index.php js/custom.js Please, commit your changes or stash them before you can switch branches. Aborting
    • 您在HEAD 提交时对分支上的某些文件进行了更改。 git status 向您展示了什么?
    • 当我签出到最后一次提交时,它显示$ git checkout 63f5522 M css/.sass-cache/bacc3ae0ceb52dd23ae7e6fc6d3812f0ace1bc3c/base.scssc M css/base.css ..M js/custom.js D slides/banenr-img1.jpg Note: checking out '63f5522'. You are in 'detached HEAD' state.... If you want to create a new branch to retain commits you create, you may do so (now or later) by using -b with the checkout command again. HEAD is now at 63f5522... 为什么签出到最后一次和第二次最后一次提交时存在差异?
    猜你喜欢
    • 2018-08-15
    • 2020-02-03
    • 2018-02-28
    • 1970-01-01
    • 1970-01-01
    • 2011-11-05
    • 1970-01-01
    • 1970-01-01
    • 2019-03-13
    相关资源
    最近更新 更多