![](/img/trans.png)
[英]What is the difference between “git checkout — .” and “git reset HEAD --hard”?
[英]Is there a difference between git checkout HEAD -- . and git reset --hard HEAD?
我已經查看了這個 stackoverflow 鏈接,但我認為我所要求的之間的細微差別是結帳 cmd 中HEAD
的使用,因為他們的建議似乎不起作用:
git reset --hard HEAD 和 git checkout 之間有區別嗎?
git checkout HEAD -- .
也清理我的集結區。 此外,關於添加到暫存區的已刪除文件的第二個答案似乎是通過git checkout HEAD -- .
回來的git checkout HEAD -- .
有沒有一種情況會得到不同的結果?
是的,除了.
如果您不在存儲庫的頂層。 我不會說以下是唯一的區別; 僅舉一個例子就足以看出它們是不同的。
首先創建一個至少有一次提交的新存儲庫(這里我做了兩次,這是我的習慣):
$ mkdir treset
$ cd treset
$ git init
Initialized empty Git repository in ...
$ echo 'for testing reset vs checkout' > README
$ git add README
$ git commit -m 'initial commit'
[master (root-commit) 058b755] initial commit
1 file changed, 1 insertion(+)
create mode 100644 README
$ echo contents for file a > file-a
$ echo contents for file b > file-b
git commit -m 'add files'
[master f505609] add files
2 files changed, 2 insertions(+)
create mode 100644 file-a
create mode 100644 file-b
此時, HEAD
和索引匹配——都包含來自提交f505609
的內容——並且工作樹也包含與該提交匹配的(正常格式)文件。 現在讓我們添加一個新文件,並將其復制到索引中:
$ echo 'uncommitted file' > foo
$ git add foo
從技術上講, git add foo
在存儲庫中創建了blob 對象a9e2570d6af8c05b57e2cefecaebeedfabc98bf2
,然后將該哈希 ID 放入索引中:
$ git ls-files --stage
100644 e16f62b2e75cf86a6f54adcfddcfd77140f238b9 0 README
100644 881d9334f4593efc7bab0dd536348abf47efed5c 0 file-a
100644 fa438bc26ce6b7a8f574bad9e63b83c912a824b9 0 file-b
100644 a9e2570d6af8c05b57e2cefecaebeedfabc98bf2 0 foo
(此Blob對象的哈希ID是用於文件已知的內容預見,由於foo
,這是其他三個文件也是如此,但他們實際上是承諾,所以這些斑點的對象是永久的。一個為foo
可以GCed ,如果我們從未真正提交它而是從索引中刪除條目。)
git checkout HEAD
如果我們使用git checkout HEAD
,我們會指示 Git 從HEAD
復制到索引中,然后將它們擴展為正常的工作樹文件。 HEAD
包含三個文件( README
、 file-a
和file-b
),因此這樣做並使用它們已有的內容更新三個工作樹文件 - 因此沒有可觀察到的影響。 1
$ git checkout HEAD -- .; ls
file-a file-b foo README
請注意,文件foo
保留在索引(再次運行git ls-files
以查看)和工作樹中。
1除非,也就是說,我們通過可用的任何操作系統級工具檢查文件修改時間或執行的系統調用等內容。 在這種情況下,我們可以判斷 Git 是否真的覆蓋了工作樹文件。 在我的系統,它實際上並沒有,因為索引散列相匹配的HEAD
哈希和stat
中相匹配的索引緩存的數據stat
從工作樹中的文件數據,所以沒有理會。 但原則上Git 將HEAD
復制到索引,然后將索引復制到工作樹,如果需要基於哈希和/或統計數據,Git 實際上會在這里觸及工作樹文件。
git reset --hard
如果我們告訴 Git 重置索引以匹配當前提交,並重置工作樹以匹配索引的更改,則操作是不同的。 這一次,Git 檢查索引並看到文件foo
存在,而它在提交中不存在。 所以 Git從索引中刪除文件foo
,並相應地更新工作樹:
$ git reset --hard HEAD; ls
HEAD is now at f505609 add files
file-a file-b README
文件foo
已從工作樹中消失。
如果我們使用git reset --mixed HEAD
,Git 會從索引中刪除foo
,但不會從工作樹中刪除。 (這種重置的默認操作——還有許多其他類型——是--mixed
。)
git restore
使用新的 Git 2.23+ git restore
命令,我們可以分別控制索引和工作樹。 首先,我們必須將foo
放回索引和工作樹中:
$ echo 'uncommitted file' > foo
$ git add foo
我們現在可以選擇是否將HEAD
復制到索引,以及是否類似地管理工作樹。 它的文檔也更加明確:
如果路徑已被跟蹤但在還原源中不存在,則將刪除該路徑以匹配源。
路徑被“跟蹤”的意思是路徑在索引中。 在這種情況下, foo
現在在索引中(由於git add
)所以它被跟蹤。 如果我們從HEAD
恢復索引, foo
將從索引中刪除,就像git reset --hard
或git reset --mixed
。 所以讓我們嘗試VonC 的命令,但使用.
(當前目錄和所有子目錄) 2作為路徑名,這里:
$ git restore --source HEAD --staged --worktree .
$ ls
file-a file-b README
所以你可以看到這與git reset --hard
具有相同的效果。 與git reset
不同, git restore
只有一項工作——盡管它有兩部分——所以我們不必擔心其他操作模式。
(這就是同時添加git switch
和git restore
原因:它們大多做與git checkout
和git reset
已經可以做的相同的事情,但它們只有一項工作,即使它有幾個部分。相比之下, git checkout
有大約三到大約七種不同的工作,這取決於您的計數方式,而git reset
有大約三到大約五種。3 )
2這個特定的存儲庫只有一個頂級目錄,所以我們不必擔心您已經在工作樹中創建了一個cd subdir
子目錄。 但是,如果你有, .
將意味着將其應用於subdir/*
files ,以便 checkout 和 reset 會更加不同。
3對於git checkout
,請考慮:
git checkout-index
)git checkout -m
帶有文件名)git checkout -m
但帶有分支名稱) 雖然這只是五個,但我們可以git checkout --ours
和git checkout --theirs
,有些人可能希望將它們與通常的“從索引中提取”風格分開計算。 當您添加創建分支( git checkout -b
)並強制重置分支( git checkout -B
)時,我們可以獲得更多。 唉, git switch
也有創建和強制重置選項!
當然,有些人可能會像git checkout
那樣將其中的一部分或全部合並到一個操作中。 這就是為什么我說“取決於你如何計數”。
對於git reset
,請考慮:
HEAD
即可重置索引和/或工作樹HEAD
重置索引和/或工作樹HEAD
)HEAD
)git reset -p
有選擇地修補(無法移動HEAD
) 所有這些都集中在一個命令git reset
。
為避免混淆,您應該使用新的實驗命令git restore
(Gti 2.23+,2019 年 8 月)。
正如我在“什么是git restore
命令? git restore
和git reset
之間有什么不同? ”中所解釋的那樣,您可以使用git restore
指定要git restore
。
這比在git checkout
依賴 HEAD 或沒有 HEAD 更明確。
要使用git restore
進行reset --hard
:
git restore --source=HEAD --staged --worktree hello.c
或者更實用但可讀性較差的簡短形式:
git restore -s@ -SW hello.c
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.