用简单的英语来说,“git reset"是什么意思?做? [英] In plain English, what does "git reset" do?

查看:20
本文介绍了用简单的英语来说,“git reset"是什么意思?做?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我看过有趣的帖子 解释关于 git reset 的微妙之处.

不幸的是,我读得越多,就越觉得我没有完全理解它.我来自 SVN 背景,Git 是一个全新的范式.我很容易变得善变,但 Git 的技术性要高得多.

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

那么 git reset 到底做了什么?请包括以下内容的详细说明:

  • 选项--hard--soft--merge;
  • HEAD 一起使用的奇怪符号,例如HEAD^HEAD~1;
  • 具体的用例和工作流程;
  • 对工作副本、HEAD 和您的整体压力水平的影响.

解决方案

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

- A - B - C (HEAD, master)

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

- A - B (HEAD, master) # - C 还在,但是没有分支指向它了

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

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

您最终处于分离的 HEAD 状态.HEAD,工作树,索引都匹配B,但是master分支留在了C.如果您此时进行新的提交 D,您会得到这个,这可能不是您想要的:

- A - B - C(主)D(头)

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

用例

我在下一部分对各种选项的描述中介绍了 git reset 的许多主要用例.它真的可以用于各种各样的事情;共同点是所有这些都涉及重置分支、索引和/或工作树以指向/匹配给定的提交.

注意事项

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

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

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

参数

解释一下手册页,最常见的用法是git reset [<commit>] [paths...],它会将给定的路径从给定的提交重置为它们的状态.如果未提供路径,则整个树将被重置,如果未提供提交,则将其视为 HEAD(当前提交).这是跨 git 命令的常见模式(例如 checkout、diff、log,尽管确切的语义有所不同),因此应该不会太令人惊讶.

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

主要工作树和索引选项

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

记住,索引是 git 的暂存区"——当你说 git add 准备提交时,它就是事情发生的地方.

  • --hard 使所有内容与您重置的提交匹配.这可能是最容易理解的.您所有的本地更改都会遭到破坏.一个主要用途是吹走你的工作但不切换提交:git reset --hard 意味着 git reset --hard HEAD,即不改变分支但摆脱所有本地更改.另一种只是将一个分支从一个地方移动到另一个地方,并保持索引/工作树同步.这是一个真正会让你失去工作的方法,因为它会修改你的工作树.在你运行任何reset --hard之前,一定要非常确定你想丢弃本地工作代码>.

  • --mixed 是默认值,即 git reset 表示 git reset --mixed.它会重置索引,但不会重置工作树.这意味着您的所有文件都完好无损,但是原始提交和您重置的提交之间的任何差异都将显示为具有 git 状态的本地修改(或未跟踪的文件).当你意识到你做了一些错误的提交,但你想保留你所做的所有工作时使用它,这样你就可以修复它并重新提交.为了提交,您必须再次将文件添加到索引中 (git add ...).

  • --soft 不接触索引工作树.您的所有文件都与 --mixed 一样完整,但所有更改都显示为 要提交的更改 并带有 git 状态(即已签入以准备提交).当你意识到你做了一些错误的提交,但工作都很好 - 你需要做的就是以不同的方式重新提交它时使用它.索引未受影响,因此您可以根据需要立即提交 - 生成的提交将具有与您重置之前相同的所有内容.

  • --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 的工作方式有一些了解,才能真正融入其中.特别是,如果您花时间仔细阅读它们,那些详细说明索引和工作树中所有各种选项和案例的文件状态的表格非常有用.(但是,是的,它们非常密集 - 它们以非常简洁的形式传达了大量上述信息.)

奇怪的符号

你提到的奇怪的符号"(HEAD^HEAD~1)只是指定提交的简写,而不必使用像 3ebe3f6.它在 "specifying revisions" 中有完整记录git-rev-parse 手册页的部分,包含大量示例和相关语法.插入符号和波浪号实际上意味着不同的东西:

  • HEAD~HEAD~1 的缩写,表示提交的第一个父级.HEAD~2 表示提交的第一个父级的第一个父级.将 HEAD~n 视为在 HEAD 之前提交 n 个"或HEAD 的第 n 代祖先".
  • HEAD^(或HEAD^1)也意味着提交的第一个父级.HEAD^2 表示提交的 second 父级.请记住,正常的合并提交有两个父项 - 第一个父项是合并到提交,第二个父项是合并的提交.一般来说,合并实际上可以有任意多个父项(章鱼合并).
  • ^~ 运算符可以串在一起,如 HEAD~3^2,第三代的第二个父HEAD 的祖先,HEAD^^2HEAD 的第一个父级的第二个父级,甚至 HEAD^^^,相当于HEAD~3.

I have seen interesting posts explaining subtleties about git reset.

Unfortunately, the more I read about it, the more it appears that I don't understand it fully. I come from a SVN background and Git is a whole new paradigm. I got mercurial easily, but Git is much more technical.

I think git reset is close to hg revert, but it seems there are differences.

So what exactly does git reset do? Please include detailed explanations about:

  • the options --hard, --soft and --merge;
  • the strange notation you use with HEAD such as HEAD^ and HEAD~1;
  • concrete use cases and work flows;
  • consequences on the working copy, the HEAD and your global stress level.

解决方案

In general, git reset's function is to take the current branch and reset it to point somewhere else, and possibly bring the index and work tree along. More concretely, if your master branch (currently checked out) is like this:

- A - B - C (HEAD, master)

and you realize you want master to point to B, not C, you will use git reset B to move it there:

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

Digression: This is different from a checkout. If you'd run git checkout B, you'd get this:

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

You've ended up in a detached HEAD state. HEAD, work tree, index all match B, but the master branch was left behind at C. If you make a new commit D at this point, you'll get this, which is probably not what you want:

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

Remember, reset doesn't make commits, it just updates a branch (which is a pointer to a commit) to point to a different commit. The rest is just details of what happens to your index and work tree.

Use cases

I cover many of the main use cases for git reset within my descriptions of the various options in the next section. It can really be used for a wide variety of things; the common thread is that all of them involve resetting the branch, index, and/or work tree to point to/match a given commit.

Things to be careful of

  • --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>).

Arguments

Paraphrasing the man page, most common usage is of the form git reset [<commit>] [paths...], which will reset the given paths to their state from the given commit. If the paths aren't provided, the entire tree is reset, and if the commit isn't provided, it's taken to be HEAD (the current commit). This is a common pattern across git commands (e.g. checkout, diff, log, though the exact semantics vary), so it shouldn't be too surprising.

For example, git reset other-branch path/to/foo resets everything in path/to/foo to its state in other-branch, git reset -- . resets the current directory to its state in HEAD, and a simple git reset resets everything to its state in HEAD.

The main work tree and index options

There are four main options to control what happens to your work tree and index during the reset.

Remember, the index is git's "staging area" - it's where things go when you say git add in preparation to commit.

  • --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.

Want to know more?

I do think man git reset is really quite good for this - perhaps you do need a bit of a sense of the way git works for them to really sink in though. In particular, if you take the time to carefully read them, those tables detailing states of files in index and work tree for all the various options and cases are very very helpful. (But yes, they're very dense - they're conveying an awful lot of the above information in a very concise form.)

Strange notation

The "strange notation" (HEAD^ and HEAD~1) you mention is simply a shorthand for specifying commits, without having to use a hash name like 3ebe3f6. It's fully documented in the "specifying revisions" section of the man page for git-rev-parse, with lots of examples and related syntax. The caret and the tilde actually mean different things:

  • 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 reset"是什么意思?做?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