如何git重置 – 哈哈一个子目录?
更新 :这将工作更直观的Git 1.8.3,看到我自己的答案 。
想象下面的用例:我想摆脱我的Git工作树的特定子目录中的所有更改,保持所有其他子目录不变。
- 
我可以做 git checkout .,但git结帐。 添加稀疏结帐排除的目录
- 
有 git reset --hard,但不会让我做一个子目录:> git reset --hard . fatal: Cannot do hard reset with paths.再说一遍: 为什么git不能通过path进行硬/软重置? 
- 
我可以使用 git diff subdir | patch -p1 -R来修改当前状态git diff subdir | patch -p1 -R,但这是一个很奇怪的做法。
什么是这个操作适当的Git命令?
 下面的脚本说明了这个问题。 在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 ab; do for g in abc; 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 -- <path>不会执行硬重置:它使用暂存的内容replace正在工作的树内容。
-   git checkout HEAD -- <path>对git checkout HEAD -- <path>进行硬重置,用HEAD提交的版本replace索引和工作树。
 正如Ajedi32所回答的,两个结帐表格都不会删除目标版本中已删除的文件 。 
 如果在HEAD中不存在额外的文件, git checkout HEAD -- <path>将不会删除它们。 
 但是,结帐可以尊重一个git update-index --skip-worktree (对于那些你想忽略的目录),如“ 为什么排除的文件不断重新出现在我的git稀疏结账中? ”中提到的那样。 
根据Git开发者Duy Nguyen的说法,他善意地实现了这个特性和一个兼容性开关 ,下面的Git 1.8.3就像预期的那样工作 :
 git checkout -- a 
  (其中a是您要硬重置的目录)。 原来的行为可以通过访问 
 git checkout --ignore-skip-worktree-bits -- a 
尝试改变
 git checkout -- a 
至
 git checkout -- `git ls-files -m -- a` 
 从1.7.0版本开始,Git的ls-files 将会使用skip-worktree标志 。 
 运行你的testing脚本(通过一些小的调整,改变git commit …到git commit -q和git status到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 
 使用build议的checkout更改输出运行testing脚本: 
 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 
 对于简单地放弃更改的情况,其他答案所build议的git checkout -- path/或git checkout HEAD -- path/ commands的效果很好。 但是,如果希望将目录重置为除HEAD之外的修订版本,则该解决scheme存在一个严重的问题:它不会删除目标修订中已删除的文件。 
相反,我已经开始使用以下命令:
git diff--cachedcommit--subdir|git apply-R --index
 这是通过find目标提交和索引之间的差异,然后将该差异反向应用于工作目录和索引。 基本上,这意味着它使索引的内容与您指定的修订版本的内容匹配。  git diff接受一个path参数的事实允许你限制这个效果到一个特定的文件或目录。 
 由于这个命令相当长,我打算经常使用它,我已经设置了一个别名,我命名为reset-checkout : 
 git config --global alias.reset-checkout '!f() { git diff --cached "$@" | git apply -R --index; }; f' 
你可以像这样使用它:
 git reset-checkout 451a9a4 -- path/to/directory 
要不就:
 git reset-checkout 451a9a4 
 重置通常会改变一切,但是你可以使用git stash来select你想要保留的东西。 正如你所提到的, stash不直接接受path,但仍然可以使用--keep-index标志保持特定的path。 在你的例子中,你会隐藏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) 
这让你到脚本的最后部分输出这个点:
 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不重新创build)。
这种方法具有非常灵活的附加好处。 你可以得到如同你想要添加特定的文件,但不是其他目录中的细粒度。
如果子目录的大小不是特别大,并且您希望远离CLI,下面是手动重置子目录的快速解决scheme:
- 切换到主分支并复制要重置的子目录。
- 现在切换回您的function分支,并用您在第1步中创build的副本replace子目录。
- 提交更改。
干杯。 您只需手动重置您的function分支中的子目录,以便与主分支相同!
Ajedi32的答案是我正在寻找,但对于一些提交我遇到了这个错误:
 error: cannot apply binary patch to 'path/to/directory' without full index line 
可能是因为该目录的一些文件是二进制文件。 将“–binary”选项添加到git diff命令修复了这个问题:
 git diff --binary --cached commit -- path/to/directory | git apply -R --index