Kiểm tra một cam kết cũ và duy trì phần đầu trên nhánh chủ?


85

Hiện tại để chuyển sang một cam kết git khác (trên cùng một nhánh ... thực ra là trên nhánh chính!), Tôi đang thực hiện lệnh

git checkout ea3d5ed039edd6d4a07cc41bd09eb58edd1f2b3a

Bây giờ, mỗi khi tôi làm điều này, git nói với tôi rằng tôi bây giờ với một cái đầu tách rời. Làm cách nào để chuyển đến một cam kết cũ hơn và vẫn duy trì phần đầu trên cùng một nhánh?


1
Sau khi bạn thực hiện lệnh đó, tham chiếu HEAD của bạn sẽ thay đổi thành cam kết đó . Sẽ không hợp lý nếu bạn muốn HEAD cũng trỏ đến một nơi khác.
Greg Hewgill

Theo những gì tôi hiểu từ tin nhắn của git, nó không đi đến đâu cả , đó là điều không mong muốn.
devoured elysium

1
Thông báo Git hiển thị khi bạn kiểm tra một cam kết cụ thể như vậy cho biết "HEAD hiện đang ở ea3d5ed ...", cho bạn biết rằng HEAD đang trỏ đến một nơi nào đó. Nó chỉ trỏ đến một nơi nào đó không có bất kỳ tên nào khác ngoại trừ HEAD (tại thời điểm này, vì a git checkoutđến tên cam kết hoặc nhánh khác sẽ chuyển HEAD đến vị trí mới đó).
Greg Hewgill

Các câu trả lời đưa ra có giải thích đầy đủ điều này không, hay có điều gì đó mà chúng ta có thể làm rõ hơn? Tôi rất vui khi làm rõ câu trả lời của tôi cho bạn nếu nó không trả lời câu hỏi của bạn.
Brian Campbell

Nếu bạn đến đây để tìm cách kiểm tra một cam kết khác trong khi vẫn giữ HEAD hoàn toàn không thay đổi (ví dụ: để hoàn nguyên về một cam kết cũ hơn): git revert --no-commit 0766c053..HEADsẽ thực hiện điều này, ở đâu0766c053 là cam kết bạn muốn kiểm tra. Đây là từ stackoverflow.com/a/21718540/525872 .
Jo Liss

Câu trả lời:


193

Hầu hết thời gian khi tôi làm điều này, tôi kiểm tra một nhánh tạm thời:

git checkout -b temp-branch-name ea3d5ed039edd6d4a07cc41bd09eb58edd1f2b3a

Sau đó, sau khi hoàn thành, tôi chỉ cần xóa nhánh


80

Nó phụ thuộc vào những gì bạn muốn làm khi bạn thanh toán cam kết đó. Nếu tất cả những gì bạn đang làm là kiểm tra nó để bạn có thể xây dựng hoặc kiểm tra bản sửa đổi đó, thì không có gì sai khi làm việc với một đầu rời. Chỉ cần nhớ kiểm tra một chi nhánh thực tế trước khi bạn thực hiện bất kỳ cam kết nào (git checkout master ví dụ:), để bạn không tạo các cam kết không được bao gồm trong bất kỳ nhánh nào.

Tuy nhiên, nếu bạn muốn thực hiện nhiều cam kết hơn bắt đầu từ thời điểm đó, bạn nên tạo một nhánh. Nếu bạn thực hiện các cam kết không được tham chiếu bởi một nhánh, chúng có thể dễ dàng bị mất và cuối cùng sẽ được dọn dẹp bởi bộ thu gom rác của git, vì không có gì đề cập đến chúng. Bạn có thể tạo một nhánh mới bằng cách chạy:

git checkout -b newbranch ea3d5ed

Để giúp hình dung, đây là một số sơ đồ minh họa cách làm việc trên một đầu tách rời khác với hoạt động trên một nhánh.

Hãy bắt đầu với 3 cam kết trên master, A, B và C. masterlà nhánh hiện tại, vì vậy hãy HEADchỉ đếnmaster , điểm nào để cam kết C.

ABC
* - * - * <- master <- HEAD

Bây giờ nếu chúng ta cam kết, git sẽ tạo một cam kết có C là cha (vì đó là cam kết hiện tại, được trỏ đến từ HEADqua master) và sẽ cập nhật masterđể trỏ đến cam kết mới đó. Tất cả các cam kết của chúng tôi hiện đã được thực hiện masterHEADhướng đến cam kết mới thông qua master.

A B C D
* - * - * - * <- master <- HEAD

Bây giờ chúng ta hãy kiểm tra B, cho chúng ta một HEAD .

A B C D
* - * - * - * <- chủ
   ^
    \-- CÁI ĐẦU

