Làm web (13) - Git: tích hợp căn bản

Bài học trước: Làm web (12) - Git: phân nhánh căn bản
-----
Cơ bản về tích hợp

Giả sử bạn đã hoàn thành công việc trên nhánh iss53 và quyết định tích hợp vào nhánh master. Nhánh master là nhánh được tích hợp vào, tạm gọi là nhánh chính, nhánh iss53 gọi là nhánh phụ. Để tích hợp, bạn cần chuyển con trỏ HEAD về nhánh chính, sau đó tích hợp bằng lệnh $ git merge.

Ví dụ,

Maxsys@DESKTOP-7LPDOL6 MINGW64 /e/langbiang (iss53)
$ git checkout master
Switched to branch 'master'

$ git merge iss53
Merge made by the 'recursive' strategy.
 index.html              |   2 +
 ting) test chuyen nhanh | 238 ++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 240 insertions(+)
 create mode 100644 index.html
 create mode 100644 ting) test chuyen nhanh

Sau khi gõ lệnh $ git merge iss53, Git sẽ mở một cửa sổ soạn thảo, cho phép nhập thông điệp (message) cho việc merge, bạn nhập thông điệp, đóng cửa sổ soạn thảo để Git hoàn thành việc tích hợp. Nếu không muốn Git mở cửa sổ soạn thảo thì thêm tham số -m “nội dung thông điệp” ngay trong lệnh $ git merge.

Ví dụ,

$ git merge iss53 -m “tich hop iss53 vao master”

Lần tích hợp này khác so với lần tích hợp nhánh hotfix trước đây. Ở lần này, lịch sử commit của master và iss53 đã đi theo hai nhánh khác nhau, do vậy, việc tích hợp không đơn là di chuyển con trỏ master tiến lên như khi tích hợp hotfix.

Xem hình minh họa,



Trong lần tích hợp này, Git sẽ tạo một commit mới, mà snapshot của commit chính là sự kết hợp của 3 snapshot khác, gồm: hai snapshot mới nhất của master và iss53, cùng với snapshot là “cha” chung (common ancestor) của master và iss53. Không giống như các commit thông thường chỉ có một commit cha, commit được tạo ra bởi quá trình merge này sẽ có hai commit cha.

Xem kết quả,

