Lưu thay đổi trong Git

Chúng ta đã biết Thiết lập kho lưu trữ trong Git bằng các câu lệnh git init, git clone, tiếp theo chúng ta sẽ tìm hiểu các câu lệnh liên quan đến việc lưu các thay đổi, đây là phần công việc chính trong quản lý phiên bản.

Luồng làm việc trong Git

Chúng ta cũng đã làm quen với các khái niệm working directory, staging area (cache), local repository (3 thành phần này nằm trên máy của bạn), remote repository (nằm trên máy chủ git), toàn bộ các khái niệm này đều nằm trong luồng làm việc của Git.

git add

Câu lệnh git add sẽ lưu tất cả các thay đổi từ working directory sang staging area (vùng đệm), nó nói với Git rằng bạn muốn cập nhật một số file trong lần commit tới. Tuy nhiên, các thay đổi này chưa ảnh hưởng đến kho lưu trữ, các thay đổi chỉ được ghi nhận cho đến khi sử dụng git commit. Khi sử dụng git add, bạn cần biết những file nào chưa đưa vào cập nhật, có thể sử dụng git status để kiểm tra.

Chúng ta cùng thực hiện với git-test đã được tạo ra trong bài thiết lập repository trong Git. Tạo một file là index.html trong git-test và lưu vào nội dung đơn giản sau:

<!DOCTYPE html>
<html>
<head>
	<title>Git example - allaravel.com</title>
</head>
<body>
	<h1>Lưu thay đổi trên Git</h1>
</body>
</html>

Tại thư mục git-test chúng ta sử dụng câu lệnh git status để xem trước và sau khi lưu thay đổi vào vùng đệm (staging area) có gì khác nhau:

c:\xampp\htdocs\git-test>git status
On branch master

Initial commit

Untracked files:
  (use "git add <file>..." to include in what will be committed)

        index.html

nothing added to commit but untracked files present (use "git add" to track)

Nó sẽ thông báo rằng file index.html chưa được đưa vào để lưu vết thay đổi (tức là file index.html chưa có trong vùng đệm), câu lệnh git add sẽ đưa file index.html vào vùng đệm.

c:\xampp\htdocs\git-test>git add index.html

Ok, chúng ta kiểm tra lại bằng lệnh git status xem thế nào

c:\xampp\htdocs\git-test>git status
On branch master

Initial commit

Changes to be committed:
  (use "git rm --cached <file>..." to unstage)

        new file:   index.html

Chúng ta thấy nó đã thông báo file mới được thêm vào vùng đệm, để loại bỏ khỏi vùng đệm sử dụng git rm –cached. Tiếp theo chúng ta thử thay đổi nội dung file index.html và thực hiện git status xem thông báo như thế nào. Nội dung file index.html đã thay đổi:

<!DOCTYPE html>
<html>
<head>
	<title>Git example - allaravel.com</title>
</head>
<body>
	<h1>Lưu thay đổi trên Git</h1>
	<p>Thay đổi lần thứ nhất</p>
</body>
</html>

Sử dụng lệnh git status

c:\xampp\htdocs\git-test>git status
On branch master

Initial commit

Changes to be committed:
  (use "git rm --cached <file>..." to unstage)

        new file:   index.html

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

        modified:   index.html

Chúng ta thấy thông báo rằng file index.html đã có trong vùng đệm và file này cũng đã được thay đổi trong working directory, để update nó sang vùng đệm (staging area) sử dụng lệnh git add.

Kết luận, câu lệnh git add sẽ lưu toàn bộ các file mới hoặc thay đổi từ working directory sang staging area. git add có các tham số tùy chọn như sau:

// Lưu thay đổi trên một file
git add <file_name>
// Lưu thay đổi toàn bộ thư mục hiện hành
git add .

Tại sao phải có vùng đệm staging area trong Git

Staging area đúng như tên gọi của nó là một vùng đệm giữa working directory và local repository. Thay vì commit tất cả thay đổi, bạn có thể nhóm các thay đổi vào vùng đệm và commit vào local repository. Nó giúp bạn chia các thay đổi ra theo các logic riêng. Bạn đang phát triển tính năng liên hệ thì bạn có thể chia ra các lần commit như commit giao diện trang liên hệ, commit phần xử lý lưu thông tin liên hệ vào CSDL… Staging area chính là điểm khác biệt của Git với SVN, Mercurial…

git commit

Câu lệnh git commit sẽ ghi một snapshot (một ảnh chụp hay là toàn bộ dữ liệu hiện có) của vùng đệm vào kho lưu trữ lịch sử thay đổi dự án (local repository). Snapshot chính là một phiên bản của dự án và bạn cần quản lý. Câu lệnh git commit sẽ mở ra một trình soạn thảo để bạn có thể đưa vào các ghi chú về lần commit này.

Nhập ghi chú commit khi sử dụng câu lệnh git commit

Hoặc có thể sử dụng tùy chọn -m để nhập vào ghi chú luôn như sau:

