4.4. Git#

4.4.1. Command navigator#

# di chuyen den folder
## nếu có space sử dụng "\ "
## "tab" để auto completion
cd <folder directory>

# hiện all content in folder
ls

# tạo content
mkdir <content name>

# remove content
rmdir <content name>

# To navigate to your home directory
cd
cd ~

# To navigate up one directory level
cd ..

# To navigate to the previous directory (or back)
cd -

# To navigate into the root directory
cd /

4.4.2. Git config#

4.4.2.1. Get git info#

# check version
git --version

# help
git --help

4.4.2.2. Modify config#

# view list config
git config --list

# user.name
git config --global user.name <name>

# user.email
git config --global user.email <email>

4.4.2.3. Git authenticate account#

Authenticate with GitHub CLI then run gh auth login in cmd

4.4.3. Git-flow#

  • Master/Prod/Main: branchs có sẵn trong git và là branchs chứa mã nguồn khởi tạo của ứng dụng và các version đã sẵn sàng để realease cho người dùng có thể sử dụng (đặt Tag trên mỗi version). Thường cấu hình cho manage tương tác. Đây là branch dùng cho sản phẩm chính thức. Đây luôn là branch ổn định nhất và nó chưa lịch sử các lần release của dự án

  • Hotfix: Khi sản phẩm trên master branch của chúng ta gặp phải trục trặc và cần có bản vá ngay lập tức thì ta sẽ tạo ra hotfix branch. Branch này tương tự như release branch nhưng nó được tạo ra từ master branch thay vì từ develop branch như release (Chú ý hotfix branch cũng cần được gộp lại với master branch với develop branch để các version develop tiếp theo đã được fix bug từ bản hotfix), teamlead sẽ tạo branchs hotfix base trên branchs Master để hotfix sau đó merge lại vào Master (thường thì sẽ không được thay đổi trược tiếp mã nguồn trên branch master nên phải lam cách này)

  • Release: Trước khi Release một phần mềm, dev team cần được tạo ra để kiểm tra lại lần cuối trước đi release sản phần để người dùng có thể sử dụng (Thuông thường mã nguồn tại thời điểm này sẽ tạo ra bản build để test và kiểm tra lại bussiness). Khi develop branch đã có đủ số tính năng cần thiết để có thể release, ta có thể tạo branch mới với tên quy ước release/tên_version. Khi đến thời điểm release ứng dụng, teamlead sẽ merge lên branchs master để release cho ngời dùng.

  • Develop: Được khởi tạo từ master branches để lưu lại tất cả lịch sử thay đổi của mã nguồn, là nhánh dùng cho sản phẩm trong quá trình phát triển. Develop branchs là merge code của tất cả các branchs feature. Khi dev team hoàn thành hết feature của một topic, teamlead sẽ review ứng dụng và merge đến branchs release để tạo ra bản một bản release cho sản phẩm.

  • Feature: Được base trên branchs Develop. Mỗi khi phát triển một feature mới chúng ta cần tạo một branchs để việt mã nguồn cho từng feature. Khi có một feature mới dev tạo một branchs mới (thường đặt theo tên feature/<tên feature đó>) base trên branchs Develop để code cho feature đó. Sau khi code xong, dev tạo merge request đến branchs develop để teamlead review mà merge lại vào branchs Develop. (Lưu ý: các Feature không được phép gộp trực tiếp với master branch)

Ngoài ra còn một số branchs mà chúng ta thường tạo ra tùy vào yêu cầu của dự án:

  • Pre-pro: môi trường chưa mã nguồi của bản buil chạy trên môi trường user test,

  • QC: Môi trường chứa mã nguồi của bản build chạy trên môi trường test,

  • BugFix: để chứa mã nguồn khi thực hiện công việc fix bug.


Git-flow là một set of command của git, để thực hiện dự án theo đúng git-flow Tham khảo cheatsheet về git-flow

# khởi tạo một git-flow cho một project, Lệnh này sẽ tạo ra hai branch ban đầu là master và develop
# Bạn sẽ cần trả lời một số câu hỏi cho việc thiết lập git-flow sau câu lệnh khởi tạo
git flow init

# tạo một feature: Sẽ tạo ra một nhánh mới có tên dạng feature/<tên-feature> từ nhánh 'develop' và tự động chuyển sang nhánh mới này 
git flow feature start <tên-feature>

