我看过interesting posts解释关于 git reset
的微妙之处 .
不幸的是,我读的越多,我就越不能完全理解它 . 我来自SVN背景,Git是一个全新的范例 . 我很容易变得善变,但Git更具技术性 .
我认为 git reset
接近 hg revert
,但似乎存在差异 .
那么_1785609究竟是做什么的呢?请包括以下详细说明:
-
选项
--hard
,--soft
和--merge
; -
与
HEAD
一起使用的奇怪表示法,例如HEAD^
和HEAD~1
; -
具体用例和工作流程;
-
对工作副本的影响,
HEAD
和您的全球压力水平 .
7 回答
通常,
git reset
的函数是获取当前分支并将其重置为指向其他位置,并可能带来索引和工作树 . 更具体地说,如果您的主分支(当前已检出)是这样的:并且你意识到你希望master指向B而不是C,你将使用
git reset B
将其移动到那里:题外话:这与结账不同 . 如果你运行
git checkout B
,你会得到这个:你最终处于一个独立的HEAD状态 .
HEAD
,工作树,索引全部匹配B
,但主分支留在C
. 如果此时你做了一个新的提交D
,你会得到这个,这可能不是你想要的:请记住,reset不会进行提交,它只是更新一个分支(指向提交的指针)以指向不同的提交 . 其余的只是索引和工作树会发生什么的详细信息 .
用例
我将在下一节中对各种选项的描述中涵盖
git reset
的许多主要用例 . 它可以真正用于各种各样的事情;常见的线程是所有这些都涉及重置分支,索引和/或工作树以指向/匹配给定的提交 .要小心的事情
--hard
会导致你真的失去工作 . 它会修改您的工作树 .git reset [options] commit
会导致你(丢弃)提交 . 在上面的玩具示例中,我们丢失了提交C
. 它仍然在回购中,您可以通过查看git reflog show HEAD
或git 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^2
,HEAD
的第三代祖先的第二个父级HEAD^^2
,HEAD
的第一个父级的第二个父级,甚至HEAD^^^
,相当于HEAD~3
.请记住,在
git
你有:HEAD
指针,它告诉您正在进行的提交工作树,表示系统上文件的状态
暂存区域(也称为索引),"stages"发生变化,以便以后可以一起提交
随着危险程度的增加:
--soft
移动HEAD
但不接触暂存区域或工作树 .--mixed
移动HEAD
并更新暂存区域,但不更新工作树 .--merge
移动HEAD
,重置暂存区域,并尝试将工作树中的所有更改移动到新的工作树中 .--hard
移动HEAD
并将您的临时区域和工作树调整为新的HEAD
,丢弃所有内容 .如果要移动到另一个提交并在没有"losing your place"的情况下进行修补,请使用
--soft
. 你需要这个很少见 .当您想要查看另一次提交时的内容时,请使用
--mixed
(这是默认设置),但您不想丢失已有的任何更改 .如果要移动到新位置,请使用
--merge
,但将已有的更改合并到工作树中 .使用
--hard
擦除所有东西,然后在新的地方开始新鲜的石板承诺 .博客Pro Git中的帖子Reset Demystified在
git reset
和git checkout
上给出了非常简单的解释 .在该帖子顶部的所有有用的讨论之后,作者将规则简化为以下简单的三个步骤:
当您提交git的东西时,首先必须暂存(添加到索引)您的更改 . 这意味着在git认为它们是提交的一部分之前,你必须git添加你想要包含在这个提交中的所有文件 . 让我们先来看看git repo的图像:
所以,现在很简单 . 我们必须在工作目录中工作,创建文件,目录和所有 . 这些更改是未跟踪的更改 . 要跟踪它们,我们需要使用 git add 命令将它们添加到git索引 . 一旦将它们添加到git索引中 . 如果我们想将它推送到git存储库,我们现在可以提交这些更改 .
但突然间我们知道我们提交了一个额外的文件,我们在索引中添加了不需要推入git存储库 . 这意味着我们不希望索引中的该文件 . 现在的问题是如何从git索引中删除该文件,因为我们使用 git add 将它们放在索引中,所以使用 git rm 是合乎逻辑的?错误! git rm 将简单地删除该文件并将删除添加到索引中 . 那么现在该怎么办:
使用:-
它清除您的索引,保持您的工作目录不变 . (只是取消暂停一切) .
它可以与许多选项一起使用 . 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 来解决问题 .
我希望它可以帮助你摆脱困惑 .
TL; DR
更长的版本
但这显然是简单化的,因此许多相当冗长的答案 . 在撤消更改的背景下阅读
git reset
对我来说更有意义 . 例如 . 看到这个:来自https://www.atlassian.com/git/tutorials/undoing-changes/git-reset
还有这个
来自https://www.atlassian.com/git/tutorials/resetting-checking-out-and-reverting/commit-level-operations
请注意,这个是一个简化的解释,旨在作为寻求理解这种复杂功能的第一步 .
对于想要在每个命令之后可视化项目状态的视觉学习者可能会有所帮助:
对于那些使用打开颜色的终端的人(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的变化(就好像它们从未存在过一样)Checkout将头部指向特定的提交 .
重置指向特定提交的分支 . (分支是指向提交的指针 . )
顺便提一下,如果你的头部没有指向分支也指向的提交,那么你就有一个分离的头 .
EDIT :最终索赔错误 . 看评论... :)