首页 文章

用简单的英语,“git reset”有什么作用?

提问于
浏览
606

我看过interesting posts解释关于 git reset 的微妙之处 .

不幸的是,我读的越多,我就越不能完全理解它 . 我来自SVN背景,Git是一个全新的范例 . 我很容易变得善变,但Git更具技术性 .

我认为 git reset 接近 hg revert ,但似乎存在差异 .

那么_1785609究竟是做什么的呢?请包括以下详细说明:

  • 选项 --hard--soft--merge ;

  • HEAD 一起使用的奇怪表示法,例如 HEAD^HEAD~1 ;

  • 具体用例和工作流程;

  • 对工作副本的影响, HEAD 和您的全球压力水平 .

7 回答

  • 900

    通常, git reset 的函数是获取当前分支并将其重置为指向其他位置,并可能带来索引和工作树 . 更具体地说,如果您的主分支(当前已检出)是这样的:

    - A - B - C (HEAD, master)
    

    并且你意识到你希望master指向B而不是C,你将使用 git reset B 将其移动到那里:

    - A - B (HEAD, master)      # - C is still here, but there's no branch pointing to it anymore
    

    题外话:这与结账不同 . 如果你运行 git checkout B ,你会得到这个:

    - A - B (HEAD) - C (master)
    

    你最终处于一个独立的HEAD状态 . HEAD ,工作树,索引全部匹配 B ,但主分支留在 C . 如果此时你做了一个新的提交 D ,你会得到这个,这可能不是你想要的:

    - A - B - C (master)
           \
            D (HEAD)
    

    请记住,reset不会进行提交,它只是更新一个分支(指向提交的指针)以指向不同的提交 . 其余的只是索引和工作树会发生什么的详细信息 .

    用例

    我将在下一节中对各种选项的描述中涵盖 git reset 的许多主要用例 . 它可以真正用于各种各样的事情;常见的线程是所有这些都涉及重置分支,索引和/或工作树以指向/匹配给定的提交 .

    要小心的事情

    • --hard 会导致你真的失去工作 . 它会修改您的工作树 .

    • git reset [options] commit 会导致你(丢弃)提交 . 在上面的玩具示例中,我们丢失了提交 C . 它仍然在回购中,您可以通过查看 git reflog show HEADgit reflog show master 找到它,但它实际上不能从任何分支访问 .

    • Git会在30天后永久删除此类提交,但在此之前,您可以通过再次指向分支来恢复C( git checkout C; git branch <new branch name> ) .

    参数

    解释手册页,最常见的用法是 git reset [<commit>] [paths...] 形式,它会将给定路径重置为给定提交的状态 . 如果没有提供路径,那就太令人惊讶了 .

    例如, git reset other-branch path/to/foo 将path / to / foo中的所有内容重置为其他分支中的状态, git reset -- . 将当前目录重置为HEAD中的状态,而简单 git reset 将所有内容重置为HEAD中的状态 .

    主要工作树和索引选项

    有四个主要选项可以控制在重置期间工作树和索引发生的情况 .

    请记住,索引是git 's 1785641 - it',当你说 git add 准备提交时,事情就会发生 .

    • --hard 使一切都与您重置的提交相匹配 . 这可能是最容易理解的 . 您所有的本地更改都会被破坏 . 一个主要用途是吹走你的工作而不是切换提交: git reset --hard 表示 git reset --hard HEAD ,即不要更改分支但是要删除所有本地更改 . 另一种是简单地将分支从一个地方移动到另一个地方,并使索引/工作树保持同步 . 这可以真正让你失去工作,因为它会修改你的工作树 . 非常确定你想在运行任何 reset --hard 之前扔掉当地的工作 .

    • --mixed 是默认值,即 git reset 表示 git reset --mixed . 它重置索引,但不重置工作树 . 这意味着您的所有文件都是完整的,但原始提交与您重置的文件之间的任何差异都将显示为具有git status的本地修改(或未跟踪文件) . 当您意识到自己做了一些错误的提交时,请使用此方法,但是您希望保留所有必须再次将文件添加到索引的工作( git add ... ) .

    • --soft 不会触及索引或工作树 . 您的所有文件都与 --mixed 完好无损,但所有更改都显示为带有git状态的 changes to be committed (即已准备好提交) . 当你意识到自己很好的时候使用它 - 所有你需要做的就是以不同的方式重新发送它 . 索引是不变的,因此您可以根据需要立即提交 - 结果提交将具有与重置之前相同的内容 .

    • --merge 最近添加了,旨在帮助您中止失败的合并 . 这是必要的,因为 git merge 实际上允许您尝试与脏工作树(具有本地修改的工作树)合并,只要这些修改在不受合并影响的文件中 . git reset --merge 重置索引(如 --mixed - 所有更改显示为本地修改),并重置受合并影响的文件,但保留其他文件 . 这将有望恢复在糟糕合并之前的一切 . 您通常将它用作 git reset --merge (意思是 git reset --merge HEAD ),因为您只想重置合并,而不是实际移动分支 . ( HEAD 尚未更新,因为合并失败)

    更具体地说,假设您已经修改了文件A和B,并且尝试在修改了文件C和D的分支中进行合并 . 由于某种原因合并失败,并且您决定中止它 . 你使用 git reset --merge . 它将C和D带回到它们在 HEAD 中的状态,但仅对A和B进行修改,因为它们不是尝试合并的一部分 .

    想了解更多?

    我确实认为 man git reset 对此非常有用 - 也许你需要对git的工作方式有所了解,尽管它们真正沉沦了 . 特别是,如果您花时间仔细阅读它们,那些详细说明索引和工作树中所有各种选项和案例的文件状态的表非常有用 . (但是,是的,他们以非常简洁的形式传达了大量上述信息 . )

    奇怪的符号

    您提到的"strange notation"( HEAD^HEAD~1 )只是用于指定提交的简写,而不必使用像 3ebe3f6 这样的哈希名称 . 它完全记录在git-rev-parse手册页的"specifying revisions" section中,包含大量示例和相关语法 . 插入符和波浪号实际上意味着different things

    • HEAD~HEAD~1 的缩写,表示提交的第一个父级 . HEAD~2 表示提交's first parent'的第一个父级 . 将 HEAD~n 视为"n commits before HEAD"或"the nth generation ancestor of HEAD" .

    • HEAD^ (或 HEAD^1 )也表示提交的第一个父级 . HEAD^2 表示提交的第二个父级 . 请记住,正常的合并提交有两个父级 - 第一个父级是合并的提交,第二个父级是合并的提交 . 一般来说,合并实际上可以有任意多个父母(章鱼合并) .

    • ^~ 运算符可以串联在一起,如 HEAD~3^2HEAD 的第三代祖先的第二个父级 HEAD^^2HEAD 的第一个父级的第二个父级,甚至 HEAD^^^ ,相当于 HEAD~3 .

    caret and tilde

  • 2

    请记住,在 git 你有:

    • HEAD 指针,它告诉您正在进行的提交

    • 工作树,表示系统上文件的状态

    • 暂存区域(也称为索引),"stages"发生变化,以便以后可以一起提交

    请包括以下详细说明: - hard, - software和--merge;

    随着危险程度的增加:

    • --soft 移动 HEAD 但不接触暂存区域或工作树 .

    • --mixed 移动 HEAD 并更新暂存区域,但不更新工作树 .

    • --merge 移动 HEAD ,重置暂存区域,并尝试将工作树中的所有更改移动到新的工作树中 .

    • --hard 移动 HEAD 并将您的临时区域和工作树调整为新的 HEAD ,丢弃所有内容 .

    具体用例和工作流程;

    • 如果要移动到另一个提交并在没有"losing your place"的情况下进行修补,请使用 --soft . 你需要这个很少见 .

    # git reset --soft example
    touch foo                            // Add a file, make some changes.
    git add foo                          // 
    git commit -m "bad commit message"   // Commit... D'oh, that was a mistake!
    git reset --soft HEAD^               // Go back one commit and fix things.
    git commit -m "good commit"          // There, now it's right.
    
    • 当您想要查看另一次提交时的内容时,请使用 --mixed (这是默认设置),但您不想丢失已有的任何更改 .

    • 如果要移动到新位置,请使用 --merge ,但将已有的更改合并到工作树中 .

    • 使用 --hard 擦除所有东西,然后在新的地方开始新鲜的石板承诺 .

  • 34

    博客Pro Git中的帖子Reset Demystifiedgit resetgit checkout 上给出了非常简单的解释 .

    在该帖子顶部的所有有用的讨论之后,作者将规则简化为以下简单的三个步骤:

    基本上就是这样 . reset命令按特定顺序覆盖这三棵树,当你告诉它时停止 . 移动HEAD指向的任何分支(如果--soft,则停止)然后,使索引看起来像那样(除非--hard停止)然后,使工作目录看起来像还有--merge和--keep选项,但我宁愿现在更简单 - 这将是另一篇文章 .

  • 1

    当您提交git的东西时,首先必须暂存(添加到索引)您的更改 . 这意味着在git认为它们是提交的一部分之前,你必须git添加你想要包含在这个提交中的所有文件 . 让我们先来看看git repo的图像:
    enter image description here

    所以,现在很简单 . 我们必须在工作目录中工作,创建文件,目录和所有 . 这些更改是未跟踪的更改 . 要跟踪它们,我们需要使用 git add 命令将它们添加到git索引 . 一旦将它们添加到git索引中 . 如果我们想将它推送到git存储库,我们现在可以提交这些更改 .

    但突然间我们知道我们提交了一个额外的文件,我们在索引中添加了不需要推入git存储库 . 这意味着我们不希望索引中的该文件 . 现在的问题是如何从git索引中删除该文件,因为我们使用 git add 将它们放在索引中,所以使用 git rm 是合乎逻辑的?错误! git rm 将简单地删除该文件并将删除添加到索引中 . 那么现在该怎么办:

    使用:-

    git重置

    它清除您的索引,保持您的工作目录不变 . (只是取消暂停一切) .

    它可以与许多选项一起使用 . git reset: --hard, --soft and --mixed 有三个主要选项 . 重置时,这些会影响除HEAD指针之外的重置 .

    首先, --hard 重置一切 . 您当前的目录将完全像您一直关注该分支一样 . 工作目录和索引将更改为该提交 . 这是我经常使用的版本 . git reset --hard 类似于 svn revert .

    接下来,完整的反面 —soft 不会重置工作树和索引 . 它只移动HEAD指针 . 这使您的当前状态的任何更改都与您在目录中切换到的提交不同,并且“暂存”以进行提交 . 如果您在本地进行提交但尚未将提交推送到git服务器,则可以重置为先前的提交,并重新发送一个良好的提交消息 .

    最后, --mixed 重置索引,但不重置工作树 . 因此,所有更改仍然存在,但是“未分级”并且需要git add'ed或 git commit -a . 我们有时会使用这个,如果我们使用git commit -a承诺了更多,我们可以使用git reset -mixed返回提交,添加我们想要提交的东西并提交它们 .

    Difference between git revert and git reset : -


    简单来说, git reset"fix-uncommited mistakes" 的命令, git revert"fix-commited mistake" 的命令 .

    这意味着如果我们在某些更改中犯了一些错误并提交并将其推送到git repo,那么 git revert 就是解决方案 . 如果我们在推送/提交之前已经识别出相同的错误,我们可以使用 git reset 来解决问题 .

    我希望它可以帮助你摆脱困惑 .

  • 6

    TL; DR

    git reset重置Staging到最后一次提交 . 使用--hard也可以将工作目录中的文件重置为上次提交 .

    更长的版本

    但这显然是简单化的,因此许多相当冗长的答案 . 在撤消更改的背景下阅读 git reset 对我来说更有意义 . 例如 . 看到这个:

    如果git revert是撤消更改的“安全”方式,您可以将git reset视为危险方法 . 使用git reset撤消(并且任何ref或reflog不再引用提交)时,无法检索原始副本 - 它是永久撤消 . 使用此工具时必须小心,因为它是唯一可能丢失工作的Git命令之一 .

    来自https://www.atlassian.com/git/tutorials/undoing-changes/git-reset

    还有这个

    在提交级别,重置是一种将分支的提示移动到另一个提交的方法 . 这可用于从当前分支中删除提交 .

    来自https://www.atlassian.com/git/tutorials/resetting-checking-out-and-reverting/commit-level-operations

  • 23

    请注意,这个是一个简化的解释,旨在作为寻求理解这种复杂功能的第一步 .

    对于想要在每个命令之后可视化项目状态的视觉学习者可能会有所帮助:


    对于那些使用打开颜色的终端的人(git config --global color.ui auto):

    git reset --soft A 你会看到B和C的东西是绿色的(上演并准备提交)

    git reset --mixed A (或 git reset A )你会看到红色的B和C的东西(未分期并准备上演(绿色),然后提交)

    git reset --hard A 并且您将不再在任何地方看到B和C的变化(就好像它们从未存在过一样)


    或者对于那些使用像'Tower'或'SourceTree'这样的GUI程序的人

    git reset --soft A ,您将看到B和C 's stuff in the '暂存文件的区域已准备好提交

    git reset --mixed A (或 git reset A )您将看到B和C 's stuff in the '未分段文件的区域已准备好移动到暂存然后提交

    git reset --hard A 并且您将不再在任何地方看到B和C的变化(就好像它们从未存在过一样)

  • 75

    Checkout将头部指向特定的提交 .

    重置指向特定提交的分支 . (分支是指向提交的指针 . )

    顺便提一下,如果你的头部没有指向分支也指向的提交,那么你就有一个分离的头 .

    EDIT :最终索赔错误 . 看评论... :)

相关问题