# Sau khi feature đó được thực hiện xong, ta có thể công bố feature đó lên remote server để mọi người cùng có thể cập nhật
# Khi bạn làm việc với những người khác trên cùng một chức năng, bạn sẽ cần đẩy (push) phần mã nguồn của bạn cho chức năng đó lên máy chủ (remote) để những người khác có thể kéo về (pull) được.
git flow feature publish <tên-feature>

# pull feature của người khác về
git flow feature pull REMOTE_NAME MYFEATURE

# Để tiến hành gộp branch đó vào develop branch ta dùng lệnh
# merge to "develop" + del "feature" branch + checkout "develop" branch
git flow feature finish <tên-feature>

# Tạo ra nhánh 'release' từ nhánh 'develop' để bắt đầu một phát hành mới
git flow release start <verion-no>

# Để công bố phần code 'release' của mình cho các thành viên khác
git flow release publish <verion-no>

# Để tiến hành merge bản release đó vào master branch và develop branch
# = merge "release" to "master" + tag "master" theo "release" + merge "release" to "dev" + del "release"
git flow release finish <version-no>

# Để tạo một bản "hotfix" ta dùng lệnh
git flow hotfix start <tên-hotfix>

# Sau khi bản "hotfix" hoàn thiện ta có thể tiến hành merge lại với "master" branch và "develop" branch như sau
git flow hotfix finish <tên-hotfix>

4.4.4. Github-flow#

Việc thao tác, vận dụng với Git-flow khá lằng nhằng, nên GitHub-flow đã đơn giản nó đi rất nhiều

Github Flow là một quy trình đơn giản dựa trên các branch với mục đích hỗ trợ team và các project được deploy một cách thường xuyên, automated deployments

Gitflow thường sử dụng cho các product có tần suất release ít hơn Github-flow,

  • GitHub-flow bao gồm nhánh master và nhánh feature.

  • Từ nhánh master dùng cho Product, tạo 1 nhánh có tên phù hợp với mục đích. (ở đây gọi là feature, gần giống với _feature/hotfix ở git-flow)

  • Làm việc trên nhánh feature đã tạo, sau khi hoàn thành công việc thì push lên nhánh remote.

  • Sử dụng chức năng tạo Pull Request của GitHub, sau khi review thì merge vào nhánh master.

  • Sau khi merge vào nhánh master thì lập tức được deploy lên Product

4.4.5. Git command#

4.4.5.1. Create new repo#

4.4.5.1.1. init#

# install git for new project
git init

4.4.5.1.2. clone#

# clone repo from link
git clone <link.git - HTTPS>

4.4.5.1.3. connect to remote#

# set new remote với tên là "origin", tạo new repo từ github.com xong copy paste SSH url của repository
git remote add origin <remote_url>

# Verifies the new remote URL
git remote -v 

4.4.5.2. File change#

4.4.5.2.1. file’s status#

4 type of status: Untracked –> Unmodified –> Modified –> Staged

(Untracked) ------------------------------add-------------------------------------->>(Staged)

(Untracked) <<----remove----- (Unmodified) -----edit---->> (Modified)-----stage---->>(Staged)

                              (Unmodified) <<------------commit----------------------(Staged)

4.4.5.2.2. status#

# check status
git status

# check status each file
git status -s

4.4.5.2.3. add#

# Thêm tất cả thay đổi
git add -A   

# track all file (exclude file/folder begin ".")
git add * 

# track all file (include file/folder begin ".") , trừ xóa file
git add .   

# add file from untracked to status
git add <filename>

# Thêm tất cả các file có phần mở rộng .c
git add *.c

4.4.5.2.4. commit#

Việc thực commit là thao tác đưa toàn bộ nội dung trong staged vào dữ liệu của Git - nó tạo ra ảnh chụp toàn bộ thư mục làm việc ở thời điểm đó.

Như sơ đồ trên nếu đã commit thì nó chuyển file từ trạng thái staged sang unmodified

# commit include the message
git commit -m "C0 - Khoi tao du an"

# commit, nhưng sửa lại commit message ngay trước đó
git commit --amend -m "Thông báo ..." 

Xóa commit khi commit nhầm

# Tuỳ vào từng trường hợp mà ta có 3 cách sau để đưa lịch sử commit về như cũ
 
# 1. Chỉ đưa HEAD về như cũ
git reset --soft HEAD~
 
# 2. Đưa HEAD và index về như cũ
git reset HEAD~
 
