如何git reset --hard一个子目录?

krl*_*mlr 178 git reset sparse-checkout git-reset

更新:从Git 1.8.3开始,这将更直观地工作,请参阅我自己的答案.

想象一下以下用例:我想摆脱我的Git工作树的特定子目录中的所有更改,保留所有其他子目录.

什么是适合此操作的Git命令?

下面的脚本说明了这个问题.在How to make files注释下面插入适当的命令- 当前命令将恢复a/c/ac应该由稀疏检出排除的文件.请注意,我希望明确恢复a/aa/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
Run Code Online (Sandbox Code Playgroud)

Von*_*onC 136

注意(由Dan Fabulich 评论):

  • git restore 不进行硬重置:它用分段内容替换工作树内容.
  • HEAD对路径执行硬重置,使用reset --hard提交中的版本替换索引和工作树.

作为回答Ajedi32,双方结账的形式不删除这是在目标修订删除的文件.
如果工作树中有额外的文件在HEAD中不存在,git checkout -- <path>则不会删除它们.

但是,结帐可以尊重git checkout HEAD -- <path>(对于那些你想忽略的目录),如" 为什么排除的文件会在我的git sparse checkout中重新出现? "中所述.

  • `git checkout HEAD -- &lt;path&gt;` 的行为似乎与硬重置不同。硬重置会删除路径中不再属于给定版本的文件。根据我的经验(git 1.8.3.3),`git checkout HEAD -- &lt;path&gt;` 不会。 (2认同)

krl*_*mlr 113

根据Git开发人员Duy Nguyen的说法,他很好地实现了该功能和兼容性开关,以下工作与Git 1.8.3一样正常:

git checkout -- a
Run Code Online (Sandbox Code Playgroud)

(a您想要硬重置的目录在哪里).可以通过访问原始行为

git checkout --ignore-skip-worktree-bits -- a
Run Code Online (Sandbox Code Playgroud)

  • 请注意,在这种情况下,"a"表示要还原的目录,因此如果您在要恢复的目录中,则命令应为`git checkout - .`,其中`.`表示当前目录. (12认同)
  • 我方面的一个评论是你必须首先使用`git reset - a`取消暂存文件夹(其中a是你要重置的目录) (5认同)
  • 感谢您跟进Git开发团队的努力,导致Git的这一变化. (4认同)
  • 如果您向该目录添加了任何新文件,请先执行“rm -rf a”。 (2认同)

Dan*_*ruz 29

尝试改变

git checkout -- a
Run Code Online (Sandbox Code Playgroud)

git checkout -- `git ls-files -m -- a`
Run Code Online (Sandbox Code Playgroud)

从版本1.7.0开始,Git就会尊重skip-worktree标志.ls-files

运行测试脚本(有一些小的调整变化git commit......到git commit -qgit statusgit 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
Run Code Online (Sandbox Code Playgroud)

使用建议的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
Run Code Online (Sandbox Code Playgroud)


Aje*_*i32 18

对于简单地丢弃更改的情况,其他答案建议的git checkout -- path/git checkout HEAD -- path/命令工作得很好.但是,当您希望将目录重置为HEAD以外的修订时,该解决方案存在严重问题:它不会删除目标修订中删除的文件.

所以相反,我已经开始使用以下命令:

git diff --cached commit -- subdir | git apply -R --index

这通过查找目标提交和索引之间的差异,然后将该差异反向应用于工作目录和索引来工作.基本上,这意味着它使索引的内容与您指定的修订的内容相匹配.git diff采用path参数的事实允许您将此效果限制为特定文件或目录.

由于这个命令相当长,而且我打算经常使用它,我已经为它设置了一个别名,我命名为reset-checkout:

git config --global alias.reset-checkout '!f() { git diff --cached "$@" | git apply -R --index; }; f'
Run Code Online (Sandbox Code Playgroud)

你可以像这样使用它:

git reset-checkout 451a9a4 -- path/to/directory
Run Code Online (Sandbox Code Playgroud)

要不就:

git reset-checkout 451a9a4
Run Code Online (Sandbox Code Playgroud)


Hua*_*uan 8

所有 Git 版本

对我来说,我将使用结账:

首先,我删除要重置的文件夹。

然后我将简单地使用结帐:

git checkout <branch> <path>
Run Code Online (Sandbox Code Playgroud)

该文件夹将被正确重置为我想要的分支。


Abr*_*kes 6

我将在这里提供一个糟糕的选择,因为我不知道如何使用 git 除了add commitand做任何事情push,这是我“恢复”子目录的方式:

我在我的本地电脑上启动了一个新的存储库,将整个事情恢复到我想要从中复制代码的提交,然后将这些文件复制到我的工作目录,add commit push等等。不要讨厌玩家,讨厌 Torvalds 先生比我们都聪明。


Meh*_*mar 5

如果子目录的大小不是特别大,并且您希望远离 CLI,这里有一个手动重置子目录的快速解决方案:

  1. 切换到master分支,复制要重置的子目录。
  2. 现在切换回您的功能分支,并将子目录替换为您刚刚在步骤 1 中创建的副本。
  3. 提交更改。

干杯。您只需手动将功能分支中的子目录重置为与主分支相同!


归档时间:

查看次数:

121007 次

最近记录:

6 年 前