Tại sao có hai cách để bỏ tập tin trong Git?


1169

Đôi khi git đề nghị git rm --cachedđể unstst một tập tin, đôi khi git reset HEAD file. Khi nào nên sử dụng?

BIÊN TẬP:

D:\code\gt2>git init
Initialized empty Git repository in D:/code/gt2/.git/
D:\code\gt2>touch a

D:\code\gt2>git status
# On branch master
#
# Initial commit
#
# Untracked files:
#   (use "git add <file>..." to include in what will be committed)
#
#       a
nothing added to commit but untracked files present (use "git add" to track)

D:\code\gt2>git add a

D:\code\gt2>git status
# On branch master
#
# Initial commit
#
# Changes to be committed:
#   (use "git rm --cached <file>..." to unstage)
#
#       new file:   a
#
D:\code\gt2>git commit -m a
[master (root-commit) c271e05] a
 0 files changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 a

D:\code\gt2>touch b

D:\code\gt2>git status
# On branch master
# Untracked files:
#   (use "git add <file>..." to include in what will be committed)
#
#       b
nothing added to commit but untracked files present (use "git add" to track)

D:\code\gt2>git add b

D:\code\gt2>git status
# On branch master
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#       new file:   b
#

20
Tại sao? Tôi muốn nói rằng bởi vì giao diện dòng lệnh của git đã phát triển một cách hữu cơ và chưa bao giờ chịu sự tái cấu trúc lớn để làm cho mọi thứ nhất quán. (Nếu bạn không đồng ý, lưu ý như thế nào git rmcó thể cả giai đoạn một xóa và cũng unstage một bổ sung )
La Mã Starkov

3
@romkyns: Tôi đồng ý rằng giao diện của Git có một số điểm kỳ lạ bởi vì nó phát triển hữu cơ, nhưng việc loại bỏ chắc chắn là một chức năng nghịch đảo của một bổ sung, vì vậy nó có hợp lý rmđể hoàn tác addkhông? Bạn nghĩ rmnên cư xử thế nào?
Zaz

6
Câu trả lời thực tế duy nhất cho câu hỏi của bạn là ngay sau khi git initkhông có HEADđể thiết lập lại.
Miles Rout

Tài liệu tốt nhất cho việc này: help.github.com/articles/changing-a-remote-s-url
ScottyBlades

4
@Zaz, tôi sẽ đưa ra ý kiến ​​của mình. rmngụ ý xóa trong một bối cảnh unix. Nó không đối nghịch với việc thêm vào chỉ mục. Một chức năng để loại bỏ các tập tin không nên bị quá tải với các chức năng để thay đổi trạng thái dàn. Nếu có các chi tiết triển khai làm cho những thứ đó thuận tiện để kết hợp, điều đó chỉ đơn giản chỉ ra việc thiếu một lớp trừu tượng chu đáo trong git, điều này sẽ làm cho tính khả dụng rõ ràng.
Joshua Goldberg

Câu trả lời:


1893

git rm --cached <filePath> không bỏ qua một tập tin, nó thực sự sẽ loại bỏ (các) tập tin khỏi repo (giả sử nó đã được cam kết trước đó) nhưng để lại tập tin trong cây làm việc của bạn (để lại cho bạn một tập tin không bị theo dõi).

git reset -- <filePath>sẽ bỏ qua mọi thay đổi theo giai đoạn cho (các) tệp đã cho.

Điều đó nói rằng, nếu bạn đã sử dụng git rm --cachedtrên một tập tin mới được dàn dựng, về cơ bản nó sẽ trông giống như bạn vừa chưa thực hiện nó vì nó chưa bao giờ được cam kết trước đó.

Cập nhật git 2.24
Trong phiên bản git mới hơn này, bạn có thể sử dụng git restore --stagedthay vì git reset. Xem tài liệu git .


71
Tôi có thể nói git rm --cachedbỏ tập tin nhưng không xóa nó khỏi thư mục làm việc.
Pierre de LESPINAY

