Mục lục Git - GitHub

Khái niệm về nhánh branch và con trỏ HEAD

Trong Git nhánh branch là hướng rẽ phát triển code, mới mục đích không làm rối hướng phát triển chính, sau đó một nhánh có thể tích hợp nhập vào nhánh chính. Nói chung trong Git luôn làm việc với các nhánh, mặc định bao giờ cũng có một nhánh chính tên là master

Nhánh chính master

1 Khởi tạo dự án với 3 commit

Giả sử bạn khởi tạo một Local Repo mới (Lưu ở thư mục c:\local\myproject), lúc đó mặc định sẽ làm việc trên nhánh chính có tên master, thực hiện sửa đổi với 3 commit thì như đã biết mỗi lần commit thì Git lưu dữ liệu là một snapshot (ảnh chụp). Kết quả là nhánh master có 3 snapshot (ký hiệu là C0, C1, C2 như hình bên và đoạn lệnh git đưới

$ git init                      #khởi tạo Repo
$ touch 0.txt                   #Tạo một file mới trong thư mục làm việc
$ git add .                     #đưa vào stage
$ git commit -m"C0"             #commit

$ touch 0.txt
$ git add .
$ git commit -m"C1"

$ touch 0.txt
$ git add .
$ git commit -m"C2"

2 Lịch sử commit

Xem lại lịch sử commit: nó cho biết đang làm việc trên nhánh có tên master, và đang ở vị trí commit cuối với mã hash là 0d7ae45 C2

$ git log --pretty=oneline
0d7ae45f93922e40c2c8dfdd14721e77476f6d01 (HEAD -> master) C2
927163bd31669a7651621e91982afbdf6f30cc1f C1
efab635085727cefd2b0271e7b72b9fd37a62614 C0

Ví trí hiện tại bạn đang làm việc ở nhánh nào, ở commit cuối nào thể hiện bằng một con trỏ có tên là HEAD

3 Xem danh sách các nhánh - branch

$ git branch
* master

Có một nhánh là master, ký hiệu * cho biết đây là nhánh hiện tại

Tạo nhánh mới, chuyển nhánh làm việc

4 Tạo một nhánh mới

Giờ đang ở nhánh master con trỏ HEAD đang thấy ở commit C2, nếu muốn tại đây có một nhánh mới tên alpha gõ lệnh sau:

$ git branch alpha

Nhánh có tên alpha đã được tạo, gõ lệnh git branch sẽ thấy Repo có 2 nhánh masteralpha nhưng con trỏ HEAD vẫn cho biết làm việc ở nhánh master. Nhánh alpha khởi đầu từ commit C2 (kế thừa master từ C2 trở về trước), hình biểu diễn tả như hình bên

5 Chuyển nhánh làm việc mới

Giờ chuyển sang làm việc trên nhanh alpha thì gõ lệnh như sau

$ git checkout alpha

Cả masteralpha hiện đang trỏ đến commit C2 nhưng con trỏ HEAD cho biết đang làm việc với nhánh alpha, xem hình bên, có thể dùng lệnh git branch kiểm tra

5 Thực hiện commit trên nhánh mới

Đang trên nhánh alpha thực hiện thay đổi và commit

$ touch 3.txt                   #Tạo một file mới trong thư mục làm việc
$ git add .                     #đưa vào stage
$ git commit -m"C4"             #commit

6 Xem lại lịch sử commit của nhánh alpha

git log --pretty=oneline
c7d81e3771b61c97c1b6e661acdb9b25ec9cb199 (HEAD -> alpha) C3
0d7ae45f93922e40c2c8dfdd14721e77476f6d01 (master) C2
927163bd31669a7651621e91982afbdf6f30cc1f C1
efab635085727cefd2b0271e7b72b9fd37a62614 C0

Thấy nhánh alpha có một commit C4, và kế thừa các commit cũ của master bắt đầu từ C2.

Nhìn hình trên thấy nhánh master dừng lại ở commit cuối C2, với các commit cũ hơn C1, C0. Nhánh đang làm việc là alpha có một commit mới là C3, và các commit cũ kế thưa từ nhánh cha master C2, C1, C0

7 Tiếp tục tạo sự thay đổi và commit trên nhánh alpha

$ echo "Update new content to file" > 1.txt  #thêm nội dung mới vào file 1.txt
$ git add .
$ git commit -m"C4"

Như vậy ở commit cuối, nhánh alpha thư mục làm việc có các file: 0.txt, 1.txt, 2.txt, 3.txt, so với master nó có thêm file 3.txt và file 1.txt có chèn nội dung mới

8 Chuyển về làm việc với nhánh master

git checkout master

Trong thư mục làm việc đã quay về trạng thái ở commit cuối của nhánh master: 0.txt, 1.txt, 2.txt còn file 1.txt chưa cập nhật nội dung gì. Con trỏ HEAD giờ cũng trỏ vào nhánh master

9 Tạo sự thay đổi và commit cho nhánh master

$ touch 4.txt
$ echo "Update code" > 0.txt
$ git add .
$ git commit -m"C5"

Trạng thái hiện tại như hình dưới, dùng lệnh git log để xem lịch sử commit trên master

10 Tiếp tục tạo nhánh mới rẽ từ master

Hiện giờ đang đứng ở master và phát triển code theo kế hoạch, nhưng code trong master cần sửa và kiểm tra ngay, với mong muốn quá trình sửa đổi code và kiểm lỗi không làm rối các dòng code đang làm với master có thể tạo ra ngay một nhánh mới đặt tên sualoigap, ngay sau đó chuyển sang nhánh sualoigap để làm việc

$ git branch sualoigap              #tạo nhánh
$ git checkout sualoigap            #chuyển nhánh

Giờ đang làm việc trên nhánh sualoigap (con trỏ HEAD), các snapshot và các nhánh trên toàn bộ Repo như hình: mastersualoigap đang cùng trỏ đến commit C5

11 Tạo sự thay đổi và commit trên nhánh sualoigap

$ echo "Sua loi tren 2.txt" > 2.txt
$ git add .
$ git commit -m"C6"

12 Xem lại lịch sử commit sualoigap

$ git log --pretty=oneline
eda207ba7b5a02f3687eb0c71f08b627ce98a7ca (HEAD -> sualoigap) C6
2c3fa4d3fe0035844314c232324afca9ffc1a288 (master) C5
0d7ae45f93922e40c2c8dfdd14721e77476f6d01 C2
927163bd31669a7651621e91982afbdf6f30cc1f C1
efab635085727cefd2b0271e7b72b9fd37a62614 C0

Nhìn vào thấy nhánh sualoigap kế thừa master kể từ C5, và có thêm một commit C6, hình ảnh các snapshot trên Repo hiện tại như sau:

Tương tự có thể thực hiện thêm nhiều commit trên sualoigap thì về logic trên Repo tương tự, ví dụ cho thêm commit nữa

$ echo "Sua loi tren 1.txt" > 1.txt
$ git add .
$ git commit -m"C7"

Giờ con trỏ HEAD cho biết đang ở commit C7 trên nhánh sualoigap

Branch Merge

13 Trộn nhánh sualoigap vào nhánh master

Khi đã hoàn thành nhiệm vụ trên nhánh sualoigap nếu muốn các kết quả của nhánh này tích hợp thay đổi vào master thì tiến hành gộp 2 nhánh lại gọi là merge. Để làm việc đó cần chuyển về làm việc trên nhánh master bằng lệnh checkout rồi cho nhánh sualoigap gộp vào master bằng lệnh merge:

$ git checkout master               #tạo nhánh
$ git merge sualoigap               #hợp nhất

Hiện giờ đang đứng ở master và nhánh sualoigap đã gộp vào master, và cả 2 nhánh đều đang trỏ vào commit C7, hình ảnh snapshot như dưới

Xóa nhánh

14 xóa nhánh sualoigap

Khi nhánh sualoigap đã gộp vào master, nếu không còn nhu cầu làm việc trên nhánh này thì có thể xóa nó đi như sau:

$ git branch -d sualoigap
$ git branch
  alpha
* master

Đã xóa, lệnh git branch liệt kê chỉ còn hai nhánh alpha và master và đang làm việc trên master. Xem lại log master thấy như sau:

$ git log --pretty=oneline
89b19cb2a226bf609bb8330dc1e2312f76bffd0e (HEAD -> master) C7
eda207ba7b5a02f3687eb0c71f08b627ce98a7ca C6
2c3fa4d3fe0035844314c232324afca9ffc1a288 C5
0d7ae45f93922e40c2c8dfdd14721e77476f6d01 C2
927163bd31669a7651621e91982afbdf6f30cc1f C1
efab635085727cefd2b0271e7b72b9fd37a62614 C0

Vậy nhánh master có các snapshot C0, C1, C2, C5, C6, C7 và hình ảnh snapshot trên Repo hiện tại như sau

branch

Xử lý xung đột khi gộp nhánh

Bây giờ giả sử có nhu cầu gộp code tại nhánh alpha vào nhánh master, trường hợp này không đơn giản như cách gộp nhánh sualoigap ở trên. Do cả 2 nhánh có nhiều commit kể từ thời điểm rẽ nhánh nên khi gộp nó sẽ xem xét sự thay đổi trên cả 2 nhánh tại ba điểm (three-way), thời điểm commit cuối của các nhánh và thời điểm rẽ nhánh, đó là các commit với snapshot C2, C4, C7 như hình dưới:

three way merge

Như hình phía trên, Repo có 2 nhánh, điểm rẽ là ở snapshot C2 (có mã hash: 0d7ae45), nghĩa là ở thời điểm đó masteralpha giống nhau, hãy trở về thời điểm snapshot C2 bằng lệnh git checkout

$ git checkout 0d7ae45f          #0d7ae45f là hash của commit C2
HEAD is now at 0d7ae45 C2

Mở thư mục làm việc ra, thì lúc này đang có các file: 0.txt, 1.txt, 2.text với nội dung trống bên trong.

Kiểm tra nhánh alpha ở commit cuối với snapshot C4:

$ git checkout alpha

Mở thư mục làm việc ra ta thấy lúc này có các file: 0.txt, 1.txt, 2.txt, 3.txt file 1.txt có nội dung Update new content to file còn các file khác rỗng.

Kiểm tra nhánh master ở commit cuối với snapshot C7:

$ git checkout master

Mở thư mục làm việc ra ta thấy lúc này có các file: 0.txt, 1.txt, 2.txt, 4.txt file 0.txt có nội dung Update code, file 1.txt có nội dung Sua loi tren 1.txt, 2.txt có nội dung Sua loi tren 2.txt còn file 4.txt rỗng

Tổng kết lại có bảng sau, bảng này là kết quả so sánh nhánh với thời điểm snapshot C2, những ô có nền xanh là bản giữ lại sau khi merge, những ô màu đỏ là tình trạng xung đội nếu merge

file master alpha Khi merge alpha vào master
0.txt có sửa đổi: Update code không sửa đổi không xung đột: lấy theo bản tại master (vì sửa đổi tại đây)
1.txt có sửa đổi: Sua loi tren 1.txt có sửa đổi: Update new content to file xung đột vì không biết lấy bản sửa đổi nào: tại master hay alpha
2.txt có sửa đổi: Sua loi tren 2.txt không sửa đổi không xung đột: lấy theo bản master
3.txt không sửa đổi có sửa đổi: tạo mới file không xung đột: lấy theo bản alpha (vì tạo file mới tại đây)
4.txt sửa đổi: tạo mới file không sửa đổi không xung đột: lấy bản master

Bảng phân tích trên cho biết sẽ có 1 xung đột xảy ra nếu gộp nhánh alpha vào master, đó chính là nội dung của file 1.txt, Git không biết là giữ lại bản nào. Ở tình trạng này cần phải xử lý các xung đột sau khi merge

15 gộp nhánh alpha vào master

$ git checkout master
$ git merge alpha       #gộp nhánh
Auto-merging 1.txt
CONFLICT (content): Merge conflict in 1.txt
Automatic merge failed; fix conflicts and then commit the result.

Dòng thông báo cho biết vẫn đang trong quá trình merge, nhưng có một xung đột về nội dung trong file 1.txt chờ bạn xử lý nó. Không như trường hợp không có xung đột nào, trường hợp này khi gộp (merge) Git không tự động tạo ra commit mới và nó tạm dừng để bạn xử lý từng file có xung đột và tạo ta 1 commit sau khi xử lý hết xung đột.

Xem lại trạng thái

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

Changes to be committed:

        new file:   3.txt

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

        both modified:   1.txt

Nó đã cho biết có file 3.txt ở sẵn trong stage những 1.txt chưa được merge

16 xử lý xung đột khi gộp nhánh

Có bao nhiêu file xung đột thì mở ra để quyết định lấy nội dung nào trong file đó. Ở đây file xung đột là 1.txt, mở ra thấy nội dung như sau:

<<<<<<< HEAD
Sua loi tren 1.txt
=======
Update new content to file
>>>>>>> alpha

Bạn thấy nội dung xung đột nó đánh dấu bên trong các ký hiệu, nội dung nằm giữa <<<<<<< HEAD======= là nội dung có từ master, còn nội dung nằm giữa =======>>>>>>> alpha là nội dung từ alpha. Giữa hai nội dung đó lấy nội dung nào thì giữ, còn lại thì xóa hết. Ví dụ file trên giữ lại nội dung như sau (nội dung từ alpha)

Update new content to file

Sau khi xử lý hết các xung đột trong từng file bằng cách như vậy, thì tiến hành đưa file vào stage và tạo ra một commit để hoàn thành việc gộp nhánh

$ git add .
$ git commit -m"C8"

Xem lại lịch sử commit trên nhánh master và alpha bằng lệnh:

$ git checkout alpha
$ git log --oneline
bbc3757 (HEAD -> alpha) C4
c7d81e3 C3
0d7ae45 C2
927163b C1
efab635 C0
$ git checkout master
$ git log --oneline
a252058 (HEAD -> master) C8
89b19cb C7
eda207b C6
2c3fa4d C5
bbc3757 (alpha) C4
c7d81e3 C3
0d7ae45 C2
927163b C1
efab635 C0

Từ đó dựng lên các hình ảnh snapshot trên Repo

gop nhanh

16 xử lý xung đột với mergetool

Ngoài cách mở từng file để quyết định giữ nội dung nào, thì Git có sẵn một công cụ trực quan để làm nhanh hơn. Bạn chỉ việc gõ lệnh

$ git mergetool

Một giao diện đồ họa mở các file xung đột xuất hiện, hãy sửa từng file. Khi đang trong giao diện đó muốn giữ nội dung từ local (master) thì gõ :diffg LO, muốn giữ nội dung từ remote (alpha) :diffg RE, sau đó thoát mergetool gõ lệnh :wqa. Sau khi thoát thì bạn đã sẵn sàng để commit

git

Trộn gộp nhánh Rebase

Ta sẽ thực hiện tạo ra một nhánh mới, sau đó gộp lại nhưng có dùng đến loại lệnh git rebase

17 tạo nhánh mới từ một thời điểm trong lịch sư commit

Ở nhánh master gõ lệnh sau lấy một bản sao thời điểm snapshot C5 (có mã hash: 2c3fa4d)

$ git checkout 2c3fa4d
You are in 'detached HEAD' ...
HEAD is now at 2c3fa4d C5

Dòng thông báo HEAD is now at 2c3fa4d C5 cho biết con trỏ HEAD bị tách khỏi tất cả các nhánh hiện có và nó ở vị trí một nhánh tạm thời phát sinh ra có tên là 2c3fa4d từ thời điểm này bạn đang làm việc trên nhánh tạm thời đó, nếu không tạo nhánh mới tại đây thì nhánh tạm thời và các commit trên nó sẽ mất khi chuyển nhánh. Giờ ta sẽ tạo ra nhánh mới với tên là beta với câu lệnh:

$ git checkout -b beta              #Tạo nhánh mới
$ git branch                        #Danh sách các nhánh
  alpha
* beta
  master

Ta đã có nhánh mới beta thư mục làm việc có nội dung như commit cuối C5

18 tạo sự thay đổi thực hiện 2 commit trên beta

$ touch 5.txt                           #Tạo một file mới trong thư mục làm việc
$ git add .
$ git commit -m"C9"                     #commit
$ echo "Modify from Beta" > 2.txt       #thay nội dung file 2.txt đang có
$ git add .
$ git commit -m"C10"                    #commit

Xem lại lịch sử commit

$ git log --oneline
9e101b7 (HEAD -> beta) C10
d391bde C9
2c3fa4d C5
0d7ae45 C2
927163b C1
efab635 C0
hinh anh snapshot

19 gộp nhánh với rebase

Có thể gộp nhánh beta vào master bằng merge ở trên, nếu vậy nó sẽ tạo ra một commit như là điểm gộp nhánh bằng cách kết hợp C10, C8 có so sánh với C5 (three-way). Tuy nhiên đôi khi bạn muốn việc gộp đó lại thực hiện theo cách cập nhật những gì thay đổi trên C10 của beta vào C8 của master mà không phải tạo ra một commit - snapshot nối tiếp, để làm điều đó thì dùng đến rebase

Với rebase thì mọi commit trên nhánh này sẽ có trong một nhánh khác. Ví dụ chạy lệnh reabase để mọi thứ trong master áp dụng vào beta:

$ git checkout beta
$ git rebase master

Quá trình rebase có thể tạm dừng do có thể có xung đột phiên bản, bạn xử lý như trên, mở file có xung đột ra sửa giữ lại nội dung nào, sửa xong gõ lệnh sau để tiếp tục rebase

$ git add .
$ git rebase --continue

Nếu rebase đang tạm dừng để chờ xử lý xung đột, có thể gõ lệnh git rebase --abort để hủy, phục hồi lại trạng thái khi chưa gõ lệnh rebase

Xem lại lịch sử commit sau khi rebase

$ git checkout beta
$ git log --oneline
544511a (HEAD -> beta) C10
be103d9 C9
1a90428 (master) C8
89b19cb C7
eda207b C6
2c3fa4d C5
bbc3757 (alpha) C4
c7d81e3 C3
0d7ae45 C2
927163b C1
efab635 C0
rebase branch

Nhánh beta đã gồm tất cả các commit từ C8 về trước của master, master vẫn dừng lại ở C8, nếu muốn quay về master và merge nó với beta để có được phiên bản hợp nhất, có trỏ HEAD dịch về cuối tới snapshot C10

$ git checkout master
$ git merge beta
$ git log --oneline

544511a (HEAD -> master, beta) C10
be103d9 C9
1a90428 C8
89b19cb C7
eda207b C6
2c3fa4d C5
bbc3757 (alpha) C4
c7d81e3 C3
0d7ae45 C2
927163b C1
efab635 C0

Lưu ý khi sử dụng rebase: vì rebase sẽ viết lại lịch sử commit trên nhánh, nên tuyệt đối không bao giờ thực hiện rebase khi các commit của nhánh đã đảy lên Repository công khai, nếu không mọi việc sẽ rối tung

Vài lệnh quản lý nhánh

$ git branch                 #Liệt kê các nhánh
$ git branch -v              #Liệt kê nhánh + commit cuối
$ git branch --merged        #Các nhánh merge với nhánh hiện tại
$ git branch --no-merged     #Các nhánh không merge với nhánh hiện tại
$ git branch -d branchname   #xóa nhánh

Xóa commit cũ, viết lại lịch sử commit

Nếu cần thay đổi N commit cuối cùng, có là xóa, ghi đè ... thì dùng lệnh

git rebase -i HEAD~N

Trong danh sách commit có thể chọn các thiết lập như sau rồi lưu lại để thực hiện

  • p hay pick thì commit đó giữ lại
  • s hay squash thì sẽ sử dụng nội dung commit này nhưng đè vào commit phía trước
  • r hay reword sẽ giữ lại commit nhưng cho phép viết lại nội dung thông điệp

Cập nhật lên Remote

git push origin +master

Remote đã xóa một commit cũ nào đó, muốn local cập nhật theo

git pull --rebase

#Hoặc
git fetch
git rebase origin/master
https://github.com/xuanthulabnet/myproject
Mục lục Git - GitHub