如何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
--cached
commit
--
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