4
Để loại bỏ một tập tin được dàn dựng để bổ sung để nó không còn được dàn dựng chắc chắn có thể được gọi là "unstaging một tập tin được dàn dựng để bổ sung", phải không? Kết quả cuối cùng không phải là xóa theo giai đoạn , đó là điều chắc chắn, do đó tôi nghĩ rằng sự hiểu lầm là hoàn toàn dễ hiểu.
Roman Starkov

4
Vì vậy, thông thường, người ta sẽ sử dụng git rm --cached <filePath>để xóa một số tệp khỏi repo sau khi nhận ra rằng nó không bao giờ có trong repo: vì vậy rất có thể chạy lệnh này và sau đó thêm các tệp có liên quan vào gitignore. Tôi có đúng không?
Adrien Be

13
Với nhiều phiếu bầu cho cả câu hỏi và câu trả lời, tôi sẽ nói rằng rõ ràng chúng ta muốn có một unstagelệnh git.
milosmns

4
"trạng thái git" khuyên ngay bây giờ: sử dụng "git restore --staged <file> ..." để
unstage

334

git rm --cachedđược sử dụng để xóa một tập tin khỏi chỉ mục. Trong trường hợp tệp đã có trong repo, git rm --cachedsẽ xóa tệp khỏi chỉ mục, để nó trong thư mục làm việc và một cam kết cũng sẽ xóa nó khỏi repo. Về cơ bản, sau khi cam kết, bạn sẽ đảo ngược tệp và giữ một bản sao cục bộ.

git reset HEAD file(mà theo mặc định là sử dụng --mixedcờ) thì khác ở chỗ trong trường hợp tệp đã có trong repo, nó thay thế phiên bản chỉ mục của tệp bằng một từ repo (HEAD), thực sự bỏ qua các sửa đổi đối với nó.

Trong trường hợp tệp không được đảo ngược, nó sẽ hủy bỏ toàn bộ tệp vì tệp không có trong ĐẦU. Trong khía cạnh này git reset HEAD filegit rm --cachedgiống nhau, nhưng chúng không giống nhau (như được giải thích trong trường hợp các tệp đã có trong repo)

Đối với câu hỏi Why are there 2 ways to unstage a file in git?- không bao giờ thực sự chỉ có một cách để làm bất cứ điều gì trong git. đó là vẻ đẹp của nó :)


7
Cả câu trả lời được chấp nhận và câu trả lời này đều tuyệt vời, và giải thích lý do tại sao bạn sẽ sử dụng cái này so với cái kia. Nhưng họ không trả lời trực tiếp câu hỏi ngầm về lý do tại sao git đề xuất hai phương pháp khác nhau. Trong trường hợp đầu tiên trong ví dụ của OP, một git init vừa được thực hiện. Trong trường hợp đó, git gợi ý "git rm --cached" bởi vì tại thời điểm đó không có cam kết nào trong kho lưu trữ và vì vậy HEAD không hợp lệ. "git reset HEAD - a" tạo ra: "gây tử vong: Không thể giải quyết 'ĐẦU' là một tham chiếu hợp lệ."
sootsnoot

5
với 'git checkout', bạn sẽ không mất tất cả các thay đổi mà bạn đã thực hiện đối với tệp chứ? Điều đó không giống với việc tạo ra một tệp, trừ khi tôi hiểu nhầm.
John Deighan

there is never really only one way to do anything in git. that is the beauty of it- Hmm tại sao ? nó luôn luôn tuyệt vời, khi chỉ có một cách rõ ràng. điều này giúp tiết kiệm rất nhiều thời gian và trí nhớ của chúng ta trong não))
Oto Shavadze

128

Rất đơn giản:

  • git rm --cached <file> làm cho git dừng theo dõi tập tin hoàn toàn (để nó trong hệ thống tập tin, không giống như git rm*)
  • git reset HEAD <file> bỏ qua bất kỳ sửa đổi nào được thực hiện đối với tệp kể từ lần xác nhận cuối cùng (nhưng không hoàn nguyên chúng trong hệ thống tệp, trái với những gì tên lệnh có thể đề xuất **). Các tập tin vẫn còn dưới sự kiểm soát sửa đổi.

