Để mở rộng câu trả lời của Ben Jackson, câu trả lời nào tốt, chúng ta hãy xem xét kỹ câu hỏi ban đầu. (Xem câu trả lời của anh ấy để biết lý do tại sao phải nhập các câu hỏi; đây là thông tin thêm về những gì đang xảy ra .)
Tôi mới sử dụng tính năng kiểm soát phiên bản và tôi hiểu rằng "cam kết" về cơ bản là tạo bản sao lưu trong khi cập nhật phiên bản 'hiện tại' mới của những gì bạn đang làm.
Điều này không hoàn toàn đúng. Sao lưu và kiểm soát phiên bản chắc chắn có liên quan - chính xác mức độ phụ thuộc vào một số thứ ở một mức độ nào đó là vấn đề quan điểm - nhưng chắc chắn có một số khác biệt, nếu chỉ về mục đích: Bản sao lưu thường được thiết kế để phục hồi sau thảm họa (máy bị lỗi, hỏa hoạn phá hủy toàn bộ tòa nhà bao gồm tất cả các phương tiện lưu trữ, v.v.). Kiểm soát phiên bản thường được thiết kế cho các tương tác chi tiết hơn và cung cấp các tính năng mà bản sao lưu không có. Các bản sao lưu thường được lưu trữ trong một thời gian, sau đó bị loại bỏ là "quá cũ": một bản sao lưu mới hơn là tất cả những gì quan trọng. Kiểm soát phiên bản thường lưu mọi phiên bản đã cam kết mãi mãi.
Điều tôi không hiểu là dàn dựng để làm gì, từ góc độ thực tế. Việc dàn dựng một thứ gì đó chỉ tồn tại dưới danh nghĩa hay nó phục vụ một mục đích? Khi bạn cam kết, nó sẽ cam kết mọi thứ, phải không?
Có và không. Thiết kế của Git ở đây hơi đặc biệt. Có những hệ thống kiểm soát phiên bản tồn tại không yêu cầu một bước dàn dựng riêng biệt. Ví dụ, Mercurial, rất giống Git về cách sử dụng, không yêu cầu một hg add
bước riêng biệt , ngoài bước đầu tiên giới thiệu một tệp hoàn toàn mới. Với Mercurial, bạn sử dụng hg
lệnh để chọn một số cam kết, sau đó bạn thực hiện công việc của mình, sau đó bạn chạy hg commit
và hoàn thành. Với Git, bạn sử dụng git checkout
, 1 sau đó bạn làm công việc của mình, sau đó bạn chạy git add
, và sau đó git commit
. Tại sao phải thêm git add
bước?
Bí mật ở đây là những gì Git gọi, khác nhau, chỉ mục hoặc khu vực tổ chức , hoặc đôi khi — hiếm khi ngày nay — bộ nhớ cache . Đây là tất cả các tên cho cùng một thứ.
Chỉnh sửa: Tôi nghĩ rằng tôi có thể đang nhầm lẫn giữa thuật ngữ. Tệp 'theo giai đoạn' có giống với tệp 'được theo dõi' không?
Không, nhưng chúng có liên quan. Một theo dõi tập tin là một trong những tồn tại trong chỉ số của Git. Để hiểu đúng về chỉ mục, tốt hơn là bạn nên bắt đầu với việc hiểu các cam kết.
Kể từ phiên bản Git 2.23, bạn có thể sử dụng git switch
thay thế git checkout
. Đối với trường hợp cụ thể này, hai lệnh này thực hiện chính xác cùng một việc. Lệnh mới tồn tại bởi vì git checkout
quá nhiều thứ; chúng được chia thành hai lệnh riêng biệt git switch
và git restore
để giúp việc sử dụng Git dễ dàng và an toàn hơn.
Cam kết
Trong Git, một cam kết lưu toàn bộ ảnh chụp nhanh của mọi tệp mà Git biết. (Git biết về những tệp nào? Chúng ta sẽ xem điều đó trong phần tiếp theo.) Những ảnh chụp nhanh này được lưu trữ ở dạng đặc biệt, chỉ đọc, chỉ Git, được nén và khử trùng lặp, nói chung chỉ bản thân Git mới có thể đọc . (Có nhiều thứ trong mỗi cam kết hơn chỉ là ảnh chụp nhanh này, nhưng đó là tất cả những gì chúng ta sẽ đề cập ở đây.)
Việc khử trùng lặp giúp tiết kiệm không gian: chúng tôi thường chỉ thay đổi một vài tệp, sau đó thực hiện một cam kết mới. Vì vậy, hầu hết các tệp trong một cam kết hầu hết giống với các tệp trong cam kết trước đó. Bằng cách đơn giản sử dụng lại các tệp đó trực tiếp, Git tiết kiệm rất nhiều dung lượng: nếu chúng ta chỉ chạm vào một tệp, bản cam kết mới chỉ chiếm dung lượng cho một bản sao mới. Ngay cả sau đó nó được nén - đôi khi rất nén, mặc dù điều này thực sự xảy ra sau đó - để một .git
thư mục thực sự có thể nhỏ hơn các tệp mà nó chứa, sau khi chúng được mở rộng thành các tệp bình thường hàng ngày. Việc khử trùng lặp là an toàn vì các tệp đã cam kết luôn được đóng băng. Không ai có thể thay đổi một, vì vậy sẽ an toàn cho các cam kết phụ thuộc vào các bản sao của nhau.
Tuy nhiên, vì các tệp được lưu trữ ở định dạng Git đặc biệt, được đóng băng mọi lúc, nên Git phải mở rộng từng tệp thành một bản sao thông thường hàng ngày. Bản sao thông thường này không phải là bản sao của Git : nó là bản sao của bạn , bạn có thể làm theo ý muốn. Git sẽ chỉ viết thư cho những thứ này khi bạn yêu cầu nó làm như vậy, để bạn có các bản sao của mình để làm việc. Những bản sao có thể sử dụng được ở bạn cây lao động hoặc công việc cây .
Điều này có nghĩa là khi bạn kiểm tra một số cam kết cụ thể, sẽ tự động có hai bản sao của mỗi tệp:
Git có một bản sao Git-ified được đóng băng mọi lúc, mọi nơi trong cam kết hiện tại . Bạn không thể thay đổi bản sao này (mặc dù tất nhiên bạn có thể chọn một cam kết khác hoặc tạo một cam kết mới).
Bạn có một bản sao định dạng bình thường trong cây công việc của mình. Bạn có thể làm bất cứ điều gì bạn muốn bằng cách sử dụng bất kỳ lệnh nào trên máy tính của bạn.
Các hệ thống điều khiển phiên bản khác (bao gồm cả Mercurial như đã đề cập ở trên) dừng ở đây, với hai bản sao này. Bạn chỉ cần sửa đổi bản sao cây công việc của mình, sau đó cam kết. Git ... không.
Chỉ số
Ở giữa hai bản sao này, Git lưu trữ bản sao thứ ba 2 của mọi tệp. Bản sao thứ ba này ở định dạng cố định , nhưng không giống như bản sao cố định trong cam kết, bạn có thể thay đổi nó. Để thay đổi nó, bạn sử dụng git add
.
Các git add
phương tiện lệnh làm cho các bản sao chỉ số của tập tin phù hợp với bản sao công việc cây . Có nghĩa là, bạn đang nói với Git: Hãy thay thế bản sao có định dạng cố định, loại bỏ trùng lặp có trong chỉ mục ngay bây giờ, bằng cách nén bản sao cây công việc đã cập nhật của tôi, khử trùng lặp nó và chuẩn bị đóng băng thành một cam kết mới. Nếu bạn không sử dụng git add
, chỉ mục vẫn giữ bản sao định dạng cố định từ cam kết hiện tại.
Khi bạn chạy git commit
, Git đóng gói bất cứ thứ gì có trong chỉ mục ngay lúc đó để sử dụng làm ảnh chụp nhanh mới. Vì nó đã ở định dạng cố định và đã được khử trùng lặp trước, nên Git không phải làm thêm nhiều việc.
Điều này cũng giải thích tất cả các tệp không được kiểm soát. Tệp chưa được kiểm soát là tệp nằm trong cây công việc của bạn nhưng không có trong chỉ mục của Git ngay bây giờ . Nó không quan trọng bằng cách nào tệp được lưu trữ trong trạng thái này. Có thể bạn đã sao chép nó từ một số nơi khác trên máy tính của mình vào cây công việc của bạn. Có thể bạn đã tạo nó mới ở đây. Có thể có được một bản sao trong chỉ số Git, nhưng bạn loại bỏ bản sao đó với git rm --cached
. Bằng cách này hay cách khác, có một bản sao ở đây trong cây công việc của bạn, nhưng không có bản sao trong chỉ mục của Git. Nếu bạn thực hiện một cam kết mới ngay bây giờ, tệp đó sẽ không có trong cam kết mới.
Lưu ý rằng git checkout
ban đầu điền vào chỉ mục của Git từ cam kết mà bạn kiểm tra. Vì vậy, chỉ mục bắt đầu khớp với cam kết. Git cũng điền vào cây công việc của bạn từ cùng một nguồn này. Vì vậy, ban đầu, cả ba đều hợp nhau. Khi bạn thay đổi các tệp trong cây công việc và git add
chúng, bây giờ chỉ mục và cây công việc của bạn khớp với nhau. Sau đó, bạn chạy git commit
và Git thực hiện một cam kết mới từ chỉ mục và bây giờ cả ba đều khớp lại.
Vì Git thực hiện các cam kết mới từ chỉ mục, chúng ta có thể đặt mọi thứ theo cách này: Chỉ mục của Git giữ cam kết tiếp theo mà bạn dự định thực hiện. Điều này bỏ qua vai trò mở rộng mà chỉ mục của Git đảm nhận trong quá trình hợp nhất xung đột, nhưng dù sao thì bây giờ chúng tôi cũng muốn bỏ qua điều đó. :-)
Đó là tất cả những gì cần làm — nhưng nó vẫn còn khá phức tạp! Nó đặc biệt phức tạp vì không có cách nào dễ dàng để xem chính xác những gì có trong chỉ mục của Git. 3 Nhưng có là một lệnh Git cho bạn biết những gì đang xảy ra, trong một cách đó là khá hữu ích, và lệnh đó là git status
.
2 Về mặt kỹ thuật, đây thực sự không phải là một bản sao . Thay vào đó, nó là một tham chiếu đến tệp Git-ified, đã được khử trùng lặp trước và mọi thứ. Ngoài ra còn có nhiều thứ khác ở đây, chẳng hạn như chế độ, tên tệp, số tổ chức và một số dữ liệu bộ nhớ cache để làm cho Git hoạt động nhanh. Nhưng trừ khi bạn bắt đầu làm việc với một số lệnh cấp thấp của Git - git ls-files --stage
và git update-index
cụ thể là - bạn chỉ có thể coi nó như một bản sao.
3 Các git ls-files --stage
lệnh sẽ cho bạn thấy tên và số dàn của mỗi tập tin trong chỉ số Git, nhưng thường đây không phải là anyway rất hữu ích.
git status
Các git status
lệnh thực sự hoạt động bằng cách chạy hai riêng biệt git diff
lệnh cho bạn (và cũng có thể làm một số công cụ hữu ích khác, chẳng hạn như nói cho bạn mà chi nhánh bạn đang ở trên).
Đầu tiên git diff
so sánh cam kết hiện tại - mà, hãy nhớ rằng, bị đóng băng mọi lúc - với bất kỳ cam kết nào trong chỉ mục của Git. Đối với các tệp giống nhau , Git sẽ không nói gì cả. Đối với các tệp khác nhau , Git sẽ cho bạn biết rằng tệp này được sắp xếp để cam kết . Điều này bao gồm hoàn toàn mới file-nếu cam kết không có sub.py
trong nó, nhưng chỉ số không có sub.py
trong nó, sau đó tập tin này được bổ sung và bất kỳ tập tin bị loại bỏ, đó là (và đang) trong cam kết nhưng không trong chỉ số bất kỳ nữa ( git rm
, có lẽ).
Thứ hai git diff
so sánh tất cả các tệp trong chỉ mục của Git với các tệp trong cây công việc của bạn. Đối với các tệp giống nhau , Git không nói gì cả. Đối với các tệp khác nhau , Git sẽ cho bạn biết rằng tệp này không được sắp xếp để cam kết . Không giống như khác biệt đầu tiên, danh sách cụ thể này không bao gồm các tệp hoàn toàn mới: nếu tệp untracked
tồn tại trong cây công việc của bạn, nhưng không có trong chỉ mục của Git, Git chỉ thêm nó vào danh sách các tệp chưa được theo dõi . 4
Cuối cùng, sau khi tích lũy được những tập tin này untracked trong một danh sách, git status
sẽ công bố những tên file quá, nhưng có một ngoại lệ đặc biệt: nếu tên của một tập tin được liệt kê trong một .gitignore
tập tin, mà ngăn chặn danh sách cuối cùng này. Lưu ý rằng việc liệt kê một tệp được theo dõi — tệp nằm trong chỉ mục của Git — ở .gitignore
đây không có tác dụng gì : tệp nằm trong chỉ mục, vì vậy nó được so sánh và được cam kết, ngay cả khi nó được liệt kê trong .gitignore
. Tệp bỏ qua chỉ ngăn chặn các khiếu nại "tệp không được theo dõi". 5
4 Khi sử dụng phiên bản ngắn của git status
- git status -s
—các tệp không được theo dõi không được tách biệt như nhau, nhưng nguyên tắc thì giống nhau. Việc tích lũy các tệp như thế này cũng cho phép git status
tóm tắt một loạt các tên tệp không được theo dõi bằng cách in tên thư mục, đôi khi. Để có được danh sách đầy đủ, hãy sử dụng git status -uall
hoặc git status -u
.
5 Liệt kê một tệp cũng làm cho việc thêm nhiều thao tác với tệp như git add .
hoặc git add *
bỏ qua tệp không được theo dõi. Phần này phức tạp hơn một chút, vì bạn có thể sử dụng git add --force
để thêm một tệp thường bị bỏ qua. Có một số trường hợp đặc biệt thường nhỏ khác, tất cả đều cộng thêm vào điều này: tệp .gitignore
có thể được gọi đúng cách hơn .git-do-not-complain-about-these-untracked-files-and-do-not-auto-add-them
hoặc một cái gì đó khó sử dụng tương tự. Nhưng điều đó quá vô lý, vì vậy .gitignore
nó là.
git add -u
,, git commit -a
v.v.
Có một số phím tắt tiện dụng cần biết ở đây:
git add .
sẽ thêm tất cả các tệp cập nhật trong thư mục hiện tại và bất kỳ thư mục con nào. Điều này tôn trọng .gitignore
, vì vậy nếu một tệp hiện chưa được theo dõi không bị khiếu nại git status
, nó sẽ không được thêm tự động.
git add -u
sẽ tự động thêm tất cả các tệp cập nhật ở bất kỳ đâu trong cây công việc của bạn . 6 Điều này chỉ ảnh hưởng đến các tệp được theo dõi . Lưu ý rằng nếu bạn đã xóa bản sao cây công việc, điều này cũng sẽ xóa bản sao chỉ mục ( git add
điều này có phải là một phần của nó làm cho chỉ mục khớp với thứ cây công việc).
git add -A
giống như chạy git add .
từ cấp cao nhất của cây công việc của bạn (nhưng hãy xem chú thích 6).
Bên cạnh đó, bạn có thể chạy git commit -a
, tương đương với chạy 7git add -u
và sau đó git commit
. Đó là, điều này giúp bạn có những hành vi tương tự thuận tiện trong Mercurial.
Nói chung, tôi khuyên bạn không nên sử dụng git commit -a
mô hình này: Tôi thấy rằng tốt hơn nên sử dụng git status
thường xuyên, xem xét kỹ đầu ra và nếu trạng thái không như bạn mong đợi, hãy tìm hiểu lý do tại sao lại như vậy. Khi sử dụng git commit -a
, quá dễ dàng để vô tình sửa đổi một tệp và thực hiện một thay đổi mà bạn không định thực hiện. Nhưng đây chủ yếu là vấn đề về sở thích / quan điểm.
6 Nếu phiên bản Git của bạn có trước Git 2.0, hãy cẩn thận ở đây: git add -u
chỉ hoạt động trên thư mục hiện tại và các thư mục con, vì vậy trước tiên bạn phải leo lên cấp cao nhất của cây công việc của mình. Các git add -A
tùy chọn có vấn đề tương tự.
7 Tôi nói gần như tương đương vì git commit -a
thực sự hoạt động bằng cách tạo thêm một chỉ mục và sử dụng chỉ mục khác đó để thực hiện cam kết. Nếu cam kết hoạt động , bạn sẽ có được hiệu quả tương tự như khi thực hiện git add -u && git commit
. Nếu cam kết không hoạt động — nếu bạn làm cho Git bỏ qua cam kết theo bất kỳ cách nào trong số nhiều cách bạn có thể làm điều đó — thì sau đó không có tệp nào được git add
-ed vì Git ném ra chỉ mục bổ sung tạm thời và quay lại sử dụng chỉ mục chính .
Có thêm các biến chứng đi kèm nếu bạn sử dụng git commit --only
ở đây. Trong trường hợp này, Git tạo ra một chỉ mục thứ ba và mọi thứ trở nên rất phức tạp, đặc biệt nếu bạn sử dụng các móc cam kết trước. Đây là một lý do khác để sử dụng các git add
hoạt động riêng biệt .