+25

Kiến thức quan trọng về Git

Trong blog này, chúng ta sẽ bắt đầu từ những khái niệm cơ bản của Git, hiểu rõ về cách Git hoạt động và tại sao nó lại trở thành một công cụ ưa thích của đông đảo nhà phát triển trên toàn thế giới. Từ đó, ta sẽ đi sâu vào các lệnh phổ biến và biết cách sử dụng chúng để quản lý mã nguồn của dự án một cách hiệu quả.

Ngoài ra, ta sẽ tìm hiểu về mô hình Git Flow - một phương pháp quản lý phiên bản có tổ chức và hiệu quả, giúp đảm bảo tính nhất quán trong quy trình phát triển phần mềm.

Thế nào là repository, branch

Repository

Là kho chứa toàn bộ thư mục, files của một project. Có 2 loại: Remote repository: Là repository được lưu trên Github, có thể chia sẻ giữa nhiều người. Local repository: Là repository lưu trữ trực tiệp trên máy tính của bạn.

Cách tạo repository:

  • Ở góc trên bên phải của github, ấn tạo New repository.
  • Trong ô “Repository name”, nhập tên Repo
  • Trong ô "Description", nhập mô tả về Description của bạn (không bắt buộc)
  • Chọn chế độ Public hoặc Private cho repository
  • Chọn Add Readme để thêm file Readme.md
  • Click Create repository.

Branch

Branch cho phép bạn tạo ra nhiều version của repository cùng lúc Vì vậy 1 repository có thể có nhiều branch, nhưng sẽ luôn có 1 branch chính (thường được đặt là main hoặc master) Các nhánh là độc lập với nhau và có thể hợp nhất lại bằng lệnh merge.

Các loại branch: Local branch: có thể được liên kết với 1 branch ở remote hoặc không. Hiển thị branch local: git branch

Remote branch: có thể fetch về local nhưng không tạo thêm branch ở local. Hiển thị branch remote: git branch -r

Cách tạo branch: git checkout -b <new_branch_name>

Xoá local branch, remote branch

Xoá local branch

Git không cho phép bạn xoá branch bạn đang sử dụng nên bạn cần checkout tới branch khác.

Ví dụ: git checkout main

Xoá local branch: git branch -d <branch>

Ví dụ: git branch -d new/feature

-d option cho phép bạn xoá branch chỉ khi nó đã từng được push hoặc merge với remote branch. Sử dụng -D nếu bạn muốn bắt buộc xoá 1 branch kể cả khi nó chưa được push hoặc merge.

Xoá remote branch

Xoá remote branch lưu ở local: git branch --delete --remotes <remote_name>/<branch_name>

hoặc

git branch -d -r <remote_name>/<branch_name>

Xoá remote branch ở remote: git push <remote> --delete <branch>

hoặc

git push <remote> :<branch>

Push branch ở local lên remote với cái tên khác

Bình thường khi ta push branch ở local lên remote thì ta sẽ dùng lệnh:

git push <remote_name> <branch_name>

Vậy nếu muốn push branch ở local lên remote nhưng 2 branch lại khác tên nhau thì sao? Lúc đó ta sẽ dùng lệnh:

git push <remote_name> <local_branch>:<remote_branch>

Thế nào là git rebase? Phân biệt git rebase với merge

Thế nào là git rebase

Rebase là 1 trong 2 công cụ git có thể tích hợp những thay đổi từ 1 branch tới những branch khác. (công cụ còn lại là merge) Rebasing là quá trình kết hợp hoặc di chuyển chuỗi commit lên trên một base commit mới.

So sánh git rebase với git merge:

Ta có 2 nhánh main và feature như sau:

A — B — C — D — E 		main
         \ 
            F — G		 feature

Sau khi merge:

A — B — C — D — E— H  main
         \ 	  /	
          F — G 	    feature

Lệnh:

git merge feature

Tạo ra commit gộp mới: commit H Lịch sử commit không thay đổi

Sau khi rebase:

A — B — C — D — E		main
                  \ 
                    F’— G’  	  feature

Lệnh:

git rebase main feature

Như trên hình trên, trước thời điểm rebase, nhánh main có base commit cơ sở giống với nhánh feature đó là: A, B, C. Đưa toàn bộ nhánh main làm base của feature, do vậy các commit tiếp theo sau base commit ban đầu của master sẽ được nối tiếp vào, trong đó có thay đổi lại lịch sử commit, xử lý xung đột giữa commit E và F để viết lại commit F thành F'.

Thế nào là git fetch? Phân biệt git fetch với git pull

