为什么 git 不能按路径进行硬/软重置?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/11200839/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-10 14:01:23  来源:igfitidea点击:

Why git can't do hard/soft resets by path?

gitgit-reset

提问by yao

$ git reset -- <file_path>can reset by path.

$ git reset -- <file_path>可以通过路径重置。

However, $ git reset (--hard|--soft) <file_path>will report an error like below:

但是,$ git reset (--hard|--soft) <file_path>会报如下错误:

Cannot do hard|soft reset with paths.

回答by Amber

Because there's no point (other commands provide that functionality already), and it reduces the potential for doing the wrong thing by accident.

因为没有意义(其他命令已经提供了该功能),并且它减少了意外做错事的可能性。

A "hard reset" for a path is just done with git checkout HEAD -- <path>(checking out the existing version of the file).

路径的“硬重置”刚刚完成git checkout HEAD -- <path>(检查文件的现有版本)。

A soft reset for a path doesn't make sense.

路径的软重置没有意义。

A mixed reset for a path is what git reset -- <path>does.

路径的混合重置是什么git reset -- <path>

回答by Aaron Kent

You can accomplishment what you're trying to do using git checkout HEAD <path>.

您可以使用git checkout HEAD <path>.

That said, the provided error message makes no sense to me (as git resetworks just fine on subdirectories), and I see no reason why git reset --hardshouldn't do exactly what you're asking of it.

也就是说,提供的错误消息对我来说毫无意义(因为git reset在子目录上工作得很好),而且我认为没有理由git reset --hard不完全按照您的要求执行。

回答by user

The question howis already answered, I'll explain the whypart.

问题如何已经回答,我将解释为什么部分。

So, what does git resetdo? Depending on the parameters specified, it can do two different things:

那么,git reset 有什么作用呢?根据指定的参数,它可以做两种不同的事情:

  • If you specify a path, it replaces the matched files in the index with the files from a commit (HEAD by default). This action doesn't affect the working tree at all and is usually used as the opposite of git add.

  • If you don't specify a path, it moves the current branch head to a specified commit and, together with that, optionally resets the index and the working tree to the state of that commit. This additionalbehavior is controlled by the mode parameter:
    --soft: don't touch the index and the working tree.
    --mixed(default): reset the index but not the working tree.
    --hard: reset the index and the working tree.
    There are also other options, see the documentation for the full list and some use cases.

    When you don't specify a commit, it defaults to HEAD, so git reset --softwill do nothing, as it is a command to move the head to HEAD (to its current state). git reset --hard, on the other hand, makes sense due to its side effects, it says move the head to HEAD andreset the index and the working tree to HEAD.

    I think it should be clear by now why this operation is not for specific files by its nature - it is intended to move a branch head in the first place, resetting the working tree and the index is secondary functionality.

  • 如果您指定路径,它会将索引中匹配的文件替换为提交中的文件(默认为 HEAD)。此操作根本不会影响工作树,通常用作 git add 的反义词。

  • 如果您不指定路径,它会将当前分支头移动到指定的提交,并且可以选择将索引和工作树重置为该提交的状态。此附加行为由模式参数控制:
    --soft:不要接触索引和工作树。
    --mixed(默认):重置索引但不重置工作树。
    --hard:重置索引和工作树。
    还有其他选项,请参阅完整列表和一些用例的文档。

    当您不指定提交时,它默认为 HEAD,因此git reset --soft不会执行任何操作,因为它是将头部移动到 HEAD(到其当前状态)的命令。git reset --hard,另一方面,由于其副作用,它说将头部移动到 HEAD并将索引和工作树重置为 HEAD。

    我认为现在应该清楚为什么此操作本质上不适用于特定文件 - 它首先旨在移动分支头,重置工作树,索引是次要功能。

回答by Pascal Nitcheu

Make sure you put a slash between origin or upstream (source) and the actual branch:

确保在原点或上游(源)和实际分支之间放置一个斜线:

git reset --hard origin/branch

or

或者

