Bản dịch tiếng Việt của cuốn sách có tựa là “Pro Git” nhằm giúp cộng đồng người Việt tham khảo thuận lợi, không nhằm mục đích thương mại thuộc về tác giả Scott Chacon và Ben Straub. 

CƠ BẢN VỀ GIT (tiếp theo)

2.2 Ghi lại các thay đổi
Lưu thay đổi xảy ra trong repo.
Đến lúc này chắc hẳn bạn có một repo trong máy, và cả một checkout  –  bản sao hiện thời của tất cả các tệp. Thường thì đến một thời khắc nào đó của dự án, bạn cần phải vừa tạo chỉnh sửa và commit những bản sửa (snapshots) ấy.
tệp trong thư mục hiện thời có thể ở trạng thái được theo dõi (tracked) hay không được theo dõi (untracked). Những tệp được theo dõi tồn tại ở cả bạn sửa mới nhất. Chúng có thể ở trạng thái đã được chỉnh sửa (modified), gốc / chưa được chỉnh sửa (unmodified) hay lên kệ chờ (staged). Nói chung, những tệp được theo dõi là những tệp được dò thấy bởi Git.

Tệp không được theo dõi là những tệp còn lại, tồn tại trong thư mục trên ổ đĩa (working directory) nhưng không nằm trong snapshot/commit (bản sửa) gần nhất hay vùng chờ (staging area), chưa được thêm vào bản sửa, chưa được chuẩn bị. Khi thư mục vừa được kích hoạt, các tệp trong thư mục nằm ở trạng thái chưa được theo dõi. Khi vừa sao chép một repo về, các tệp trong repo nằm ở trạng thái được theo dõi nhưng chưa được chỉnh sửa – vì Git vừa mới sao chép và checkout repo qua phiên bản mới nhất và bạn chưa thực sự đụng chạm gì.

Khi bạn chỉnh sửa một tệp, Git đánh dấu tệp đó là đã được chỉnh sửa (Git nhận ra tệp đó trở nên khác đi so với phiên bản gần nhất của nó). Sau đó, bạn có quyền chọn ra những tệp đó và stage chúng (đúng nghĩa là bạn chọn ra và để chúng lên kệ chờ) và commit những thay đổi ấy. Cứ nhứ vậy lặp lại cho đến khi bạn sẵn sàng qua bước tiếp theo. Hình 12. Vòng đời trạng thái của các tệp

 Trạng thái của các tệp

Công cụ chính để kiểm tra trạng thái bên trong repo của bạn là git status. Ví dụ, dùng lệnh này bên trong repo vừa sau khi clone thì đầu ra là:

$ git status
On branch master
Your branch is up-to-date with ‘origin/master’.
nothing to commit, working directory clean

Dòng đầu thông báo rằng các tệp của repo trên ổ đĩa đều đang ở nhánh master – là so với máy chủ ban đầu, tệp trên ổ đĩa chưa nhảy nhánh. Ở chương này chúng ta chỉ hoạt động ở nhánh master nên bạn không cần bận tâm nhiều. Các chi tiết về nhánh và phiên bản sẽ được đề cập thêm ở Git Branching (chưa dịch).
Dòng dưới nghĩa là tệp trên ổ đĩa đã được theo dõi và chưa bị chỉnh sửa. Nếu có tệp nào chưa được theo dõi thì chúng sẽ được liệt kê. Thư mục hiện vẫn còn rỗng, Git vì thế không phát hiện tệp nào chưa được theo dõi.
Ví dụ nữa, lần này mình thêm một tệp là README. Cho rằng tệp chưa tồn tại trước đó, giờ được tạo ra, git status sẽ liệt kê tệp này ở đầu ra:

$ echo ‘My Project’ > README
$ git status
On branch master
Your branch is up-to-date with ‘origin/master’.
Untracked tệps:
  (use “git add <tệp>…” to include in what will be committed)

