2013-03-14 64 views
107

UPDATE: This will work more intuitively as of Git 1.8.3, see my own answer .如何git重置 - 一個子目錄?

想象一下下面的用例:我想擺脫我的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 
+3

'git stash && git stash drop'怎麼樣? – CharlesB 2013-03-14 08:47:28

+0

'git checkout -/path/to/subdir /'怎麼辦? – iberbeu 2013-03-14 08:49:22

+2

@CharlesB:'git stash'不接受路徑參數... – krlmlr 2013-03-14 09:13:31

回答

68

據Git開發者Duy Nguyen親切實施the feature and a compatibility switch,以下按預期工作as of Git 1.8.3

git checkout -- a 

(其中a是您要硬復位的目錄)。原來的行爲可以通過

git checkout --ignore-skip-worktree-bits -- a 
+4

感謝您跟隨Git開發團隊的努力,導致了Git中的這一變化。 – 2014-05-07 12:43:28

+11

注意,在這種情況下,「a」意味着你想要恢復的目錄,所以如果你在你想恢復的目錄中,那麼該命令應該是'git checkout - .',其中'.'表示當前目錄。 – 2014-06-05 14:52:45

+1

我的一個意見是,你必須首先用 'git reset - a' (其中a是你想要重置的目錄)取代文件夾 – Boyan 2016-01-21 15:02:25

83

注意事項(如commentedDan Fabulich)認爲:

  • git checkout -- <path>沒有做硬重置:它取代上演內容的工作樹內容。
  • git checkout HEAD -- <path>做硬重置爲路徑,與版本更換兩個索引和工作樹從HEAD提交。

由於answeredAjedi32,雙方結賬形成不刪除這是在目標修訂刪除的文件。
如果在HEAD中不存在額外的工作樹中的文件,git checkout HEAD -- <path>將不會刪除它們。

但是該結賬可以遵循git update-index --skip-worktree(對於那些您想忽略的目錄),如「Why do excluded files keep reappearing in my git sparse checkout?」中所述。

+0

請澄清。在'git checkout HEAD - .'之後,稀疏結帳排除的文件重新出現。 git update-index --skip-worktree應該做什麼? – krlmlr 2013-03-14 09:08:22

+0

@krlmlr skip-worktree或assume-unchanged是嘗試在索引中爲git創建條目「隱形」的兩種方法:http://fallengamer.livejournal.com/93321.html,http://stackoverflow.com/q/13630849/6309和http://stackoverflow.com/a/6139470/6309 – VonC 2013-03-14 09:11:51

+0

這是如何幫助我解決問題的? – krlmlr 2013-03-14 09:14:51

4

復位通常會改變一切,但你可以使用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) 

這讓你到一個地步,你的腳本的最後一部分將輸出這樣的:

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沒有被重新創建)。

這種方法具有額外的好處:非常靈活;你可以得到儘可能細緻的,你想添加特定的文件,但不是其他的,在目錄中。

+0

這很好,但我不得不'git add'除了'a',對吧?聽起來很難在實踐中。 – krlmlr 2013-03-16 22:42:01

+1

@krlmlr並非如此。你可以'git add .'然後'git reset a'來添加除'a'之外的所有內容。 – 2013-03-17 01:32:40

+1

@krlmlr另外,值得注意的是'git add'不會添加已刪除的文件。所以,如果你只是恢復刪除的文件,'git add .'會添加所有修改過的文件,但不會刪除已刪除的文件。 – 2013-03-17 01:34:52

22

嘗試改變

git checkout -- a 

git checkout -- `git ls-files -m -- a` 

自1.7.0版本,Git的ls-fileshonors the skip-worktree flag

運行測試腳本(有一些小的調整,改變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 

與建議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 
+0

聽起來不錯。但是,首先應該不要'git checkout'尊重「skip-worktree」位? – krlmlr 2013-03-16 22:47:35

+0

快速回顧['checkout.c'](https://github.com/git/git/glob/master/builtin/checkout.c)和['tree.c'](https:// github。 com/git/git/blob/master/tree.c)不會顯示使用skip-worktree標誌。 – 2013-03-16 23:00:56

+0

這很簡單,在實踐中很有用,即使我必須爲此命令設置一個bash別名。 Duy Nguyen將[回覆](http://permalink.gmane.org/gmane.comp.version-control.git/218904)添加到我的Git郵件列表郵件中,讓我們看看是否會彈出一個更友好的替代方案不久。 – krlmlr 2013-03-23 15:03:19

12

訪問了簡單地丟棄的變化的情況下,git checkout -- path/git checkout HEAD -- path/命令建議的其他答案工作的偉大。但是,如果希望將目錄重置爲HEAD以外的版本,該解決方案有一個重大問題:它不會刪除目標修訂中已刪除的文件。

所以不是,我使用下面的命令開始:

git diff --cachedcommit--subdir| git apply -R --index

這是通過發現目標之間的差異提交和索引,然後應用該DIFF反向工作目錄和索引。基本上,這意味着它使索引的內容與您指定的修訂版的內容相匹配。 git diff採用路徑參數的事實允許您將此效果限制爲特定的文件或目錄。

由於這個命令比較長,我打算頻繁使用它,我已經爲它設置一個別名,我命名爲reset-checkout

git config --global alias.reset-checkout '!f() { git diff --cached "[email protected]" | git apply -R --index; }; f' 

您可以使用它像這樣:

git reset-checkout 451a9a4 -- path/to/directory 

或者只是:

git reset-checkout 451a9a4 
+0

我昨天看到了你的評論,並且今天進行了實驗。你的別名很有幫助。 +1 – VonC 2015-01-16 17:51:06

1

Ajedi32answer是我一直在尋找,但對於一些提交我就遇到了這個錯誤:

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 
2

如果子目錄的大小並沒有特別巨大的,你想留在CLI走,這裏有一個快速的解決方案,以手動重設子目錄:

  1. 切換到主分支並複製要重置的子目錄。
  2. 現在切換回您的功能分支,並用您剛在步驟1中創建的副本替換子目錄。
  3. 提交更改。

乾杯。您只需手動重置您的功能分支中的子目錄,使其與主分支相同!

+0

我沒有看到這個答案的任何投票,但有時這是成功的最簡單的保證途徑。 – 2017-10-16 12:40:41

相關問題