我看过一些有趣的帖子,解释git重置的微妙之处。
不幸的是,我读得越多,就越觉得我并没有完全理解它。我有SVN的背景,Git是一个全新的范例。我很容易得到mercurial,但是Git的技术含量更高。
我认为git重置接近hg恢复,但似乎有区别。
git重置到底是做什么的?请详细说明:
选项——硬、软和合并; 你对HEAD使用的奇怪符号,如HEAD^和HEAD~1; 具体的用例和工作流程; 对工作副本、HEAD和整体压力水平的影响。
我看过一些有趣的帖子,解释git重置的微妙之处。
不幸的是,我读得越多,就越觉得我并没有完全理解它。我有SVN的背景,Git是一个全新的范例。我很容易得到mercurial,但是Git的技术含量更高。
我认为git重置接近hg恢复,但似乎有区别。
git重置到底是做什么的?请详细说明:
选项——硬、软和合并; 你对HEAD使用的奇怪符号,如HEAD^和HEAD~1; 具体的用例和工作流程; 对工作副本、HEAD和整体压力水平的影响。
一般来说,git reset的功能是获取当前分支并将其重置为指向其他地方,并可能将索引和工作树也一并带来。更具体地说,如果你的主分支(当前签出)是这样的:
- A - B - C (HEAD, master)
并且你意识到你想要master指向B,而不是C,你将使用git重置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重置的许多主要用例。它真的可以用于各种各样的事情;共同的线程是,所有这些都涉及重置分支、索引和/或工作树,以指向/匹配给定的提交。
需要注意的事情
--hard can cause you to really lose work. It modifies your work tree. git reset [options] commit can cause you to (sort of) lose commits. In the toy example above, we lost commit C. It's still in the repo, and you can find it by looking at git reflog show HEAD or git reflog show master, but it's not actually accessible from any branch anymore. Git permanently deletes such commits after 30 days, but until then you can recover C by pointing a branch at it again (git checkout C; git branch <new branch name>).
参数
转述手册页,最常见的用法是git reset [<commit>][路径…],它将从给定的提交中将给定的路径重置为它们的状态。如果没有提供路径,则重置整个树,如果没有提供提交,则将其作为HEAD(当前提交)。这是git命令的常见模式(例如,checkout, diff, log,尽管确切的语义有所不同),所以它不应该太令人惊讶。
例如,git reset other-branch path/to/foo将路径/to/foo中的所有内容重置为其在other-branch中的状态,git reset——。将当前目录重置为其在HEAD中的状态,简单的git重置将所有内容重置为其在HEAD中的状态。
主要工作树和索引选项
有四个主要选项来控制重置期间工作树和索引的变化。
请记住,索引是git的“暂存区”——当你说git添加准备提交时,它就是东西所在的地方。
--hard makes everything match the commit you've reset to. This is the easiest to understand, probably. All of your local changes get clobbered. One primary use is blowing away your work but not switching commits: git reset --hard means git reset --hard HEAD, i.e. don't change the branch but get rid of all local changes. The other is simply moving a branch from one place to another, and keeping index/work tree in sync. This is the one that can really make you lose work, because it modifies your work tree. Be very very sure you want to throw away local work before you run any reset --hard. --mixed is the default, i.e. git reset means git reset --mixed. It resets the index, but not the work tree. This means all your files are intact, but any differences between the original commit and the one you reset to will show up as local modifications (or untracked files) with git status. Use this when you realize you made some bad commits, but you want to keep all the work you've done so you can fix it up and recommit. In order to commit, you'll have to add files to the index again (git add ...). --soft doesn't touch the index or work tree. All your files are intact as with --mixed, but all the changes show up as changes to be committed with git status (i.e. checked in in preparation for committing). Use this when you realize you've made some bad commits, but the work's all good - all you need to do is recommit it differently. The index is untouched, so you can commit immediately if you want - the resulting commit will have all the same content as where you were before you reset. --merge was added recently, and is intended to help you abort a failed merge. This is necessary because git merge will actually let you attempt a merge with a dirty work tree (one with local modifications) as long as those modifications are in files unaffected by the merge. git reset --merge resets the index (like --mixed - all changes show up as local modifications), and resets the files affected by the merge, but leaves the others alone. This will hopefully restore everything to how it was before the bad merge. You'll usually use it as git reset --merge (meaning git reset --merge HEAD) because you only want to reset away the merge, not actually move the branch. (HEAD hasn't been updated yet, since the merge failed) To be more concrete, suppose you've modified files A and B, and you attempt to merge in a branch which modified files C and D. The merge fails for some reason, and you decide to abort it. You use git reset --merge. It brings C and D back to how they were in HEAD, but leaves your modifications to A and B alone, since they weren't part of the attempted merge.
想知道更多吗?
我确实认为男人git重置真的很好-也许你确实需要一点git工作方式的感觉,让他们真正沉浸其中。特别是,如果你花时间仔细阅读它们,那些详细描述索引和工作树中所有不同选项和情况的文件状态的表是非常非常有用的。(但是,是的,它们非常密集——它们以非常简洁的形式传达了大量上述信息。)
奇怪的符号
您提到的“奇怪的符号”(HEAD^和HEAD~1)只是指定提交的一种简写,而不必使用像3ebe3f6这样的散列名称。它在git-rev-parse手册页的“指定修订”部分中有完整的文档,其中有许多示例和相关语法。插入号和波浪号实际上有不同的含义:
HEAD~ is short for HEAD~1 and means the commit's first parent. HEAD~2 means the commit's first parent's first parent. Think of HEAD~n as "n commits before HEAD" or "the nth generation ancestor of HEAD". HEAD^ (or HEAD^1) also means the commit's first parent. HEAD^2 means the commit's second parent. Remember, a normal merge commit has two parents - the first parent is the merged-into commit, and the second parent is the commit that was merged. In general, merges can actually have arbitrarily many parents (octopus merges). The ^ and ~ operators can be strung together, as in HEAD~3^2, the second parent of the third-generation ancestor of HEAD, HEAD^^2, the second parent of the first parent of HEAD, or even HEAD^^^, which is equivalent to HEAD~3.
记住在git中你有:
HEAD指针,它告诉你你在做什么提交 工作树,它表示系统上文件的状态 暂存区(也称为索引),它“暂存”更改,以便稍后可以一起提交
请详细说明: ——硬,——软,——融合;
按危险程度递增:
-轻轻移动头部,但不触及集结区或工作树。 mixed移动HEAD并更新暂存区,但不更新工作树。 ——merge移动HEAD,重置暂存区域,并尝试将工作树中的所有更改移动到新的工作树中。 -hard移动HEAD,调整你的集结区和工作树到新的HEAD,扔掉所有东西。
具体的用例和工作流;
当你想要转移到另一个地方,修补事情而不“失去你的位置”时,使用-软。你很少需要这个。
--
# 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(这是默认值)。 使用—合并当你想移动到一个新的点,但合并你已经有到工作树的变化。 使用——在新的提交时很难清除所有内容并重新开始。
博客Pro Git中的Reset Demystified给出了一个关于Git重置和Git checkout的非常简单的解释。
在那篇文章的顶部进行了有益的讨论之后,作者将这些规则简化为以下简单的三步:
基本上就是这样。reset命令以特定顺序覆盖这三棵树,当您命令它停止时停止。 移动HEAD指向的分支(如果—软) 然后,让索引看起来像这样(停止在这里,除非——很难) 然后,让工作目录看起来像这样 还有“合并”和“保留”选项,但我现在宁愿让事情简单一些——这将是另一篇文章。
当你向git提交一些东西时,你首先必须stage(添加到索引中)你的更改。这意味着在git认为这些文件是提交的一部分之前,你必须在git中添加所有你想要包含在这个提交中的文件。让我们先看一下git回购的图像:
现在很简单了。我们必须工作在工作目录,创建文件,目录和所有。这些更改是未跟踪的更改。为了跟踪它们,我们需要使用git add命令将它们添加到git索引中。一旦它们被添加到git索引。我们现在可以提交这些更改,如果我们想把它推到git存储库。
但在提交时,我们突然意识到,我们在索引中添加的一个额外文件不需要推入git存储库。这意味着我们不希望这个文件出现在索引中。 现在的问题是如何从git索引中删除该文件,因为我们使用git添加把它们放在索引中,使用git rm是合乎逻辑的?错了!Git rm将简单地删除文件并将删除添加到索引中。那么现在该怎么做呢:
使用:
去重置
它清除你的索引,保持你的工作目录不变。(简单地取消所有内容)。
它可以使用与它的数量选项。 git重置有三个主要选项:——hard,——soft和——mixed。除了重置时的HEAD指针外,这些还会影响get重置的内容。
首先,硬重置一切。如果您一直在跟踪该分支,那么当前目录将完全相同。工作目录和索引将更改为该提交。这是我最常用的版本。Git重置——很难像SVN恢复。
接下来,完全相反的-soft不会重置工作树或索引。它只移动HEAD指针。这将使您的当前状态与您要切换到的目录中的提交不同,并且“阶段性”提交。如果你在本地进行了一次提交,但还没有将提交推到git服务器,你可以重置到上一次提交,并使用一个好的提交消息重新进行一次提交。
最后,——mixed重置索引,但不重置工作树。所以这些更改仍然存在,但是是“非分期的”,需要git add 'ed或git commit -a。我们有时会使用这个,如果我们提交了比git commit -a更多的东西,我们可以用git reset -mixed返回提交,添加我们想要提交的东西,然后只提交那些。
git revert和git reset的区别:-
简单来说,git reset是一个“修复未提交的错误”的命令,git revert是一个“修复已提交的错误”的命令。
这意味着如果我们在某些更改中犯了一些错误,并提交了相同的更改并将其推给了git repo,那么git revert就是解决方案。如果我们在推送/提交之前发现了同样的错误,我们可以使用git reset来修复这个问题。
我希望它能帮助你摆脱困惑。
请注意,这是一个简化的解释,旨在作为寻求理解这个复杂功能的第一步。
对于那些想要在这些命令之后可视化他们的项目状态的视觉学习者来说,这可能是有帮助的:
适用于开启彩色终端机的用户 (git配置——global color。ui汽车):
git重置-软A,你会看到B和C的东西在绿色(分期和准备提交)
git重置-混合A(或git重置A),你会看到B和C的东西在红色(未分期和准备分期(绿色),然后提交)
git重置-硬A,你将不再看到B和C的变化在任何地方(将好像他们从来没有存在过)
或者对于那些使用像“塔”或“SourceTree”这样的GUI程序的人
git重置——软A,你会看到B和C的东西在“阶段性文件”区域准备提交
git reset—混合A(或git reset A),你会看到B和C的东西在“unstaging files”区域准备移动到staging,然后提交
git重置-硬A,你将不再看到B和C的变化在任何地方(将好像他们从来没有存在过)
博士TL;
git reset重置Staging到最后一次提交。使用——很难将工作目录中的文件重置到最后一次提交。
完整版
但这显然过于简单,因此会有许多相当啰嗦的答案。对我来说,在撤销更改的上下文中阅读git重置更有意义。请看这个:
如果git恢复是一种“安全”的方法来撤销更改,您可以考虑git 复位为危险方法。当你撤销与git重置(和 提交不再被任何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
签出将头部指向特定的提交。
重置在特定提交时指向一个分支。(分支是指向提交的指针。)
顺便说一句,如果你的头没有指向分支也指向的提交,那么你就有了一个分离的头。(结果证明是错的。看到评论…)
我不总是做git重置,但当我这样做时,我看到这个:
* 444668f (HEAD -> main) C
|
* c3739b7 B
|
* 207e8a1 A
|
* 38fab46 Initial commit
git reset --hard 207e8
* 207e8a1 (HEAD -> main) A
|
* 38fab46 Initial commit
To retrieve the changes, use --soft instead
HEAD移到A (main也是,因为HEAD指向main)。git reset不会“重置”B和C。你仍然可以使用git log的——reflog选项看到B和C:
git log --graph --oneline --all --reflog
警告
在你做git重置之前,
如果你有非阶段性的变化: 如果用力,就会被丢弃 使用——mixed(默认),它将与阶段性更改混合,检索到的更改将提交更改 如果你有阶段性的变化: 如果用力,就会被丢弃 使用——mixed,它将与非阶段更改和检索到的提交更改混合 使用——soft,它将与检索到的提交更改混合在一起
要摆脱它们,你可以使用git stash,但我更喜欢只创建一个新分支,并为那里的阶段性和非阶段性更改创建一个单独的提交。然后使用git rebase + git重置当我需要他们回来。