README
nothing added to commit but untracked tệps present (use “git add” to track)
 tệp ‘README’ (không có đuôi) chưa được theo dõi, và Git liệt kê tệp ở mục “Untracked files” (những tệp chưa được theo dõi) kèm theo gợi ý nên xử lí thế nào (stage tệp đó bằng git add) (sai ngữ pháp tiếng Anh). Untracked nghĩa là tệp đó không tồn tại trong lần commit gần nhất. Git cho thêm phần staging để bắt buộc ta chọn lọc lại tệp cần thiết, tránh lưu những tệp nhị phân dư thừa.
Trong trường hợp này, ta muốn tệp README được theo dõi
Theo dõi các tệp

Để bắt đầu theo dõi một tệp, phải stage tệp đó, nghĩa là dùng git add. Để theo dõi README, dùng git add README. Sau đó kiểm tra lại trạng thái repo, bạn có thể thấy README đã được theo dõi và được staged để chuẩn bị cho commit.
$ git status
On branch master
Your branch is up-to-date with ‘origin/master’.
Changes to be committed:
  (use “git restore –staged <tệp>…” to unstage)
new tệp:   README

tệp sẵn sàng cho việc commit sẽ được hiển thị dưới “Changes to be commited”. Lần này, nếu bạn commit thật thì phiên bản của tệp vừa được staged này sẽ trở thành một phần lịch sử của tệp. Điều này giải thích vì sao khi kích hoạt thư mục ta lại dùng git add ., là để bắt đầu theo dõi các tệp trong ấy. git add nhận đường dẫn đến một tệp hay cả một thư mục. Nếu là thư mục, từng tệp và thư mục trong thư mục đó sẽ được đem vào staging area. Điều này giải thích vì sao git add phải thêm dấu “.”, tức chỉ chính thư mục rễ, là chỉ mọi tệp trong repo.
Stage các tệp
Chúg ta đã bàn về việc stage những tệp chưa được theo dõi. Giờ đến những tệp đã được theo dõi. Giả sử trong thư mục trên ổ đĩa có tệp đã được theo dõi là CONTRIBUTING.md. Nếu bạn thay đổi nội dung trong CONTRIBUTING.md rồi chạy git status thì đầu ra sẽ nhắc bạn ngay:
$ git status
On branch master
Your branch is up-to-date with ‘origin/master’.
Changes to be committed:
  (use “git reset HEAD <tệp>…” to unstage)
new tệp:   README
Changes not staged for commit:
  (use “git add <tệp>…” to update what will be committed)
  (use “git checkout — <tệp>…” to discard changes in working directory)
modified:   CONTRIBUTING.md
Giờ đây tệp được liệt kê dưới “Changes not staged for commit” – tệp đã được theo dõi, đã được chỉnh sửa nhưng chưa được staged. Và như lần trước, đầu ra có gợi ý ta stage tệp đó bằng git add <tệp>, hoặc dùng git checkout — <tệp> để phục hồi tệp đó trở lại trạng thái trước khi thay đổi.
Có thể thấy, git add mang nhiều công dụng khác nhau, từ việc bắt đầu việc theo dõi tệp, đến việc stage tệp, và sau này là đánh dấu giải quyết các điểm hợp / mâu thuẫn. Từ add trong git add không chỉ nghĩa là “thêm tệp” mà là “thêm nội dung vào chờ cho commit tiếp theo”
Chạy git add để stage tệp CONTRIBUTING.md, rồi chạy git status:
$ git add CONTRIBUTING.md
$ git status
On branch master
Your branch is up-to-date with ‘origin/master’.
Changes to be committed:
  (use “git reset HEAD <tệp>…” to unstage)

new tệp:   README
modified:   CONTRIBUTING.md

Cả 2 tệp đã sẵn sàng cho commit. Tuy nhiên, giả sử bạn chợt nhớ và liền quay lại chỉnh CONTRIBUTING.md thêm tí nữa trước khi commit. Bạn mở tệp đó trực tiếp qua vim CONTRIBUTING.md (vim là một trình biên tập văn bản phổ biến với dân lập trình chuyên nghiệp và là phần mềm riêng). Giả sử như thế, nếu sau đó bạn git status thì đầu ra cho thấy:
$ vim CONTRIBUTING.md
$ git status
On branch master
Your branch is up-to-date with ‘origin/master’.
Changes to be committed:
  (use “git reset HEAD <tệp>…” to unstage)
new tệp:   README
modified:   CONTRIBUTING.md