git commit -m "<message>"
$ git commit -m "Lan commit thu nhat"
[master (root-commit) 1821b82] Lan commit thu nhat
 1 file changed, 10 insertions(+)
 create mode 100644 index.html

Chúng ta cũng có thể bỏ qua vùng đệm mà commit thẳng lên local repository bằng lệnh git commit với tùy chọn -a.

$git commit -a -m "Lan commit thu nhat"

Trong Git, các snapshot luôn được commit lên kho lưu trữ cục bộ (local repository), đây là điều khác biệt cơ bản so với SVN, SVN commit lên kho lưu trữ trung tâm (central repository). Git không muốn bạn phải tương tác ngay với kho lưu trữ trung tâm khi bạn chưa sẵn sàng. Một sự khác biệt nữa là SVN lưu sự khác biệt giữa các lần thay đổi còn Git lưu trữ các snapshot.

git stash

Để giúp bạn hiểu git stash tôi đưa ra một ví dụ, giả sử bạn đang phát triển một tính năng mới cho sản phẩm, bạn đang tiến hành viết mã thì một yêu cầu mới của khách hàng đến, bạn cần lưu lại tính năng này trong vài giờ và quay trở lại viết tiếp, bạn không thể commit tính năng này khi đang dở dang, do đó bạn cần một không gian lưu trữ tạm thời và git stash được sử dụng cho yêu cầu này. Git stash cũng thường được sử dụng khi bạn đổi sang một branch khác mà ở branch hiện tại vẫn đang dở dang.

Ví dụ luồng làm việc của Git stash

git stash save <comment>

Câu lệnh này sẽ lưu lại nội dung đang làm việc dở dang và xóa tất cả khỏi vùng đệm giúp cho branch bạn đang làm việc trở nên “sạch sẽ”. Nếu không có thêm tùy chọn save <comment> thì

git stash

sẽ dùng tên mặc định theo kiểu WIP on <branch> <last commit message> cho lần lưu này. Sau khi đã lưu lại, bạn có thể xem danh sách các lần thay đổi bằng cách thêm tùy chọn list

git stash list

Chúng ta thực hành tiếp ví dụ với file index.html theo thứ tự như sau:

  1. Thêm dòng <p>Thay đổi lần thứ hai</p> vào index.html
  2. git add index.html
  3. git status xem thế nào.
  4. Thêm dòng <p>Thay đổi lần thứ ba</p> vào index.html
  5. git status xem thế nào.
  6. git stash save “Dang lam o thay doi lan thu 3”
  7. git stash list
  8. Mở file index.html lại để xem kết quả
Admin@Admin-PC MINGW64 /c/xampp/htdocs/git-test (master)
$ git status
On branch master
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

        modified:   index.html

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

Admin@Admin-PC MINGW64 /c/xampp/htdocs/git-test (master)
$ git add .

Admin@Admin-PC MINGW64 /c/xampp/htdocs/git-test (master)
$ git status
On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

        modified:   index.html


Admin@Admin-PC MINGW64 /c/xampp/htdocs/git-test (master)
$ git stash save "Dang thay doi lan 3"
Saved working directory and index state On master: Dang thay doi lan 3
HEAD is now at 49a88f3 Thay doi lan 1

Admin@Admin-PC MINGW64 /c/xampp/htdocs/git-test (master)
$ git stash list
stash@{0}: On master: Dang thay doi lan 3

Ok, giờ xem lại file index.html thì nội dung quay trở về lúc đầu trước khi chúng ta thực hiện từng đấy công việc. Chúng ta thực hiện thêm một lần nữa:

  1. Thêm dòng <p>Thay đổi lần thứ tư</p>
  2. git status
  3. git stash save “Dang thay doi lan 4”
  4. git status
  5. Kiểm tra file index.html
  6. git stash list

Câu lệnh git stash list có thể in ra màn hình theo định dạng màu sắc giúp chúng ta nhìn dễ hơn, ví dụ sau:

git stash list --pretty=format:'%Cgreen%h %Cred* %C(yellow)%s'

Kết quả danh sách đầu ra sẽ hiển thị màu dễ nhìn hơn

Sử dụng pretty format trong câu lệnh git stash list

Để xem các lần thay đổi này có những gì chúng ta sử dụng lệnh git stash show, mặc định nó sẽ hiện ra chi tiết lần thay đổi gần nhất, bạn có thể thêm các tham số đầu vào stash@{<revision>}.

Xem chi tiết các lần thay đổi bằng git stash show

Để xem chi tiết hơn nữa có thể dùng thêm cờ -p

git stash show stash@{1} -p

Chúng ta đã lưu lại các lần thay đổi các nhau, tiếp theo chúng ta sẽ tìm cách quay về với các lần thay đổi này. Câu lệnh git stash apply sẽ giúp chúng ta quay lại các lần thay đổi.

Admin@Admin-PC MINGW64 /c/xampp/htdocs/git-test (master)
$ git stash apply stash@{1}
On branch master
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

        modified:   index.html

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

Chúng ta xem lại file index.html thì thấy quay lại trước khi chúng ta sử dụng git stash save “Dang thay doi lan 3”.