Lệnh git fetch là gì?

Lệnh git fetch được sử dụng để tải xuống các nội dung từ Remote repository mà không làm thay đổi trạng thái của Local repository (các dữ liệu như commit, các file, refs).

Hiểu một cách đơn giản, khi bạn thực hiện lệnh git fetch, git sẽ thu thập và lưu trữ những thay đổi mới từ các branch của Remote repository về máy tính của bạn, nhưng không hợp nhất chúng với Local repository.

Với git fetch, bạn có thể theo dõi các commit người khác đã cập nhật lên server, đồng thời nắm bắt được những thông tin khác nhau giữa remote và local.

Phân biệt git pull và git fetch

Git fetch và git pull đều được sử dụng để tải về remote content. Tuy nhiên, git fetch được coi là phiên bản ‘an toàn’ hơn của git pull. Khi sử dụng, lệnh này sẽ tải xuống remote content mà không cập nhật trạng thái hoạt động của local repository. Từ đó, nội dung công việc hiện tại của bạn không bị ảnh hưởng.

Ngược lại, lệnh git pull sẽ tải xuống remote content và ngay lập tức thực hiện git merge để merge commit cho remote content mới. Nếu bạn có các thay đổi đang chờ xử lý, điều này có thể vô tình khiến local repository rơi vào trạng thái xung đột.

Ngoài ra, git fetch thường được dùng trong trường hợp có nhiều người làm việc trên cùng một nhánh. Còn git pull chỉ nên sử dụng khi có một người làm việc trên nhánh để hạn chế xung đột. Bạn chỉ nên dùng lệnh git pull trên một thư mục làm việc sạch (không có thay đổi đã cam kết).

Câu lệnh:

git fetch <remote> 
git pull <remote>

Thế nào là git stash?

Lệnh git stash lưu những thay đổi chưa được commit để sử dụng về sau. Ví dụ:

$ git status
On branch main
Changes to be committed:

    new file:   style.css

Changes not staged for commit:

    modified:   index.html

$ git stash
Saved working directory and index state WIP on main: 5002d47 our new homepage
HEAD is now at 5002d47 our new homepage

$ git status
On branch main
nothing to commit, working tree clean

Bây giờ bạn có thể tự do thay đổi, tạo commit mới, chuyển branches, sau đó quay lại và sử dụng stash khi bạn muốn. Lưu ý là stash chỉ có thể sử dụng ở local, nó không được chuyển lên server khi bạn push. Áp dụng lại những thay đổi stash: Bạn có thể sử dụng lệnh stash pop

$ git status
On branch main
nothing to commit, working tree clean
$ git stash pop
On branch main
Changes to be committed:

    new file:   style.css

Changes not staged for commit:

    modified:   index.html

Dropped refs/stash@{0} (32b3aa1d185dfe6d57b3c3cc3b32cbf3e380cc6a)

Pop stash loại bỏ những thay đổi từ stash của bạn và áp dụng lại chúng vào working copy. Ngoài ra, bạn có thể tái áp dụng thay đổi từ working copy và giữ chúng với git stash apply:

$ git stash apply
On branch main
Changes to be committed:

    new file:   style.css

Changes not staged for commit:

    modified:   index.html

Việc này rất hữu dụng nếu bạn muốn áp dụng những thay đổi stash tương tự cho nhiều branch. Một số lệnh với stash

  • Xem danh sách stash: git stash list
  • Apply stash gần nhất và xóa stash đó:git stash pop
  • Apply stash: git stash apply stash@{<index>}
  • Xem nội dung stash: git stash show stash@{<index>}
  • Xóa stash: git stash drop stash@{<index>}
  • Xóa toàn bộ stash: git stash clear

Xoá bỏ trạng thái của một số git commit gần đây

Cách 1: dùng git reset hard git reset --hard <commit_id>

Hoặc

git reset --hard HEAD~<index>

VD:

git reset --hard 1d91fzq

Lệnh này sẽ xóa bỏ toàn bộ các commit trước đó đưa branch về trạng thái của commit có Id được chỉ định.

git reset --hard HEAD~2

Cách 2: dùng git revert

git revert <commit_id>

hoặc

git revert HEAD~<index>

VD:

git revert 1d91fzq

git revert HEAD~1

Lệnh này sẽ tạo 1 commit mới với nội dung đảo ngược lại một commit cũ. Kết quả sau khi commit mới được tạo thì branch sẽ loại bỏ thay đổi của commit cũ. Các commit mới hơn commit cũ vẫn được giữ nguyên. Nói đơn giản là chỉ commit được chỉ định sẽ bị xóa bỏ. Sử dụng revert an toàn hơn reset vì nó tạo ra commit mới còn reset thì xoá lịch sử commit cũ.

