$ git reset -- <file_path>
可以按路径重置。
然而,$ git reset (--hard|--soft) <file_path>
会报告以下错误:
Cannot do hard|soft reset with paths.
因为没有必要(其他命令已经提供了该功能),而且这样可以减少意外执行错误操作的可能性。
对于路径的“硬重置”只需使用 git checkout HEAD -- <path>
命令(将文件恢复到现有版本)。
路径的“软重置”无意义。
对于路径的“混合重置”,可以使用 git reset -- <path>
命令实现。
git checkout -- <path>
应该用 git reset --hard <path>
来替代,这样更加合理... - vergenztgit checkout -- <path>
不会进行硬重置,它会用暂存区的内容替换工作目录中的内容。git checkout HEAD -- <path>
会对指定路径进行硬重置,将索引和工作目录都替换为HEAD提交中的版本。 - Dan Fabulichreset --hard
命令可以提供这一缺失的功能。Git 已经非常强大,因此“我们不允许你这样做是为了保护你”的借口已经站不住脚了:有很多方法可以“无意中”做错事情。即使如此,只要你有 git reflog
命令,所有这些都不重要。 - void.pointergit reset --hard -- <path>
。因为它确实存在合法的使用场景。 - Mariusz Pawelskigit restore
。 - Patrickgit checkout HEAD <path>
来完成你想做的事情。git reset
在子目录上也能正常工作),我看不出为什么git reset --hard
不应该完全满足你的要求。如何进行硬重置已经有答案,我来解释一下其中的原因。
那么,git reset是什么呢?取决于指定的参数,它可以执行两个不同的操作:
如果您指定了路径,它将用提交(默认为HEAD)中文件替换索引中匹配的文件。此操作完全不影响工作树,通常用作git add的相反操作。
如果您没有指定路径,则将当前分支头移动到指定的提交,并且可选地将索引和工作树重置为该提交的状态。此附加行为由模式参数控制:
--soft:不要触摸索引和工作树。
--mixed(默认值):重置索引但不重置工作树。
--hard:重置索引和工作树。
还有其他选项,请参见文档以获取完整列表和一些用例。
当您没有指定提交时,默认为HEAD,因此git reset --soft
什么也不会做,因为它是一个将head移动到HEAD(到其当前状态)的命令。另一方面,git reset --hard
由于其副作用而是有意义的,它说将head移动到HEAD,并将索引和工作树重置为HEAD。
我认为现在应该清楚了,为什么这个操作并不针对特定文件 - 它旨在首先移动分支头部,在此过程中重置工作树和索引是次要的功能。
git checkout
命令可用了?而将重置执行相同的操作会更加困扰用户。我的回答是,--hard
选项不适用于特定文件,因为它是分支重置模式,而不是索引重置模式。工作树重置被命名为 checkout,你可以在其他答案中看到。我认为这些都只是 Git 用户界面的糟糕设计。 - usergit checkout
的第一个选项进行比较:git reset --
仅设置索引,而 git checkout --
仅设置工作树? - seeker_of_bacon请确保在源和实际分支之间加入斜杠:
git reset --hard origin/branch
或者
git reset --hard upstream/branch`
git checkout
,我们可以从任何区域(无论是来自存储库中的提交还是来自提交或暂存区的单个文件)填充工作树(甚至是默认值)。git reset COMMIT -- files
,因为仅使用一些文件覆盖HEAD是没有意义的!reset
仍然是丢弃对暂存区所做更改的最佳命令名称,并且考虑到唯一的数据源是存储库,那么就“扩展功能”而不是创建新命令。git reset -- <files>
已经有点特殊:它不会覆盖HEAD。在我看来,所有这些变化都将是例外。即使我们可以构想一个--hard
版本,其他版本(例如--soft
)也没有意义。git reset -- <files>
感觉就像是因为这是一个有用的功能,但是没有人确定应该放在哪个命令中而添加进来的。幸运的是,现在我们有了更加理智的 git restore
,它具有 git checkout -- <path>
、git checkout <commit> -- <path>
和 git reset [<commit>] -- <path>
的功能,其默认值更加合理,并且还有更多以前无法完成的功能(与被接受的答案相反。现在你终于可以轻松地恢复工作树,而不会影响索引)。 - Mariusz Pawelski本答案现已被我更广泛的回答所引用:所有关于在git中检查文件或目录的问题。
为什么git无法按路径进行硬重置/软重置?
实际上是可以的,只需使用几个命令即可,而不是一个。以下是具体步骤:
警告:在开始此过程之前,git status
应该是完全干净的!否则,您将面临永久丢失 git status
显示的任何未提交更改的风险,因为 git clean -fd
'f'orce 删除当前工作树(文件系统)中不在提交或分支commit_hash
中指定的路径下的所有文件和目录。因此,任何尚未提交的内容都会永久丢失,就像您使用了rm
一样!
--soft
重置:# How to "soft reset" "path/to/some/file_or_dir" to its state exactly as it was
# at commit or branch `commit_hash`.
#
# SEE WARNING ABOVE!
git reset commit_hash -- path/to/some/file_or_dir
git checkout-index -fa
git clean -fd # SEE WARNING ABOVE!
--hard
重置:# How to "hard reset" "path/to/some/file_or_dir" to its state exactly as it was
# at commit or branch `commit_hash`.
#
# SEE WARNING ABOVE!
git reset commit_hash -- path/to/some/file_or_dir
git checkout-index -fa
git clean -fd # SEE WARNING ABOVE!
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
或 --hard
git reset又称:如何手动完成以下任一无效命令的等效操作:
# 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
git checkout commit_hash -- path/to/some/file_or_dir
...然后,你可以通过以下几个命令一起完成上面的假设命令所做的事情。
--soft
路径重置描述:使本地path/to/some/file_or_dir
与commit_hash
中的file_or_dir
相同,同时删除本地路径目录中不存在于commit_hash
目录中的文件(如果path/to/some/file_or_dir
是一个目录)。最后将所有更改“暂存”(已添加但未提交)。
git reset commit_hash -- path/to/some/file_or_dir
git checkout-index -fa
git clean -fd
# 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://dev59.com/qnA75IYBdhLWcg3wxsNn#66589020
# and https://dev59.com/IXVD5IYBdhLWcg3wNIzc#12184274
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
--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
。但是,当像这样“硬重置”仅几个文件或路径时,您不能仅将分支指针移动到该其他commit_hash
,因此实现此命令的合理行为的其他方法就是像上面所做的那样添加这些“未添加”或“重置”的更改的新提交。git
不支持原生的按路径--hard
重置选项的原因;也许是因为按路径进行--hard
重置需要添加一个新提交,这与不添加新提交的正常--hard
行为略有不同,而只是“重置”到(将分支指针移动到)给定提交。
然而,这并不能自然地解释为什么git
不允许至少按路径进行--soft
git重置,因为这对我来说似乎更标准。
git
术语当您阅读man git reset
页面时,您需要了解一些git术语:
nemo
、nautilus
或thunar
)中浏览文件系统时看到的文件和文件夹的状态。<tree-ish>
= 提交哈希值或分支名称git status
时看到绿色部分。这些都是已经git add
(“暂存”)但尚未提交的更改。当您运行git add some_file
时,通过将其更改移动到索引中,“暂存”了some_file
。现在可以说some_file
已被“添加”,“暂存”或“在索引中”(都是同一件事)。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_hash
中,同时也将 HEAD
设置为指向 commit_hash
,或者对于指定的文件或目录,就像它指向 commit_hash
一样(通过添加必要的更改来使工作树中的 some_file_or_dir
看起来像 commit_hash
中的 some_file_or_dir
)。
git
术语中,"working tree"的意思是"您的本地文件系统"(就像计算机通常在文件夹管理器中或在终端中导航时看到文件和文件夹一样),而"index"或"index file"的意思是"当您git add
或' stage '文件时,它们所在的位置。"当您运行git status
时,所有以绿色显示的文件均为“已暂存”,或者位于“索引”或“索引文件”中(同一件事)。 (来源:Git中HEAD、工作树和索引之间有什么区别?)。man git reset
中的一些重要部分:
git reset [--soft | --mixed [-N] | --hard | --merge | --keep] [-q] [<commit>]
In the third form [the form shown above], set the current branch head (
HEAD
) to<commit>
, optionally modifying index and working tree to match. The<tree-ish>/<commit>
defaults toHEAD
in all forms.
并且:
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.
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.
git reset
手册列出了三种调用方式:
其中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>
(reco
的助记符:重置并检查o
ut)
git reset --soft HEAD~1 文件名 撤销提交但更改保留在本地。文件名 可以是 -- 表示所有已提交的文件。
无法使用路径执行软重置。
- alt
git checkout
不会进行硬重置。 - Patrick