$ git log --oneline --decorate --all --graph
*   7e24aee (HEAD -> master) Merge branch 'iss53'
|\
| * b765740 (iss53) da them logo [issue 53]
| * 1cef1be them logo cho home page
* | e31a627 sua lai logo
|/
* 47ed23e thay doi tren master branch
| * cd69866 (testing) test chuyen nhanh
|/
* a89362e (tag: v1.4-lw, tag: v1.3, tag: v1.2,

Xem hình minh họa,


  
Việc tích hợp đã hoàn thành, nhánh iss53 không còn cần thiết nữa, do vậy bạn có thể xóa nó đi, và kết thúc công việc có số hiệu #53.

$ git branch -d iss53
Deleted branch iss53 (was b765740).

$ git log --oneline --decorate --all --graph
*   7e24aee (HEAD -> master) Merge branch 'iss53'
|\
| * b765740 da them logo [issue 53]
| * 1cef1be them logo cho home page
* | e31a627 sua lai logo
|/
* 47ed23e thay doi tren master branch
| * cd69866 (testing) test chuyen nhanh
|/
* a89362e (tag: v1.4-lw, tag: v1.3, tag: v1.2,

Giải quyết xung đột khi tích hợp

Việc trộn nhánh không phải luôn luôn thành công. Có thể có xung đột (confict) giữa hai nhánh. Xung đột sẽ xảy ra nếu trước đó, ở trên cả hai nhánh, bạn đều sửa trên cùng một vị trí của cùng một tập tin, nghĩa là Git không thể lấy nội dung này đè lên nội dung kia. Khi đó Git sẽ thông báo nội dung tích hợp bị xung đột.

Ví dụ,

Maxsys@DESKTOP-7LPDOL6 MINGW64 /e/langbiang (master)
$ git merge iss123 -m "tich hop iss123 vao master"
Auto-merging conflict_test.txt
CONFLICT (content): Merge conflict in conflict_test.txt
Automatic merge failed; fix conflicts and then commit the result.

Khi bị xung đột, Git sẽ không tự động tạo ra commit mới cho việc tích hợp. Nó sẽ ngưng lại, và chờ bạn xử lý phần nội dung bị xung đột. Để biết tập tin nào không được tích hợp do bị xung đột, sử dụng lệnh $ git status.

Ví dụ,

$ git status
On branch master
Your branch is ahead of 'origin/master' by 7 commits.
  (use "git push" to publish your local commits)

You have unmerged paths.
  (fix conflicts and run "git commit")
  (use "git merge --abort" to abort the merge)

Unmerged paths:
  (use "git add <file>..." to mark resolution)

        both modified:   conflict_test.txt

Các tập tin có xung đột mà chưa được xử lý được gọi là unmerged (chưa được tích hợp). Git sẽ sử dụng định dạng chuẩn (do Git quy định) để đánh dấu vùng nội dung bị xung đột. Bạn có thể mở tập tin có xung đột bằng trình soạn thảo mã nguồn bất kì (ví dụ notepad) để quan sát vùng xung đột. Ví dụ nội dung của tập tin conflict_test.txt sẽ như sau:

viet tren nhanh master
<<<<<<< HEAD
viet tren nhanh hotfix
=======
viet tren nhanh iss123
>>>>>>> iss123

Nội dung bị xung đột được đánh dấu bằng khối: <<<<<     ====     >>>>>. Trong đó, phần trên dấu ==== thuộc “nhánh chính” (trong ví dụ này là nhánh master, con trỏ HEAD đang trỏ tới master), phần dưới dấu ==== thuộc “nhánh phụ” (trong ví dụ này là nhánh iss123). Để xử lý xung đột, bạn có thể chọn phần trên, phần dưới hoặc kết hợp cả hai phần lại cho hợp lý. Ví dụ, bạn có thể xử lý xung đột trên bằng cách thay thế toàn bộ khối “<<<< …. >>>> iss123” bằng nội dung: “viet tren nhanh”.

Ba hàng có chứa <<<<, ====, và >>>> sẽ bị xóa đi. Sau khi đã xử lý các xung đột trên mỗi tập tin, bạn cần lưu lại, đóng chương trình soạn thảo, chạy lệnh $ git add <tên tập tin> để báo cho Git biết là xung đột đã được xử lý. Git xem các tập tin được đưa vào khu vực tổ là đã được xử lý xung đột.

Sau khi đã xử lý xung đột xong, bạn gõ lệnh $ git commit để hoàn thành việc tích hợp đang bị ngưng lại. Git sẽ mở lại thông điệp bạn đã nhập trong lệnh $ git merge trước đó, bạn có thể chỉnh sửa lại thông điệp nếu cần thiết, đóng cửa sổ soạn thảo thông điệp để hoàn thành việc trộn nhánh.

Ví dụ,

$ git commit
[master e71b39c] tich hop iss123 vao master

Xem kết quả,

$ git log --all --graph --decorate --oneline
*   e71b39c (HEAD -> master) tich hop iss123 vao master
|\
| * a8ac313 (iss123) them noi dung tren iss123
* | e90d0e6 (hotfix) them noi dung tren hotfix
|/
* 3993e98 them conflict_test file
*   7e24aee Merge branch 'iss53'

Bạn có thể cài đặt các công cụ giao diện đồ hoạ để xử lý xung đột. Ví dụ, công cụ meld (http://meldmerge.org/). Đọc thêm về các công cụ merge tại đây: https://git-scm.com/book/en/v2/Git-Tools-Advanced-Merging#_advanced_merging

Lab 30. Tạo ra tình huống có xung đột khi tích hợp.

– Làm việc trên local repo

– Trên nhánh master, tạo tập tin conflict_test.txt, nhập nội dung vào dòng đầu tiên “viet tren nhanh master”, commit

– Tạo hai nhánh hotfix và iss123

– Chuyển qua nhánh hotfix, nhập thêm nội dung cho conflict_test.txt vào dòng thứ 2 là “viet tren nhanh hotfix”, commit

– Chuyển qua nhánh iss123, nhập thêm nội dung cho conflict_test.txt vào dòng thứ 2 là “viet tren nhanh iss123”

– Tích hợp hotfix vào master

– Tích hợp iss123 vào master

– Xử lý xung đột thủ công bằng một trình soạn mã


– Thực hiện các thao tác còn lại để hoàn thành việc tích hợp.
-----------
Cập nhật [14/10/2019]
-----------
Xem thêm: Tổng hợp các bài viết về Làm web
Xem thêm: Làm web (14) - Git: quản lý nhánh

Làm web (12) - Git: phân nhánh căn bản

Bài học trước: Làm web (11) - Git: tạo nhánh
-----

3.8.4     Phân nhánh và tích hợp


Đọc thêm tài liệu tiếng Anh:


Để dễ hiểu về phân nhánh và tích hợp, hãy theo dõi một tình huống đơn giản sau:

1. Bạn đang lập trình để tạo một website

2. Bạn tạo một nhánh mới để  làm một chức năng mới cho website (tên nhánh là iss53)

3. Bạn làm việc trên nhánh vừa tạo

Đang làm việc thì có thông báo: một chức năng đã làm trước đó (nằm trên nhánh production) của website bị lỗi, bạn nhận được yêu cầu cần phải sửa gấp. Khi đó, bạn sẽ thực hiện các bước sau:

1. Chuyển qua nhánh production

2. Tạo một nhánh mới để sửa lỗi cho website

3. Sau khi sửa lỗi xong, tích hợp nhánh đó vào nhánh production và đưa website vào hoạt động bình thường

4. Chuyển sang nhánh mà bạn đang làm việc trước đó (nhánh iss53)

Cơ bản về phân nhánh

Giả sử bạn đang làm việc trên một dự án, và đã thực hiện commit một số lần trên nhánh master.

Xem hình minh họa,




Bạn quyết định là sẽ thực hiện công việc (issue) có số hiệu là #53. Thường thì mỗi nhóm hoặc công ty sẽ có quy ước riêng về việc đặt tên cho các công việc, ví dụ iss01, iss02.

Để vừa tạo và chuyển sang nhánh mới, sử dụng lệnh $ git checkout kèm theo tham số -b.

Ví dụ,

$ git checkout -b iss53
Switched to a new branch 'iss53'

Lệnh trên là gộp của hai lệnh sau:

$ git branch iss53
$ git checkout iss53

Xem kết quả,

$ git log --oneline --decorate
47ed23e (HEAD -> iss53, master) thay doi tren master branch
a89362e (tag: v1.4-lw, tag: v1.3, tag: v1.2, tag: v1.1, origin/master, origin/HEAD, phienban1) Update testdesktop.txt

Xem hình minh họa,



Bạn lập trình và thực hiện một số commit. Kết quả là nhánh iss53 sẽ tiến về phía trước.

Ví dụ,

$ git add *
$ git commit -m "them logo cho home page"
[iss53 1cef1be] them logo cho home page
 2 files changed, 239 insertions(+)

Xem kết quả,

$ git log --oneline --decorate
1cef1be (HEAD -> iss53) them logo cho home page
47ed23e (master) thay doi tren master branch
a89362e (tag: v1.4-lw, tag: v1.3, tag: v1.2, tag: v1.1, origin/master, origin/HEAD, phienban1) Update testdesktop.txt

Xem hình minh họa,



Đang làm công việc có số hiệu #53 thì bạn nhận được thông báo là một chức năng của website đã làm trước đó có lỗi, bạn cần phải sửa lỗi ngay. Nhờ có Git, bạn sẽ không phải sửa lỗi trực tiếp trên phần việc đang làm dở dang tại nhánh iss53, bạn cũng không phải khôi phục lại trạng thái của dự án tại thời điểm trước khi thực hiện công việc #53. Việc đơn giản bạn cần làm lúc này là chuyển về nhánh master.

Tuy nhiên, trước khi bạn chuyển nhánh, cần lưu ý là nếu khu vực làm việc và khu vực stage có chứa các thay đổi chưa được commit, nếu nó có xung đột (conflict) với nhánh sẽ chuyển qua (nhánh mới), thì Git sẽ không cho phép chuyển nhánh. Vì vậy, tốt nhất là thực hiện commit các thay đổi trên nhánh cũ rồi mới chuyển nhánh (cũng có thể thực hiện commit amend hoặc stash-sẽ tìm hiểu sau).

Giả sử bạn đã commit hết các thay đổi trên iss53 và sẵn sàng chuyển về nhánh master.

$ git checkout master
Switched to branch 'master'

Lúc này, thư mục làm việc (số tập tin, nội dung các tập tin) của bạn sẽ ở trạng thái giống như khi bạn chưa thực hiện công việc #53. Thực tế Git đã tự động tham chiếu tới snapshop tương ứng với commit cuối cùng trên nhánh master, để thêm/xóa/sửa các tập tin trong thư mục làm việc, sao cho giống với trạng thái của commit cuối cùng. Việc của bạn bây giờ là tập trung vào sửa lỗi.

Công việc tiếp theo cần làm là, bạn sẽ tạo nhánh mới có tên là hotfix, thực hiện việc sửa lỗi trên nhánh hotfix cho tới khi hoàn thành công việc.

$ git checkout -b hotfix
Switched to a new branch 'hotfix'

$ git log --oneline --decorate
47ed23e (HEAD -> hotfix, master) thay doi tren master branch
a89362e (tag: v1.4-lw, tag: v1.3, tag: v1.2, tag: v1.1, origin/master, origin/HEAD, phienban1) Update testdesktop.txt
Sửa lỗi xong và commit,
$ git add *
$ git commit -m "sua lai logo"
[hotfix e31a627] sua lai logo
 1 file changed, 1 insertion(+)
 create mode 100644 hotfix.html

Xem kết quả,

$ git log --oneline --decorate
e31a627 (HEAD -> hotfix) sua lai logo
47ed23e (master) thay doi tren master branch
a89362e (tag: v1.4-lw, tag: v1.3, tag: v1.2, tag: v1.1, origin/master, origin/HEAD, phienban1) Update testdesktop.txt

Xem hình minh họa,



Sau khi kiểm tra kĩ, để đảm bảo lỗi đã được sửa, bạn cần phải tích hợp nhánh hotfix vào lại nhánh master. Mục đích là cập nhật lại website đang hoạt động, nhằm khắc phục lỗi vừa xuất hiện. Để tích hợp (merge) vào nhánh master (nhánh chính), bạn cần phải chuyển sang “nhánh chính” và sử dụng lệnh $ git merge <tên nhánh phụ>.

Ví dụ,

$ git checkout master
Switched to branch 'master'

$ git merge hotfix
Updating 47ed23e..e31a627
Fast-forward
 hotfix.html | 1 +
 1 file changed, 1 insertion(+)
 create mode 100644 hotfix.html

Kết quả,

$ git log --oneline --decorate
e31a627 (HEAD -> master, hotfix) sua lai logo
47ed23e thay doi tren master branch
a89362e (tag: v1.4-lw, tag: v1.3, tag: v1.2, tag: v1.1, origin/master, origin/HEAD, phienban1) Update testdesktop.txt



Nếu để ý, trong kết quả của lệnh $ git merge có dòng chữ “fast-forward”. Chữ fast-forward nghĩa là “tua nhanh”, hiểu nôm na là tiến nhanh về phía trước. Tại sao lại có chữ này ở đây? Quan sát ở hình minh họa phía trên sẽ thấy: nhánh master đang trỏ tới commit C2, hotfix đang trỏ tới commit C4, mà C4 lại là commit kế tiếp của C2. Như vậy, thực sự ở đây không có sự “tích hợp” nào hết, mà Git chỉ đơn giản di chuyển con trỏ của nhánh master tiến về phía trước. Kết quả là cả hai nhánh master và hotfix đểu trỏ tới commit C4. Fast-forward nghĩa là  “tích hợp mà như không tích hợp”.

Vậy là việc sửa lỗi đã hoàn thành, con trỏ trong nhánh master đang trỏ tới commit mới nhất, và website đã sẵn sàng cho người dùng sử dụng.

Sửa lỗi xong, bạn đã có thể quay lại công việc dở dang khi trước. Nhánh hotfix bây giờ không cần thiết nữa, nhánh master đang chứa tất cả các commit của hotfix rồi, vì vậy bạn có thể xóa hotfix đi (nhắc lại: nhánh chỉ là một tập tin chứa 40 kí tự, đó là giá trị SHA-1 của commit mà nhánh đó trỏ tới).

Để xóa một nhánh, sử dụng lệnh $ git branch với tham số -d.

Ví dụ,

$ git branch -d hotfix
Deleted branch hotfix (was e31a627).

Bạn quay lại nhánh iss53 để làm việc, thực hiện commit.

Ví dụ,

$ git checkout iss53
Switched to branch 'iss53'
$ git commit -m "da them logo [issue 53]"
[iss53 b765740] da them logo [issue 53]
 1 file changed, 1 insertion(+)
$ git log --oneline --decorate --all --graph
* b765740 (HEAD -> iss53) da them logo [issue 53]
* 1cef1be them logo cho home page
| * e31a627 (master) sua lai logo
|/
* 47ed23e thay doi tren master branch

Xem hình minh họa,



Để ý là công việc bạn đã thực hiện trong nhánh hotfix sẽ không có trong nhánh iss53. Muốn tích hợp công việc mà bạn đã thực hiện trên nhánh hotfix vào nhánh iss5, có hai cách: một là đứng tại nhánh iss53, tích hợp nhánh master vào; hai là chờ tới khi bạn quyết định trộn nhánh iss53 vào nhánh master.

Lab 29. Hiểu về phân nhánh và fast-forward.

– Làm việc cá nhân

– Tạo một local repo

– Sau mỗi câu lệnh có dấu (*), sử dụng lệnh $ git log --oneline --decorate, hoặc --all --graph để xem kết quả, ghi lại kết quả của mỗi bước

– Thay đổi nội dung trong local repo và thực hiện 03 commit (*)

– Tạo nhánh mới, đặt tên là iss100 (*)

– Chuyển sang nhánh iss100 thực hiện 02 commit (*)

– Quay về nhánh master, tạo nhánh mới có tên hotfix01 (*)

– Chuyển sang nhánh hotfix01, thực hiện thay đổi trên local repo và commit 02 lần (*)

– Tích hợp nhánh hotfix01 và nhánh master (*)

– Xóa nhánh hotfix01 (*)

– Chuyển sang nhánh iss100, thay đổi trên local repo và thực hiện commt 02 lần (*)


– Fast-forward là gì? nó xảy ra khi nào?
</////12
-----------
Cập nhật [27/05/2020]
-----------
Xem thêm: Tổng hợp các bài viết về Làm web
Xem thêm: Làm web (13) - Git: tích hợp căn bản

Làm web (11) - Git: tạo nhánh

Bài học trước: Làm web (10) - Git: đánh dấu
-----

3.8        Phân nhánh

3.8.1     Nhánh là gì?


Ở các phần trước, bạn đã biết cách lấy một kho chứa từ trên mạng về, thực hiện thêm thông tin vào kho chứa (ví dụ lập trình thêm một chức năng cho dự án), sau đó đẩy ngược lên mạng để chia sẻ cho các thành viên còn lại.

Để cập nhật kết quả mới nhất của các thành viên khác, bạn sẽ sử dụng lệnh $ git pull để kéo các thông tin mới từ remote repo về, trộn vào local repo. Cách làm này sẽ gặp trục trặc khi mã nguồn của thành viên khác làm cho chương trình của bạn bị lỗi. Hoặc đoạn mã của bạn có lỗi mà bạn lại đẩy (push) lên remote repo. Nói chung là có khá nhiều hạn chế nếu tất cả các thành viên cùng lập trình trên nhánh master.

Phần này sẽ giới thiệu một giải pháp, giúp bạn khắc phục được những nhược điểm trên, để quá trình làm dự án được hiệu quả hơn. Đó là phân nhánh trong Git.

Đọc tài liệu tiếng Anh:


Định nghĩa phân nhánh: branching means you diverge from the main line of development and continue to do work without messing with that main line.

Phân nhánh (branching) là việc tạo ra một nhánh mới từ luồng công việc chính, nhờ vậy các công việc được thực hiện độc lập trên nhánh mới mà không làm ảnh hưởng đến luồng chính.

Trong Git việc tạo nhánh không mất nhiều thời gian và có rất nhiều ưu điểm trong quá trình sử dụng. Vì vậy, Git khuyến khích các lập trình viên thường xuyên tạo và trộn nhánh trong quá trình phát triển dự án.

Để hiểu về phân nhánh, cần tìm hiểu kĩ hơn về việc tổ chức lưu trữ dữ liệu của Git. Như đã đề cập ở các phần trước, Git không lưu trữ chuỗi các sự thay đổi mà nó lưu trữ chuỗi các ảnh của dự án (các snapshot). Mỗi khi commit (lưu lại trạng thái hiện tại của dự án trong Git), Git sẽ “chụp một ảnh” ghi lại nội dung của tất cả tập tin tại thời điểm đó và tạo một tham chiếu tới “ảnh” vừa chụp. Những tập tin không có thay đổi sẽ không cần “chụp ảnh”, mà chỉ cần tạo tham chiếu tới tập tin gốc (để tiết kiệm không gian lưu trữ). Xem hình minh họa,



Khi thực hiện commit, Git sẽ tạo ra một “đối tượng commit”, lưu trong kho chứa Git, gồm các thông tin sau:

– Một con trỏ, trỏ tới snapshot chứa các nội dung (tập tin, thư mục)  mà bạn đã stage

– Tên của tác giả (author), email, và thông điệp đi kèm (message)

– Có thể có: không, một hoặc nhiều các con trỏ, trỏ tới commit cha của commit đó. Commit lần đầu không có cha, commit bình thường có một commit cha, các commit được tạo ra từ việc tích hợp (merge) hai hay nhiều nhánh sẽ có nhiều commit cha.

Để dễ hiểu, giả sử bạn có một kho chứa gồm ba tập tin (README, test.rb và LICENSE), khi bạn thực hiện stage ba tập tin,

$ git add README test.rb LICENSE

Git sẽ thực hiện việc “băm” từng tập tin để tạo ra mã SHA-1, đây là tên mới của tập tin, được sử dụng để thao tác trong Git, Git không quan tâm tới tên thật của mỗi tập tin. Phiên bản (nội dung) hiện tại của tập tin được lưu vào kho chứa của Git, mã SHA-1 được thêm vào khu vực stage. Git gọi mỗi phiên bản của một tập tin trong kho chứa là một blob.

[Blob is an abbreviation for “binary large object”. When we git add a file such as example_file.txt , git creates a blob object containing the contents of the file. Blobs are therefore the git object type for storing files.]

Khi chạy lệnh commit,

$ git commit -m 'initial commit of my project'

Git sẽ “băm” tất cả các thư mục của dự án, mỗi thư mục tạo thành một đối tượng, các đối tượng này được đặt tên theo mã SHA-1, các đối tượng được tổ chức theo dạng cây, thư mục gốc tương đương với nút gốc của cây. Cây này được lưu trong kho chứa của Git. Trong “đối tượng commit” có một con trỏ, trỏ tới nút gốc của cây, nhờ vậy Git có thể dễ dàng khôi phục được các “ảnh” của dự án khi cần.

Lúc này kho chứa Git sẽ chứa năm đối tượng sau (xem hình minh họa),



Năm đối tượng đó là: ba blob (5b1d3, 911eb, cba0a) chứa nội dung của ba tập tin; một đối tượng chứa thông tin về cây thư mục (92ec2); một “đối tượng commit” (98ca9) chứa con trỏ, trỏ tới nút gốc của cây thư mục, và các thông tin về tác giả (author), người commit (committer), thông điệp (message).

Có thể hiểu nôm na, một ảnh (snapshot) của dự án sẽ gồm cây thư mục và các blob tương ứng.
Sau khi thực hiện thay đổi trên dự án, rồi commit, thì commit sau sẽ có một con trỏ, trỏ tới commit trước đó (commit cha). Sau hai lần commit, lịch sử của dự án sẽ có dạng như sau, (commit đầu tiên có SHA-1 là 98ca9, lần thứ hai là 34ac2, lần thứ ba là f30ab)



Lab 27. Khảo sát một số đối tượng (commit, tree và blob) trong git.

– Tạo một kho chứa trên máy cục bộ (ví dụ tên là example_objects)

– Tạo một tập tin (example_file.txt) trong kho chứa, thực hiện add và commit

– Mở thư mục .git/objects sẽ thấy các đối tượng của git được tạo ra, để ý ba đối tượng đầu tiên (ví dụ: 3d, b2, e6).

– Sử dụng lệnh $ git cat-file –t <mã SHA-1> để kiểm tra xem loại của ba đối tượng đầu tiên? (kiểm tra xem là: commit, tree, hay blob). Ví dụ:

$ git cat-file -t 3d67701726ea1086341586101f272b9a029c9510
commit

(nhớ nối thêm 2 kí tự đầu tiên của thư mục vào mã SHA-1, ở ví dụ trên đã thực hiện nối “3d” vào “67701726ea1086341586101f272b9a029c9510”).

– Sử dụng lệnh $ git cat-file –p <mã SHA-1> để xem nội dung của các tập tin đi kèm với mã SHA-1. Mã SHA-1 của tập tin example_file.txt trên máy của bạn là gì?
Ví dụ:
$ git cat-file -p b25d4c8f7e67444504c50e428d23d981bae9f087
100644 blob e69de29bb2d1d6434b8b29ae775ad8c2e48c5391    example_file.txt

– Thêm nội dung cho tập tin example_file.txt, thực hiện commit, sử dụng $git cat-file –p để xem lại nội dung của tập tin example_file.txt.

Tham khảo: https://matthew-brett.github.io/curious-git/git_object_types.html

Trong Git, nhánh là một con trỏ có thể di chuyển được, nó trỏ tới một trong các commit ở trên. Khi tạo một kho chứa, và thực hiện commit lần đầu là bạn đã tạo ra nhánh mặc định có tên là master, đây là nhánh bình thường như các nhánh sẽ tạo sau này, nhánh master sẽ trỏ tới commit bạn vừa thực hiện. Mỗi khi bạn thực hiện một commit, con trỏ của nhánh master sẽ tự động di chuyển về phía trước.

Hình dưới đây là nhánh master và lịch sử commit của nó, (v1.0 là con trỏ của tag, master là con trỏ nhánh, HEAD là con trỏ cho biết nhánh hiện thời mà bạn đang làm việc)



3.8.2     Tạo nhánh mới


Tạo nhánh mới chính là việc tạo ra một con trỏ mới, trỏ tới commit hiện tại và đặt cho con trỏ mới một cái tên. Để tạo nhánh mới sử dụng lệnh $ git branch <tên nhánh>.

Ví dụ, tạo nhánh mới có tên là testing,

$ git branch testing

Khi đó, kho chứa sẽ có hai nhánh (master và testing), cùng trỏ tới commit mới nhất (f30ab), và có cùng lịch sử commit. Xem hình minh họa,



Tại một thời điểm, kho chứa có thể có rất nhiều nhánh. Để biết được hiện thời bạn đang ở nhánh nào? Git sử dụng thêm một con trỏ khác có tên là HEAD. HEAD là một con trỏ, nó trỏ đến nhánh hiện hành trên kho chứa cục bộ (nhánh bạn đang làm việc). Khi tạo nhánh mới con trỏ HEAD không tự trỏ tới nhánh vừa tạo, mà nó vẫn nằm trên nhánh hiện thời, người dùng cần phải thực hiện lệnh chuyển nhánh một cách tường minh.

Xem hình minh họa về con trỏ HEAD,



Để biết được con trỏ HEAD đang nằm trên nhánh nào? sử dụng lệnh $ git log với tham số --decorate

Ví dụ, con trỏ HEAD đang trỏ tới nhánh master,

$ git log --oneline --decorate
a89362e (HEAD -> master, tag: v1.4-lw, tag: v1.3, tag: v1.2, tag: v1.1, origin/master, origin/HEAD, testing, phienban1) Update testdesktop.txt
c2aa729 Add files via upload
a31e6f1 conglg them mot tap tin
abaf964 commit lan 1 amend
6b49bd0 add noi dung 83_git commit amend thêm m?i
9a99d67 them mot tap tin 8thang3
0cf8354 (tag: v1.4) doi ten tap tin
a6c7b42 doi ten

3.8.3     Chuyển nhánh


Để chuyển qua một nhánh khác (nhánh có tồn tại), cũng có nghĩa là cho con trỏ HEAD trỏ vào nhánh khác, sử dụng lệnh $ git checkout <tên nhánh>

Ví dụ, chuyển sang nhánh testing và kiểm tra bằng lệnh $ git log --decorate

$ git checkout testing
Switched to branch 'testing'

$ git log --oneline --decorate
a89362e (HEAD -> testing, tag: v1.4-lw, tag: v1.3, tag: v1.2, tag: v1.1, origin/master, origin/HEAD, phienban1, master) Update testdesktop.txt
c2aa729 Add files via upload
a31e6f1 conglg them mot tap tin
abaf964 commit lan 1 amend
6b49bd0 add noi dung 83_git commit amend thêm m?i
9a99d67 them mot tap tin 8thang3
0cf8354 (tag: v1.4) doi ten tap tin

Xem hình minh họa,



Việc chuyển nhánh này có ý nghĩ gì? hãy thay đổi nội dung của một tập tin, rồi thực hiện commit.

$ git add *

$ git commit -m "test chuyen nhanh"
[testing cd69866] test chuyen nhanh
 1 file changed, 1 insertion(+)

$ git log --oneline --decorate
cd69866 (HEAD -> testing) test chuyen nhanh
a89362e (tag: v1.4-lw, tag: v1.3, tag: v1.2, tag: v1.1, origin/master, origin/HEAD, phienban1, master) Update testdesktop.txt
c2aa729 Add files via upload

Sau lệnh commit, nhánh hiện thời (con trỏ HEAD đang trỏ tới testing) sẽ tiến về phía trước một bước.
Xem hình minh họa,



Để ý sẽ thấy, sau khi commit, nhánh testing tiến về phía một bước, trong khi nhánh master vẫn trỏ tới commit tại thời điểm bạn thực hiện lệnh $ git checkout để chuyển nhánh.

Giờ sẽ chuyển sang nhánh master,

$ git checkout master
Switched to branch 'master'
Your branch is up to date with 'origin/master'.

Sau lệnh $ git checkout master, Git sẽ thực hiện hai việc: một là chuyển con trỏ HEAD quay trở lại nhánh master, hai là phục hồi (revert) các tập tin trong thư mục làm việc trở về thời điểm mà con trỏ master đang trỏ tới (phục hồi snapshot). Điều này cũng có nghĩa là các thay đổi bạn thực hiện từ thời điểm này sẽ được tách ra theo một luồng khác (so với nhánh testing), các thay đổi khi bạn thực hiện trên nhánh testing đã bị “tua lại” để bạn có thể phát triển dự án theo một hướng khác.

Xem hình minh họa,



Chú ý: thao tác chuyển nhánh sẽ làm thay đổi các tập tin tại thư mục làm việc trong kho chứa. Nếu bạn thực hiện chuyển qua các nhánh cũ hơn, thư mục làm việc cũng được phục hồi lại trạng thái tại thời điểm mà bạn thực hiện commit cuối cùng trên nhánh. Nếu Git không thực hiện được việc phục hồi lại trạng thái các tập tin này, cũng có nghĩa là việc chuyển nhánh sẽ không được thực hiện.

Giờ sẽ tạo một số thay đổi trong các tập tin và commit thêm một lần nữa,

$ git add *
Maxsys@DESKTOP-7LPDOL6 MINGW64 /e/langbiang (master)

$ git commit -m "thay doi tren master branch"
[master 47ed23e] thay doi tren master branch
 1 file changed, 1 insertion(+)
$ git log --oneline --decorate
47ed23e (HEAD -> master) thay doi tren master branch
a89362e (tag: v1.4-lw, tag: v1.3, tag: v1.2, tag: v1.1, origin/master, origin/HEAD, phienban1) Update testdesktop.txt
c2aa729 Add files via upload

Bây giờ lịch sử của dự án đã được tách ra thành hai nhánh riêng biệt là master và testing.

Xem hình minh họa,



Tới thời điểm này, bạn đã thực hiện việc tạo và chuyển sang một nhánh mới (testing), thực hiện thay đổi trên dự án, commit, rồi sau đó chuyển ngược lại nhánh chính (master), cũng thực hiện thay đổi và commit trên nhánh master. Sự thay đổi trên tập tin và commit đều được thực hiện một cách biệt lập trên hai nhánh riêng biệt. Trong quá trình làm việc, bạn có thể di chuyển qua lại giữa các nhánh để làm việc và tích hợp các nhánh lại khi cần thiết.

Có thể sử dụng lệnh $ git log --oneline --decorate --graph --all để xem lịch sử các commit, các nhánh và lịch sử chia nhánh.

Ví dụ,

* 47ed23e (HEAD -> master) thay doi tren master branch
| * cd69866 (testing) test chuyen nhanh
|/
* a89362e (tag: v1.4-lw, tag: v1.3, tag: v1.2, tag: v1.1, origin/master, origin/HEAD, phienban1) Update testdesktop.txt
* c2aa729 Add files via upload
* a31e6f1 conglg them mot tap tin
* abaf964 commit lan 1 amend
* 6b49bd0 add noi dung 83_git commit amend thêm m?i
* 9a99d67 them mot tap tin 8thang3
* 0cf8354 (tag: v1.4) doi ten tap tin

Một nhánh trong Git là một tập tin chứa 40 kí tự, đó là giá trị SHA-1 của commit mà nhánh đó trỏ tới. Do vậy, việc tạo và hủy nhánh được thực hiện rất nhanh chóng, chỉ là thao tác ghi/xóa vào tập tin 41 kí tự (40 kí tự của SHA-1 và một dòng mới).

Lab 28. Thực hiện các yêu cầu sau về tạo nhánh.

– Tạo một local repo với ba tập tin bất kì

– Thực hiện thay đổi tên các tập tin và commit ba lần với các message khác nhau

– Tạo nhánh mới với tên bất kì (ví dụ testing)

– Chuyển sang nhánh testing, thực hiện thay đổi tập tin trong local repo, thực hiện commit một lần

– Chuyển lại về nhánh master, thực hiện thay đổi tập tin trong local repo, thực hiện commit một lần

– Điền giá trị SHA-1 của mỗi commit vào bảng sau:


SHA-1 của commit lần 4 trên nhánh master
SHA-1 của commit lần 1
SHA-1 của commit lần 2
SHA-1 của commit lần 3


SHA-1 của commit lần 1 trên nhánh testing

 </////11
-----------
Cập nhật [27/05/2020]
-----------
Xem thêm: Tổng hợp các bài viết về Làm web
Xem thêm: Làm web (12) - Git: phân nhánh căn bản

Làm web (10) - Git: đánh dấu

Bài học trước: Làm web (09) - Git: thao tác với remote repo (tt)
-----

3.6        Đánh dấu


Git cho phép đánh dấu (tag) các mốc quan trọng trong lịch sử của dự án, ví dụ các thời điểm phát hành phiên bản của dự án, như v1.0, v1.1.

Xem danh sách các tag

Để xem danh sách các tag sử dụng lệnh $ git tag. Ví dụ,

$ git tag       
v1.0
v1.1
v1.2
v1.3
v1.4-lw

Lệnh này sẽ hiển thị danh sách các tag theo thứ tự bảng chữ cái.

Thực tế, một kho chứa sẽ có rất nhiều tag, do vậy, bạn có thể lọc các tag để hiển thị bằng cách sử dụng mẫu (pattern). Ví dụ, chỉ hiển thị các tag có chuỗi bắt đầu là v1, (-l là viết tắt của --list)

$ git tag -l 'v1.*'
v1.0
v1.1
v1.2
v1.3
v1.4-lw

Xem thông tin một Tag

Sử dụng lệnh $ git show [tên tag] để xem thông tin của một tag, thông tin gồm: người tag, ngày tag, thông điệp, thông tin về commit.

Ví dụ,

$ git show v1.3
tag v1.3
Tagger: Lê Gia Công <legiacong@gmail.com>
Date:   Tue Mar 19 19:28:45 2019 +0700

phien ban 1.3

commit a89362ebd9224568e65afdfcacd9a4a6a3b30d8d (HEAD -> master, tag: v1.3, tag: v1.2, tag: v1.1, tag: v1.0, origin/master, origin/HEAD)
Author: legiacong <legiacong@gmail.com>
Date:   Fri Mar 15 16:16:55 2019 +0700

    Update testdesktop.txt

diff --git a/testdesktop.txt b/testdesktop.txt
index 994c3d7..94fdb0a 100644
--- a/testdesktop.txt
+++ b/testdesktop.txt
@@ -1 +1,2 @@
-?sncdfdfd
\ No newline at end of file
+?sncdfdfd
+ hkhk

Thêm tag

Git có hai loại tag là lightweight và annotated.

– lightweight: giống như một nhánh (branch) nhưng không có thay đổi, nó chỉ là một con trỏ, trỏ tới một commit

– annotated: được lưu trữ trong cơ sở dữ liệu của Git như một đối tượng đầy đủ; nó được băm (hash); chứa tên người tag, địa chỉ email và ngày tháng; có thông điệp đi kèm; có thể được ký và xác thực bằng GNU privacy Guard (GPG).

Thông thường annotated được khuyến khích sử dụng do có nhiều thông tin hơn. Tuy nhiên, nếu chỉ muốn tag tạm thời hoặc không muốn lưu trữ thông tin như một đối tượng thì nên sử dụng tag kiểu lightweight.

Tag annotated

Để thêm tag kiểu annotated sử dụng lệnh $ git tag với tham số -a, ví dụ,

$ git tag -a v1.3 -m "phien ban 1.3"

Tham số -m dùng để tạo thông điệp cho tag, nếu không sử dụng tham số -m và không có thông điệp đi kèm, lệnh trên sẽ mở cửa sổ soạn thảo để bạn nhập thông điệp.

Tag lightweight

Đây là cách thứ hai để tạo tag cho một commit. Để tạo tag kiểu này, Git sẽ lấy mã băm của commit và lưu vào một tập tin. Lệnh để tạo là $ git tag [tên của tag]. Lưu ý là không có thêm các tham số -a, -s, hay -m giống như khi tạo một tag annotated.

Ví dụ,

$ git tag v1.4-lw

Khi xem thông tin của tag v1.4-lw sẽ không thấy có thông tin về người tag, ngày tag và thông điệp.

Ví dụ,

$ git show v1.4-lw
commit a89362ebd9224568e65afdfcacd9a4a6a3b30d8d (HEAD -> master, tag: v1.4-lw, tag: v1.3, tag: v1.2, tag: v1.1, tag: v1.0, origin/master, origin/HEAD)
Author: legiacong <legiacong@gmail.com>
Date:   Fri Mar 15 16:16:55 2019 +0700

    Update testdesktop.txt
 …

Tag muộn

Bạn có thể tạo tag cho các commit đã được thực hiện trước đó. Thử xem lịch sử commit của một kho chứa có nội dung như sau,

$ git log --pretty=oneline

a89362ebd9224568e65afdfcacd9a4a6a3b30d8d (HEAD -> master, tag: v1.4-lw, tag: v1.3, tag: v1.2, tag: v1.1, tag: v1.0, origin/master, origin/HEAD) Update testdesktop.txt
c2aa7297c78eeabf98abf6dede2bf1da9ec252e1 Add files via upload
a31e6f175ca84e6dd9fb8b8d636d50aeda33e8ad conglg them mot tap tin
abaf96485d1c1f3247be9b8f1d1ea1c4e8b2b626 commit lan 1 amend
6b49bd01d35a7043604435b39d2facdd0a0dc800 add noi dung 83_git commit amend thêm m?i
9a99d676b551666a057a35c9dfa7067079f3bb6f them mot tap tin 8thang3
0cf8354bcbd0ad93ebf5bf336f3e49a47a00d8f4 doi ten tap tin
a6c7b4268d5c421c6fe1b6e444bcff8abeb89c19 doi ten

Giả sử bạn quên không tag cho dự án với phiên bản là v1.4 tại commit có thông điệp là “doi ten tap tin”. Bạn vẫn có thể tạo tag tại thời điểm này bằng cách thêm mã băm (hoặc một phần mã băm) vào cuối lệnh tạo tag.

Ví dụ,

$ git tag -a v1.4 -m "Phien ban 1.4" 0cf8354

Kiểm tra lại, sẽ thấy commit đã được tag,

$ git tag
v1.0
v1.1
v1.2          
v1.3
v1.4
v1.4-lw
$ git show v1.4
tag v1.4
Tagger: Lê Gia Công <legiacong@gmail.com>
Date:   Wed Mar 20 20:57:36 2019 +0700

Phien ban 1.4

commit 0cf8354bcbd0ad93ebf5bf336f3e49a47a00d8f4 (tag: v1.4)
Author: Lê Gia Công <legiacong@gmail.com>
Date:   Wed Mar 6 15:47:47 2019 +0700

    doi ten tap tin

Chia sẻ tag

Khi bạn thực hiện đẩy local repo lên remote repo, các tag sẽ không được gửi lên, mà bạn cần phải thực hiện thêm công việc gửi tag lên remote repo nếu muốn chia sẻ cho người khác.

Để đẩy tag lên remote repo, sử dụng lệnh $ git push origin <tên tag>.

Ví dụ,

$ git push origin v1.4
Enumerating objects: 1, done.
Counting objects: 100% (1/1), done.
Writing objects: 100% (1/1), 174 bytes | 43.00 KiB/s, done.
Total 1 (delta 0), reused 0 (delta 0)
To https://github.com/legiacong/langbiang.git
 * [new tag]         v1.4 -> v1.4

Nếu muốn đẩy một lần tất cả các tag lên remote repo thì sử dụng thêm tham số --tags, lệnh này sẽ đẩy lên remote repo tất cả các tag mà remote repo chưa có.

Ví dụ,

$ git push origin --tags
Enumerating objects: 4, done.
Counting objects: 100% (4/4), done.
Delta compression using up to 4 threads
Compressing objects: 100% (4/4), done.
Writing objects: 100% (4/4), 319 bytes | 106.00 KiB/s, done.
Total 4 (delta 3), reused 0 (delta 0)
remote: Resolving deltas: 100% (3/3), done.
To https://github.com/legiacong/langbiang.git
 * [new tag]         v1.0 -> v1.0
 * [new tag]         v1.1 -> v1.1
 * [new tag]         v1.2 -> v1.2
 * [new tag]         v1.3 -> v1.3
 * [new tag]         v1.4-lw -> v1.4-lw

Xóa tag

Để xóa tag tại local repo, sử dụng lệnh $ git tag -d <tên tag>.

Ví dụ,

$ git tag -d v1.0
Deleted tag 'v1.0' (was af50413)

Lệnh trên chỉ xóa được tag tại local repo, muốn xóa tag tại remote repo có thể sử dụng một trong hai cách sau:

Cách một (bản chất của cách này là đẩy một tag không có nội dung về remote repo–để ý khoảng trắng trước dấu :),

$ git push <remote repo> :refs/tags/<tên tag>

Ví dụ,

 $ git push origin :refs/tags/v1.0
To https://github.com/legiacong/langbiang.git
 - [deleted]         v1.0

Cách hai,

$ git push origin --delete <tên tag>

Ví dụ,

$ git push origin --delete v1.2
To https://github.com/legiacong/langbiang.git
 - [deleted]         v1.2

Check out theo tag

Nếu muốn xem nội dung của các tập tin tại một thời điểm đặt tag, và chuyển con trỏ của Git (HEAD) về thời điểm tương ứng, sử dụng lệnh $ git checkout <tên tag>

Ví dụ,

$ git checkout v1.1
Note: checking out 'v1.1'.

You are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by performing another checkout.

If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -b with the checkout command again. Example:

  git checkout -b <new-branch-name>

HEAD is now at a89362e Update testdesktop.txt

Lệnh trên sẽ đặt kho chứa ở tình trạng “detached HEAD”, hiểu nôm na là tình trạng mất định hướng. Trong tình trạng “detached HEAD”, nếu bạn thực hiện thay đổi kho chứa và commit, tag vẫn giữ nguyên, nhưng commit sẽ không thuộc vào bất kỳ nhánh nào, và bạn không có cách nào để truy cập được tới commit trừ mã băm của nó. Giải pháp trong trường hợp này là tạo một nhánh mới.

Ví dụ,

$ git checkout -b phienban1 v1.1
Switched to a new branch 'phienban1'

Khi đó, những thay đổi trên nhánh phienban1 (nếu được commit) sẽ làm cho nội dung của kho chứa khác đi so với thời điểm tag v1.1.

3.7        Phím tab và Git alias


Phím tab

Trong khi gõ lệnh Git bằng Git Bash trong Windows, có nhiều lệnh bạn sẽ không thể nhớ chính xác, hãy bấm phím tab hai lần để có được các gợi ý nhắc lệnh.

Ví dụ, bạn muốn gõ lệnh git commit nhưng bạn không nhớ chữ commit, trong trường hợp như vậy bạn chỉ cần gõ $ git com <tab><tab>, Git sẽ hiển thị các gợi ý cho bạn

$ git co
commit   config
Sau đó bạn gõ thêm chữ m và <tab> để hoàn thành lệnh.
Ví dụ, khi bạn cần gõ các tham số, $ git log --s <tab> <tab>
$ git log --s
--shortstat                --since=                   --submodule
--show-signature           --sparse                   --submodule=
--simplify-by-decoration   --src-prefix=              --summary
--simplify-merges          --stat

Alias

Để gõ lệnh nhanh có thể gán cho lệnh các tên mới ngắn gọn hơn, hoặc dễ nhớ hơn. Ví dụ, thay vì phải gõ lệnh là $ git checkout, thì chỉ cần gõ là $ git co.

Cách làm,

$ git config --global alias.co checkout

Một số ví dụ khác,

$ git config --global alias.br branch
$ git config --global alias.ci commit
$ git config --global alias.st status

Có thể mở tập tin .gitconfig hoặc dùng lệnh git config --list để xem kết quả trong tập tin cấu hình.


Lab 24. Thao tác với Tag.

– Làm việc theo nhóm

– Giả định nhóm đã có một remote repo (trên Github hoặc Gitlab)

– Mỗi thành viên cập nhật remote repo về local repo (clone hoặc pull)

– Trên local repo, mỗi thành viên chỉnh sửa/thêm dữ liệu để thực hiện 3 commit và đặt 3 tag (2 kiểu annotated, 1 kiểu lightweight)

– Đẩy local repo lên remote repo

– Đẩy các tag lên remote repo

– Thực hiện các thao tác: tag muộn, xóa tag trên local repo, xóa tag trên remote repo, check out theo tag

– Kéo remote repo về máy để xem kết quả liên quan đến tag

– Sử dụng phím tab cho chức năng nhắc lệnh

– Đặt tên mới cho một số lệnh (alias)


</////10
-----------
Cập nhật [21/3/2019]
-----------
Xem thêm: Tổng hợp các bài viết về Làm web
Xem thêm: Làm web (11) - Git: tạo nhánh