Changes not staged for commit:
  (use “git add <tệp>…” to update what will be committed)
  (use “git checkout — <tệp>…” to discard changes in working directory)
modified:   CONTRIBUTING.md

CONTRIBUTING.md giờ đây xuất dưới cả “Changes to be commited” và “Changes not staged for commit” – vừa được staged vừa chưa được staged? Đây là vì ở lần trước khi bạn dùng git add, Git chỉ stage dữ liệu của tệp đó tại đúng thời điểm đó. Nếu bây giờ bạn commit, phiên bản trước của CONTRIBUTING.md sẽ được dùng thay vì phiên bản chứa những chỉnh sửa vặt ở phút cuối. Vì vậy, nếu đã chỉnh sửa tệp sau git add, hãy nhớ chạy git add một lần nữa để stage phiên bản mới nhất của tệp đó.

$ git add CONTRIBUTING.md
$ git status
On branch master
Your branch is up-to-date with ‘origin/master’.
Changes to be committed:
  (use “git reset HEAD <tệp>…” to unstage)

new tệp:   README
modified:   CONTRIBUTING.md

Theo dõi những tệp – tóm tắt

Git status cho đầu ra với câu từ dễ hiểu song lại rất dài. Git có một flag (đuôi gắn vào cuối lệnh) để bạn có thể kiểm tra những thay đổi một cách súc tích hơn. Chạy git status -s hay git status –short hay git status –porcelain (giống như 2 lệnh trước nhưng không in màu để máy dễ đọc hơn), bạn sẽ có:

$ git status -s
  M README
MM Rakefile
A     lib/git.rb
M    lib/simplegit.rb

??   LICENSE.txt

Các kí tự bên trái có thể được chia làm hai cột. Các kí tự ở cột trong là trạng thái của tệp trên ổ đĩa tính, còn ở cột ngoài là trạng thái của tệp trong staging area.

 

Ở ví dụ trên, ?? nghĩa là tệp chưa được theo dõi, M nghĩa là đã được chỉnh sửa, A là đã được staged.

README, chẳng hạn, đã được chỉnh sửa trên ổ đĩa tính song chưa được staged, còn lib/simplegit.rb thì đã được chỉnh sửa và staged. Rakefile đã được chỉnh sửa, staged nhưng rồi chỉnh sửa lần nữa – xuất hiện 2 phiên bản. Trong khi đó, LICENSE.txt là tệp mới chưa được theo dõi, còn lib/git.rb – cũng là một tệp mới – thì vừa được theo dõi và staged.

Bác bỏ những tệp

Thường, trong repo của bạn sẽ xuất hiện những loại tệp mà bạn không muốn Git tự thêm vào hay thông báo là chưa theo dõi. Những tệp này đa số được tự tạo ra, những mục ẩn, log files, cache, những tệp từ trình xây dựng, biên dịch hay từ hệ thống nói chung. Bạn cần một danh sách đen, tránh một số tệp không cần thiết. Trong các trường hợp như vậy, bạn có thể liệt kê các khuôn mẫu, biểu hiện của những tệp đó trong .gitignore. Dùng cat để hiển thị nội dung bên trong một tệp .gitignore điển hình, ta có:

$ cat .gitignore
*.[oa]
*.log
*.tmp
*~


Dòng đầu tiên ra lệnh Git không tính những tệp kết thúc bằng .o hoặc .a – những tệp object, archive, có thể là sản phẩm của việc biên dịch code. Dòng hai và ba thì chỉ vào những tệp kết với .log hoặc .tmp – những tệp log ghi lại quá trình, những tệp lưu trữ tạm thời,… Dòng tư chỉ những tệp có tên kết thúc bằng dấu ‘~’, là sản phẩm của những trình biên tập văn bản như EMACS dùng để đánh dấu tệp tạm thời. Tương tự, còn nhiều loại tệp khác không cần thiết và có cùng đuôi để dễ dàng xử lí.

Bạn nên tập thói quen tự chỉnh sửa .gitignore để tránh lỡ commit những tệp dư thừa trong repo của bạn.

