Ở series bài viết này, mình sẽ hướng dẫn các bạn sử dụng Git và dùng Github hay Gitlab để phối hợp cùng làm việc trong một nhóm ở công ty như thế nào.
Nếu bạn tò mò muốn tìm hiểu xem Git, Github hay Gitlab là gì, được sử dụng như thế nào, workflow như thế nào, giải quyết những vấn đề gì, thì bài viết này dành cho bạn. Mình viết bài viết này cho chính mình nhiều năm về trước khi còn là sinh viên và dành cho bạn đỡ phải vật lộn với Git giống như mình.
Giới thiệu
Nếu bạn search Google tìm hiểu xem Git là gì và sử dụng như thế nào, có lẽ bạn sẽ gặp một đống những thuật ngữ chuyên ngành. Ngày xưa mình còn có ông anh đưa mình một khóa học Udemy tận 8 tiếng toàn những thuật ngữ cao siêu để "hiểu" về Git. Làm sao mà bạn có thể trở thành một lập trình viên nếu chỉ để hiểu một công cụ mà ai cũng sử dụng mỗi ngày cần bằng Tiến sĩ như thế?
May mắn thay mọi thứ không tệ đến như vậy. Bạn không cần phải tốt nghiệp Đại học rồi cần bằng Tiến sĩ để biết sử dụng Git. Mình cho rằng để biết sử dụng một công cụ bạn không cần phải biết toàn bộ những chi tiết kỹ thuật về công cụ đó, quan trọng là bạn phải biết sử dụng nó để làm gì. Khi bạn biết được "tại sao", bạn có thể tự tìm cách giải quyết "làm thế nào".
Để làm ra một chiếc búa có lẽ bạn 3 năm kinh nghiệm đi rừng chặt cây, 4 năm kinh nghiệm thợ mộc, 5 năm kinh nghiệm đào mỏ, 6 năm kinh nghiệm thợ rèn, nhưng để biết sử dụng một chiếc búa, có lẽ bạn chỉ cần biết dùng đầu nào của búa để đóng đinh là được.
Thay vì tập trung vào những chi tiết kỹ thuật cao siêu, ở bài viết này mình sẽ cố gắng giải thích cho bạn bằng những từ ngữ đơn giản nhất có thể, và để bạn nhìn thấy những vấn đề mà một người lập trình viên thường ngày gặp phải và giải quyết như thế nào.
Vấn đề mà Git và Github / Gitlab giải quyết là gì?
Tưởng tượng bạn làm project thuyết trình nhóm. Bạn tạo mới file Powerpoint mới trên Google Drive của bạn và share nó cho các thành viên nhóm. Với một nhóm lập trình viên, file Powerpoint tương đương với một project, còn Google Drive sẽ tương đương với Github / Gitlab. Bạn có thể tưởng tượng Github / Gitlab giống như Google Drive / Dropbox trong lập trình vậy.
Nhưng có vấn đề là làm sao để ngăn không cho một thành viên nhóm vô tình chỉnh sửa nhầm vào file?
Ở Google Drive với Powerpoint bạn không có cách nào cả, nhưng may mắn thay ở thế giới lập trình bạn có thể giải quyết vấn đề đó bằng sự kết hợp của Git, Github hay Gitlab.
Hãy tưởng tượng thế này, để giải quyết vấn đề có thành viên nhóm vô tình chỉnh sửa nhầm, mỗi lần mỗi người muốn chỉnh sửa đều phải làm như sau:
- Tạo ra một bản copy của file đó
- Chỉnh sửa trên bản copy đó
- Sau khi làm xong thành viên đó sẽ hỏi leader sẽ review bản copy đó
- Chỉ khi leader đồng ý thì những thay đổi mới được merge vào trong bản chính trên Google Drive
Giải pháp này có thể quá oằn tà là vằn ở Google Drive, nhưng thực ra nó lại khá đơn giản trong thế giới lập trình với sự giúp đỡ của Git. Bạn chỉ cần biết một số command Git đơn giản là được.
Nhưng trước hết, hãy tìm hiểu xem Git là gì, làm những gì và giải quyết những vấn đề gì. Mình có thể foreshadow trước một xíu:
- Download file từ Google Drive = git clone
- Tạo bản copy của file đó = tạo branch mới
- Upload lên Google Drive = git push
- Hỏi leader review bản copy = tạo Pull Request / Merge Request
- Download bản mới nhất trên Google Drive = git pull
- 2 người chỉnh sửa cùng một chỗ trong file = conflict
Git là gì
Git là một phần mềm bạn có thể cài đặt trên máy tính của bạn (giống như Chrome, VSCode, Team Viewer,...). Bạn có thể vào https://git-scm.com/ để tải xuống và click một đống Yes để cài đặt.
Vậy Git làm những gì? Trong mắt đa phần các coder quèn như mình, Git làm 2 việc chính:
- Quản lý phiên bản (versioning)
- Tạo nhánh (branch)
Quản lý phiên bản (versioning) là gì?
Bạn đã từng thử rollback về version cũ hơn một file Google Docs chưa? Kiểu như trong nhóm có người vô tình xóa đi cái gì quan trọng, cả nhóm sẽ cuống lên rollback về một version cũ để xem xem cái quan trọng đó còn không ấy.
Git cho phép bạn thế trong lập trình. Bạn có thể tạo một version mới của project của bạn hay rollback lại version cũ bất cứ lúc nào bạn muốn!
Tạo một version mới
Ở trong Git, khi bạn muốn tạo một version mới, bạn "commit". Mỗi một version có một message tương ứng do bạn chọn. Đây là câu lệnh để bạn bảo Git tạo một version mới:
git commit -m "Your message"
Nhưng bình tĩnh, trước khi bạn chạy lệnh này, hãy tưởng tượng rằng bạn cần phải bảo Git file nào thay đổi.
Ví dụ như project của bạn có 1 file index.js
. Bạn vừa thay đổi nó từ console.log("hi there")
thành console.log("hello there")
. Bây giờ, bạn muốn tạo một version mới của project.
Đầu tiên, bạn phải nói cho Git rằng file index.js
đã thay đổi:
git add index.js
Bây giờ, bạn mới có thể tạo một version mới với message là "change from hi to hello there":
git commit -m "change from hi to hello there"
Túm cái váy lại, có 2 bước để tạo một version mới cho project:
git add
git commit -m
Tại sao mình lại phải làm 2 bước git add
trước khi git commit -m
? Chẳng lẽ Git không tự biết được file nào đã thay đổi hay sao? Hãy tưởng tượng tình huống này.
Giả sử bạn có 2 file index.js
và main.js
. Nhưng bạn chưa muốn tạo ra một version mới chứa những thay đổi của main.js
vội. Lúc đó bạn sẽ muốn Git chỉ quan tâm để index.js
mà không cần quan tâm đến main.js
:
git add index.js
rồi
git commit -m "change to hello there"
2 bước git add
rồi mới git commit -m
là để dành cho những trường hợp như thế này.
Điều này xảy ra khá nhiều khi bạn làm việc ở dự án thật: bạn thường phải thay đổi những file config để chạy project trên máy tính của mình. Lúc đó bạn sẽ bảo Git chỉ quan tâm đến những file bạn code chứ đừng commit những file config.
Bây giờ hãy giải thích một chút về các "thuật ngữ chuyên ngành" như "staging" hay "untracked files".
Có 3 loại file:
- File mà bạn thay đổi nhưng chưa nói với Git (chưa
git add
) - File mà bạn thay đổi và đã nói với Git (đã
git add
) - File mà bạn đã commit
Khi người ta nói về:
- Staging area: hãy nghĩ về "file mà Git biết rằng đã thay đổi"
- Stage file đó lên:
git add
file đó - Untracked files: hãy nghĩ về "file mà bạn chưa nói cho Git rằng nó đã thay đổi"
Để xem tất cả những file đang ở trong "staging area" và tất cả những "untracked file", bạn có thể dùng lệnh git status
:
git status
Đây là ví dụ của mình:
On branch master
Your branch is up to date with 'origin/master'. Changes to be committed: (use "git restore --staged <file>..." to unstage) modified: packages/solid-project/src/App.tsx Changes not staged for commit: (use "git add <file>..." to update what will be committed) (use "git restore <file>..." to discard changes in working directory) modified: packages/solid-project/src/hooks/createWindowHeight.ts
Điều này có nghĩa là mình có file createWindowHeight.ts
chưa thông báo với Git (chưa git add
) và file App.tsx
mình đã nói với Git (đã git add
) nhưng chưa commit.
Từ bây giờ, thay vì nói rằng "nói với Git rằng file đó thay đổi", mình sẽ nói git add
file đó hay stage file đó lên. Lúc trước mình nói rằng git add
cũng giống như nói với Git rằng file đó thay đổi nhưng đó là không chính xác về mặt kỹ thuật một chút nào. git add
sẽ giống như mình bảo Git rằng hãy ghi nhớ những thay đổi của file đó để mình chuẩn bị commit lên hơn là bảo Git file đó đã thay đổi. Lúc trước mình nói vậy vì với người mới bắt đầu, "tưởng tượng" vậy đã là "đủ tốt" rồi. Bây giờ sau khi đã hiểu Git giải quyết những vấn đề gì và một chút thuật ngữ cơ bản, bạn có thể tìm đọc Git pro book trên trang chủ của họ https://git-scm.com/book/en/v2 để hiểu hơn về Git hoạt động thế nào.
Xem tất cả version của project
Để xem tất cả các version của project, bạn có thể dùng lệnh sau:
git log
Đây là một ví dụ của project của mình:
commit 3f345bfcbbcbaf56f2b7a90a72fb6558a255d3df (HEAD -> master)
Author: Your Name <you@example.com>
Date: Thu Jan 30 00:53:33 2025 add examples commit 67851c04de24214b25099cd5e3a128c06bd4551a
Author: Your Name <you@example.com>
Date: Mon Dec 9 13:44:31 2024 initial commit
Đây là một chút hình vẽ minh họa để giải thích:
Project của mình hiện giờ có 2 version:
- Version 67851c04de24214b25099cd5e3a128c06bd4551a
- Version 3f345bfcbbcbaf56f2b7a90a72fb6558a255d3df
Hiện giờ mình đang ở version mới nhất 67851c04de24214b25099cd5e3a128c06bd4551a. Bạn thấy HEAD trỏ vào version đó nghĩa là vậy.
Ở những hình vẽ minh họa trước, mình dùng từ "version 1", "version 2", "version 3",... nhưng Git sử dụng một chuỗi những ký tự không lặp nhau (như kiểu id vậy) để đánh dấu mỗi version. Thay vì nói "version 1", Git nói "version 67851c04de24214b25099cd5e3a128c06bd4551a".
Thực tế những chuỗi ký tự này đặc biệt đến mức bạn chỉ cần dùng 5 - 10 ký tự của nó để Git biết được nó là version nào. Ví dụ như "version 67851c", "version 3f345bfc",...
Từ bây giờ mình sẽ gọi những cái này là "version hash".
Luyện tập một chút
Hãy luyện tập một chút trước khi chuyển qua phần tiếp theo. Mình sẽ dùng kết quả của phần này cho phần kế tiếp. Nếu bạn không muốn bật máy tính và bắt đầu gõ lệnh ngay lập tức, thì đừng lo, mình sẽ vẽ một đống hình minh họa để bạn hiểu mình đang làm gì.
Đầu tiên, hãy tạo một thư mục mới. Mình sẽ đặt tên là simple-git-tutorial
.
Tiếp đến, hãy mở Git Bash (hay Command Prompt, Termial, Powershell đều được) ở thư mục đó và gõ:
git init
Lệnh này có nghĩa là mình bảo Git hãy bắt đầu quản lý tất cả các file trong thư mục này.
Một chút giải thích thêm: khi bạn chạy git init
ở một thư mục, git sẽ tạo ra một thư mục ẩn tên là ".git" ở trong thư mục đó để lưu trữ toàn bộ thông tin cần thiết để quản lý version.
Đây là ví dụ của mình:
Nếu bạn không nhìn thấy thư mục ẩn .git này ở Windows, hãy chọn View rồi bấm vào Hidden items!
Tiếp theo, hãy tạo file index.js
với nội dung như sau:
console.log("hi there");
Bây giờ, hãy tạo một version mới. Ở trong Command Prompt, gõ lệnh sau:
git add index.js
rồi:
git commit -m "add hi there"
Để xem tất cả các version, chạy lệnh sau:
git log
Đây là ví dụ của mình:
commit e1c439a92efef55036a54250a8fa58d8db2e7981
Author: Your Name <you@example.com>
Date: Thu Jan 30 13:28:32 2025 add hi there
Hãy cùng luyện tập cả quá trình tạo một version mới một lần nữa. Ở file index.js
, hãy sửa "hi there" thành "hello there":
console.log("hello there")
Tiếp theo, hãy tạo một version mới của project này:
git add index.js
rồi:
git commit -m "change to hello there"
Đây là hình vẽ minh họa giải thích những gì mình vừa làm:
Để xem tất cả các version, hãy chạy lệnh sau:
git log
Đây là một ví dụ của mình:
commit 56908bb558478bc61d7319b870beb459d9853ae1
Author: Your Name <you@example.com>
Date: Thu Jan 30 13:29:56 2025 change to hello there commit e1c439a92efef55036a54250a8fa58d8db2e7981
Author: Your Name <you@example.com>
Date: Thu Jan 30 13:28:32 2025 add hi there
Cuối cùng đây là hình vẽ minh họa tổng những gì mình vừa làm trong phần này:
Xem những version trước đó
Để xem một version trước đó, bạn có thể dùng git checkout [version-hash]
như thế này:
git checkout e1c439a92efef55036a54250a8fa58d8db2e7981
Vì Git chỉ cần 5 - 10 ký tự của version hash để biết đó là version nào, mình có thể viết ngắn gọn như thế này:
git checkout e1c439a
Nếu bạn mở index.js
, bạn sẽ thấy nó thay đổi từ
console.log("hello there")
thành:
console.log("hi there")
Bây giờ nếu mình muốn chuyển lại về version mới nhất, mình chỉ cần dùng git checkout
với version hash của version mới nhất như sau:
git checkout 56908bb5
Nhưng bình tĩnh!!! Để tìm được version hash của version mới nhất, mình cần dùng git log
. Nhưng mà git log
của mình không trả ra version mới nhất nữa:
commit e1c439a92efef55036a54250a8fa58d8db2e7981 (HEAD -> master)
Author: Your Name <you@example.com>
Date: Thu Jan 30 13:28:32 2025 add hi there
Đừng lo, bạn chỉ cần dùng git log --all
là được. Đây là ví dụ của mình:
commit 56908bb558478bc61d7319b870beb459d9853ae1 (master)
Author: Your Name <you@example.com>
Date: Thu Jan 30 13:29:56 2025 change to hello there commit e1c439a92efef55036a54250a8fa58d8db2e7981 (HEAD)
Author: Your Name <you@example.com>
Date: Thu Jan 30 13:28:32 2025 add hi there
Tip: nếu git log
không in ra tất cả các version, hãy sử dụng git log --all
Bây giờ sau khi git log --all
bạn có thể tìm được version hash của version mới nhất và chuyển về đó.
git checkout 56908bb5
Tiếp đến, hãy nói một chút về "detached HEAD".
Khi bạn dùng git checkout
chuyển đến một version nào đó, HEAD sẽ trỏ vào version đó. Lúc này bạn đang ở trong trạng thái "detached HEAD". Bạn tưởng tượng lúc này Git đang ở trong trạng thái cho bạn "preview" project của bạn ở version đó trông như thế nào.
Bình thường thì bạn sẽ không muốn ở trong "detached HEAD" trừ khi bạn hiểu chính xác mình đang làm gì. Để thoát khỏi trạng thái "detached HEAD", hãy sử dụng lệnh sau:
git checkout master
master
ở đây là branch hiện tại của mình. Ở phần tiếp theo khi nói về branch mình sẽ giải thích kỹ hơn về lệnh này.
Rollback về một version cũ
Để rollback về một version cũ, bạn có thể dùng git reset --hard [version-hash]
như sau:
git reset --hard e1c439a9
Nhưng hãy rất cẩn thận khi sử dụng lệnh này vì nó sẽ thực sự xóa tất cả những version sau đó. git log --all
sẽ không show lại những version mới nhất đâu!
Thực ra, bạn nên dùng lệnh git switch -c
như sau:
git switch -c new-branch-name
nhưng mình sẽ giải thích ở lệnh này ở phần 2 khi nói về branch.
File .gitignore
Cuối cùng mình sẽ nói về file .gitignore
. Đã bao giờ bạn thấy file này chưa?
File .gitignore
là gì và nó làm gì?
Có những file và thư mục mà bạn không bao giờ muốn thêm vào bất kỳ version nào của project của bạn cả.
Ví dụ như thư mục node_modules
chẳng hạn. Miễn là bạn có file package.json
và file package-lock.json
bạn có thể npm install
lại thư mục node_modules
bất kỳ lúc nào bạn muốn.
Hơn nữa, thử tưởng tượng máy bạn là Window và bạn share project cho một người khác dùng Macbook. Vì người đó dùng khác hệ điều hành với bạn, để chạy project người đó sẽ cần xóa thư mục node_modules
và npm install
lại. Bây giờ nếu người đó chạy lệnh git status
họ sẽ thấy một đống thay đổi trong thư mục node_modules
mặc dù thực tế họ chưa thay đổi project này một chút nào cả.
Bạn sẽ chỉ muốn lưu những thứ thật sự cần thiết trong những version của bạn.
Để tránh Git báo những thay đổi không cần thiết này, và quan trọng hơn là tránh việc git add
và git commit -m
nhầm thư mục node_modules
đó, bạn có thể tạo file .gitignore
với nội dung thế này:
/node_modules
Đây là một vài những file và thư mục hay được thêm vào .gitignore
:
.DS_Store
*.local
*.log* node_modules
dist/ .vscode/*
!.vscode/extensions.json
.idea
Tóm tắt
Hãy tóm tắt lại những lệnh mình đã học ở phần này:
git init
: bảo Git bắt đầu quản lý version ở foldergit add
: stage file đógit commit -m
: tạo một version mớigit status
: xem tất cả những file chưagit add
và đãgit add
git log
,git log --all
: show tất cả versiongit checkout [version hash]
: xem (preview) một version cũgit reset --hard [version hash]
: rollback về một version cũ (hãy rất cẩn thận khi sử dụng lệnh này!)
Đó là những lệnh Git cơ bản mà mình dùng thường ngày
Ở phần tiếp theo mình sẽ nói về branch của Git.
Credits
Nếu bạn thích con cá mà mình sử dụng, hãy xem: https://thenounproject.com/browse/collection-icon/stripe-emotions-106667/.