Git một từ khá quen thuộc với hầu hết dân Dev, chắc ai cũng đã biết các lệnh như clone, pull, push… nhưng bạn biết git hoạt động, lưu trữ dữ liệu như thế nào chưa, làm sao để làm việc hiệu quả với git, những lệnh nào bạn nên biết? Chúng ta cùng tìm hiểu qua bài viết.
Sơ đồ hoạt động của git
Toàn bộ quá trình hoạt động của git được thể hiện qua 4 trạng thái:
- Workspace: là nơi chứa file sau khi bạn clone source code từ repository chung về máy của mình, là nơi chứa file để bạn làm việc.
- Staging: sau khi bạn thay đổi nội dung của một file nào đó, bạn phải thực hiện lệnh git add thì file dữ liệu đó mới được git nhận biết và phân tích xem bạn đã thay đổi gì để tiến hành xử lý. Sau khi thực hiện add, các file đó sẽ được chuyển sang trạng thái staging.
- Local repository: là repository ở trên máy của bạn, nó là một ánh xạ của remote repository, hầu hết thời gian bạn sẽ làm việc ở local repository. Cần phân biệt rõ giữa local và remote, bạn có thể làm bất kỳ thao tác gì ở local repository mà không ảnh hưởng gì đến remote repository, sự thay đổi của bạn chỉ ảnh hưởng đến remote repository khi bạn thực hiện push dữ liệu.
- Remote repository: là repository chung của toàn bộ dự án, mọi người sẽ lấy source code từ đấy về máy local của mình để làm việc. Như trong hình vẽ bạn thấy 2 repository, một là origin và một là upstream, đây chỉ là tên được gán với remote repository đó ở local bạn có thể đặt tên khác.
- Upstream: là repository chung của toàn bộ dự án, mọi người mới vào dự án sẽ lấy source code từ đây để làm việc. Chính xác hơn là các bạn Fork repository này về thành repository của mình để làm việc, vì thường bạn sẽ không có quyền làm trên repository chính của dự án.
- Origin: là repository bạn đã Fork từ repository chung (upstream), bạn sẽ làm việc ở trên repository này như sửa, xóa,… thậm chí xóa luôn repository này cũng không ảnh hưởng gì dự án. Sau khi bạn push dữ liệu lên origin repository bạn sẽ làm thêm một thao tác nữa là tạo Pull request, lúc này code của bạn sẽ được người quản lý repository chung thấy và merge vào đó nếu cần.
Các trường hợp thường gặp khi làm dự án
Bài viết này sẽ không nói về ý nghĩa của các lệnh mà sẽ nói về cách giải quyết các trường hợp hay gặp khi làm dự án.
Thêm các remote repository
Như trên tác giả có đề cập đến nhiều kiểu remote repository khác nhau, trong bài này chúng ta sẽ đề cập đến hai kiểu là origin và upstream.
Đầu tiên chúng ta phải Fork repository chung của dự án về thành repository riêng của cá nhân mình, như hình:
Tiếp theo bạn thực hiện clone repository từ tài khoản của mình và thêm upstream repository vào môi trường local.
1
2
3
4
5
6
7
|
lapth_fpvr4jk@DESKTOP–MHB1IUD MINGW64/d/Projects/NewsPager/gitprocess
$git clonehttps://[email protected]/laptran/gitprocess.git
Cloning into‘gitprocess’...
remote:Enumerating objects:3,done.
remote:Counting objects:100%(3/3),done.
remote:Total3(delta0),reused0(delta0),pack–reused0
Unpacking objects:100%(3/3),done.
|
1
2
3
4
5
6
7
8
9
10
|
lapth_fpvr4jk@DESKTOP–MHB1IUD MINGW64/d/Projects/NewsPager/gitprocess
$cd gitprocess/
lapth_fpvr4jk@DESKTOP–MHB1IUD MINGW64/d/Projects/NewsPager/gitprocess/gitprocess(master)
$git remote add upstream https://[email protected]/lapth/gitprocess.git
lapth_fpvr4jk@DESKTOP–MHB1IUD MINGW64/d/Projects/NewsPager/gitprocess/gitprocess(master)
$git remote–v
origin https://[email protected]/laptran/gitprocess.git (fetch)
origin https://[email protected]/laptran/gitprocess.git (push)
upstream https://[email protected]/lapth/gitprocess.git (fetch)
upstream https://[email protected]/lapth/gitprocess.git (push)
|
Tạo mới branch
Khi bắt đầu làm một chức năng mới nào bạn đều phải tạo mới một branch để làm việc trên đó, branch mới phải dựa vào một feature branch có sẵn nào đó hay master, cách làm đơn giản nhất là fetch toàn bộ upstream (chú ý: upstream) về, checkout feature branch chính và tạo mới từ đó.
1
2
3
4
5
6
7
8
9
10
|
lapth_fpvr4jk@DESKTOP–MHB1IUD MINGW64/d/Projects/NewsPager/gitprocess/gitprocess(master)
$git fetch upstream
remote:Enumerating objects:10,done.
remote:Counting objects:100%(10/10),done.
remote:Compressing objects:100%(6/6),done.
remote:Total8(delta0),reused5(delta0),pack–reused0
Unpacking objects:100%(8/8),done.
From https://github.com/lapth/gitprocess
*[newbranch] UserManagement->upstream/UserManagement
*[newbranch] master ->upstream/master
|
1
2
3
4
|
lapth_fpvr4jk@DESKTOP–MHB1IUD MINGW64/d/Projects/NewsPager/gitprocess/gitprocess(master)
$git checkout UserManagement
Switched toanewbranch‘UserManagement’
Branch‘UserManagement’set up totrack remote branch‘UserManagement’from‘upstream’.
|
1
2
3
|
lapth_fpvr4jk@DESKTOP–MHB1IUD MINGW64/d/Projects/NewsPager/gitprocess/gitprocess(UserManagement)
$git checkout–bAddUser
Switched toanewbranch‘AddUser’
|
Sau khi bạn code xong thì sẽ push lên origin repository và PR vào upstream nhánh master, bạn PR vào nhánh khác sẽ không được, bạn sẽ nhận được lỗi liên quan đến non-fast-forward.
Commit source code vào local repository
Để commit source đã thay đổi vào local repository thường bạn cần thực hiện 2 lệnh: add và commit
- Add: có thể add từng file hay toàn bộ các file thay đổi, file mới.
- Commit: commit các file đã add vào local repository.
1
2
3
4
5
6
7
8
9
|
lapth_fpvr4jk@DESKTOP–MHB1IUD MINGW64/d/Projects/NewsPager/gitprocess/gitprocess(AddUser)
$git status
On branch AddUser
Untracked files:
(use“git add <file>…”toinclude inwhat will be committed)
AddUser.js
nothing added tocommit but untracked files present(use“git add”totrack)
|
1
2
3
4
5
6
7
8
|
lapth_fpvr4jk@DESKTOP–MHB1IUD MINGW64/d/Projects/NewsPager/gitprocess/gitprocess(AddUser)
$git add.
lapth_fpvr4jk@DESKTOP–MHB1IUD MINGW64/d/Projects/NewsPager/gitprocess/gitprocess(AddUser)
$git commit–m“Add user feature”
[AddUser cfad5bb]Add user feature
1file changed,0insertions(+),0deletions(–)
create mode100644AddUser.js
|
Bạn chú ý, mỗi lần commit, bạn cần có 1 commit message kèm theo để giải thích xem commit đó bạn làm gì. Vậy trong trường hợp bạn thay đổi nhiều lần, cứ mỗi lần là 1 message khác nhau, như vậy rất khó theo dõi khi xem commit log. Khi làm việc nhóm theo mình mỗi lần bạn submit thay đổi bạn chỉ cần một commit log message là được. Một lệnh bạn có thể tận dụng để không phải tạo ra quá nhiều commit log là: commit –amend. Lệnh này sẽ gộp thay đổi của bạn với commit lần trước và dùng lại commit log message của lần trước đó.
1
2
3
4
5
6
7
8
9
10
|
lapth_fpvr4jk@DESKTOP–MHB1IUD MINGW64/d/Projects/NewsPager/gitprocess/gitprocess(AddUser)
$git status
On branch AddUser
Changes notstaged forcommit:
(use“git add <file>…”toupdate what will be committed)
(use“git checkout — <file>…”todiscard changes inworking directory)
modified: AddUser.js
no changes added tocommit(use“git add”and/or“git commit -a”)
|
1
2
|
lapth_fpvr4jk@DESKTOP–MHB1IUD MINGW64/d/Projects/NewsPager/gitprocess/gitprocess(AddUser)
$git add.
|
1
2
3
4
5
6
|
lapth_fpvr4jk@DESKTOP–MHB1IUD MINGW64/d/Projects/NewsPager/gitprocess/gitprocess(AddUser)
$git commit—amend
[AddUser3d685ac]Add user feature
Date:Thu May710:23:092020+0700
1file changed,1insertion(+)
create mode100644AddUser.js
|
1
2
3
4
|
lapth_fpvr4jk@DESKTOP–MHB1IUD MINGW64/d/Projects/NewsPager/gitprocess/gitprocess(AddUser)
$git status
On branch AddUser
nothing tocommit,working tree clean
|
Đẩy branch ở local repository lên remote repository
Git cung cấp cho bạn một lệnh push, để đẩy toàn bộ 1 branch lên remote repository, khi push dữ liệu bạn chú ý là cần push dữ liệu vào remote repository nào, như hình trên chúng ta có 2 remote repository, trong thực tế bạn có thể có nhiều hơn, ví dụ: remote repository của bạn khác trong team bạn đã lấy về để review, chạy thử…
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
lapth_fpvr4jk@DESKTOP–MHB1IUD MINGW64/d/Projects/NewsPager/gitprocess/gitprocess(AddUser)
$git push origin AddUser
Enumerating objects:4,done.
Counting objects:100%(4/4),done.
Delta compression using up to4threads
Compressing objects:100%(2/2),done.
Writing objects:100%(3/3),329bytes|329.00KiB/s,done.
Total3(delta0),reused0(delta0)
remote:
remote:Createapull request for‘AddUser’on GitHub by visiting:
remote: https://github.com/laptran/gitprocess/pull/new/AddUser
remote:
Tohttps://github.com/laptran/gitprocess.git
*[newbranch] AddUser->AddUser
|
Tuy nhiên không phải lúc nào bạn cũng push được vì branch local và remote bị conflict, đặc biệt khi bạn dùng commit –amend.
1
2
3
4
|
lapth_fpvr4jk@DESKTOP–MHB1IUD MINGW64/d/Projects/NewsPager/gitprocess/gitprocess(AddUser)
$git push origin AddUser
Tohttps://github.com/laptran/gitprocess.git
![rejected] AddUser->AddUser(non–fast–forward)
|
Một cách để giải quyết nhanh nếu bạn đã chắc chắn mọi thay đổi ở local là chính xác, bạn thêm lệnh force.
1
2
3
4
5
6
7
8
9
10
|
lapth_fpvr4jk@DESKTOP–MHB1IUD MINGW64/d/Projects/NewsPager/gitprocess/gitprocess(AddUser)
$git push origin AddUser–f
Enumerating objects:4,done.
Counting objects:100%(4/4),done.
Delta compression using up to4threads
Compressing objects:100%(2/2),done.
Writing objects:100%(3/3),331bytes|331.00KiB/s,done.
Total3(delta0),reused0(delta0)
Tohttps://github.com/laptran/gitprocess.git
+3d685ac…5697e9fAddUser->AddUser(forced update)
|
Lưu tạm để làm việc khác
Khi bạn đang làm việc mà một bạn khác trong team đến và nhờ xem một đoạn code trên máy mình đang làm thì sao? Git có cung cấp cho bạn 1 lệnh là stash, lệnh này sẽ lưu tạm mọi thay đổi trên workspace và chuyển về trạng thái sạch cho bạn, sau đó bạn có thể checkout đến branch mình cần xem. Chú ý: bạn không nên sử dụng phương án này thường xuyên nếu dùng Windows, vì rất hay gặp lỗi và không thể phục hồi lại dữ liệu đã lưu.
1
2
3
4
5
6
7
8
9
10
|
lapth_fpvr4jk@DESKTOP–MHB1IUD MINGW64/d/Projects/NewsPager/gitprocess/gitprocess(AddUser)
$git status
On branch AddUser
Changes notstaged forcommit:
(use“git add <file>…”toupdate what will be committed)
(use“git checkout — <file>…”todiscard changes inworking directory)
modified: AddUser.js
no changes added tocommit(use“git add”and/or“git commit -a”)
|
1
2
3
|
lapth_fpvr4jk@DESKTOP–MHB1IUD MINGW64/d/Projects/NewsPager/gitprocess/gitprocess(AddUser)
$git stash save some_name
Saved working directory andindex state On AddUser:some_name
|
1
2
3
|
lapth_fpvr4jk@DESKTOP–MHB1IUD MINGW64/d/Projects/NewsPager/gitprocess/gitprocess(AddUser)
$gitstashlist
stash@{0}:On AddUser:some_name
|
1
2
3
4
5
6
7
8
9
10
|
lapth_fpvr4jk@DESKTOP–MHB1IUD MINGW64/d/Projects/NewsPager/gitprocess/gitprocess(AddUser)
$gitstashapplystash@{0}
On branch AddUser
Changes notstaged forcommit:
(use“git add <file>…”toupdate what will be committed)
(use“git checkout — <file>…”todiscard changes inworking directory)
modified: AddUser.js
no changes added tocommit(use“git add”and/or“git commit -a”)
|
Đồng bộ code mới nhất do người khác đẩy vào repository chung (upstream)
Khi bạn đang làm việc mà có một bạn khác đã commit code vào repository chung, làm sao để tích hợp với code bạn đó vào nhánh mình đang làm việc? Git cung cấp cho bạn cơ chế rebase khá tiện lợi, bạn cần fetch thông tin của upstream, sau đó rebase với nhánh mong muốn.
1
2
3
4
5
6
7
8
9
|
lapth_fpvr4jk@DESKTOP–MHB1IUD MINGW64/d/Projects/NewsPager/gitprocess/gitprocess(AddUser)
$git fetch upstream
remote:Enumerating objects:5,done.
remote:Counting objects:100%(5/5),done.
remote:Compressing objects:100%(2/2),done.
remote:Total3(delta0),reused3(delta0),pack–reused0
Unpacking objects:100%(3/3),done.
From https://github.com/lapth/gitprocess
7665d71..2b89e0e UserManagement->upstream/UserManagement
|
1
2
3
|
lapth_fpvr4jk@DESKTOP–MHB1IUD MINGW64/d/Projects/NewsPager/gitprocess/gitprocess(AddUser)
$git rebase–iupstream/UserManagement
Successfully rebased andupdated refs/heads/AddUser.
|
Chú ý: Khi rebase, bạn phải rebase từ nhánh mà mình đã checkout ra, nếu bạn rebase từ nhánh khác, code của bạn không còn đúng nữa và sau này bạn cũng không thể nào tạo PR vào nhánh chức năng mà bạn đã checkout ra từ đó.
Đang code mà nghe tin sét đánh là commit hôm qua của bạn tạo bug blocker
Tìm hiểu một lúc thấy nó liên quan gì đến mình đâu, thế thì điều tra thôi, bạn cần biết ai tạo ra bug đó. Cách đơn giản nhất là bạn dò theo commit history xem ai đã đẩy code lên từ hôm qua, phân vùng và checkout từng commit đó, thử reproduce bug với commit đã checkout xem ai là chính chủ. Nói ngắn gọn thì chỉ có 2 lệnh: git log [–oneline] và git checkout <commit hash> hoặc git reset.
1
2
3
4
5
6
7
|
lapth_fpvr4jk@DESKTOP–MHB1IUD MINGW64/d/Projects/NewsPager/gitprocess/gitprocess(AddUser)
$git log—oneline
7b7f65d(HEAD->AddUser)Add user feature
2b89e0e(upstream/UserManagement)some change from upstream
7665d71(UserManagement)User management
f8875ef Add user
4cfce03(origin/master,origin/HEAD,master)Initial commit
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
lapth_fpvr4jk@DESKTOP–MHB1IUD MINGW64/d/Projects/NewsPager/gitprocess/gitprocess(AddUser)
$git checkout2b89e0e
Note:checking out‘2b89e0e’.
You are in‘detached HEAD’state.You can look around,make experimental
changes andcommit them,andyou can discard any commits you make inthis
state without impacting any branches by performing another checkout.
Ifyou want tocreateanewbranch toretain commits you create,you may
doso(now orlater)by using–bwith the checkout command again.Example:
git checkout–b<new–branch–name>
HEAD isnow at2b89e0esome change from upstream
|
1
2
3
4
5
6
|
lapth_fpvr4jk@DESKTOP–MHB1IUD MINGW64/d/Projects/NewsPager/gitprocess/gitprocess((2b89e0e…))
$git log—oneline
2b89e0e(HEAD,upstream/UserManagement)some change from upstream
7665d71(UserManagement)User management
f8875ef Add user
4cfce03(origin/master,origin/HEAD,master)Initial commit
|
Như ví dụ trên, tác giả đã thử checkout xem commit với commit hash là “2b89e0e” có gây lỗi không.
Chú ý: bạn cần lưu công việc của mình cẩn thận trước khi thực hiện checkout tránh vô tình làm mất dữ liệu ở local của mình.
Làm sao commit code vào nhiều branh khác nhau
Khi bạn hot fix cho một bug trên product hay nhánh chuẩn bị release thì bạn cần đẩy code đó lên nhiều hơn một branch, thường là branch master hay release và sau đó it nhất là branch feature hay dev branch. Để làm việc đó một cách thuận tiện nhất, git cung cấp cho bạn lệnh git cherry-pick <commit hash>.
1
2
3
4
5
6
7
8
|
lapth_fpvr4jk@DESKTOP–MHB1IUD MINGW64/d/Projects/NewsPager/gitprocess/gitprocess(HotFix–V1–Ticket123)
$git log—oneline
c1e6f45(HEAD->HotFix–V1–Ticket123,origin/HotFix–V1–Ticket123)Hot fix Ticket123
b4f34b9(tag:v1,upstream/master,upstream/V1,V1)Merge pull request#1 from lapth/UserManagement
2b89e0e(upstream/UserManagement)some change from upstream
7665d71(UserManagement)User management
f8875ef Add user
4cfce03(origin/master,origin/HEAD,master)Initial commit
|
1
2
3
|
lapth_fpvr4jk@DESKTOP–MHB1IUD MINGW64/d/Projects/NewsPager/gitprocess/gitprocess(HotFix–V1–Ticket123)
$git checkout AddUser
Switched tobranch‘AddUser’
|
1
2
3
4
5
|
lapth_fpvr4jk@DESKTOP–MHB1IUD MINGW64/d/Projects/NewsPager/gitprocess/gitprocess(AddUser)
$git cherry–pick c1e6f45
[AddUser84f9736]Hot fix Ticket123
Date:Thu May711:20:072020+0700
1file changed,1insertion(+),1deletion(–)
|
1
2
3
4
5
6
7
8
|
lapth_fpvr4jk@DESKTOP–MHB1IUD MINGW64/d/Projects/NewsPager/gitprocess/gitprocess(AddUser)
$git log—oneline
84f9736(HEAD->AddUser)Hot fix Ticket123
7b7f65dAdd user feature
2b89e0e(upstream/UserManagement)some change from upstream
7665d71(UserManagement)User management
f8875ef Add user
4cfce03(origin/master,origin/HEAD,master)Initial commit
|
Như trên tác giả đã thực hiện một cherry-pick của hot fix “HotFix-V1-Ticket123” sang nhánh AddUser.
Giải quyết code conflict khi merge
Khi bạn update code mới về từ remote repository thường sinh ra lỗi conflict vì code ở local và remote cùng thay đổi một nội dung hoặc git không phân tích được sự thay đổi của file đó. Khi gặp code conflict bạn chỉ có cách duy nhất là fix từng file rồi sau đó add vào rồi tiếp tục quá trình update.
Kết luận
Git rất tốt cho việc lưu trữ code, đánh code version,… nhưng bạn cần chú ý các loại git. Như https://github.com/ đây là nơi lưu trữ chung và mặc định là mọi repository tạo ra là public, đã có rất nhiều dự án bị đền bù, bị chấm dứt hợp đồng vì vô tình đưa code dự án lên github ở chế độ public.
Để an toàn hơn khi sử dụng git, tránh những lỗi vô tình. Bạn có thể chọn môi trường khác như là https://bitbucket.org/ với chế độ mặc định là private hay https://about.gitlab.com/install/ để tạo một server riêng cho dự án,…
Chúc bạn thành công!
Trần Hữu Lập – FPT Software
FPT TechInsight chính thức ra mắt chuyên mục mới – “GÓC NHÌN BẠN ĐỌC” là nơi bạn đọc có thể chia sẻ những bài viết về công nghệ mới, kinh nghiệm lập trình, tham gia dự án,… đến cộng đồng yêu công nghệ. Các bài viết chất lượng và được đăng tải trên website FPT TechInsight sẽ nhận được một khoản nhuận bút hấp dẫn thay lời cảm ơn vì những đóng góp của tác giả cho cộng đồng.