git reset --hard upstream/branch`

回答by F Pereira

There's a very important reason behind that: the principles of checkoutand reset.

有背后的一个很重要的原因:原则checkoutreset

In Git terms, checkoutmeans "bring into the current working tree". And with git checkoutwe can fill the working tree with data from anyarea, being it from a commit in the repository or individual files from a commit or the staging area(which is the even the default).

在 Git 术语中,checkout 的意思是“带入当前的工作树”。并且git checkout我们可以用来自任何区域的数据填充工作树,无论是来自存储库中的提交还是来自提交或暂存区(甚至是默认值)的单个文件。

In turn, git resetdoesn't have this role. As the name suggests, it will reset the current ref but alwayshaving the repositoryas a source, independently of the "reach" (--soft, --mixed or --hard).

反过来, git reset没有这个角色。顾名思义,它将重置当前引用,但始终存储库作为源,独立于“范围”(--soft、--mixed 或--hard)。

Recap:

回顾:

  • checkout: From anywhere (index / repo commit) -> working tree
  • reset: Repo commit -> Overwrite HEAD (and optionally index and working tree)
  • 结帐:从任何地方(索引/回购提交)-> 工作树
  • reset: Repo commit -> Overwrite HEAD (以及可选的索引和工作树)

Therefore what can be a bit confusing is the existence of git reset COMMIT -- filessince "overwriting HEAD" with only some files doesn't make sense!

因此,可能有点令人困惑的是存在,git reset COMMIT -- files因为仅使用某些文件“覆盖 HEAD”是没有意义的!

In the absence of an official explanation, I can only speculate that the git developers found that resetwas still the best name of a command to discard changes made to the staging area and, given the only data source was the repository, then "let's extend the functionality" instead of creating a new command.

在没有官方解释的情况下,我只能推测 git 开发人员发现这reset仍然是放弃对暂存区所做更改的命令的最佳名称,并且鉴于唯一的数据源是存储库,那么“让我们扩展功能”,而不是创建新命令。

So somehow git reset -- <files>is already a bit exceptional: it won't overwrite the HEAD. IMHO all such variations would be exceptions. Even if we can conceive a --hardversion, others (for example --soft) wouldn't make sense.

所以不知何故git reset -- <files>已经有点例外:它不会覆盖 HEAD。恕我直言,所有这些变化都是例外。即使我们可以构思一个--hard版本,其他版本(例如--soft)也没有意义。

回答by Tom Hale

Explanation

解释

The git resetmanuallists 3 ways of invocation:

git reset手册列出了 3 种调用方式:

  • 2 are file-wise: These do not affect the working tree, but operate only on the files in the index specified by <paths>:

    • git reset [-q] [<tree-ish>] [--] <paths>..
    • git reset (--patch | -p) [<tree-ish>] [--] [<paths>...]
  • 1 is commit-wise: Operates on all filesin the referenced <commit>, and mayaffect the working tree:

    • git reset [<mode>] [<commit>]
  • 2 是文件方面的:这些不影响工作树,但仅对由 指定的索引中的文件进行操作<paths>

    • git reset [-q] [<tree-ish>] [--] <paths>..
    • git reset (--patch | -p) [<tree-ish>] [--] [<paths>...]
  • 1 是 commit-wise:对引用中的所有文件进行操作<commit>,并可能影响工作树:

    • git reset [<mode>] [<commit>]

There's no mode of invocation that operates only on specified files andaffects the working tree.

没有仅对指定文件进行操作影响工作树的调用模式。

Workaround

解决方法

If you want to both:

如果你想两者兼得:

  • Reset the index/cache version of a file(s)
  • Checkout the file(s) (ie, make the working tree match the index and commit version)
  • 重置文件的索引/缓存版本
  • 签出文件(即,使工作树与索引和提交版本匹配)

You can use this alias in your git config file:

你可以在你的 git 配置文件中使用这个别名:

[alias]
  reco   = !"cd \"${GIT_PREFIX:-.}\" && git reset \"$@\" && git checkout \"$@\" && git status --short #"  # Avoid: "fatal: Cannot do hard reset with paths."

You can then do one of:

然后,您可以执行以下操作之一:

$ git reco <paths>

$ git reco <branch/commit> <paths>

$ git reco -- <paths>

(Mnenonic for reco: reset && checkout)

(助记符recoreset && check out)

回答by Peiti Li

git reset --soft HEAD~1 filenameundo the commit but changes remain in local. filenamecould be -- for all commited files

git reset --soft HEAD~1 filename撤消提交但更改保留在本地。文件名可以是 -- 对于所有提交的文件