Sau khi quay lại các thay đổi, có những thay đổi không cần thiết chúng ta có thể xóa khỏi danh sách bằng git stash drop stash@{<revision>} như sau:

Admin@Admin-PC MINGW64 /c/xampp/htdocs/git-test (master)
$ git stash apply stash@{1}
On branch master
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

        modified:   index.html

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

Admin@Admin-PC MINGW64 /c/xampp/htdocs/git-test (master)
$ git stash drop stash@{1}
Dropped stash@{1} (3a6f97c72f713439ae6efd47e5a0e0003849e9a5)

Hoặc

git stash pop stash@{1}

Hoặc thậm chí nếu chúng ta xóa toàn bộ danh sách các lần thay đổi

git stash clear

Như vậy câu lệnh git stash có khá nhiều thứ chúng ta cùng tóm tắt lại cho dễ nhớ:

  • git stash: Lưu thay đổi với title mặc định WIP on <branch> <last commit message>
  • git stash save <title>: Lưu lại thay đổi với title.
  • git stash list: Danh sách các lần thay đổi
  • git stash show stash@{<revision>}: Xem thay đổi, sử dụng cờ -p để xem chi tiết lần thay đổi đó.
  • git stash apply stash@{<revision>}: Trở về lần thay đổi <revision>
  • git stash drop stash@{<revision>}: Xóa lần thay đổi <revision>
  • git stash pop stash@{<revision>}: Trở về và xóa lần thay đổi <revision>
  • git stash clear: Xóa toàn bộ các lần thay đổi.

.gitignore

Git phân loại các file trong working directory thành 3 loại:

  • tracked: một file đã được sử dụng câu lệnh git add để đưa vào staging area hoặc git commit để đưa vào local repository.
  • untracked: file chưa được đưa vào staging area hoặc local repository.
  • ignored: là các file bỏ qua trong các xử lý của Git.

Trong project có thể có rất nhiều các loại file mà không cần xử lý trong quản lý phiên bản như:

  • File metadata như các file tạo ra bởi các IDE (Môi trường tích hợp phát triển)
  • File có thể sử dụng kịch bản để tạo ra như các thư mục vendor trong các ứng dụng PHP có thể sử dụng Composer để tạo ra hoặc thư mục node_modules sử dụng cho npm…
  • Các file runtime như file log, cache hoặc các file tạm .tmp
  • Các file sử dụng bởi hệ điều hành như .DS_Store, Thumbs.db

Để bỏ qua các file này trong quản lý phiên bản bằng Git, chúng ta sẽ đưa các danh sách này vào file .gitignore ở thư mục gốc của kho lưu trữ. .gitignore có một số các mẫu tạo ra các quy tắc để bỏ qua xử lý:

Mẫu Ví dụ Mô tả
**/logs logs/debug.log
logs/monday/foo.bar
build/logs/debug.log
Sử dụng hai dấu * nếu bạn muốn bỏ qua tất cả các thư mục trong kho lưu trữ có từ logs
**/logs/debug.log logs/debug.log
build/logs/debug.log
không bỏ qua
logs/build/debug.log
*.log debug.log
foo.log
.log
logs/debug.log
Sử dụng dấu * để bỏ qua tất cả các thư mục, file có kết thúc là log
*.log
!important.log
debug.log
trace.log
không bỏ qua
important.log
logs/important.log
*.log
!important/*.log
trace.*
debug.log
important/trace.log
không bỏ qua
important/debug.log
/debug.log debug.log
không bỏ qua
logs/debug.log
debug.log debug.log
logs/debug.log
debug?.log debug0.log
debugg.log
không bỏ qua
debug10.log
Dấu ? cho bất kỳ 1 ký tự
debug[0-9].log debug0.log
debug1.log
không bỏ qua
debug10.log
dấu [a-z], [0-9] sử dụng cho một khoảng giá trị
debug[01].log debug0.log
debug1.log
không bỏ qua
debug2.log
debug01.log
dấu [abc], [123] sử dụng cho giá trị xác định trong tập cho trước
debug[!01].log debug2.log
không bỏ qua
debug0.log
debug1.log
debug01.log
Phủ định của []
debug[a-z].log debuga.log
debugb.log
không bỏ qua
debug1.log
logs logs
logs/debug.log
logs/latest/foo.bar
build/logs
build/logs/debug.log
tất cả các thư mục và file có logs là bỏ qua
logs/ logs/debug.log
logs/latest/foo.bar
build/logs/foo.bar
build/logs/latest/debug.log
có thêm dấu / để ám chỉ thư mục, tất cả các thư mục có logs sẽ được bỏ qua.
logs/
!logs/important.log
logs/debug.log
logs/important.log
logs/**/debug.log logs/debug.log
logs/monday/debug.log
logs/monday/pm/debug.log
logs/*day/debug.log logs/monday/debug.log
logs/tuesday/debug.log
không bỏ qua
logs/latest/debug.log
logs/debug.log logs/debug.log
không bỏ qua
debug.log
build/logs/debug.log

 

Add Comment