Mọi thứ hoạt động tốt ở đây; chúng tôi có thể xem tất cả các tệp, xây dựng chương trình của mình, kiểm tra nó, v.v. Chúng tôi thậm chí có thể tạo các cam kết mới; nhưng nếu chúng tôi làm như vậy, không có nhánh nào mà chúng tôi đang ở, vì vậy chúng tôi không thể trỏ bất kỳ nhánh nào vào cam kết mới đó. Điều duy nhất chỉ vào nó là HEAD:

A B C D
* - * - * - * <- chủ
    \
     * <- ĐẦU
     E

Nếu sau đó chúng tôi quyết định kiểm tra masterlại, sẽ không có gì đề cập đến E.

A B C D
* - * - * - * <- master <- HEAD
    \
     *
     E

Vì không có gì đề cập đến nó, nên có thể khó tìm và git coi các cam kết không có tham chiếu nào sẽ bị bỏ (chúng xảy ra khá phổ biến nếu bạn rebase hoặc cài đặt các bản vá lỗi hoặc thực hiện thao tác lịch sử thú vị khác; chúng thường đại diện cho các bản vá bị bỏ qua mà bạn không còn quan tâm). Sau một khoảng thời gian nhất định, git sẽ coi đó là rác, sẽ bị loại bỏ trong lần thu rác tiếp theo.

Vì vậy, thay vì kiểm tra một bản sửa đổi trần và nhận được một phần đầu tách rời, nếu bạn cảm thấy mình sẽ thực hiện nhiều cam kết hơn, bạn nên sử dụng git checkout -b branch Bđể tạo một nhánh và kiểm tra nó. Giờ đây, các cam kết của bạn sẽ không bị mất, vì chúng sẽ được đưa vào một nhánh mà bạn có thể dễ dàng tham khảo và hợp nhất sau này.

A B C D
* - * - * - * <- chủ
   ^
    \ - chi nhánh <- HEAD

Nếu bạn quên làm điều này và tạo cam kết ngoài một nhánh, không cần phải lo lắng. Bạn có thể tạo một nhánh tham chiếu đến bản sửa đổi head với git checkout -b branch. Nếu bạn đã quay trở lại masternhánh và nhận ra rằng bạn đã quên một cam kết bị lạc, bạn có thể tìm nó bằng cách sử dụng git reflog, nó sẽ hiển thị cho bạn lịch sử về những gì cam kết HEADđã chỉ đến trong vài ngày qua. Bất kỳ thứ gì vẫn còn trong bản cập nhật sẽ không được thu thập rác và nói chung các tham chiếu sẽ được lưu trong bản ghi lại ít nhất 30 ngày.


Tôi không hoàn toàn rõ ràng tại sao khi bạn kiểm tra một cam kết cũ, phần đầu lại bị tách ra . Có thể vấn đề là trên đầu tách rời có nghĩa là gì? Mặt khác, nếu kiểm tra một cam kết cũ với một phần đầu tách rời sẽ chỉ làm mất nó, tại sao mọi người lại làm điều đó? Chỉ để lộn xộn và thử mọi thứ? Tại sao git cho phép nó, ngay từ đầu?
devoured elysium

5
@devoured elysium "đầu tách rời" có nghĩa là bạn có một tham chiếu HEADđang trỏ trực tiếp vào SHA-1 của một cam kết, thay vì chỉ vào một nhánh mà lần lượt chỉ vào một cam kết. Vì đầu của bạn không tham chiếu đến một nhánh, Git không biết nhánh nào sẽ cập nhật khi bạn thêm các cam kết mới. Như tôi đã giải thích ở đầu câu trả lời của mình, hoàn toàn ổn nếu bạn quay lại phiên bản cũ chỉ để xây dựng hoặc kiểm tra mã; bạn luôn có thể quay lại chi nhánh có git checkout masterhoặc tương tự. Nó chỉ là một vấn đề nếu bạn cam kết trong khi bạn có một cái đầu tách rời.
Brian Campbell

@BrianCampbell - Sau khi thực hiện các cam kết trên chi nhánh (nơi đầu của bạn hiện đang ở), sau đó bạn hợp nhất chi nhánh vào B và hợp nhất B thành chính. Bạn nên làm gì tiếp theo?
amey1908

Lời giải thích của bạn đã khiến một loạt những thứ khác 'kích thích' tôi. Cảm ơn bạn. Bây giờ tôi có thể cuối cùng đã hiểu git ...
Joe

8

Nếu bạn chỉ muốn quay lại cam kết trước đó để chơi với nó mà không thực hiện bất kỳ thay đổi nào, bạn có thể làm

git co <previous-commit-id>

bạn sẽ ở trên một nhánh có tên "(không có nhánh)" sau lệnh này.

Xác nhận điều này bởi

git br

Sau khi bạn đã chơi với mã đã cam kết trước đó, bạn có thể chuyển sang nhánh mà bạn đã sử dụng