# 3. Đưa cả index, working tree về 1 commit trước đó
git reset --hard HEAD~

Hồi phục commit đã xóa

# Xem lại toàn bộ lịch sử commit
git reflog 

# Chọn commit muốn phục hồi và khôi phục lại
# git reset --hard HEAD@{2}
git reset --hard <commit>

Xóa bỏ 1 vài commit gần đây

# tạo ra một commit mới đảo ngược lại những thay đổi trong commit được chỉ định
git revert <commit-hash-code>

4.4.5.2.5. log#

# check log commit
git log --oneline 

# xem log nhánh master của remote origin
!git log --oneline origin/master

4.4.5.2.6. diff#

check sự khác nhau nội dung trong thư mục đang làm việc, staged với commit cuối

Nó cho biết bản có màu đỏ là bản gốc (ở commit cuối), bản có màu xanh là bản đang sửa đổi.

# Kiểm tra thay đổi giữa hai commit
git diff commit1 commit2

# Kiểm tra sự thay đổi của hai nhánh
git diff branch1 branch2

4.4.5.2.7. rollback#

Chuyển trạng thái file về commit trước đó / phục hồi file

# chuyển về commit trước
git checkout -- <tên file>

# phục hồi file test2.html nếu xóa
git checkout [hash] filename # khôi phục lại filename
git reset HEAD test2.html
git checkout -- test2.html

4.4.5.3. Branch command#

# check current brancha
git branch

# tao branch mới
git branch <branch_name> 

# switch to other branch
git checkout <branch> 

# add and switch new branch
git checkout -b <branch> 

 # rename branch
git branch -m <newname>

# delete branch remotely
git push origin --delete <remoteBranchName> 

# Xóa branch đã tồn tại
git branch -d BRANCH_NAME

# Xóa branch có commit nhưng chưa được merge
git branch -D BRANCH_NAME

Phục hồi branch đã xóa

# Xem lại toàn bộ lịch sử commit
git reflog
 
# Từ các commit này, chọn rồi tạo branch mới
git branch <tên branch> <commit>

4.4.5.4. Repository command#

Ưu tiên sử dụng luồng Git-flow hoặc github-flow ở trên.

4.4.5.4.1. fetch#

git fetch 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), 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 remotelocal.

# Tải về thông tin của tất cả các nhánh của remote có trong folder "origin"
git fetch origin

# Get all
git fetch --all

# tải 1 nhánh cụ thể
git fetch origin <branch>

4.4.5.4.2. merge#

merge sẽ tạo ra một commit mới là kết hợp từ 2 commit cuối cùng của 2 nhánh cần gộp vào với nhau. Log commit sẽ không bị thay đổi và thứ tự các commit sẽ được sắp xếp theo thời gian tạo commit.

==>

# chuyển nhánh "master"
git checkout master

# Lấy code mới nhất về nhánh master trên local
git pull origin master

# merge "feature1" into current branch (master)
git merge feature1             

Lưu ý khi merge: Không nên merge trực tiếp code vào develop branch, mà hãy tạo và push lên feature branch, sau đó sử dụng merge request:

  • tạo merge request để teamlead hoặc review có thể review mã nguồi trước khi merge để đảm bảo tính toàn vẹn của mã nguồn

  • người review sẽ comment trực tiếp cần thay đổi lên merge request để giảm thời gian trao đổi tăng tính hiệu quả khi làm việc nhóm

  • tạo merge request để lưu lại lịch sử thay đổi của mã nguồi.Khi có vấn đề về lỗi, chất lượng phần mềm…. chúng ta có thể xem lại tất cả những sự thay đổi trên từ dòng code ( việc này có thể kiếm tra bằng cách kiểm tra trên từng commit nhưng commit thì rất nhiều)

  • đây còn là nơi để lưu lại các comment của người review, các lỗi thông thường để các member sao không còn mắc lại lỗi cũ và là nơi để học hỏi code lẫn nhau thông qua việc xem lại sự thay đổi từng dòng code của member khác.

  • Để tránh conflict code, nên thường xuyên merge code ở branchs về để đảm bảo code hiện tại là mới nhất, merge code của branchs trước và sau khi code nếu có conflict thì merge conflict trước khi tạo merge request.

Thông thường thì tất cả các thay đổi về mã nguồi của branchs develop, release, master đều thông qua merge request (trừ mã nguồn lúc khởi tạo dự án).

