如何 git reset --hard 子目录?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/15404535/
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
How to git reset --hard a subdirectory?
提问by krlmlr
UPDATE2: With Git 2.23 (August 2019), there's a new command
git restore
that does this, see the accepted answer.UPDATE: This will work more intuitively as of Git 1.8.3, see my own answer.
UPDATE2:使用 Git 2.23(2019 年 8 月),有一个新命令
git restore
可以执行此操作,请参阅已接受的答案。更新:从 Git 1.8.3 开始,这将更直观地工作,请参阅我自己的答案。
Imagine the following use case: I want to get rid of all changes in a specific subdirectory of my Git working tree, leaving all other subdirectories intact.
想象一下以下用例:我想删除 Git 工作树的特定子目录中的所有更改,而保留所有其他子目录。
I can do
git checkout .
, but git checkout . adds directories excluded by sparse checkoutThere is
git reset --hard
, but it won't let me do it for a subdirectory:> git reset --hard . fatal: Cannot do hard reset with paths.
I can reverse-patch the current state using
git diff subdir | patch -p1 -R
, but this is a rather weird way of doing this.
我可以
git checkout .
,但是git checkout 。添加稀疏结帐排除的目录有
git reset --hard
,但它不会让我为子目录执行此操作:> git reset --hard . fatal: Cannot do hard reset with paths.
我可以使用 反向修补当前状态
git diff subdir | patch -p1 -R
,但这是一种相当奇怪的方法。
What is the proper Git command for this operation?
此操作的正确 Git 命令是什么?
The script below illustrates the problem. Insert the proper command below the How to make files
comment -- the current command will restore the file a/c/ac
which is supposed to be excluded by the sparse checkout. Note that I do notwant to explicitly restore a/a
and a/b
, I only "know" a
and want to restore everything below. EDIT: And I also don't "know" b
, or which other directories reside on the same level as a
.
下面的脚本说明了这个问题。在How to make files
注释下方插入正确的命令——当前命令将恢复a/c/ac
应该被稀疏检出排除的文件。请注意,我不希望明确恢复a/a
和a/b
,我只“知道” a
,并希望恢复下的所有内容。编辑:而且我也不“知道” b
,或者其他哪些目录与a
.
#!/bin/sh
rm -rf repo; git init repo; cd repo
for f in a b; do
for g in a b c; do
mkdir -p $f/$g
touch $f/$g/$f$g
git add $f/$g
git commit -m "added $f/$g"
done
done
git config core.sparsecheckout true
echo a/a > .git/info/sparse-checkout
echo a/b >> .git/info/sparse-checkout
echo b/a >> .git/info/sparse-checkout
git read-tree -m -u HEAD
echo "After read-tree:"
find * -type f
rm a/a/aa
rm a/b/ab
echo >> b/a/ba
echo "After modifying:"
find * -type f
git status
# How to make files a/* reappear without changing b and without recreating a/c?
git checkout -- a
echo "After checkout:"
git status
find * -type f
采纳答案by VonC
With Git 2.23 (August 2019), you have the new command git restore
使用 Git 2.23(2019 年 8 月),您拥有新命令git restore
git restore --source=HEAD --staged --worktree -- aDirectory
# or, shorter
git restore -s@ -SW -- aDirectory
That would replace both the index and working tree with HEAD
content, like an reset --hard
would, but for a specific path.
这将用HEAD
内容替换索引和工作树,就像一个reset --hard
会,但用于特定路径。
Original answer (2013)
原始答案 (2013)
Note (as commentedby Dan Fabulich) that:
git checkout -- <path>
doesn't do a hard reset: it replaces the working tree contents with the staged contents.git checkout HEAD -- <path>
does a hard reset for a path, replacing both the index and the working tree with the version from theHEAD
commit.
git checkout -- <path>
不进行硬重置:它用暂存内容替换工作树内容。git checkout HEAD -- <path>
对路径进行硬重置,用HEAD
提交中的版本替换索引和工作树。
As answeredby Ajedi32, both checkout forms don't remove files which were deleted in the target revision.
If you have extra files in the working tree which don't exist in HEAD, a git checkout HEAD -- <path>
won't remove them.
正如Ajedi32所回答的那样,两种结帐表格都不会删除在目标修订版中删除的文件。
如果工作树中有 HEAD 中不存在的额外文件,git checkout HEAD -- <path>
则不会删除它们。
Note: With git checkout --overlay HEAD -- <path>
(Git 2.22, Q1 2019), files that appear in the index and working tree, but not in <tree-ish>
are removed, to make them match <tree-ish>
exactly.
注意:在git checkout --overlay HEAD -- <path>
(Git 2.22, Q1 2019) 中,出现在索引和工作树中但不在其中的文件将<tree-ish>
被删除,以使它们<tree-ish>
完全匹配。
But that checkout can respect a git update-index --skip-worktree
(for those directories you want to ignore), as mentioned in "Why do excluded files keep reappearing in my git sparse checkout?".
但是该结帐可以尊重 a git update-index --skip-worktree
(对于您要忽略的那些目录),如“为什么被排除的文件不断重新出现在我的 git 稀疏结帐中?”中所述。
回答by krlmlr
According to Git developer Duy Nguyen who kindly implemented the feature and a compatibility switch, the following works as expected as of Git 1.8.3:
根据 Git 开发人员 Duy Nguyen 的说法,他善意地实现了该功能和兼容性开关,从Git 1.8.3 开始,以下内容按预期工作:
git checkout -- a
(where a
is the directory you want to hard-reset). The original behavior can be accessed via
(a
您要硬重置的目录在哪里)。可以通过以下方式访问原始行为
git checkout --ignore-skip-worktree-bits -- a
回答by Dan Cruz
Try changing
尝试改变
git checkout -- a
to
到
git checkout -- `git ls-files -m -- a`
Since version 1.7.0, Git's ls-files
honors the skip-worktree flag.
从 1.7.0 版本开始,Git尊重 skip-worktree 标志。ls-files
Running your test script (with some minor tweaks changing git commit
... to git commit -q
and git status
to git status --short
) outputs:
运行你的测试脚本(有一些小的调整改变git commit
... togit commit -q
和git status
to git status --short
)输出:
Initialized empty Git repository in /home/user/repo/.git/
After read-tree:
a/a/aa
a/b/ab
b/a/ba
After modifying:
b/a/ba
D a/a/aa
D a/b/ab
M b/a/ba
After checkout:
M b/a/ba
a/a/aa
a/c/ac
a/b/ab
b/a/ba
Running your test script with the proposed checkout
change outputs:
使用建议的checkout
更改输出运行测试脚本:
Initialized empty Git repository in /home/user/repo/.git/
After read-tree:
a/a/aa
a/b/ab
b/a/ba
After modifying:
b/a/ba
D a/a/aa
D a/b/ab
M b/a/ba
After checkout:
M b/a/ba
a/a/aa
a/b/ab
b/a/ba
回答by Ajedi32
For the case of simply discarding changes, the git checkout -- path/
or git checkout HEAD -- path/
commands suggested by other answers work great. However, when you wish to reset a directory to a revision other than HEAD, that solution has a significant problem: it doesn't remove files which were deleted in the target revision.
对于简单地放弃更改的情况,其他答案建议的git checkout -- path/
orgit checkout HEAD -- path/
命令效果很好。但是,当您希望将目录重置为 HEAD 以外的修订版时,该解决方案有一个重大问题:它不会删除在目标修订版中删除的文件。
So instead, I have begun using the following command:
因此,我开始使用以下命令:
This works by finding the diff between the target commit and the index, then applying that diff in reverse to the working directory and index. Basically, this means that it makes the contents of the index match the contents of the revision you specified. The fact that git diff
takes a path argument allows you to limit this effect to a specific file or directory.
这是通过找到目标提交和索引之间的差异,然后将该差异反向应用于工作目录和索引来实现的。基本上,这意味着它使索引的内容与您指定的修订的内容相匹配。git diff
接受路径参数这一事实允许您将此影响限制为特定文件或目录。
Since this command fairly long and I plan on using it frequently, I have set up an alias for it which I named reset-checkout
:
由于这个命令相当长并且我打算经常使用它,我为它设置了一个别名,我命名为reset-checkout
:
git config --global alias.reset-checkout '!f() { git diff --cached "$@" | git apply -R --index; }; f'
You can use it like this:
你可以这样使用它:
git reset-checkout 451a9a4 -- path/to/directory
Or just:
要不就:
git reset-checkout 451a9a4
回答by Abraham Brookes
I'm going to offer a terrible option here, since I have no idea how to do anything with git except add
commit
and push
, here's how I "reverted" a subdirectory:
我将在这里提供一个糟糕的选择,因为我不知道如何使用 git 除了add
commit
and做任何事情push
,这是我“恢复”子目录的方式:
I started a new repository on my local pc, reverted the whole thing to the commit I wanted to copy code from and then copied those files over to my working directory, add
commit
push
et voila. Don't hate the player, hate Mr Torvalds for being smarter than us all.
我在我的本地电脑上启动了一个新的存储库,将整个事情恢复到我想从中复制代码的提交,然后将这些文件复制到我的工作目录,add
commit
push
等等。不要讨厌玩家,讨厌 Torvalds 先生比我们都聪明。
回答by Jonathan Wren
A reset will normally change everything, but you can use git stash
to pick what you want to keep. As you mentioned, stash
doesn't accept a path directly, but it can still be used to keep a specific path with the --keep-index
flag. In your example, you would stash the b directory, then reset everything else.
重置通常会改变一切,但您可以使用它git stash
来选择要保留的内容。正如您所提到的,stash
不直接接受路径,但它仍然可以用于保留带有--keep-index
标志的特定路径。在您的示例中,您将隐藏 b 目录,然后重置其他所有内容。
# How to make files a/* reappear without changing b and without recreating a/c?
git add b #add the directory you want to keep
git stash --keep-index #stash anything that isn't added
git reset #unstage the b directory
git stash drop #clean up the stash (optional)
This gets you to a point where the last part of your script will output this:
这会让您到达脚本的最后一部分将输出以下内容:
After checkout:
# On branch master
# Changes not staged for commit:
#
# modified: b/a/ba
#
no changes added to commit (use "git add" and/or "git commit -a")
a/a/aa
a/b/ab
b/a/ba
I believe this was the target result (b remains modified, a/* files are back, a/c is not recreated).
我相信这是目标结果(b 仍然被修改,a/* 文件回来了,a/c 没有重新创建)。
This approach has the added benefit of being very flexible; you can get as fine-grained as you want adding specific files, but not other ones, in a directory.
这种方法的额外好处是非常灵活;您可以根据需要在目录中添加特定文件而不是其他文件。
回答by Mehul Parmar
If the size of the subdirectory is not particularly huge, AND you wish to stay away from the CLI, here's a quick solution to manuallyreset the sub-directory:
如果子目录的大小不是特别大,并且您希望远离 CLI,这里有一个手动重置子目录的快速解决方案:
- Switch to master branch and copy the sub-directory to be reset.
- Now switch back to your feature branch and replace the sub-directory with the copy you just created in step 1.
- Commit the changes.
- 切换到 master 分支,复制要重置的子目录。
- 现在切换回您的功能分支并用您刚刚在步骤 1 中创建的副本替换子目录。
- 提交更改。
Cheers. You just manually reset a sub-directory in your feature branch to be same as that of master branch !!
干杯。您只需手动将功能分支中的子目录重置为与主分支相同的子目录!
回答by khelkun
Ajedi32's answeris what I was looking for but for some commits I ran into this error:
Ajedi32的答案是我一直在寻找的,但对于某些提交,我遇到了这个错误:
error: cannot apply binary patch to 'path/to/directory' without full index line
error: cannot apply binary patch to 'path/to/directory' without full index line
May be because some files of the directory are binary files. Adding '--binary' option to the git diff command fixed it:
可能是因为目录的一些文件是二进制文件。将 '--binary' 选项添加到 git diff 命令修复了它:
git diff --binary --cached commit -- path/to/directory | git apply -R --index
回答by Cochise Ruhulessin
What about
关于什么
subdir=thesubdir
for fn in $(find $subdir); do
git ls-files --error-unmatch $fn 2>/dev/null >/dev/null;
if [ "$?" = "1" ]; then
continue;
fi
echo "Restoring $fn";
git show HEAD:$fn > $fn;
done