【发布时间】:2012-06-27 09:18:07
【问题描述】:
$ git reset -- <file_path>可以通过路径重置。
但是,$ git reset (--hard|--soft) <file_path> 会报如下错误:
Cannot do hard|soft reset with paths.
【问题讨论】:
$ 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>。这更有意义......
git checkout -- <path> 不进行硬重置;它用暂存的内容替换工作树的内容。 git checkout HEAD -- <path> 对路径进行硬重置,将索引和工作树替换为 HEAD 提交中的版本。
reset --hard 带有路径将提供这个缺失的部分。 Git 已经如此强大,以至于“我们不让你这样做是为了保护自己”的借口是零水:有很多方法可以“偶然”做错事。当您拥有git reflog 时,这些都不重要。
git reset --hard -- <path>。它有合法的用例。
您可以使用git checkout HEAD <path> 完成您想做的事情。
也就是说,提供的错误消息对我来说毫无意义(因为git reset 在子目录上工作得很好),我看不出为什么git reset --hard 不应该完全按照你的要求去做。
【讨论】:
问题如何已经是answered了,我会解释为什么部分。
那么,git reset 做了什么?根据指定的参数,它可以做两种不同的事情:
如果指定路径,它会将索引中的匹配文件替换为来自提交的文件(默认为 HEAD)。这个动作完全不影响工作树,通常与 git add 相对。
如果您不指定路径,它会将当前分支头移动到指定的提交,并且连同该提交,可选择将索引和工作树重置为该提交的状态。此附加行为由 mode 参数控制:
--soft:不要触摸索引和工作树。
--混合(默认):重置索引但不重置工作树。
--hard:重置索引和工作树。
还有其他选项,请参阅文档以获取完整列表和一些用例。
当您不指定提交时,它默认为 HEAD,因此git reset --soft 将什么也不做,因为它是将头部移动到 HEAD(到其当前状态)的命令。另一方面,git reset --hard 之所以有意义,是因为它的副作用,它表示将头部移动到 HEAD并且将索引和工作树重置为 HEAD。
我认为现在应该清楚为什么该操作本质上不适用于特定文件 - 它的目的首先是移动分支头,重置工作树,索引是次要功能。
【讨论】:
git checkout 命令提供?并且重置来做同样的事情会进一步混淆用户。我的回答是 --hard 选项不适用于特定文件,因为它是分支重置的模式,而不是索引重置。工作树重置名为结帐,您可以在其他答案中阅读。所有这一切都只是 Git 用户界面的糟糕设计,恕我直言。
git checkout:git reset -- 只设置索引,而git checkout -- 只设置工作树?
这背后有一个非常重要的原因:checkout 和reset 的原则。
在 Git 术语中,checkout 的意思是“进入当前工作树”。使用git checkout,我们可以使用来自任何 区域的数据填充工作树,无论是来自存储库中的提交还是来自提交或 的单个文件暂存区(甚至是默认值)。
反过来,git reset 没有这个角色。顾名思义,它将重置当前 ref,但 总是 将 repository 作为源,独立于“reach”(--soft、--mixed 或 - -硬)。
回顾:
因此,可能有点令人困惑的是git reset COMMIT -- files 的存在,因为只用一些文件“覆盖 HEAD”是没有意义的!
在没有官方解释的情况下,我只能推测 git 开发人员发现 reset 仍然是放弃对暂存区所做更改的命令的最佳名称,并且鉴于唯一的数据源是存储库,然后“让我们扩展功能”而不是创建新命令。
所以不知何故,git reset -- <files> 已经有点特殊:它不会覆盖 HEAD。恕我直言,所有这些变化都是例外。即使我们可以构思出--hard 版本,其他版本(例如--soft)也没有任何意义。
【讨论】:
git reset -- <files> 好像被添加了一样,因为这是一个有用的功能,但没有人确定它应该放在哪个命令中。幸运的是,现在我们有更健全的git restore,它具有git checkout -- <path>git checkout <commit> -- <path> 和git reset [<commit>] -- <path> 的功能,具有更健全的默认值和更多以前无法做到的功能(与公认的答案相反。现在你终于可以轻松恢复刚刚工作的树,无需触及索引)。
确保在源或上游(源)与实际分支之间放置一个斜线:
git reset --hard origin/branch
或
git reset --hard upstream/branch`
【讨论】:
为什么 git 不能通过路径进行硬/软重置?
可以。它只需要几个命令就是全部,而不仅仅是一个。方法如下:
如何通过路径重置--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 或--hard git 重置又名:如何手动完成这些无效命令的等效:
# 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
...那么,您可以完成上面假设的命令与下面这几个命令一起完成的操作。
--soft 路径重置描述: 使您的本地path/to/some/file_or_dir 与file_or_dir 在commit_hash 中的外观相同,同时删除本地路径目录中的文件(如果path/to/some/file_or_dir 是一个目录)在commit_hash 的目录中不存在。最后将所有更改“暂存”(添加但未提交)。
git reset commit_hash -- path/to/some/file_or_dir
git checkout-index -fa
git clean -fd
如果允许这样的命令,上述结果正是我对路径上的 --soft 重置所期望的结果。
有关git checkout-index -fa 和git clean -fd 部分的更多信息,请在此处查看我的其他答案:Using git, how do you reset the working tree (local file system state) to the state of the index ("staged" files)?。
请注意,您应该在每个单独的命令之后运行git status,以查看每个命令正在执行的操作。以下是各个命令的解释:
# 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
--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 added(“staged”)但尚未提交的所有更改。当您运行 git add some_file 时,您通过将其更改移动到 index 来“暂存”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看起来像@ 987654403@commit_hash。
此外,在git 术语中,“工作树” 表示“您的本地文件系统”(因为您的计算机通常会在文件夹管理器中或在终端中导航时看到文件和文件夹), "index" 或 "index file" 表示“当你git add 时文件所在的位置,或‘暂存’它们。”当你运行git status 时,所有以绿色显示的文件都是“暂存的”,或者在“索引”或“索引文件”中(同样的事情)。 (来源:What's the difference between HEAD, working tree and index, in Git?)。
现在,考虑到这一点,以下是来自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.
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 manual 列出了 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>
(reco 的助记符:reset && checkout)
【讨论】:
git reset --hard "origin/username/feature"
git --version # 2.14.1
在 Mac 上为我工作。当我的分支不包含斜杠时,我使用这个不带引号的命令。
【讨论】:
git reset --soft HEAD~1 filename 撤消提交,但更改保留在本地。 filename 可以是 -- 对于所有提交的文件
【讨论】:
Cannot do soft reset with paths.