Git cơ bản và các thủ thuật khi làm dự án

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. lapth_fpvr4jk@DESKTOP-MHB1IUD MINGW64 /d/Projects/NewsPager/gitprocess
  2. $ git clone https://laptran@github.com/laptran/gitprocess.git
  3. Cloning into ‘gitprocess’…
  4. remote: Enumerating objects: 3, done.
  5. remote: Counting objects: 100% (3/3), done.
  6. remote: Total 3 (delta 0), reused 0 (delta 0), pack-reused 0
  7. Unpacking objects: 100% (3/3), done.
  1. lapth_fpvr4jk@DESKTOP-MHB1IUD MINGW64 /d/Projects/NewsPager/gitprocess
  2. $ cd gitprocess/
  3. lapth_fpvr4jk@DESKTOP-MHB1IUD MINGW64 /d/Projects/NewsPager/gitprocess/gitprocess (master)
  4. $ git remote add upstream https://laptran@github.com/lapth/gitprocess.git
  5. lapth_fpvr4jk@DESKTOP-MHB1IUD MINGW64 /d/Projects/NewsPager/gitprocess/gitprocess (master)
  6. $ git remote -v
  7. origin https://laptran@github.com/laptran/gitprocess.git (fetch)
  8. origin https://laptran@github.com/laptran/gitprocess.git (push)
  9. upstream https://laptran@github.com/lapth/gitprocess.git (fetch)
  10. upstream https://laptran@github.com/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. lapth_fpvr4jk@DESKTOP-MHB1IUD MINGW64 /d/Projects/NewsPager/gitprocess/gitprocess (master)
  2. $ git fetch upstream
  3. remote: Enumerating objects: 10, done.
  4. remote: Counting objects: 100% (10/10), done.
  5. remote: Compressing objects: 100% (6/6), done.
  6. remote: Total 8 (delta 0), reused 5 (delta 0), pack-reused 0
  7. Unpacking objects: 100% (8/8), done.
  8. From https://github.com/lapth/gitprocess
  9. [new branch] UserManagement -> upstream/UserManagement
  10. [new branch] master -> upstream/master
  11. [new branch] UserManagement -> upstream/UserManagement
  12. [new branch] master -> upstream/master
  1. lapth_fpvr4jk@DESKTOP-MHB1IUD MINGW64 /d/Projects/NewsPager/gitprocess/gitprocess (master)
  2. $ git checkout UserManagement
  3. Switched to a new branch ‘UserManagement’
  4. Branch ‘UserManagement’ set up to track remote branch ‘UserManagement’ from ‘upstream’.
  1. lapth_fpvr4jk@DESKTOP-MHB1IUD MINGW64 /d/Projects/NewsPager/gitprocess/gitprocess (UserManagement)
  2. $ git checkout -b AddUser
  3. Switched to a new branch ‘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.

lapth_fpvr4jk@DESKTOP-MHB1IUD MINGW64 /d/Projects/NewsPager/gitprocess/gitprocess (AddUser)
$ git status
On branch AddUser
Untracked files:
(use “git add …” to include in what will be committed)

AddUser.js

