Bạn thấy tài liệu Git nói những điều như
Chi nhánh phải được hợp nhất hoàn toàn trong ĐẦU.
Nhưng HEAD
chính xác thì Git là gì?
Bạn thấy tài liệu Git nói những điều như
Chi nhánh phải được hợp nhất hoàn toàn trong ĐẦU.
Nhưng HEAD
chính xác thì Git là gì?
Câu trả lời:
Bạn có thể nghĩ về ĐẦU là "nhánh hiện tại". Khi bạn chuyển đổi các nhánh với git checkout
, phiên bản CHÍNH thay đổi để trỏ đến đỉnh của nhánh mới.
Bạn có thể thấy những gì CHÍNH chỉ ra bằng cách làm:
cat .git/HEAD
Trong trường hợp của tôi, đầu ra là:
$ cat .git/HEAD
ref: refs/heads/master
TRƯỚC có thể đề cập đến một sửa đổi cụ thể không liên quan đến tên chi nhánh. Tình huống này được gọi là một ĐẦU tách ra .
Để trích dẫn người khác :
Một cái đầu chỉ đơn giản là một tham chiếu đến một đối tượng cam kết. Mỗi đầu có một tên (tên chi nhánh hoặc tên thẻ, v.v.). Theo mặc định, có một cái đầu trong mỗi kho lưu trữ được gọi là master. Một kho lưu trữ có thể chứa bất kỳ số lượng người đứng đầu. Tại bất kỳ thời điểm nào, một đầu được chọn là đầu hiện tại. Cái đầu này được đặt bí danh là CHÍNH, luôn ở thủ đô ".
Lưu ý sự khác biệt này: một đầu người đầu mối (chữ thường) đề cập đến bất kỳ một trong những đầu được đặt tên trong kho lưu trữ; ĐẦU TIÊN (chữ hoa) chỉ dành riêng cho người đứng đầu hiện đang hoạt động. Sự khác biệt này được sử dụng thường xuyên trong tài liệu Git.
Một nguồn tốt khác nhanh chóng bao gồm các hoạt động bên trong của git (và để hiểu rõ hơn về người đứng đầu / ĐẦU) có thể được tìm thấy ở đây . Tài liệu tham khảo (ref :) hoặc người đứng đầu hoặc chi nhánh có thể được coi như ghi chú sau nó bị mắc kẹt trong các cam kết trong lịch sử cam kết. Thông thường họ chỉ đến đỉnh của một loạt các cam kết, nhưng chúng có thể được di chuyển xung quanh với git checkout
hoặc git reset
vv
git checkout HEAD~2
), không phải là id cam kết của một đầu đã biết. Xem bài viết tại eagain.net/articles/git-for-computer-scientists để được giải thích kỹ hơn.
git revert
đây không phải là một ví dụ điển hình về việc chuyển một nhánh thành không nằm ở đầu, bởi vì git revert
chỉ cần tạo một số cam kết mới và vẫn để lại nhánh hiện tại ở đầu (mới).
commit
s, reset
s, vv
Tôi khuyến nghị định nghĩa này từ nhà phát triển github Scott Chacon [ tham khảo video ]:
Đầu là chi nhánh hiện tại của bạn. Nó là một tài liệu tham khảo mang tính biểu tượng. Nó là một tài liệu tham khảo cho một chi nhánh. Bạn luôn có ĐẦU, nhưng ĐẦU sẽ chỉ vào một trong những con trỏ khác này, đến một trong những nhánh bạn đang ở. Nó là cha mẹ của cam kết tiếp theo của bạn. Đó là những gì nên được kiểm tra lần cuối vào thư mục làm việc của bạn ... Đây là trạng thái được biết đến cuối cùng của thư mục làm việc của bạn.
Toàn bộ video sẽ giới thiệu công bằng cho toàn bộ hệ thống git, vì vậy tôi cũng khuyên bạn nên xem tất cả nếu có thời gian.
HEAD chỉ là một con trỏ đặc biệt trỏ đến nhánh cục bộ mà bạn hiện đang ở.
Từ sách Pro Git , chương 3.1 Phân nhánh Git - Các nhánh trong một Nutshell , trong phần Tạo một nhánh mới :
Điều gì xảy ra nếu bạn tạo một chi nhánh mới? Vâng, làm như vậy tạo ra một con trỏ mới để bạn di chuyển xung quanh. Giả sử bạn tạo một nhánh mới gọi là thử nghiệm. Bạn làm điều này với lệnh git nhánh:
$ git branch testing
Điều này tạo ra một con trỏ mới tại cùng một cam kết mà bạn hiện đang trên
Làm thế nào để Git biết bạn đang ở chi nhánh nào? Nó giữ một con trỏ đặc biệt gọi là CHÍNH. Lưu ý rằng điều này khác rất nhiều so với khái niệm CHÍNH trong các VCS khác mà bạn có thể đã quen, chẳng hạn như Subversion hoặc CVS. Trong Git, đây là một con trỏ tới nhánh cục bộ mà bạn hiện đang ở. Trong trường hợp này, bạn vẫn đang làm chủ. Lệnh git nhánh chỉ tạo một nhánh mới - nó không chuyển sang nhánh đó.
34ac2
trong ví dụ trên, thì bây giờ CHÍNH sẽ chỉ vào cam kết đó và nó được gọi là ĐẦU tách rời. Ở trạng thái này, bạn cũng có thể thực hiện thay đổi, thử nghiệm và cam kết thay đổi, nhưng một khi bạn kiểm tra một nhánh khác, bạn sẽ mất tất cả các thay đổi của mình, trừ khi tất nhiên bạn tạo một nhánh mới.
git log
và có một cái gì đó như thế commit ad0265... HEAD -> foo ...
có nghĩa là foo
chi nhánh là một tham chiếu để xác nhận id ad0265
. Làm một kiểm tra của tài liệu tham khảo văn bản foo
không phải là một đầu tách ra. Thực hiện kiểm tra id cam kết ad0265
sẽ dẫn đến một cái đầu tách ra. Có thể tôi đang thiếu một chút tinh tế trong những gì bạn đang truyền đạt. Tôi hy vọng bức tường văn bản này sẽ giúp khám phá nơi tôi bị lạc.
Giả sử nó không phải là một trường hợp đặc biệt gọi là "ĐẦU tách rời", như đã nêu trong cuốn sách O'Reilly Git, ấn bản thứ 2, tr69, HEAD
có nghĩa là:
HEAD
luôn đề cập đến các cam kết gần đây nhất trên chi nhánh hiện tại. Khi bạn thay đổi chi nhánh,HEAD
được cập nhật để tham khảo cam kết mới nhất của chi nhánh mới.
vì thế
HEAD
là "mẹo" của chi nhánh hiện tại .
Lưu ý rằng chúng ta có thể sử dụng HEAD
để tham khảo các cam kết gần đây nhất và sử dụng HEAD~
như cam kết trước mẹo và HEAD~~
hoặc HEAD~2
như cam kết thậm chí sớm hơn, v.v.
Có một quan niệm sai lầm, có lẽ tinh tế, nhưng quan trọng trong một số những câu trả lời này. Tôi nghĩ tôi sẽ thêm câu trả lời của mình để làm sáng tỏ nó.
Là
HEAD
gì
HEAD
là một tài liệu tham khảo mang tính biểu tượng chỉ ra bất cứ nơi nào bạn đang ở trong lịch sử cam kết của mình. Nó theo bạn bất cứ nơi nào bạn đi, bất cứ điều gì bạn làm, như một cái bóng. Nếu bạn thực hiện một cam kết, HEAD
sẽ di chuyển. Nếu bạn kiểm tra một cái gì đó, HEAD
sẽ di chuyển. Dù bạn làm gì, nếu bạn đã chuyển đi đâu đó mới trong lịch sử cam kết của mình, HEAD
đã di chuyển cùng với bạn. Để giải quyết một quan niệm sai lầm phổ biến: bạn không thể tách rời khỏi HEAD
. Đó không phải là một trạng thái CHÍNH tách rời. Nếu bạn từng thấy mình suy nghĩ: "ồ không, tôi đang ở trạng thái TUYỆT VỜI! Tôi đã mất ĐẦU!" Hãy nhớ rằng, đó là ĐẦU của bạn. TRƯỚC là bạn. Bạn chưa tách ra khỏi ĐẦU, bạn và ĐẦU của bạn đã tách ra khỏi thứ khác.
HEAD
có thể chỉ ra một cam kết, có, nhưng thông thường thì không. Hãy để tôi nói rằng một lần nữa. Thông thường HEAD
không chỉ ra một cam kết. Nó trỏ đến một tài liệu tham khảo chi nhánh. Nó được gắn vào nhánh đó và khi bạn làm một số thứ nhất định (ví dụ, commit
hoặc reset
), nhánh đính kèm sẽ di chuyển cùng HEAD
. Bạn có thể thấy những gì nó đang chỉ bằng cách nhìn dưới mui xe.
cat .git/HEAD
Thông thường bạn sẽ nhận được một cái gì đó như thế này:
ref: refs/heads/master
Đôi khi bạn sẽ nhận được một cái gì đó như thế này:
a3c485d9688e3c6bc14b06ca1529f0e78edd3f86
Đó là những gì xảy ra khi HEAD
điểm trực tiếp đến một cam kết. Điều này được gọi là một ĐẦU tách ra, bởi vì HEAD
đang chỉ đến một cái gì đó không phải là một tham chiếu chi nhánh. Nếu bạn thực hiện một cam kết trong trạng thái này master
, không còn được gắn bó HEAD
, sẽ không còn di chuyển cùng với bạn. Không quan trọng cam kết đó ở đâu. Bạn có thể ở cùng một cam kết với nhánh chính của bạn, nhưng nếu HEAD
chỉ vào cam kết chứ không phải là nhánh, nó sẽ bị tách ra và một cam kết mới sẽ không được liên kết với tham chiếu nhánh.
Bạn có thể nhìn vào đồ họa này nếu bạn thử bài tập sau. Từ một kho lưu trữ git, chạy này. Bạn sẽ nhận được một cái gì đó hơi khác nhau, nhưng các bit chính của họ sẽ ở đó. Khi đến lúc kiểm tra cam kết trực tiếp, chỉ cần sử dụng bất kỳ hàm băm viết tắt nào bạn nhận được từ đầu ra đầu tiên (đây là a3c485d
).
git checkout master
git log --pretty=format:"%h: %d" -1
# a3c485d: (HEAD -> master)
git checkout a3c485d -q # (-q is for dramatic effect)
git log --pretty=format:"%h: %d" -1
# a3c485d: (HEAD, master)
OK, do đó, có một sự khác biệt nhỏ trong đầu ra ở đây. Kiểm tra cam kết trực tiếp (thay vì chi nhánh) cho chúng ta dấu phẩy thay vì mũi tên. Bạn nghĩ gì, chúng ta đang ở trong một trạng thái CHÍNH? HEAD vẫn đang đề cập đến một sửa đổi cụ thể được liên kết với một tên chi nhánh. Chúng ta vẫn ở trong nhánh chính, phải không?
Bây giờ cố gắng:
git status
# HEAD detached at a3c485d
Không. Chúng tôi đang ở trạng thái 'tách ra'.
Bạn có thể thấy cùng một đại diện (HEAD -> branch)
so (HEAD, branch)
với git log -1
.
HEAD
là bạn. Nó chỉ ra bất cứ điều gì bạn đã kiểm tra, bất cứ nơi nào bạn đang có. Thông thường đó không phải là một cam kết, nó là một chi nhánh. Nếu HEAD
không trỏ đến một cam kết (hoặc thẻ), ngay cả khi đó là cùng một cam kết (hoặc thẻ) mà một nhánh cũng trỏ đến, thì bạn (và HEAD
) đã bị tách ra khỏi nhánh đó. Vì bạn không có chi nhánh gắn liền với mình, nên chi nhánh sẽ không đi theo bạn khi bạn thực hiện các cam kết mới. HEAD
, tuy nhiên, sẽ.
.git/HEAD
là những gì phần mềm coi là CHÍNH.
HEAD
đề cập đến cam kết hiện tại mà bản sao làm việc của bạn trỏ đến, tức là cam kết mà bạn hiện đã thanh toán. Từ tài liệu Linux Kernel chính thức về việc chỉ định các bản sửa đổi Git :
HEAD
Đặt tên cho cam kết mà bạn dựa trên các thay đổi trong cây làm việc.
Tuy nhiên, lưu ý rằng trong phiên bản 1.8.4 sắp tới của Git, @
cũng có thể được sử dụng như một cách viết tắt HEAD
, như được lưu ý bởi người đóng góp Git Junio C Hamano trong blog Git Blame của mình :
Thay vì gõ "ĐẦU", bạn có thể nói "@", ví dụ: "git log @".
Người dùng Stack Overflow VonC cũng tìm thấy một số thông tin thú vị về lý do tại sao @
được chọn làm tốc ký trong câu trả lời của mình cho câu hỏi khác .
Ngoài ra, trong một số môi trường, không cần thiết phải viết hoa HEAD
, đặc biệt là trong các hệ điều hành sử dụng các hệ thống tệp không phân biệt chữ hoa chữ thường, cụ thể là Windows và OS X.
Hãy xem Tạo và chơi với các chi nhánh
HEAD thực sự là một tệp có nội dung xác định trong đó biến HEAD đề cập đến:
$ cat .git/HEAD
ref: refs/heads/master
$ cat .git/refs/heads/master
35ede5c916f88d8ba5a9dd6afd69fcaf773f70ed
Trong kho lưu trữ này, nội dung của tệp CHÍNH đề cập đến tệp thứ hai có tên refs / Heads / master . Tệp refs / Heads / master chứa hàm băm của lần xác nhận gần đây nhất trên nhánh chính.
Kết quả là các điểm CHÍNH đến cam kết nhánh chính từ tệp .git / refs / Heads / master .
Tôi chỉ muốn nói chi tiết một vài điều trong câu trả lời được chấp nhận của Greg Hewgil. Theo Hướng dẫn bỏ túi Git
Chi nhánh:
bản thân nhánh được định nghĩa là tất cả các điểm có thể truy cập trong biểu đồ xác nhận từ cam kết được đặt tên (mẹo tip của nhánh).
ĐẦU: Một loại Ref đặc biệt
Giới thiệu đặc biệt xác định chi nhánh bạn đang ở ...
Tham chiếu
Git định nghĩa hai loại tài liệu tham khảo, hoặc con trỏ được đặt tên, mà nó gọi là ref refs:
- Một ref đơn giản, trỏ trực tiếp vào ID đối tượng (thường là một cam kết hoặc thẻ)
- Một ref mang tính biểu tượng (hoặc symref), trỏ đến một ref khác (đơn giản hoặc tượng trưng)
Như Greg đã đề cập, HEAD có thể ở trong "trạng thái tách rời". Vì vậy, HEAD có thể là một ref đơn giản (đối với một HEAD tách rời) hoặc symref.
nếu HEAD là một ref mang tính biểu tượng cho một nhánh hiện có, thì bạn là người trên nhánh đó. Mặt khác, nếu HEAD là một ref đơn giản trực tiếp đặt tên cho một cam kết bằng ID SHA-1 của nó, thì bạn không phải là trên bất kỳ chi nhánh nào, mà là trong chế độ ĐẦU tách rời, xuất hiện khi bạn kiểm tra một số trước đó cam kết kiểm tra.
Tôi nghĩ rằng 'ĐẦU' là kiểm tra cam kết hiện tại. Nói cách khác, 'ĐẦU' chỉ vào cam kết hiện đang được kiểm tra.
Nếu bạn vừa nhân bản và chưa kiểm tra, tôi không biết nó trỏ đến cái gì, có thể là một số vị trí không hợp lệ.
master
nhánh - vì vậy, HEAD sẽ trỏ đến master.
master
, nhưng không phải lúc nào cũng vậy. Xemremote set-head
remote set-head
, chỉ tác động đến nhánh mặc định cục bộ và sẽ không thay đổi mặc định tại máy chủ.
Đầu chỉ vào đầu của chi nhánh hiện đang kiểm tra.
Trong kho lưu trữ của bạn, có một thư mục .git. Mở tệp ở vị trí này: .git \ refs \ Heads. Mã (băm sha-1) trong tệp đó (chính trong hầu hết các trường hợp) sẽ là lần xác nhận gần đây nhất, tức là mã được thấy trong đầu ra của lệnh git log
. Thông tin thêm về thư mục .git: http : // git yet.com/advified/2009/03/23/whats-inside-your-git-directory.html
git reset HEAD^
, và sau đó, cam kết gần đây nhất (mẹo trước đây) không còn được chỉ ra bởi đầu cành.
Sau khi đọc tất cả các câu trả lời trước đó, tôi vẫn muốn rõ ràng hơn. Blog này tại trang web chính thức của git http://git-scm.com/blog đã cho tôi những gì tôi đang tìm kiếm:
Đầu trong Git là con trỏ tới tham chiếu nhánh hiện tại, lần lượt là một con trỏ đến lần xác nhận cuối cùng bạn đã thực hiện hoặc lần xác nhận cuối cùng đã được kiểm tra trong thư mục làm việc của bạn. Điều đó cũng có nghĩa nó sẽ là cha mẹ của lần cam kết tiếp theo bạn làm. Nói chung đơn giản nhất khi nghĩ về nó vì CHÍNH là ảnh chụp nhanh về cam kết cuối cùng của bạn.
HEAD
không phải là một cam kết; nó chỉ đến một.
checkout HEAD^
, bây giờ CHÍNH thậm chí không trỏ đến ảnh chụp nhanh cam kết cuối cùng trên bất kỳ chi nhánh nào.
commit
, merge
, rebase
, log
, vv Tuy nhiên, khái niệm có thể "(con trỏ tới) vị trí hiện tại" là một bản tóm tắt tốt.
Cảm giác như đó HEAD
chỉ là một thẻ cho lần xác nhận cuối cùng mà bạn đã kiểm tra.
Đây có thể là đỉnh của một nhánh cụ thể (chẳng hạn như "chính") hoặc một số cam kết ở giữa của một nhánh ("đầu tách rời")
Ngoài tất cả các định nghĩa, điều mắc kẹt trong tâm trí tôi là, khi bạn thực hiện một cam kết, GIT tạo ra một đối tượng cam kết trong kho lưu trữ. Các đối tượng cam kết nên có một cha mẹ (hoặc nhiều cha mẹ nếu đó là một cam kết hợp nhất). Bây giờ, làm thế nào để git biết cha mẹ của cam kết hiện tại? Vì vậy, HEAD là một con trỏ tới (tham chiếu của) cam kết cuối cùng sẽ trở thành cha mẹ của cam kết hiện tại.
Hai điều này có thể làm bạn bối rối:
cái đầu
Chỉ vào tài liệu tham khảo có tên một chi nhánh được gửi gần đây. Trừ khi bạn sử dụng tham chiếu gói, các đầu thường được lưu trữ trong $ GIT_DIR / refs / Heads /.
CÁI ĐẦU
Nhánh hiện tại hoặc cây làm việc của bạn thường được tạo từ cây CHÍNH đang trỏ tới. Đầu phải chỉ vào một đầu, ngoại trừ bạn đang sử dụng một đầu tách rời.
Hãy xem http://git-scm.com/book/en/Git-Branching-What-a-Branch-Is
Hình 3-5. Tập tin CHÍNH chỉ vào chi nhánh bạn đang ở.
HEAD
đề cập đến phụ thuộc vào việc bạn đang nói về một repo trần so với một repo không trần. Trong ngữ cảnh của một repo không trần, nó thực sự đề cập đến cam kết hiện đã thanh toán, không yêu cầu phải có một nhánh gắn liền với nó (tức là khi ở HEAD
trạng thái tách rời ).
Một nhánh thực sự là một con trỏ chứa ID xác nhận, chẳng hạn như 17a5 . CÁI ĐẦU là một con trỏ tới một nhánh mà người dùng hiện đang làm việc.
CÁI ĐẦU có một filw tham chiếu trông như thế này:
tham chiếu:
Bạn có thể kiểm tra các tệp này bằng cách truy cập .git/HEAD
.git/refs
vào kho lưu trữ mà bạn đang làm việc.
Git
là tất cả về cam kết.
Và Head
trỏ đến cam kết mà bạn hiện đang kiểm tra.
$ git cat-file -t HEAD
commit
Bất cứ khi nào bạn kiểm tra một chi nhánh, CHÍNH chỉ đến cam kết mới nhất trên chi nhánh đó. Nội dung của HEAD có thể được kiểm tra như bên dưới (đối với nhánh chính):
$ cat .git/refs/heads/master
b089141cc8a7d89d606b2f7c15bfdc48640a8e25
Là một khái niệm, người đứng đầu là phiên bản mới nhất trong một chi nhánh. Nếu bạn có nhiều hơn một đầu trên mỗi nhánh có tên, bạn có thể đã tạo nó khi thực hiện các cam kết cục bộ mà không hợp nhất, tạo hiệu quả một nhánh không tên.
Để có kho lưu trữ "sạch", bạn nên có một đầu cho mỗi nhánh được đặt tên và luôn hợp nhất với một nhánh được đặt tên sau khi bạn làm việc tại địa phương.
Điều này cũng đúng với Mercurial .