git co <the-branch-you-were-on>

"(Không có chi nhánh)" sẽ tự động bị xóa. Bằng cách này, bạn không cần tạo một nhánh tạm thời.


5

Git's HEAD chỉ đơn giản là một con trỏ cho biết có gì trong thư mục làm việc. Nếu bạn muốn kiểm tra một cam kết không phải là người đứng đầu một chi nhánh, bạn chỉ cần chuyển hướng HEAD của mình đến điểm cam kết đó. Không có cách nào xung quanh nó. Bạn có thể tạo một nhánh tạm thời tại cam kết đó, nhưng dù sao thì HEAD cũng sẽ được điều hướng khỏi chính.

Đó là lời giải thích ngắn gọn. Chi tiết dưới đây hy vọng sẽ giúp bạn hiểu HEAD và master khác nhau như thế nào:

Thông thường, mọi thứ trông như thế này:

C ← refs/heads/master ← HEAD 
↓
B
↓
A

Có nghĩa là: “Cha của C là B, và cha của B là A. Cái nhánh chính đang trỏ đến C, và tôi hiện đã kiểm tra nội dung của cái. Ngoài ra, khi tôi cam kết, bản chính sẽ được cập nhật. ”

Một số giả định được ngầm hiểu trong điều này là cần thiết để hiểu rõ về đồ thị cam kết. Cụ thể, các cam kết chỉ đề cập đến cha mẹ của họ và nội dung của một nhánh là những cam kết đó (và chỉ những cam kết đó) có thể đạt được bằng cách nhấp vào các liên kết mẹ. Nội dung (chưa sửa đổi) của cây làm việc và chỉ mục phải tương ứng với cam kết do HEAD đặt tên, gián tiếp (“tượng trưng”) hoặc trực tiếp (“tách rời”).

Vì vậy, nếu bạn muốn kiểm tra một cam kết cũ, HEAD phải được cập nhật để trỏ đến cam kết mong muốn. git-checkoutchỉ làm điều đó:

C ← refs/heads/master 
↓
B ← HEAD
↓
A

Bây giờ, bạn đã bỏ lại chi nhánh của mình sau lưng bạn, vì bạn đang nhìn vào một thứ gì đó cũ kỹ. Điều đó hoàn toàn ổn, vì lời khuyên "đầu tách rời" nói với bạn một cách bình tĩnh (tôi nhấn mạnh):

Bạn có thể xem xét xung quanh, thực hiện các thay đổi thử nghiệm và cam kết chúng, đồng thời bạn có thể hủy bất kỳ cam kết nào bạn thực hiện ở trạng thái này mà không ảnh hưởng đến bất kỳ nhánh nào bằng cách thực hiện một quy trình khác.

Mặt khác, trong khi thiết lập lại chi nhánh của bạn cũng nhận được HEAD ở nơi nó cần, nó sẽ có một hiệu ứng rất khác!

C
↓
B ← refs/heads/master ← HEAD
↓
A

Cam kết C sẽ trở thành rác, vì bạn đã tuyên bố rằng bạn không muốn nó là một phần của nhánh chính nữa.

Tóm lại, tất cả những gì bạn phải làm là hiểu ý nghĩa của git bởi “HEAD” - đó là vị trí của bạn , không phải nơi bất kỳ nhánh nhất định nào. Và nếu vị trí của bạn không giống với vị trí của chi nhánh, không có lựa chọn nào khác ngoài việc sử dụng HEAD tách rời.

(Có lẽ cũng nên xem xét GitHub, gitk hoặc gitweb để duyệt lịch sử cam kết, nếu việc trật tự HEAD của bạn tiếp tục khiến bạn khó chịu.)


1

Câu hỏi này hơi mơ hồ, nhưng nếu bạn chỉ muốn thay đổi các tệp trong cây làm việc của mình, bạn có thể chỉ cần thực hiện điều này:

git checkout [commit|branch] -- .

Sau đó, bạn có thể thực hiện các thay đổi và tạo một cam kết mới nếu muốn. Điều này đôi khi khá hữu ích.


0

Tôi nghĩ rằng tôi hiểu câu hỏi của bạn. Đây là những gì tôi tìm thấy để giải quyết nó. và không có giải pháp GUI cho nó, bạn chỉ có thể sử dụng lệnh để giải quyết nó, và nó thực sự đơn giản.

bước 1: tạo một thẻ cam kết cũ mà bạn muốn quay lại.

thích thẻ v2.0

bước 2: git checkout v2.0

đây rồi, bây giờ HEAD của bạn đang chỉ vào cam kết 'v2.0', nhưng chủ vẫn đang chỉ vào cam kết cuối cùng.

C:\Program Files\Git\doc\git\html\git-checkout.html tài liệu này có thể giúp bạn

hoặc gõ git help <checkout>

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.