nothing added to commit but untracked files present (use “git add” to track)

  1. lapth_fpvr4jk@DESKTOP-MHB1IUD MINGW64 /d/Projects/NewsPager/gitprocess/gitprocess (AddUser)
  2. $ git add .
  3. lapth_fpvr4jk@DESKTOP-MHB1IUD MINGW64 /d/Projects/NewsPager/gitprocess/gitprocess (AddUser)
    $ git commit -m “Add user feature”
    [AddUser cfad5bb] Add user feature
    1 file changed, 0 insertions(+), 0 deletions(-)
    create mode 100644 AddUser.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. lapth_fpvr4jk@DESKTOP-MHB1IUD MINGW64 /d/Projects/NewsPager/gitprocess/gitprocess (AddUser)
  2. $ git status
  3. On branch AddUser
  4. Changes not staged for commit:
  5. (use “git add …” to update what will be committed)
  6. (use “git checkout — …” to discard changes in working directory)
  7. modified: AddUser.js
  8. no changes added to commit (use “git add” and/or “git commit -a”)
  1. lapth_fpvr4jk@DESKTOP-MHB1IUD MINGW64 /d/Projects/NewsPager/gitprocess/gitprocess (AddUser)
  2. $ git add .
  1. lapth_fpvr4jk@DESKTOP-MHB1IUD MINGW64 /d/Projects/NewsPager/gitprocess/gitprocess (AddUser)
  2. $ git commit –amend
  3. [AddUser 3d685ac] Add user feature
  4. Date: Thu May 7 10:23:09 2020 +0700
  5. 1 file changed, 1 insertion(+)
  6. create mode 100644 AddUser.js
  1. lapth_fpvr4jk@DESKTOP-MHB1IUD MINGW64 /d/Projects/NewsPager/gitprocess/gitprocess (AddUser)
  2. $ git status
  3. On branch AddUser
  4. nothing to commit, 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. lapth_fpvr4jk@DESKTOP-MHB1IUD MINGW64 /d/Projects/NewsPager/gitprocess/gitprocess (AddUser)
  2. $ git push origin AddUser
  3. Enumerating objects: 4, done.
  4. Counting objects: 100% (4/4), done.
  5. Delta compression using up to 4 threads
  6. Compressing objects: 100% (2/2), done.
  7. Writing objects: 100% (3/3), 329 bytes | 329.00 KiB/s, done.
  8. Total 3 (delta 0), reused 0 (delta 0)
  9. remote:
  10. remote: Create a pull request for ‘AddUser’ on GitHub by visiting:
  11. remote: https://github.com/laptran/gitprocess/pull/new/AddUser
  12. remote:
  13. To https://github.com/laptran/gitprocess.git
  14. [new branch] AddUser -> AddUse

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. lapth_fpvr4jk@DESKTOP-MHB1IUD MINGW64 /d/Projects/NewsPager/gitprocess/gitprocess (AddUser)
  2. $ git push origin AddUser
  3. To https://github.com/laptran/gitprocess.git
  4. ! [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. lapth_fpvr4jk@DESKTOP-MHB1IUD MINGW64 /d/Projects/NewsPager/gitprocess/gitprocess (AddUser)
  2. $ git push origin AddUser -f
  3. Enumerating objects: 4, done.
  4. Counting objects: 100% (4/4), done.
  5. Delta compression using up to 4 threads
  6. Compressing objects: 100% (2/2), done.
  7. Writing objects: 100% (3/3), 331 bytes | 331.00 KiB/s, done.
  8. Total 3 (delta 0), reused 0 (delta 0)
  9. To https://github.com/laptran/gitprocess.git
  10. 3d685ac…5697e9f AddUser -> 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. lapth_fpvr4jk@DESKTOP-MHB1IUD MINGW64 /d/Projects/NewsPager/gitprocess/gitprocess (AddUser)
  2. $ git status
  3. On branch AddUser
  4. Changes not staged for commit:
  5. (use “git add …” to update what will be committed)
  6. (use “git checkout — …” to discard changes in working directory)
  7. modified: AddUser.js
  8. no changes added to commit (use “git add” and/or “git commit -a”)
  1. lapth_fpvr4jk@DESKTOP-MHB1IUD MINGW64 /d/Projects/NewsPager/gitprocess/gitprocess (AddUser)
  2. $ git stash save some_name
  3. Saved working directory and index state On AddUser: some_name
  1. lapth_fpvr4jk@DESKTOP-MHB1IUD MINGW64 /d/Projects/NewsPager/gitprocess/gitprocess (AddUser)
  2. $ git stash list
  3. stash@{0}: On AddUser: some_name
  1. lapth_fpvr4jk@DESKTOP-MHB1IUD MINGW64 /d/Projects/NewsPager/gitprocess/gitprocess (AddUser)
  2. $ git stash apply stash@{0}
  3. On branch AddUser
  4. Changes not staged for commit:
  5. (use “git add …” to update what will be committed)
  6. (use “git checkout — …” to discard changes in working directory)
  7. modified: AddUser.js
  8. no changes added to commit (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. lapth_fpvr4jk@DESKTOP-MHB1IUD MINGW64 /d/Projects/NewsPager/gitprocess/gitprocess (AddUser)
  2. $ git fetch upstream
  3. remote: Enumerating objects: 5, done.
  4. remote: Counting objects: 100% (5/5), done.
  5. remote: Compressing objects: 100% (2/2), done.
  6. remote: Total 3 (delta 0), reused 3 (delta 0), pack-reused 0
  7. Unpacking objects: 100% (3/3), done.
  8. From https://github.com/lapth/gitprocess
  9. 7665d71..2b89e0e UserManagement -> upstream/UserManagement
  1. lapth_fpvr4jk@DESKTOP-MHB1IUD MINGW64 /d/Projects/NewsPager/gitprocess/gitprocess (AddUser)
  2. $ git rebase -i upstream/UserManagement
  3. Successfully rebased and updated 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. lapth_fpvr4jk@DESKTOP-MHB1IUD MINGW64 /d/Projects/NewsPager/gitprocess/gitprocess (AddUser)
  2. $ git log –oneline
  3. 7b7f65d (HEAD -> AddUser) Add user feature
  4. 2b89e0e (upstream/UserManagement) some change from upstream
  5. 7665d71 (UserManagement) User management
  6. f8875ef Add user
  7. 4cfce03 (origin/master, origin/HEAD, master) Initial commit
  1. lapth_fpvr4jk@DESKTOP-MHB1IUD MINGW64 /d/Projects/NewsPager/gitprocess/gitprocess (AddUser)
  2. $ git checkout 2b89e0e
  3. Note: checking out ‘2b89e0e’.
  4. 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.
  5. 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:
  6. git checkout -b
  7. HEAD is now at 2b89e0e some change from upstream
  1. lapth_fpvr4jk@DESKTOP-MHB1IUD MINGW64 /d/Projects/NewsPager/gitprocess/gitprocess ((2b89e0e…))
  2. $ git log –oneline
  3. 2b89e0e (HEAD, upstream/UserManagement) some change from upstream
  4. 7665d71 (UserManagement) User management
  5. f8875ef Add user
  6. 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. lapth_fpvr4jk@DESKTOP-MHB1IUD MINGW64 /d/Projects/NewsPager/gitprocess/gitprocess (HotFix-V1-Ticket123)
  2. $ git log –oneline
  3. c1e6f45 (HEAD -> HotFix-V1-Ticket123, origin/HotFix-V1-Ticket123) Hot fix Ticket123
  4. b4f34b9 (tag: v1, upstream/master, upstream/V1, V1) Merge pull request #1 from lapth/UserManagement
  5. 2b89e0e (upstream/UserManagement) some change from upstream
  6. 7665d71 (UserManagement) User management
  7. f8875ef Add user
  8. 4cfce03 (origin/master, origin/HEAD, master) Initial commit
  1. lapth_fpvr4jk@DESKTOP-MHB1IUD MINGW64 /d/Projects/NewsPager/gitprocess/gitprocess (HotFix-V1-Ticket123)
  2. $ git checkout AddUser
  3. Switched to branch ‘AddUser’
  1. lapth_fpvr4jk@DESKTOP-MHB1IUD MINGW64 /d/Projects/NewsPager/gitprocess/gitprocess (AddUser)
  2. $ git cherry-pick c1e6f45
  3. [AddUser 84f9736] Hot fix Ticket123
  4. Date: Thu May 7 11:20:07 2020 +0700
  5. 1 file changed, 1 insertion(+), 1 deletion(-)
  1. lapth_fpvr4jk@DESKTOP-MHB1IUD MINGW64 /d/Projects/NewsPager/gitprocess/gitprocess (AddUser)
  2. $ git log –oneline
  3. 84f9736 (HEAD -> AddUser) Hot fix Ticket123
  4. 7b7f65d Add user feature
  5. 2b89e0e (upstream/UserManagement) some change from upstream
  6. 7665d71 (UserManagement) User management
  7. f8875ef Add user
  8. 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

nguồn: techinsight.com.vn


Trả lời

Close Menu