Có một số quy ước khi viết trong .gitignore là:

 

    • Những dòng trống, những dòng bắt đầu bằng # sẽ không được tính.
    • Có thể sử dụng những glob patterns (*,?,[],…) và áp dụng chúng cho mọi nhánh của thư mục một cách đệ quy.
    • Bắt đầu một kiểu mẫu với dấu ‘/’ để giảm tránh việc loop
    • Kết thúc một kiểu mẫu với dấu ‘/’ để chỉ chính xác một thư mục
    • Nghịch đảo điều kiện bằng dấu ‘!’

Bạn có thể nhận ra rằng các glob patterns ở đây được dùng như trong shell. Dấu sao‘ * ‘ tìm không hoặc nhiều điểm chung; dấu ngoặc vuông ‘[abc]’ tìm điểm chung với bất kì kí tự nào bên trong ngoặc (a,b hoặc c); dấu chấm hỏi ‘ ? ‘ tìm điểm chung với một kí tự duy nhất; dấu ngoặc vuông kèm tập hợp khoảng ‘[0-9]’ tìm điểm chung với bất kì số trong khoảng đó; và hai dấu sao‘ ** ‘ để chỉ bất kì các nhánh, thư mục phụ nào ở trung gian, như a/**/z thì có tập hợp con các kết quả là a/z, a/b/z, a/b/c/z và vân vân.

 

Dẫn chứng:

# mọi tệp .a đều không được tính

*.a

# trừ lib.a là ngoại lệ

!lib.a

# chỉ lược bỏ TODO trong thư mục hiện tại, nhưng vẫn tính TODO trong các nhánh khác như subdir/TODO

/TODO

# lược bỏ mọi tệp bên trong nhánh build

build/

# không tính doc/notes.txt, nhưng vẫn tính doc/server/arch.txt

doc/*.txt

# mặc mọi tệp pdf trong nhánh doc và cũng như trong mọi nhánh khác chỉa ra từ đó.

doc/**/*.pdf


Thêm nữa: để tạo đà cho khởi đầu dự án của bạn, GitHub có sẵn danh sách các tệp .gitignore minh họa cho nhiều dự án và ngôn ngữ lập trình khác nhau tại https://github.com/github/gitignore.

Ghi chú: Thường thì một repo chứa một tệp .gitignore tại thư mục rễ rồi áp dụng cho cả repo. Tuy nhiên bạn có thể thêm .gitignore ở các nhánh (thư mục phụ). Các tệp .gitignore trong mục thư mục chỉ có thể áp dụng luật của mình trong các nhánh từ thư mục chứa tệp đó về sau. Repo nguồn mở của nhân hệ điều hành Linux có 206 tệp .gitignore. Để biết thêm, dùng man gitignore trên Linux.

 

So sánh thay đổi, trước và sau khi staged

 

Nếu bạn cảm thấy cách biểu diễn của git status còn quá mông lung, hay bạn muốn biết rõ hơn, từng thay đổi ở đâu chứ không đơn giản chỉ là tệp có bị thay đổi hay không thì bạn có thể dùng git diff. Ở đây ta chỉ nói sơ về git diff. Lệnh này sẽ giải quyết hai vấn đề cấp bách là “có những gì đã bị chỉnh nhưng chưa được staged” và “nội dung commit sẽ ra sao sau bao chỉnh sửa”. Nếu git status trả lời ngắn gọn bằng cách liệt kê tên tệp, thì git diff cho bạn biết từng dòng chính xác đã được thêm vào hay bị xóa đi, qua đó là một sự so sánh tương quan.

 

Ví dụ bạn tái chỉnh và stage tệp README và chỉnh CONTRIBUTING.md mà chưa stage. Chạy git status và đầu ra sẽ hiện ra như sau:

$ git status

On branch master

Your branch is up-to-date with ‘origin/master’.

Changes to be committed:

  (use “git reset HEAD <tệp>…” to unstage)

 

    modified:   README

 

Changes not staged for commit:

  (use “git add <tệp>…” to update what will be committed)

  (use “git checkout — <tệp>…” to discard changes in working directory)

    modified:   CONTRIBUTING.md

 

Để kiểm những gì đã chỉnh như chưa stage, gõ git diff, không cần điều kiện hay flag gì thêm:

 

