How to git reset --hard a subdirectory?
UPDATE: This will work more intuitively as of Git 1.8.3, see my own answer.
想象一下以下用例:我想摆脱所有的变化在特定的子目录树中的所有其他工作,离开完整的子目录。
-
我可以
git checkout . 的Git,但结帐。用稀疏目录排除增加。 -
有一个
git reset --hard ,但它不会让我做任何子目录:1
2> git reset --hard .
fatal: Cannot do hard reset with paths.再使用:为什么不能用硬/软resets的路径?
-
我可以使用的当前状态的反
git diff subdir | patch -p1 -R 补丁,但这是一个相当奇怪的方式这样做。
什么是git命令的正确操作是这样的吗?
下面示出了剧本的问题。插入在适当的命令,下面的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | #!/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 |
注意(如Dan Fabulich所评论的)以下内容:
git checkout -- 不做硬重置:它将工作树内容替换为阶段性内容。git checkout HEAD -- 对路径进行硬重置,用HEAD 提交的版本替换索引和工作树。
正如Ajedi32所回答的,两个签出表单都不会删除目标修订版中删除的文件。如果在工作树中有额外的文件,而这些文件在head中不存在,那么
注:对于
但是这个签出可以尊重
根据Git开发人员Duy Nguyen的说法,他很好地实现了这个特性和一个兼容性开关,从Git 1.8.3开始,以下工作是可以预期的:
1 | git checkout -- a |
(其中
1 | git checkout --ignore-skip-worktree-bits -- a |
尝试改变
1 | git checkout -- a |
到
1 | git checkout -- `git ls-files -m -- a` |
。
从1.7.0版开始,Git的
运行您的测试脚本(通过一些小的调整来更改
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | 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 |
使用建议的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | 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 |
。
对于简单放弃更改的情况,其他答案建议的
因此,我开始使用以下命令:
git diff --cached commit -- subdir | git apply -R --index
号
这可以通过找到目标提交和索引之间的差异,然后将该差异反向应用到工作目录和索引。基本上,这意味着它使索引的内容与您指定的修订内容相匹配。
由于这个命令相当长,我计划经常使用它,所以我为它设置了一个别名,我将其命名为
1 | git config --global alias.reset-checkout '!f() { git diff --cached"$@" | git apply -R --index; }; f' |
。
您可以这样使用它:
1 | git reset-checkout 451a9a4 -- path/to/directory |
或者只是:
1 | git reset-checkout 451a9a4 |
。
我将在这里提供一个糟糕的选择,因为除了
我在我的本地PC上启动了一个新的存储库,将整个过程恢复到我想从中复制代码的承诺,然后将这些文件复制到我的工作目录
重置通常会改变一切,但您可以使用
1 2 3 4 5 | # 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) |
这将使您到达脚本的最后一部分将输出此内容的位置:
1 2 3 4 5 6 7 8 9 10 | 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 |
号
我认为这是目标结果(b仍然被修改,a/*文件返回,a/c不被重新创建)。
这种方法还有一个额外的好处,那就是非常灵活;您可以根据需要在目录中添加特定的文件,而不是添加其他文件,从而获得细粒度。
如果子目录的大小不是特别大,并且您希望远离CLI,下面是手动重置子目录的快速解决方案:
干杯。您只需手动将功能分支中的子目录重置为与主分支相同的目录!!
Ajedi32的答案是我正在寻找的,但是对于一些提交,我遇到了这个错误:
埃多克斯1〔22〕
可能是因为目录中的某些文件是二进制文件。将"--binary"选项添加到git diff命令可以修复它:
1 | git diff --binary --cached commit -- path/to/directory | git apply -R --index |
号