165

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

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

Cannot do hard|soft reset with paths.
4

9 回答 9

169

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

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

路径的软重置没有意义。

路径的混合重置就是这样git reset -- <path>做的。

于 2012-06-26T04:39:54.010 回答
21

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

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

于 2014-12-22T21:30:26.170 回答
12

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

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

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

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

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

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

于 2017-09-20T18:23:05.680 回答
5

这背后有一个非常重要的原因:and的原理checkoutreset

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

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

回顾:

  • 结帐:从任何地方(索引/回购提交)-> 工作树
  • 重置:回购提交 -> 覆盖 HEAD(以及可选的索引和工作树)

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

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

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

于 2019-01-21T01:50:51.650 回答
3

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

git reset --hard origin/branch

或者

git reset --hard upstream/branch`
于 2019-07-27T23:33:15.983 回答
2

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

它可以。它只需要几个命令就是全部,而不仅仅是一个。方法如下:

TLDR

如何--soft通过路径进行重置:

git reset commit_hash -- path/to/some/file_or_dir
git checkout-index -fa
git clean -fd

如何--hard通过路径进行重置:

git reset commit_hash -- path/to/some/file_or_dir
git checkout-index -fa
git clean -fd
git commit -m "hard reset path/to/some/file_or_dir to its state \
as it was at commit_hash"

完整答案:

在 git 版本 2.17.1 中测试(检查你的git --version)。

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

我不知道究竟是为什么,但我猜是因为git要么做出了你和我都不同意的开发决定,要么因为git根本不完整,仍然需要实现这一点。另请参阅下面“--hard按路径重置”部分下方提供的有关此内容的其他见解。单个路径上的真正--hard重置不能与--hard整个分支的重置相同。

但是,我们可以使用一些命令手动完成所需的行为。请注意git checkout commit_hash -- path/to/some/file_or_dir,由于下面解释的原因,单独不是其中之一。

在继续之前,您应该了解做什么git reset、什么是工作树索引--soft以及--hard通常做什么git reset。如果您对这些主题有任何疑问,请先阅读下面的“背景知识”部分。

如何按路径执行--soft或git 重置--hard

又名:如何手动完成这些无效命令的等效项:

# hypothetical commands not allowed in git, since `git` does NOT 
# allow `--soft` or `--hard` resets on paths

git reset --soft commit_hash -- path/to/some/file_or_dir
git reset --hard commit_hash -- path/to/some/file_or_dir

由于上述命令是不允许的,并且由于此checkout命令与上述假设命令的作用不同,因此此checkout命令也不会删除本地存在的不在以下位置的文件或文件夹commit_hash

git checkout commit_hash -- path/to/some/file_or_dir

...然后,您可以一起完成上面的假设命令与下面的这几个命令一起执行的操作。

1.--soft按路径重置

描述:使您的本地path/to/some/file_or_dir与. 最后将所有更改“暂存”(添加但未提交)。file_or_dircommit_hashpath/to/some/file_or_dircommit_hash

git reset commit_hash -- path/to/some/file_or_dir
git checkout-index -fa
git clean -fd

--soft如果允许这样的命令,上述结果正是我对路径重置的期望。

有关git checkout-index -fagit clean -fd部分的更多信息,请在此处查看我的其他答案:使用 git,如何将工作树(本地文件系统状态)重置为索引状态(“暂存”文件)?.

请注意,您应该git status在每个单独的命令之后运行 a 以查看每个命令在执行时执行的操作。以下是各个命令的解释:

# Stage some changes in path/to/some/file_or_dir, by adding them to the index,
# to show how your local path/to/some/file_or_dir SHOULD look in order to
# match what it looks like at `commit_hash`, but do NOT actually _make_ 
# those changes in yourlocal file system. Rather, simply "untrack" files 
# which should be deleted, and do NOT stage for commit changes which should 
# NOT have been made in order to match what's in `commit_hash`.
git reset commit_hash -- path/to/some/file_or_dir
git status

# Now, delete and discard all your unstaged changes.

# First, copy your index (staged/added changes) to your working file 
# tree (local file system). See this answer for these details:
# https://stackoverflow.com/a/66589020/4561887
# and https://stackoverflow.com/a/12184274/4561887
git checkout-index -fa
git status

# 'f'orce clean, including 'd'irectories. This means to **delete** 
# untracked local files and folders. The `git reset` command above
# is what made these untracked. `git clean -fd` is what actually 
# removes them.
git clean -fd
git status

2.--hard按路径重置

描述:执行上述--soft重置步骤,然后提交更改:

git reset commit_hash -- path/to/some/file_or_dir
git checkout-index -fa
git clean -fd
git commit -m "hard reset path/to/some/file_or_dir to its state \
as it was at commit_hash"

现在,为了更好地衡量和作为最终检查,您可以运行git reset commit_hash -- path/to/some/file_or_dir,然后运行git status​​。你会看到git status没有任何变化,因为--hard上面的路径重置是成功的,所以这个调用git reset commit_hash -- path/to/some/file_or_dir什么也没做。优秀的; 有效!

这些结果与真正的重置并不完全相同--hard因为真正的--hard重置不会添加新的提交git commit。相反,它只是强制您当前签出的分支指向另一个commit_hash. 但是,当仅像这样“硬重置”几个文件或路径时,您不能只将分支指针移动到指向 other commit_hash,因此除了添加如上所述,带有这些“未添加”或“重置”更改的新提交。

这种见解也可能是git本机不支持--hard按路径重置选项的原因;也许是因为--hard通过路径重置需要添加一个新的提交,这与--hard不添加新提交的正常行为略有不同,而只是“重置”到(将分支指针移动到)给定的提交。

但是,这本身并不能解释为什么git至少不允许--soft通过路径重置 git,因为这对我来说似乎更标准。

背景知识

1. 基本git术语

在阅读这些man git reset页面时,您需要了解一些 git 术语:

  1. 工作树= 本地文件系统;这是指您在终端或 GUI 文件管理器(如nemonautilusthunar.
  2. <tree-ish>= 提交哈希或分支名称
  3. index = 你在运行时看到的绿色git status。这些都是git added(“暂存”)但尚未提交的所有更改。当您运行时,您通过将其更改移动到indexgit add some_file来“暂存” 。您现在可以说是“添加”、“暂存”或“在索引中”(都是一样的东西)。some_filesome_file

2.man git reset页面

当您阅读这些解决方案时,请注意以下man git reset状态(重点添加):

git reset <paths>是相反的git add <paths>

换句话说,git reset commit_hash -- some_file_or_dir可以“取消添加”,或添加相反的更改(从而撤消这些更改)some_file_or_dir,包含在 commit 或 branchcommit_hash中,同时还设置HEAD为指向commit_hash,或者好像它指向commit_hash指定的文件或目录(同样,通过添加必要的更改以some_file_or_dir工作树中进行看起来像some_file_or_dir.commit_hash

此外,在git术语中,“工作树”表示“您的本地文件系统”(因为您的计算机通常会在文件夹管理器中或在终端中导航时看到文件和文件夹),而“索引”“索引文件”表示“位置文件在你的时候去哪里git add,或者‘暂存’它们。” 当你运行时git status,所有以绿色显示的文件都是“暂存”的,或者在“索引”或“索引文件”中(同样的事情)。(来源:Git 中 HEAD、工作树和索引有什么区别?)。

现在,考虑到这一点,这里有一些重要的部分来自man git reset

git reset [--soft | --mixed [-N] | --hard | --merge | --keep] [-q] [<commit>]

在第三种形式[如上所示的形式]中,将当前分支头(HEAD)设置为<commit>,可选地修改索引和工作树以匹配。<tree-ish>/<commit>默认为所有HEAD形式。

和:

git reset [-q] [<tree-ish>] [--] <paths>...
    This form resets the index entries for all <paths> to
    their state at <tree-ish>. (It does not affect the working
    tree or the current branch.)
    
    **This means that `git reset <paths>` is the opposite of `git
    add <paths>`.**
      
    After running git reset <paths> to update the index entry,
    you can use git-checkout(1) to check the contents out of
    the index to the working tree. Alternatively, using git-
    checkout(1) and specifying a commit, you can copy the
    contents of a path out of a commit to the index and to the
    working tree in one go.

和:

git reset [<mode>] [<commit>]
    This form resets the current branch head to <commit> and
    possibly updates the index (resetting it to the tree of
    <commit>) and the working tree depending on <mode>. If
    <mode> is omitted, defaults to "--mixed". The <mode> must
    be one of the following:
     
    --soft
        Does not touch the index file or the working tree at
        all (but resets the head to <commit>, just like all
        modes do). This leaves all your changed files "Changes
        to be committed", as git status would put it.
     
    --mixed
        Resets the index but not the working tree (i.e., the
        changed files are preserved but not marked for commit)
        and reports what has not been updated. This is the
        default action.
     
        If -N is specified, removed paths are marked as
        intent-to-add (see git-add(1)).
     
    --hard
        Resets the index and working tree. Any changes to
        tracked files in the working tree since <commit> are
        discarded.

3.您还应该熟悉该man git checkout-index页面。

请记住,“索引”包含所有添加或“暂存”的文件(运行时显示为绿色git status),而“工作树”指的是您实际的本地文件系统(还包含运行时显示为红色git status的更改)。

在最基本的层面上,它的作用如下:

来自man git checkout-index

NAME
       git-checkout-index - Copy files from the index to the working tree

和:

-f, --force
    forces overwrite of existing files

-a, --all
    checks out all files in the index. Cannot be used together with
    explicit filenames.

参考:

  1. [我的答案——直接适用,以及我需要能够在上面写出整个答案的前导答案!]使用 git,如何将工作树(本地文件系统状态)重置为索引状态(“staged “文件)?
  2. 如何从当前 Git 工作树中删除本地(未跟踪)文件
  3. 如何丢弃 Git 中未暂存的更改?
  4. Git中的HEAD,工作树和索引有什么区别?

有关的:

  1. [我的回答]如何从另一个分支只获取一个文件
于 2021-03-09T02:03:21.677 回答
0

解释

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

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

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

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

没有只对指定文件运行影响工作树的调用模式。

解决方法

如果你想要两者:

  • 重置文件的索引/缓存版本
  • 签出文件(即,使工作树与索引和提交版本匹配)

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

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

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

$ git reco <paths>

$ git reco <branch/commit> <paths>

$ git reco -- <paths>

(助记符recoreset && check out)

于 2018-09-13T12:07:16.343 回答
-1
git reset --hard "origin/username/feature"
git --version # 2.14.1

在 Mac 上为我工作。当我的分支不包含斜杠时,我使用这个不带引号的命令。

于 2021-11-22T23:37:02.400 回答
-3

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

于 2015-08-25T21:21:11.470 回答