$ git diff

diff –git a/CONTRIBUTING.md b/CONTRIBUTING.md

index 8ebb991..643e24f 100644

— a/CONTRIBUTING.md

+++ b/CONTRIBUTING.md

@@ -65,7 +65,8 @@ branch directly, things can get messy.

 Please include a nice description of your changes when you submit your PR;

 if we have to read the whole diff to figure out why you’re contributing

 in the first place, you’re less likely to get feedback and have your change

-merged in.

+merged in. Also, split your changes into comprehensive chunks if your patch is

+longer than a dozen lines.

 

 If you are starting to work on a particular area, feel free to submit a PR

 that highlights your work in progress (and note in the PR title that it’s

Lệnh này so sánh những tệp trên ổ đĩa và . Kết quả thông báo những chỉnh sửa trên ổ đĩa mà chưa được stage.

Còn nếu ưu tiên của bạn là kiểm tra thay đổi trước khi commit, dùng git diff –staged để so những thay đổi đã được staged so với lần commit gần nhất.

 

$ git diff –staged

diff –git a/README b/README

new tệp mode 100644

index 0000000..03902a1

— /dev/null

+++ b/README

@@ -0,0 +1 @@

+My Project

Lưu ý rằng git diff không hiển thị mọi thay đổi kể từ lần commit gần nhất, mà chỉ những thay đổi chưa được staged. Nếu đã staged, git diff sẽ có đầu ra trống, nên git diff –staged sẽ phù hợp hơn.

 

Một ví dụ nữa là trường hợp bạn đã stage CONTRIBUTING.md nhưng lại chỉnh tệp một lần nữa, lúc này bạn dùng git diff để xem thay đổi nào đã được staged hoặc chưa được staged. Nếu tình hình trong repo cho kết quả: 

$ git add CONTRIBUTING.md

$ echo ‘# test line’ >> CONTRIBUTING.md

$ git status

On branch master

Your branch is up-to-date with ‘origin/master’.

Changes to be committed:

  (use “git reset HEAD <tệp>…” to unstage)

 

    modified:   CONTRIBUTING.md

 

Changes not staged for commit:

  (use “git add <tệp>…” to update what will be committed)

  (use “git checkout — <tệp>…” to discard changes in working directory)

 

    modified:   CONTRIBUTING.md

Giờ bạn dùng git diff để xem những gì chưa được staged. 

 

$ git diff

diff –git a/CONTRIBUTING.md b/CONTRIBUTING.md

index 643e24f..87f08c8 100644

— a/CONTRIBUTING.md

+++ b/CONTRIBUTING.md

@@ -119,3 +119,4 @@ at the

 ## Starter Projects

 

 See our [projects list](https://github.com/libgit2/libgit2/blob/development/PROJECTS.md).

+# test line

Và dùng git diff –cached hay –staged để xem những thay đổi đã được staged (–staged đồng nghĩa với –cached):

$ git diff –cached

diff –git a/CONTRIBUTING.md b/CONTRIBUTING.md

index 8ebb991..643e24f 100644

— a/CONTRIBUTING.md

+++ b/CONTRIBUTING.md

@@ -65,7 +65,8 @@ branch directly, things can get messy.

 Please include a nice description of your changes when you submit your PR;

 if we have to read the whole diff to figure out why you’re contributing

 in the first place, you’re less likely to get feedback and have your change

-merged in.

+merged in. Also, split your changes into comprehensive chunks if your patch is

+longer than a dozen lines.

 

 If you are starting to work on a particular area, feel free to submit a PR

 that highlights your work in progress (and note in the PR title that it’s

Ghi chú: 

Git diff nhưng là công cụ bên thứ ba: trong các bài sau git diff sẽ tiếp tục được sử dụng. Mặc dù vậy, vẫn còn nhiều hướng tiếp cận diff nếu bạn ưa hơn một trình hiển thị có đồ họa chỉnh chu hơn. Nếu bạn chạy git difftool thay vì git diff, bạn có thể xem những diffs này trong phần mềm như emerge, vimdiff và nhiều chương trình khác (bao gồm các sản phẩm thương mại). Để tìm hiểu thêm những sự lựa chọn khác bạn có thể có, dùng git difftool –tool-help.

 

Commit (những thay đổi của) các tệp.

 

Sau khi chỉnh chu staging area, đã đến lúc bạn commit. Nhớ lại rằng tất cả những gì chưa được staged – bao gồm những tệp bạn chưa chạy git add để thêm vào kể từ lần cuối chỉnh sửa chúng – sẽ không đi cùng commit này. Chúng sẽ vẫn nằm trên ổ đĩa, các tệp bị đánh dấu đã được chỉnh sửa. Cho rằng bạn đã chạy git status lần cuối và kiểm tra thấy mọi thứ đã được staged kĩ càng. Bạn đã sẵn sàng commit. Chỉ cần dùng lệnh git commit để làm việc này.

 

$ git commit

 

Nhập như vậy vào, trình biên tập văn bản của bạn sẽ hiện lên.

 

Ghi chú: Cái này phụ thuộc vào environment variable (biến môi trường) tên EDITOR trong shell của bạn. Thường đường dẫn sẽ hướng đến vim hoặc emacs, song bạn có thể điều chỉnh thoải mái qua git config –global core.editor. Xem thêm ở bài 1 về cách chuẩn bị làm việc vơi với Git.

 

Trình biên tập văn bản sẽ hiện lên đầu ra như sau (ví dụ màn hình của vim)

 

# Please enter the commit message for your changes. Lines starting

# with ‘#’ will be ignored, and an empty message aborts the commit.

# On branch master

# Your branch is up-to-date with ‘origin/master’.

#

# Changes to be committed:

# new tệp:   README

# modified:   CONTRIBUTING.md

#

~

~

~

“.git/COMMIT_EDITMSG” 9L, 283C

Lời nhắn / bình luận thông thường bao gồm một dòng đầu trống để ghi lời nhắn của bản thân, cộng đầu ra git status gần đây nhất nhưng bị commented out (bình luận hóa, bằng cách để dấu thăng ở trước). Bạn có thể để yên đầu ra ấy để ghi chú những thay đổi, hoặc xóa hết và ghi lại lời nhắn của mình.

 

Ghi chú: bạn vẫn có thể kèm một đầu ra chi tiết hơn. Kèm theo flag -v khi gõ git commit, bạn sẽ kèm thêm đầu ra của git diff vào lời nhắn.

 

Khi bạn đã viết xong, lưu và thoát trình biên tập văn bản, Git tạo một commit với lời nhắn commit đã ghi (bỏ kết quả của git status và git diff)

 

Tối ưu hơn, bạn có thể gõ ngay lời nhắn khi đang gõ lệnh bằng cách thêm flag -m như thế này:

 

$ git commit -m “Story 182: fix benchmarks for speed”

[master 463dc4f] Story 182: fix benchmarks for speed

 2 tệps changed, 2 insertions(+)

 create mode 100644 README

Làm vậy, bạn đã đính kèm lời nhắn “Story 182: fix benchmarks for speed”. Sau khi tạo commit, đầu ra cho bạn một số thông tin như nhánh mà bạn commit đến (là master), dãy SHA-1 checksum của commit này (463dc4f), số tệp được chỉnh, số dòng được thêm xóa,…

 

Lưu ý rằng, cũng như add, việc commit chỉ tính dữ liệu của các tệp ở một khoảnh khắc. Những gì bạn chưa có cơ hội stage vẫn sẽ nằm đó với trạng thái đã bị chỉnh sửa, và lần sau bạn có thể stage và commit những thay đổi của chúng, ghi lại lịch sử repo một cách đầy đủ hơn. Mỗi lần bạn commit là lưu lại một khoảnh khắc trong lịch sử repo, từ đó dùng làm backup hay để đối chiếu sau này. 

 

Bỏ bước staging

 

Mặc dù việc chu chuốc cho mỗi commit qua staging là thú vị biết bao, đôi lúc việc này chỉ phức tạp hóa vấn đề. Git đã chuẩn bị phím tắt cho người muốn bỏ bước staging: thêm flag -a vào git commit khiến git tự động git add các tệp đã được theo dõi từ trước rồi commit liền tay: 

$ git status

On branch master

Your branch is up-to-date with ‘origin/master’.

Changes not staged for commit:

  (use “git add <tệp>…” to update what will be committed)

  (use “git checkout — <tệp>…” to discard changes in working directory)

 

    modified:   CONTRIBUTING.md

 

no changes added to commit (use “git add” and/or “git commit -a”)

$ git commit -a -m ‘Add new benchmarks’

[master 83e38c7] Add new benchmarks

 1 tệp changed, 5 insertions(+), 0 deletions(-)

Bạn không cần chạy git add để stage CONTRIBUTING.md trước khi commit, vì flag -a đã tính hết mọi tệp đã được chỉnh sửa vào danh sách add. Thuận tiện không nói gì, đôi lúc câu lệnh này gây nên những hệ quả bạn không nghĩ đến.

 

Xóa các tệp

 

Các tệp với chức năng của Git chỉ thực sự tồn tại nếu chúng được theo dõi. Xóa tệp theo kiểu Git là xóa tệp khỏi staging area, là bỏ theo dõi tệp, rồi commit chính thay đổi đó. Lệnh git rm vừa làm thế, vừa xóa tệp trên ổ đĩa để lần sau không còn thông báo rằng tệp chưa được theo dõi.

 

Nếu bạn chỉ xóa trực tiếp từ ổ đĩa, dùng git status thì thấy tệp sẽ vẫn hiện lên ở “Changes not staged for commit” (nghĩa là unstaged, vì thay đổi diễn ra trên ổ đĩa nhưng chưa diễn ra ở staging area).

 

$ rm PROJECTS.md

$ git status

On branch master

Your branch is up-to-date with ‘origin/master’.

Changes not staged for commit:

  (use “git add/rm <tệp>…” to update what will be committed)

  (use “git checkout — <tệp>…” to discard changes in working directory)

 

        deleted:    PROJECTS.md

 

no changes added to commit (use “git add” and/or “git commit -a”)

Nên phải dùng git rm thì việc xóa tệp mới được staged.

 

$ git rm PROJECTS.md

rm ‘PROJECTS.md’

$ git status

On branch master

Your branch is up-to-date with ‘origin/master’.

Changes to be committed:

  (use “git reset HEAD <tệp>…” to unstage)

 

    deleted:    PROJECTS.md

Nếu bạn commit sau đó, tệp sẽ biến mất và không còn được theo dõi. Trường hợp tệp bị xóa trên ổ đĩa sau khi đã được staged, xóa tệp cần phải bị bắt buộc qua việc thêm flag -f. Đây là cầu dao tránh việc xóa tệp theo quán tính, nhất là khi thay đổi đó chưa được committed, lưu lại, và không thể được phục hồi. 

 

Lẽ đương nhiên bạn muốn có khả năng xóa tệp trong staging area nhưng trừ trên ổ đĩa, bỏ theo dõi nhưng không muốn xóa tệp hoàn toàn. Ví dụ, bạn quên thêm vào .gitignore một số tệp không cần thiết, lỡ tay stage hết và hậu quả là một chùm tệp biên dịch .a trong staging area. Nếu vậy, dùng flag –cached:

$ git rm –cached README

Bạn có thể kèm đường dẫn tệp, thư mục hay các glob patterns nói chung vào lệnh. Cũng nghĩa là bạn có thể gõ như: 

$ git rm log/\*.log

Để ý dấu ‘\” đằng trước dấu sao. Khi chạy trên bash ta tính cả filename expansion (quy ước dùng glob patterns) của Git và của shell. Nói chung, lệnh trên xóa các tệp đuôi .log trong thư mục log/. Tương tự, bạn có câu lệnh như:

$ git rm \*~

Lệnh này xóa các tệp kết thúc tên bằng ~.

 

“Di chuyển” các tệp

 

Không giống như các hệ thống VCS khác, Git không trực tiếp theo dõi sự dịch chuyển của tệp, không có siêu dữ liệu nào đánh dấu tệp có vừa bị đổi tên hay không. Mặc dù vậy, Git vẫn có một số cách nhận biết, nhưng đó vấn đề chúng ta sẽ bàn về sau.

 

Bởi, cũng hơi dễ nhầm lẫn khi ta dùng lệnh git mv. Nếu muốn đổi tên tệp, ta dùng:

$ git mv tệp_from tệp_to

Theo lí thuyết, đây là việc cut và paste tệp với chức năng đổi tên đầu cuối. Bình thường, bạn có thể coi nó như chức năng đổi tên. Thực tế, nếu bạn chạy lệnh này rồi git status, bạn sẽ thấy gần như Git xem đây là đổi tên:

$ git mv README.md README

$ git status

On branch master

Your branch is up-to-date with ‘origin/master’.

Changes to be committed:

  (use “git reset HEAD <tệp>…” to unstage)

 

    renamed:    README.md -> README

Nó đồng nghĩa với chạy dãy lệnh sau:

$ mv README.md README

$ git rm README.md

$ git add README

Dù cách nào đi chăng nữa, Git vẫn cho rằng đó là quá trình đổi tên. Chỉ rằng, một câu lệnh thì hết sức tối giản và thuận tiện hơn 3. Quan trọng hơn, đó là sự thích ứng, vì để bổ trợ cho việc add/rm các tệp về sau để chuẩn bị commit, trước đó bạn có thể dùng biết bao cách để đổi tên tệp. ?

 

Bạn nên tập thói quen tự chỉnh sửa .gitignore để tránh lỡ commit những tệp dư thừa trong repo của bạn.

Có một số quy ước khi viết trong .gitignore là:

    • Những dòng trống, những dòng bắt đầu bằng # sẽ không được tính.
    • Có thể sử dụng những glob patterns (*,?,[],…) và áp dụng chúng cho mọi nhánh của thư mục một cách đệ quy.
    • Bắt đầu một kiểu mẫu với dấu ‘/’ để giảm tránh việc loop
    • Kết thúc một kiểu mẫu với dấu ‘/’ để chỉ chính xác một thư mục
    • Nghịch đảo điều kiện bằng dấu ‘!’

Bạn có thể nhận ra rằng các glob patterns ở đây được dùng như trong shell. Dấu sao‘ * ‘ tìm không hoặc nhiều điểm chung; dấu ngoặc vuông ‘[abc]’ tìm điểm chung với bất kì kí tự nào bên trong ngoặc (a,b hoặc c); dấu chấm hỏi ‘ ? ‘ tìm điểm chung với một kí tự duy nhất; dấu ngoặc vuông kèm tập hợp khoảng ‘[0-9]’ tìm điểm chung với bất kì số trong khoảng đó; và hai dấu sao‘ ** ‘ để chỉ bất kì các nhánh, thư mục phụ nào ở trung gian, như a/**/z thì có tập hợp con các kết quả là a/z, a/b/z, a/b/c/z và vân vân.

Dẫn chứng:

# mọi tệp .a đều không được tính

*.a

# trừ lib.a là ngoại lệ

!lib.a

# chỉ lược bỏ TODO trong thư mục hiện tại, nhưng vẫn tính TODO trong các nhánh khác như subdir/TODO

/TODO

# lược bỏ mọi tệp bên trong nhánh build

build/

# không tính doc/notes.txt, nhưng vẫn tính doc/server/arch.txt

doc/*.txt

# mặc mọi tệp pdf trong nhánh doc và cũng như trong mọi nhánh khác chỉa ra từ đó.

doc/**/*.pdf


Thêm nữa: để tạo đà cho khởi đầu dự án của bạn, GitHub có sẵn danh sách các tệp .gitignore minh họa cho nhiều dự án và ngôn ngữ lập trình khác nhau tại https://github.com/github/gitignore.

Ghi chú: Thường thì một repo chứa một tệp .gitignore tại thư mục rễ rồi áp dụng cho cả repo. Tuy nhiên bạn có thể thêm .gitignore ở các nhánh (thư mục phụ). Các tệp .gitignore trong mục thư mục chỉ có thể áp dụng luật của mình trong các nhánh từ thư mục chứa tệp đó về sau. Repo nguồn mở của nhân hệ điều hành Linux có 206 tệp .gitignore. Để biết thêm, dùng man gitignore trên Linux.

Leave a Reply

Your email address will not be published. Required fields are marked *