4.4.5.4.3. rebase#

rebase sẽ đưa toàn bộ branch Feature lên trên top branch master, làm thay đổi lịch sử commit.

==>

# Chuyển sang nhánh master
git checkout master

# Lấy code mới nhất về nhánh master trên local
git pull origin master

# Quay lại branch bạn muốn bổ sung phần thay đổi của master mới nhất trước khi chạy đến các commit của BRANCH_NAME
git checkout BRANCH_NAME

# BRANCH_NAME sẽ được bắt đầu bằng last commit của master, rồi mới chạy các commit tiếp theo của BRANCH_NAME
git rebase master        

Nếu bị conflict khi rebase

# Kiểm tra các file bị conflict
git status

# Sửa các file bị conflict (sửa bằng tay)

# Sau khi sửa conflict, dùng lệnh add để phản ảnh sự điều chinhr
git add FILE_PATH

# Sau khi sửa conflict, tiếp tục lại lệnh rebase
git rebase --continue

# Hủy rebase
git rebase --abort

4.4.5.4.4. cherry-pick#

cherry-pick tương tự với mergerebase là lấy thay đổi từ một branch này và gộp vào branch khác, tuy nhiên cherry-pick chỉ gộp một commit được chỉ định từ một nhánh khác vào nhánh hiện tại trong khi mergerebase sẽ gộp toàn bộ các commit lại.

Để sử dụng cherry-pick, ta cần xem lại log các commit sau đó lấy mã hash của commit cần được cherry-pickcheckout sang nhánh cần được gộp commit của mã hash kia và thực hiện lệnh:

git cherry-pick <commit-hash-code>

Ví dụ lấy commit C từ nhánh master gộp vào nhanh cherry-pick:

===>

4.4.5.4.5. pull#

pull = fetch + merge

git pull <tên-remote> <tên-remote-branch>

4.4.5.4.6. push#

push local repository to remote repository

# set new remote với tên là "origin", tạo new repo từ github.com xong copy paste SSH url của repository
git remote add origin <remote_url>

# Verifies the new remote URL
git remote -v 

# add file to stage status
git add .

# commit file to git snapshoot with the message comment with push
git commit -m "initial commit"

# đẩy từ local repo lên remote repo với folder tên "origin"
git push origin <remote branch>

# force push the file is in working
git push origin <branch> --force 

4.4.5.5. git stash#

Khi muốn tạm dừng công việc hiện tại và chuyển sang một branch khác. git stash cho bạn khả năng lưu lại trạng những thay đổi mà bạn đã tạo ra mà không cần thiết phải commit nó giúp bạn có thể dễ dàng chuyển sang nhánh khác làm việc và sau đó quay lại và tiếp tục những gì bạn đang làm ở nhánh đó

# Để lưu được những thay đổi mà không cần commit nó
git add .

# Tạm thời lưu lại các phần công việc còn đang làm dở
git stash -u

# xem lại các thay đổi đã lưu
git stash list
 
# Chuyển sang một branch khác và làm việc
git checkout -b other-branch

#### ~làm việc, làm việc, làm việc~

git add <các file cần thiết>
git commit -m "commit message"
 
# Trở về branch cũ
git checkout origin-branch
 
# Lấy lại các nội dung công việc đang làm dở trước đó
git stash pop

# Để xóa toàn bộ những lần đã lưu
git stash clear

# Để drop một lần lưu chỉ định
git stash drop 'stash@{n}'

4.4.6. Git ignore#

File .gitignore sử dụng để chỉ định các file/folder mà sẽ untrack từ git. Trong file .gitignore sẽ là list danh sách các TH:

  • Một file cụ thể cần ignore: example.exe

  • Một folder cụ thể cần ignore: example_folder/

  • Dấu ! phía trước có ý nghĩa phủ định: !abc/example.exe

  • Sử dụng 1 * để tìm các file có cùng định dạng.

Ví dụ 1: *.xml (ignore tất cả các file .xml trong project)

Ví dụ 2: config/*.xml (ignore các file *.xml trong thư mục config)

  • Sử dụng ** để có hiệu lực cho các thư mục không cần định rõ tên.

Ví dụ 1: **/foo (ignore tất cả file/thư mục có tên là “foo” ở mọi nơi trong project)

Ví dụ 2: folder/** (ignore tất cả các file bên trong folder)

Tips:

  • Sử dụng # để comment và có thể để cách dòng cho dễ đọc.

  • Khi ignore thư mục nên có dấu / ở sau tên thư mục để nhận biết đó là thư mục, nếu không nó có thể là coi là thư mục hoặc file hay symbolic link.

  • Cách tạo gitignore

# create file gitignore
touch .gitignore

# các file trong git cache vẫn sẽ được git quản lý mặc dù đã list trong gitignore, phải xoá git cache (xong add các file lại để commit)
git rm -r --cached .

4.4.7. Git tags#

Tag là chức năng đặt tên một cách đơn giản của Git, nó cho phép ta xác định một cách rõ ràng các phiên bản mã nguồn (code) của dự án. Ta có thể coi tag như một branch không thay đổi được. Một khi nó được tạo (gắn với 1 commit cụ thể) thì ta không thể thay đổi lịch sử commit ấy được nữa.

4.4.8. Git CI/CD#

CI (Continuous Integration)CD (Continuous Delivery) tức là quá trình tích và triển khai hợp nhanh và liên tục, là quá trình tự động thực hiện các quá trình build, test, release, deploy khi có các trigger như commit/merge code lên một branch định sẵn hoặc có thể là tự động chạy theo một lịch cố định

  • khi hoàn thành một feature thì teamlead tạo merge request rồi merge vào branch develop, đúng 5h chiều hàng ngày hệ thống sẽ tự động build, test, quét sonar…. và deploy lên develop eviroment (quá trình này là CD).(không trigger merger code để deploy với branch này vì code được merge vào đây liên tục nếu trigger merger code sẽ dẫn đến việc build liên tục, làm chậm hệ thống)

  • Với branch prepro thì sẽ được trigger mỗi lần có sự thay đổi về code sẽ tự động thực hiện các bước như với branch develop.

  • Với branch master thì có hơi khách một chúc, Git cũng sẽ tự động trigger và tiến hành các bước build, run unit test, quét sonar…. nhưng không tiến hành deploy (quá trình này chỉ là CI) lên server mà chỉ được deploy khi có confirm từ một người có quyền hoặc deploy bằng tay để đảm bảo quá trình delevery sản phẩm không xảy ra lỗi và đúng với thời gian khách hàng mong muốn.

4.4.8.1. GitHub Actions#

Github actions được sinh ra để hỗ trợ việc tự động hóa các tác vụ trong vòng đời của một phần mềm. Git actions hoạt động theo hướng sự kiện, nghĩa là nó sẽ thực hiện một loạt commands đã được định nghĩa sẵn khi có một sự kiện được xảy ra.

Ví dụ như, bạn có thể cấu hình để mỗi khi có người tạo một mergers request lên một repository nào đó hệ thống sẽ tự động run commands để run các unit test case của bạn.

Tham khảo

4.4.9. Git Submodule#

1. Bản chất: Submodule là một con trỏ đến một commit cụ thể của repo Child (C), chứ không phải là bản sao hoàn chỉnh của repo Child trong repo Parent (P).

2. Thêm submodule

git submodule add https://github.com/example/repo-c.git <C folder>
git commit -m "Add submodule C"

3. Cách lưu trữ: Repo C vẫn là một repository độc lập, nhưng repo P chỉ lưu trữ một tệp .gitmodules chứa thông tin về submodule và commit hash của repo C. Khi cập nhật repo B, repo A chỉ lưu commit hash mới của repo B chứ không lưu trực tiếp nội dung thay đổi. Do đó:

  • Khi clone repo P, submodule C không tự động tải về, cần chạy lệnh:

git submodule update --init --recursive
  • Nếu muốn luôn cập nhật submodule C tự động về commit mới nhất khi pull repo P, bạn có thể chạy:

git submodule update --remote --merge

4. Cập nhật (Push/Pull) repo C trong repo P

  • Pull

cd <C folder>
git pull origin main
cd ..
git add C
git commit -m "Update submodule C"
  • Push

cd <C folder>
git status  # Kiểm tra trạng thái
git add .    # Thêm tất cả thay đổi
git commit -m "Cập nhật code trong repo C"
git push origin <branch_name>
cd ..
git add <C folder>
git commit -m "Cập nhật submodule C lên commit mới nhất"
git push origin <branch_name>

5. Xóa submodule nếu không cần nữa

git submodule deinit -f -- <C folder>
git rm -rf C
rm -rf .git/modules/C