Gộp nhiều commit thành 1 commit

Bước 1: Di chuyển vào nhánh chứa các commit cần gộp Trước tiên, hãy đảm bảo bạn đang ở trên nhánh chứa các commit mà bạn muốn gộp. Sử dụng lệnh sau để chuyển đổi sang nhánh đó:

git checkout <branch_name>

Giả sử bạn đang làm việc trên một nhánh có tên “feature” và đã tạo ra 5 commit nhỏ khi thực hiện tính năng đó. Bạn muốn gộp các commit này thành một commit duy nhất để giữ cho lịch sử commit sạch sẽ.

git checkout feature

Bước 2: Chạy lệnh rebase với tùy chọn -i Sau khi chuyển sang nhánh chứa các commit, chạy lệnh rebase với tùy chọn -i (interactive) để mở giao diện tương tác cho việc chỉnh sửa lịch sử commit:

git rebase -i HEAD~<commit_count>

Ở đây, <commit_count> là số lượng commit bạn muốn gộp. Ví dụ, nếu bạn muốn gộp 5 commit cuối cùng, bạn có thể sử dụng HEAD~5.

git rebase -i HEAD~5

Sau khi chạy lệnh trên, một giao diện tương tác sẽ mở trong trình soạn thảo mặc định của bạn, hiển thị danh sách các commit từ cũ đến mới. Mỗi commit sẽ có một dòng bắt đầu bằng “pick”.

Bước 3: Sửa lịch sử commit Bây giờ, bạn sẽ chỉnh sửa lịch sử commit để gộp các commit lại với nhau.

Chuyển các dòng “pick” sang “squash” (hoặc viết tắt là “s“) cho các commit bạn muốn gộp vào commit trước đó.

Sau đó lưu và đóng trình soạn thảo bằng cách nhấn phím ESC và gõ :x

Quá trình rebase sẽ bắt đầu. Git sẽ áp dụng các thay đổi đã chọn và gộp các commit lại thành một commit duy nhất.

Bước 4: Push lên nhánh từ xa Cuối cùng, hãy đảm bảo rằng bạn push các thay đổi lên nhánh từ xa của mình để lưu trữ commit đã gộp:

git push -f origin feature

Phân biệt git reset, git reset --hard, git reset --soft

Lưu ý --mixed là mặc định của lệnh reset.

Khi bạn thay đổi 1 file trong thư mục, để commit thay đổi đó, bạn cần add vào index— sử dụng git add. Sự khác biệt giữa --mixed và --soft là những thay đổi đã được add vào index hay chưa.

Vậy nếu ta đang ở branch main với những commits sau:

  • A - B - C (main)

HEAD trỏ đến C và index lúc này là C.

Khi ta chạy lệnh git reset --soft B thì main (HEAD) trỏ tới B, nhưng index vẫn có những thay đổi từ C. Nếu lúc này dùng lệnh git commit, ta sẽ có commit mới giống như những thay đổi của C.

Bây giờ với

  • A - B - C (main)

Dùng lệnh git reset --mixed B (giống với git reset B). Một lần nữa, main và HEAD trỏ đến B, nhưng lần này index là B.

Nếu chạy lệnh git commit, ta sẽ thấy không có chuyện gì xảy ra cả vì index lúc này là HEAD.

Ta vẫn có những thay đổi tại thư mục làm việc nhưng vì chúng không nằm trong index, git status sẽ hiển thị chúng như unstaged.

Để có thể commit, bạn cần dùng lệnh add và sau đó commit như bình thường.

--hard cũng giống với --mixed (thay đổi HEAD và index), ngoại trừ việc --hard cũng thay đổi thư mục làm việc hiện tại. Nếu ta đang ở C và chạy git reset --hard B thì sau đó thay đổi sẽ được add trong C.

Tất cả những thay đổi chưa commit sẽ bị loại bỏ và files trong working copy sẽ match với commit B.

Vì bạn có thể mất thay đổi vĩnh viễn nếu dùng cách này nên bạn nên chạy git status trước khi sử dụng hard reset để chắc chắn là thư mục hiện tại đang clean hoặc bạn thấy okay nếu bị mất những thay đổi chưa commit.

Thế nào là cherry-pick, khi nào thì dùng cherry-pick

Git Cherry Pick

git cherry-pick là một command rất mạnh có thể cho phép git commit bất kỳ có thể được pick bởi tham chiếu và thêm nó vào working HEAD hiện tại.