Nếu trước đó tệp không nằm trong kiểm soát sửa đổi (nghĩa là bạn không tạo ra tệp mà bạn mới chỉnh sửa git addlần đầu tiên), thì hai lệnh có cùng tác dụng, do đó xuất hiện hai cách này là "hai cách thực hiện một việc gì đó ".

* Hãy ghi nhớ cảnh báo @DrewT đề cập đến câu trả lời của anh ấy, liên quan đến git rm --cachedmột tập tin đã được cam kết trước đó vào kho lưu trữ. Trong bối cảnh của câu hỏi này, về một tệp vừa được thêm và chưa được cam kết, không có gì phải lo lắng.

** Tôi đã rất sợ trong một thời gian dài lúng túng khi sử dụng lệnh git reset vì tên của nó - và ngày nay tôi vẫn thường tìm kiếm cú pháp để đảm bảo rằng tôi không làm hỏng. ( cập nhật : Cuối cùng tôi đã dành thời gian để tóm tắt việc sử dụng git resettrong một trang tldr , vì vậy bây giờ tôi có một mô hình tinh thần tốt hơn về cách thức hoạt động của nó và tham khảo nhanh khi tôi quên một số chi tiết.)


Đó làgit rm <file> --cached
neonmate 10/07/2015

8
Tôi thực sự không nghĩ rằng bản chỉnh sửa ngày 4 tháng 8 năm 2015 cho câu trả lời này là một sự cải tiến tổng thể. Nó có thể đã sửa lỗi chính xác về kỹ thuật (tôi không cảm thấy đủ điều kiện để đánh giá điều đó), nhưng tôi e rằng nó làm cho giọng điệu của câu trả lời khó tiếp cận hơn nhiều, bằng cách giới thiệu ngôn ngữ như "không bắt buộc phải bắt đầu theo dõi một tệp hiện không bị theo dõi ", Và sử dụng biệt ngữ như" chỉ mục "và" ĐẦU ", chính xác là loại công cụ khiến người mới bắt đầu sợ hãi. Nếu ai đó có thể, vui lòng chỉnh sửa để khôi phục ngôn ngữ thân thiện với người mới hơn.
waldyrious

5
Đồng ý với @waldyrious. Câu trả lời ban đầu có thể không được đưa ra khỏi sách giáo khoa git, nhưng nó đã trả lời câu hỏi ở mức độ kỹ thuật đầy đủ. Các chi tiết kỹ thuật nên được làm rõ trong các bình luận, không phải là một chỉnh sửa làm lu mờ ý định ban đầu.
Simon Robb

Tôi đã hoàn nguyên bản chỉnh sửa. Tôi tin rằng đã có sự xác nhận đầy đủ của cộng đồng (trong các bình luận trước đó và các phiếu bầu cho họ) rằng việc chỉnh sửa là bất lợi cho sự rõ ràng của câu trả lời.
Waldyrious

Lưu ý @DrewT cảnh báo rằng nếu sử dụng rm --cachedvà đẩy, bất kỳ ai kéo cùng một nhánh sẽ có (các) tệp thực sự bị xóa khỏi cây làm việc của họ.
Tom Hale

53

Chủ đề này hơi cũ, nhưng tôi vẫn muốn thêm một chút trình diễn vì nó vẫn không phải là một vấn đề trực quan:

me$ git status
# On branch master
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#   new file:   to-be-added
#   modified:   to-be-modified
#   deleted:    to-be-removed
#

me$ git reset -q HEAD to-be-added

    # ok

me$ git reset -q HEAD to-be-modified

    # ok

me$ git reset -q HEAD to-be-removed

    # ok

# or alternatively:

me$ git reset -q HEAD to-be-added to-be-removed to-be-modified

    # ok

me$ git status
# On branch master
# Changes not staged for commit:
#   (use "git add/rm <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#   modified:   to-be-modified
#   deleted:    to-be-removed
#
# Untracked files:
#   (use "git add <file>..." to include in what will be committed)
#
#   to-be-added
no changes added to commit (use "git add" and/or "git commit -a")

git reset HEAD(không có -q) đưa ra cảnh báo về tệp đã sửa đổi và mã thoát của nó là 1 sẽ được coi là lỗi trong tập lệnh.

Chỉnh sửa: git checkout HEAD to-be-modified to-be-removedcũng hoạt động để không tạo ảnh, nhưng loại bỏ hoàn toàn thay đổi khỏi không gian làm việc

Cập nhật git 2.23.0: Thỉnh thoảng, các lệnh thay đổi. Bây giờ, git statusnói:

  (use "git restore --staged <file>..." to unstage)

... hoạt động cho cả ba loại thay đổi


Cảm ơn, không hoàn toàn rõ ràng từ hai câu trả lời đầu tiên (có lẽ chỉ là sự thiếu hiểu biết của tôi về thuật ngữ) mà git reset để lại các sửa đổi trong tệp cục bộ (trái ngược với kiểm tra git sẽ hoàn nguyên chúng).
súpdog

Bạn nên đưa ra cảnh báo khi bắt đầu về phiên bản, bởi vì phiên bản cũ sẽ xóa các tệp trong các phiên bản mới
Luis Mauricio

@LuisMauricio lệnh nào xóa tập tin?
Daniel Alder

@DanielAlder sry, tôi chỉ kiểm tra lại, nó không xóa, lỗi của tôi.
Luis Mauricio

36

nếu bạn vô tình dàn dựng các tệp mà bạn không muốn cam kết và muốn chắc chắn rằng bạn giữ các thay đổi, bạn cũng có thể sử dụng:

git stash
git stash pop

điều này thực hiện thiết lập lại thành CHÍNH và áp dụng lại các thay đổi của bạn, cho phép bạn tạo lại các tệp riêng lẻ cho cam kết. điều này cũng hữu ích nếu bạn quên tạo một nhánh tính năng cho các yêu cầu kéo ( git stash ; git checkout -b <feature> ; git stash pop).


3
Đây là một giải pháp sạch sẽ và ít đáng lo ngại hơn nhiều so với việc gõ "git rm"
Subimage 18/07/17

1
git stashcó các lợi ích liên quan khác, bởi vì nó tạo ra các mục trong reflog mà sau đó sẽ có sẵn trong tương lai. khi nghi ngờ, hãy tiếp tục và thực hiện git stash(ví dụ git stash save -u "WIP notes to self"('-u' là bao gồm bất kỳ tệp mới / chưa được mã hóa nào trong cam kết stash) ... sau đó thử git reflog show stashxem danh sách các cam kết stash và sha của chúng. bí danh nhưalias grs="git reflog show stash"
cweekly

15

Hai lệnh này có một số khác biệt tinh tế nếu tệp đang đề cập đã có trong repo và dưới sự kiểm soát phiên bản (đã cam kết trước đó, v.v.):

  • git reset HEAD <file> bỏ tập tin trong cam kết hiện tại.
  • git rm --cached <file>cũng sẽ bỏ tập tin cho các cam kết trong tương lai. Nó không được tạo ra cho đến khi nó được thêm lại với git add <file>.

Và có một sự khác biệt quan trọng hơn:

  • Sau khi chạy git rm --cached <file>và đẩy chi nhánh của bạn đến điều khiển từ xa, bất kỳ ai kéo chi nhánh của bạn từ xa sẽ nhận được tệp HOẠT ĐỘNG bị xóa khỏi thư mục của họ, mặc dù trong bộ làm việc cục bộ của bạn, tệp sẽ không bị khóa (tức là không bị xóa khỏi thư mục).

Sự khác biệt cuối cùng này rất quan trọng đối với các dự án bao gồm tệp cấu hình trong đó mỗi nhà phát triển trong nhóm có một cấu hình khác nhau (nghĩa là cài đặt url, ip hoặc cổng cơ sở khác nhau) vì vậy nếu bạn đang sử dụng git rm --cached <file> bất kỳ ai kéo chi nhánh của mình sẽ phải tự thủ công lại. tạo cấu hình hoặc bạn có thể gửi chúng cho bạn và họ có thể chỉnh sửa lại về cài đặt ip của họ (v.v.), vì việc xóa chỉ ảnh hưởng đến mọi người kéo chi nhánh của bạn từ xa.


10

Giả sử bạn stagecó toàn bộ thư mục thông qua git add <folder>, nhưng bạn muốn loại trừ một tệp khỏi danh sách theo giai đoạn (tức là danh sách tạo khi chạy git status) và giữ các sửa đổi trong tệp bị loại trừ (bạn đang làm việc trên một cái gì đó và nó chưa sẵn sàng để cam kết, nhưng bạn không muốn mất việc ...). Bạn chỉ có thể sử dụng:

git reset <file>

Khi bạn chạy git status, bạn sẽ thấy bất kỳ tệp nào bạn resetđang có unstagedvà phần còn lại của các tệp bạn addedvẫn còn trong stageddanh sách.


10

1.

D:\code\gt2>git status
# On branch master
#
# Initial commit
#
# Changes to be committed:
#   (use "git rm --cached <file>..." to unstage)
#
#       new file:   a

(sử dụng "git rm --cached ..." để bỏ qua)

  • git là một hệ thống con trỏ

  • bạn chưa có một cam kết nào để thay đổi con trỏ của bạn thành

  • cách duy nhất để 'lấy các tệp ra khỏi nhóm được chỉ ra' là xóa các tệp bạn đã nói với git để theo dõi các thay đổi

2.

D:\code\gt2>git commit -m a
[master (root-commit) c271e05] a
0 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 a

cam kết -ma

  • bạn đã cam kết, " đã lưu "

3.

D:\code\gt2>git status
# On branch master
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#       new file:   b
#

(sử dụng "git reset HEAD ..." để bỏ ngỏ)

  • bạn đã thực hiện một cam kết trong mã của bạn tại thời điểm này
  • bây giờ bạn có thể đặt lại con trỏ của mình về cam kết ' trở lại lần lưu cuối cùng '

1
Đây thực sự là câu trả lời duy nhất trả lời đúng câu hỏi, IMO. Nó thực sự trả lời câu hỏi, đó không phải là "sự khác biệt giữa 'git rm --cached' và 'git reset HEAD' mà là 'tại sao git không nhất quán đưa ra cả hai tùy chọn?', Câu trả lời là không có ĐẦU để đặt lại đến khi bạn git initlần đầu tiên
Miles Rout

5

Tôi ngạc nhiên khi không ai nhắc đến glog reflog ( http://git-scm.com/docs/git-reflog ):

# git reflog
<find the place before your staged anything>
# git reset HEAD@{1}

Reflog là một lịch sử git không chỉ theo dõi các thay đổi đối với repo mà còn theo dõi các hành động của người dùng (Ví dụ: kéo, thanh toán đến các chi nhánh khác, v.v.) và cho phép hoàn tác các hành động đó. Vì vậy, thay vì bỏ qua các tập tin được dàn dựng sai, nơi bạn có thể trở lại điểm mà bạn không tạo ra các tập tin.

Điều này tương tự git reset HEAD <file>nhưng trong một số trường hợp nhất định có thể chi tiết hơn.

Xin lỗi - không thực sự trả lời câu hỏi của bạn, nhưng chỉ đưa ra một cách khác để bỏ qua các tệp mà tôi sử dụng khá thường xuyên (tôi cho một câu trả lời giống như của Ryan Stewart và waldyrious rất nhiều.);) Tôi hy vọng nó có ích.


5

Chỉ dùng:

git reset HEAD <filename>

Điều này sẽ loại bỏ tệp và giữ các thay đổi bạn đã làm với nó, do đó, bạn có thể thay đổi các nhánh nếu bạn muốn và git addcác tệp đó sang nhánh khác thay thế. Tất cả các thay đổi được lưu giữ.


3

Đối với tôi, dường như git rm --cached <file>loại bỏ tệp khỏi chỉ mục mà không xóa tệp khỏi thư mục nơi đơn giản git rm <file>sẽ thực hiện cả hai, giống như một hệ điều hành rm <file>sẽ xóa tệp khỏi thư mục mà không xóa phiên bản.


1

Chỉ dành cho phiên bản 2.23 trở lên,

Thay vì những gợi ý này, bạn có thể sử dụng git restore --staged <file>để unstage(các) tệp.


Nó hoạt động với cả các tùy chọn --stagecũng như --staged.
dhana1310
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.