Nói chung, git reset
chức năng của nó là lấy nhánh hiện tại và đặt lại nó để chỉ ra một nơi khác, và có thể mang chỉ mục và cây công việc đi cùng. Cụ thể hơn, nếu chi nhánh chính của bạn (hiện đã được kiểm tra) giống như thế này:
- A - B - C (HEAD, master)
và bạn nhận ra rằng bạn muốn chủ chỉ đến B, không phải C, bạn sẽ sử dụng git reset B
để di chuyển nó đến đó:
- A - B (HEAD, master) # - C is still here, but there's no branch pointing to it anymore
Digression: Điều này khác với thanh toán. Nếu bạn chạy git checkout B
, bạn sẽ nhận được điều này:
- A - B (HEAD) - C (master)
Bạn đã kết thúc trong một trạng thái ĐẦU tách ra. HEAD
, cây làm việc, chỉ mục tất cả khớp B
, nhưng nhánh chủ bị bỏ lại tại C
. Nếu bạn thực hiện một cam kết mới D
tại thời điểm này, bạn sẽ nhận được điều này, đây có thể không phải là điều bạn muốn:
- A - B - C (master)
\
D (HEAD)
Hãy nhớ rằng, thiết lập lại không thực hiện các cam kết, nó chỉ cập nhật một nhánh (là con trỏ tới một cam kết) để trỏ đến một cam kết khác. Phần còn lại chỉ là chi tiết về những gì xảy ra với chỉ mục và cây công việc của bạn.
Trường hợp sử dụng
Tôi trình bày nhiều trường hợp sử dụng chính git reset
trong phần mô tả của tôi về các tùy chọn khác nhau trong phần tiếp theo. Nó thực sự có thể được sử dụng cho nhiều thứ khác nhau; luồng chung là tất cả chúng liên quan đến việc đặt lại nhánh, chỉ mục và / hoặc cây công việc để trỏ đến / khớp với một cam kết đã cho.
Những điều cần cẩn thận
--hard
có thể khiến bạn thực sự mất việc. Nó sửa đổi cây làm việc của bạn.
git reset [options] commit
có thể khiến bạn (sắp xếp) mất các cam kết. Trong ví dụ đồ chơi ở trên, chúng tôi mất cam kết C
. Nó vẫn còn trong repo, và bạn có thể tìm thấy nó bằng cách nhìn vào git reflog show HEAD
hoặc git reflog show master
, nhưng nó không thực sự thể truy cập từ bất kỳ chi nhánh nữa.
Git xóa vĩnh viễn các cam kết như vậy sau 30 ngày, nhưng cho đến lúc đó bạn có thể khôi phục C bằng cách trỏ một nhánh vào nó một lần nữa ( git checkout C; git branch <new branch name>
).
Tranh luận
Diễn giải trang man, cách sử dụng phổ biến nhất là ở dạng git reset [<commit>] [paths...]
, sẽ thiết lập lại các đường dẫn đã cho về trạng thái của chúng từ cam kết đã cho. Nếu các đường dẫn không được cung cấp, toàn bộ cây sẽ được đặt lại và nếu cam kết không được cung cấp, thì nó được coi là CHÍNH (cam kết hiện tại). Đây là một mẫu phổ biến trên các lệnh git (ví dụ: thanh toán, diff, log, mặc dù ngữ nghĩa chính xác khác nhau), vì vậy nó không quá ngạc nhiên.
Ví dụ, git reset other-branch path/to/foo
đặt lại mọi thứ trong đường dẫn / đến / foo về trạng thái của nó trong nhánh khác, git reset -- .
đặt lại thư mục hiện tại về trạng thái của nó trong HEAD và đơn giản git reset
đặt lại mọi thứ về trạng thái của nó trong HEAD.
Các tùy chọn chỉ mục và cây công việc chính
Có bốn tùy chọn chính để kiểm soát những gì xảy ra với cây công việc và chỉ mục của bạn trong quá trình thiết lập lại.
Hãy nhớ rằng, chỉ mục là "khu vực tổ chức" của git - đó là nơi mọi thứ diễn ra khi bạn nói git add
để chuẩn bị cam kết.
--hard
làm cho mọi thứ khớp với cam kết bạn đã đặt lại. Đây là cách dễ hiểu nhất, có lẽ. Tất cả các thay đổi cục bộ của bạn bị tắc nghẽn. Một mục đích sử dụng chính là thổi bay công việc của bạn nhưng không chuyển đổi các cam kết: git reset --hard
có nghĩa git reset --hard HEAD
là không thay đổi chi nhánh mà loại bỏ tất cả các thay đổi cục bộ. Cái khác chỉ đơn giản là di chuyển một nhánh từ nơi này sang nơi khác và giữ cho chỉ mục / cây công việc được đồng bộ hóa. Đây là một trong những thực sự có thể làm cho bạn mất việc, bởi vì nó sửa đổi cây công việc của bạn. Hãy rất chắc chắn rằng bạn muốn vứt bỏ công việc địa phương trước khi bạn chạy bất kỳ reset --hard
.
--mixed
là mặc định, git reset
có nghĩa là git reset --mixed
. Nó đặt lại chỉ mục, nhưng không phải là cây làm việc. Điều này có nghĩa là tất cả các tệp của bạn đều còn nguyên vẹn, nhưng bất kỳ sự khác biệt nào giữa cam kết ban đầu và cam kết bạn đặt lại sẽ hiển thị dưới dạng sửa đổi cục bộ (hoặc các tệp không bị theo dõi) với trạng thái git. Sử dụng điều này khi bạn nhận ra bạn đã thực hiện một số cam kết xấu, nhưng bạn muốn giữ tất cả công việc bạn đã thực hiện để bạn có thể khắc phục và đề xuất. Để cam kết, bạn sẽ phải thêm lại tệp vào chỉ mục ( git add ...
).
--soft
không chạm vào chỉ mục hoặc cây làm việc. Tất cả các tệp của bạn vẫn còn nguyên vẹn như với --mixed
, nhưng tất cả các thay đổi hiển thị như changes to be committed
với trạng thái git (nghĩa là đã được kiểm tra để chuẩn bị cho việc cam kết). Sử dụng điều này khi bạn nhận ra mình đã thực hiện một số cam kết xấu, nhưng tất cả đều tốt - tất cả những gì bạn cần làm là đề xuất nó theo cách khác. Chỉ mục không bị ảnh hưởng, vì vậy bạn có thể cam kết ngay lập tức nếu bạn muốn - cam kết kết quả sẽ có tất cả nội dung giống như bạn đã ở trước khi bạn đặt lại.
--merge
đã được thêm vào gần đây và nhằm giúp bạn hủy bỏ việc hợp nhất không thành công. Điều này là cần thiết bởi vì git merge
thực sự sẽ cho phép bạn thử hợp nhất với một cây công việc bẩn (một với các sửa đổi cục bộ) miễn là các sửa đổi đó nằm trong các tệp không bị ảnh hưởng bởi hợp nhất. git reset --merge
Đặt lại chỉ mục (như --mixed
- tất cả các thay đổi hiển thị dưới dạng sửa đổi cục bộ) và đặt lại các tệp bị ảnh hưởng bởi việc hợp nhất, nhưng chỉ để lại các thay đổi khác. Điều này hy vọng sẽ khôi phục lại mọi thứ như trước khi hợp nhất xấu. Bạn sẽ thường sử dụng nó dưới dạng git reset --merge
(có nghĩa git reset --merge HEAD
) bởi vì bạn chỉ muốn đặt lại hợp nhất, không thực sự di chuyển chi nhánh. ( HEAD
chưa được cập nhật, vì việc hợp nhất thất bại)
Để cụ thể hơn, giả sử bạn đã sửa đổi các tệp A và B và bạn cố gắng hợp nhất trong một nhánh đã sửa đổi các tệp C và D. Việc hợp nhất không thành công vì một số lý do và bạn quyết định hủy bỏ nó. Bạn sử dụng git reset --merge
. Nó đưa C và D trở lại cách họ tham gia HEAD
, nhưng để lại các sửa đổi của bạn cho A và B, vì chúng không phải là một phần của sự hợp nhất đã cố gắng.
Bạn muốn biết thêm?
Tôi nghĩ rằng man git reset
nó thực sự khá tốt cho việc này - có lẽ bạn cần một chút cảm giác về cách thức hoạt động của git để chúng thực sự chìm trong mặc dù. Đặc biệt, nếu bạn dành thời gian để đọc chúng một cách cẩn thận, những bảng chi tiết trạng thái của các tệp trong chỉ mục và cây công việc cho tất cả các tùy chọn và trường hợp khác nhau là rất rất hữu ích. (Nhưng vâng, chúng rất dày đặc - chúng đang truyền tải rất nhiều thông tin trên dưới dạng rất súc tích.)
Ký hiệu lạ
"Ký hiệu lạ" ( HEAD^
và HEAD~1
) bạn đề cập chỉ đơn giản là một cách viết tắt để chỉ định các cam kết, mà không phải sử dụng tên băm như thế nào 3ebe3f6
. Nó được ghi lại đầy đủ trong phần "chỉ định sửa đổi" của trang man cho git-rev-parse, với rất nhiều ví dụ và cú pháp liên quan. Dấu mũ và dấu ngã thực sự có nghĩa là những thứ khác nhau :
HEAD~
là viết tắt của HEAD~1
và có nghĩa là cha mẹ đầu tiên của cam kết. HEAD~2
có nghĩa là cha mẹ đầu tiên của cam kết. Hãy nghĩ về HEAD~n
"n cam kết trước ĐẦU" hoặc "tổ tiên thế hệ thứ n của ĐẦU".
HEAD^
(hoặc HEAD^1
) cũng có nghĩa là cha mẹ đầu tiên của cam kết. HEAD^2
có nghĩa là cha mẹ thứ hai của cam kết . Hãy nhớ rằng, một cam kết hợp nhất bình thường có hai cha mẹ - cha mẹ đầu tiên là cam kết hợp nhất và cha mẹ thứ hai là cam kết được hợp nhất. Nói chung, sáp nhập thực sự có thể có nhiều cha mẹ tùy ý (sáp nhập bạch tuộc).
- Các toán tử
^
và ~
toán tử có thể được xâu chuỗi lại với nhau, như trong HEAD~3^2
, cha mẹ thứ hai của tổ tiên thế hệ thứ ba của HEAD
, HEAD^^2
cha mẹ thứ hai của cha mẹ đầu tiên của HEAD
, hoặc thậm chí HEAD^^^
, tương đương với HEAD~3
.