Cherry picking là hành động lấy 1 commit ở branch này và áp dụng nó ở một branch khác.

git cherry-pick có thể sử dụng để undo những thay đổi. Ví dụ 1 commit được tạo ở branch sai. Bạn có thể chuyển tới branch đúng và cherry-pick commit tới nơi nó nên được đặt.

Khi nào sử dụng cherry pick

git cherry-pick là một công cụ hữu dụng nhưng không phải lúc nào cũng tốt trong thực tế.

Cherry picking có thể gây ra commit trùng lặp, và nhiều tình huống có thể sử dụng cherry pick nhưng merge truyền thống lại được ưa dùng hơn.

Một số trường hợp nên sử dụng cherry pick:

Team collaboration Nhiều khi 1 team sẽ gặp trường hợp thành viên phải làm việc với cùng 1 code.

Có thể tính năng mới của sản phẩm có backend và frontend component.

Việc đó dẫn tới có nhiều code được sử dụng chung giữa 2 bộ phận sản phẩm.

Có thể lập trình viên backend tạo ra cấu trúc dữ liệu mà frontend cũng cần sử dụng.

Khi đó frontend developer có thể sử dụng git cherry-pick để lấy những commit mà cấu trúc dữ liệu đó được tạo ra.

Bug hotfixes Khi một bug được tìm thấy, cần phải fix nhanh nhất có thể. Ví dụ trường hợp 1 lập trình viên bắt đầu phát triển tính năng mới. Trong quá trình phát triển họ đã nhận diện được những lỗi có thể xảy ra, lúc này developer tạo một commit để vá bug này.

Commit này có thể được cherry-pick thẳng tới main branch để fix bug trước khi nó ảnh hưởng tới nhiều người dùng khác.

Undoing changes and restoring lost commits Đôi khi một nhánh tính năng có thể bị cũ và không được merge vào main hoặc đôi khi pull request có thể bị close mà ko merge.

Nhưng git không bao giờ mất những commit đó và bằng những command như git log hay git reflog, họ có thể tìm thấy và cherry-pick chúng lại.

Git flow

Git Flow là một mô hình quy trình quản lý mã nguồn được phát triển bởi Vincent Driessen vào năm 2010. Nó cung cấp một tập hợp các quy tắc và các nhánh Git để quản lý dự án phần mềm một cách có tổ chức và hiệu quả. Mô hình Git Flow thường được sử dụng trong các dự án có quy mô lớn hoặc dài hạn và trong nhóm phát triển lớn.

Mô hình Git Flow bao gồm hai nhóm chính của các nhánh:

  1. Nhánh chính (Main Branches):

    • master: Nhánh master chứa mã nguồn ổn định và được coi là sản phẩm ở mỗi điểm thời gian. Các commit trên nhánh master thường được đánh dấu bằng các phiên bản phần mềm (ví dụ: v1.0, v2.0).
    • develop: Nhánh develop là nơi phát triển chính của dự án diễn ra. Các tính năng mới và sửa lỗi được tích hợp vào nhánh này và thường được kiểm tra kỹ trước khi đưa vào nhánh master.
  2. Nhánh hỗ trợ (Supporting Branches):

    • feature: Nhánh feature được sử dụng để phát triển các tính năng mới. Mỗi tính năng được phát triển trên một nhánh feature riêng và sau khi hoàn thành, nó được hợp nhất (merge) vào nhánh develop.
    • release: Nhánh release được sử dụng để chuẩn bị cho việc phát hành phiên bản mới. Trên nhánh release, các sửa lỗi nhỏ có thể được thực hiện và được kiểm tra trước khi đưa vào nhánh masterdevelop.
    • hotfix: Nhánh hotfix được sử dụng để sửa lỗi ngay lập tức trên phiên bản đang hoạt động trong nhánh master. Sau khi sửa lỗi xong, nó được hợp nhất vào nhánh masterdevelop.

Sử dụng Git Flow, nhóm phát triển có thể tổ chức công việc một cách rõ ràng và đảm bảo rằng các tính năng và phiên bản được kiểm tra cẩn thận trước khi đưa vào bản phát hành cuối cùng. Tính nhất quán của mô hình này giúp giảm thiểu xung đột mã nguồn và tạo ra các phiên bản phần mềm ổn định và đáng tin cậy.

KẾT LUẬN

Trên đây là một số những kiến thức về git mà mình đã tìm hiểu được. Mong nó sẽ giúp ích được cho mọi người. Happy coding!!! 😘


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí