9 minute read

Introduction

Git là công cụ mà Dev nào cũng phải biết. Để hiểu về Git thì mình recommend đọc cuốn Pro Git là đủ. Đối với mình thì hiểu đến 80% cuốn đó là ổn.

Dưới đây là kiến thức mình học được từ cuốn sách và từ kinh nghiệm của bản thân mình. Một số phần mình sẽ skip (được liệt kê ở phần Note)

About Git

Git là một Distributed VCS, được tạo bởi cha đẻ của Linux vào năm 2005. Điểm khác biệt lớn nhất của Git so với các hệ thống DVCS cùng thời là ở cách Git làm việc với data. Git coi dữ liệu nó quản lý là stream of snapshots thay vì stream of changes.

“Every time you commit, or save the state of your project in Git, it basically takes a picture of what all your files look like at that moment and stores a reference to that snapshot. To be efficient, if files have not changed, Git doesn’t store the file again, just a link to previous identitcal file it has already stored.”

Điều này đem lại một số lợi ích lớn khi sử dụng Git, chúng ta sẽ tìm hiểu tiếp ở các phần tiếp theo.

Hầu hết các thao tác đều có thể thực hiện offline. -> nhanh, tiện. Git rất đảm bảo, sử dụng SHA-1 để checksum -> ko file nào thay đổi mà Git không biết.

“Everything in Git is check-summed before it is stored and is then referred to by that checksum.”

Git thường chỉ thực hiện thao tác thêm dữ liệu: Một khi đã commit thì khá khó để làm mess up mà không thể revert.

Trong Git, files sẽ có 3 states:

  • modified: chỉnh sửa nhưng chưa commit.
  • staged: files đã mark là chuẩn bị để commit.
  • commited: files đã nằm an toàn trong local database.

  • untracked: chưa có trong git

Git Basics

Setup Git

Git có kèm theo công cụ git config để thay đổi configuration variables, những biến này có thể ở 3 vị trí:

  • /etc/gitconfig: chứa biến áp dụng với toàn bộ user trong hệ thống. -> --system
  • ~/.gitconfig hoặc ~/.config/git/config: chưa biến dành riêng cho bạn -> --global
  • config file: nằm trong folder .git, dành riêng cho project đó. -> --local.

Mình có thể lựa chọn tầng config bằng cách sử dụng option --system, --global, --local. Khi cài đặt lần đầu thì cần config user.name và user.email vì Git sẽ cần 2 thông tin này để hoạt động, nên sử dụng option --global để config.

git config --global user.name "minhdq99hp"
git config --global user.email "minhdq99hp@gmail.com"

git config --global core.editor emacs   # mac dinh co the la nano hoac vim

git config --list # xem danh sach cac setting da cai dat

Basic Commands

Phần này thì mình sẽ viết những câu lệnh mà mình hay sử dụng, trên thực tế, còn rất nhiều lệnh, nhiều option có thể hữu ích khác. Nhưng mình thấy rằng các câu lệnh dưới đây là đủ, nếu cần những nhu cầu phức tạp hơn thì có thể sử dụng GUI tool trên Github, Gitlab,…

# basic commands
git init

# discard changes in file
git checkout -- <file>  # dangerous

# stage file or track file or marking merge-conflicted as resolved
git add
# unstange file
git reset HEAD <file>

git commit
git commit -a -m "First commit" # skip staging
git commit -amend   # undo commit -> go to the staged area

git status
git status -s   # short status


# remove file
git rm <file>   # delete from git and also delete it from filesystem
git rm --cached <file>  # rm from git only

# move file or rename file
git mv <file>

# show log
git log

git log --graph # show log graph


# working with remote
git clone

git remote -v
git remote add <remote_name> <url>
git remote set-url <remote_name> <new_url>


git fetch   # pull down all the data from remote that you don't have yet. (all branches)

git pull    # fetch and merge.

git push <remote_name> <branch>

.gitignore

The rules for the patterns you can put in the .gitignore file are as follows:

  • Standard glob patterns
  • ** match nested directory
  • [abc] match a or b or c
  • You can end patterns with a forward slash (/) to specify a directory.
  • You can negate a pattern by starting it with an exclamation point (!).

.gitignore use glob patterns.

Example;

# ignore all .a files
*.a

# exclude
!lib.a

# only ignore the TODO file in the root directory, not subdir/TODO
/TODO

# ignore all files in any directory named build
build/

# ignore doc/notes.txt, but not doc/server/arch.txt
doc/*.txt

# ignore all .pdf files in the doc/ directory and any of its subdirectories
doc/**/*.pdf

GitHub duy trì một danh sách các .gitignore cho các project và ngôn ngữ ở đây

Viewing your staged and unstaged changes

Use git diff to view your staged and unstaged changes.

  • git diff: see what are changed but not staged
  • git diff --staged (staged and cached are synonyms): see what have been staged.

Dùng VSCode thì nó cũng hỗ trợ tính năng này.

Tagging

Git has the ability to tag specific points in a repo’s history as being important (Ex: v1.0, v2.0). Thường thì sẽ sử dụng để mark release points.

Git supports two types of tags:

  • lightweight: a lightweight tag là một pointer trỏ đến một commit cụ thể. (thực chất nó là một branch luôn)
  • annotated: được store như một object đầy đủ trong database của Git. Chứa nhiều thông tin hơn như: tên người tag, email, date, message. và có thể signed và verifed.

Theo mặc định thì git push sẽ không share tag lên, cần phải thêm thủ công:

git push <remote_name> <tag_name>

git push <remote_name> --tags   # them nhieu tag cung mot luc

# Listing your tags
git tag     # list all available tags
git tag -l "v1.0*"  # search for tags

# Create annotated tag
git tag -a v1.4 -m "my version 1.4"

# Create a lightweight tag
git tag v1.4-lw

# Tag a specific commit
git tag v1.4-lw <commit_checksum>

Git Alias

Phần này cũng tiện đấy, cơ mà mình không dùng vì hay làm việc với remote server. Sẽ mất công nhớ và phải config trên remote server nữa thì mới sử dụng được. Với lại các câu lệnh của git cũng khá đơn giản rồi, mình không có nhu cầu sử dụng phức tạp hơn.

Git Branching

Đây chính là “killer feature” của Git. Đối với những DVCS khác, việc chia branch rất phức tạp, tốn kém. Nhưng đối với Git, tạo branch là một điều luôn được recommend trong git flow. Tại sao việc tạo branch trong Git lại dễ dàng là bởi cách Git xử lý data (stream of snapshots). Mỗi branch thực chất chỉ là một pointer trỏ đến commit.

HEAD: current branch, symbolic refer tới branch đang làm việc

Có 2 cách để “nhập branch”:

  • Merge: tạo một commit mới dựa trên 3 điểm (commit chung gần nhất, commit sẽ merge vào và commit sẽ được merge). Câu lệnh là git merge <branch_b> (nếu đang ở branch_a thì có nghĩa là sẽ có một merge commit được tạo ở branch_a). Trong quá trình merge thì sẽ có lúc Git không thể merge tự động, cần mình resolve conflict.
  • Rebase: git sẽ tách base của branch_b (tạo những commit tương ứng và commit thẳng vào branch_a). Như vậy, có thể thấy rằng lúc đó lịch sử của git sẽ trở thành tuyến tính, thay vì rẽ nhánh như merge. Tuy nhiên, nó cũng kèm theo drawback rất dở là sẽ conflict lịch sử với người khác nếu không cẩn thận (tất nhiên là vẫn work around được). Điều cần ghi nhớ ở đây là chỉ rebase trên những nhánh của mình quản lý.

Undoing Changes

Reference:

Git checkout

Git clean

Git revert

Git revert có thể hiểu là một cách an toàn để undo commit (forward-moving undo). Trên thực tế, nó sẽ tạo ra invert commit, điều này sẽ tránh động đến git history.

Khác với Git checkout hay Git reset, git revert không di chuyển pointer. Ngoài ra, Git revert có thể trỏ đến bất kì commit nào, trong khi git reset chỉ có thể hoạt động backward từ commit hiện tại.

# prevent creating commit, only put inverse changes in staging index and working directory.
git revert -n  --no-commit

Git reset

Đây là lệnh dùng để undo change, nó có 3 kiểu: soft, mixed, hard. 3 kiểu này liên quan đến Git’s internal state management mechanism: The commit tree (HEAD), the staging index, and the working directory

Git rm

GitHub

Forking Project

GitHub hỗ trợ fork là để người ngoài có thể contribute kể cả khi không có quyền push branch. Lúc đó, người ngoài sẽ fork project về và tạo pull request.

GitHub Flow

GitHub là nơi chuyên để open-source, thế nên họ cũng xây dựng một flow riêng xoay quanh việc tạo pull request để contribute.

  1. Fork the project
  2. Create a topic branch from master.
  3. Make some commits to improve the project
  4. Push this branch to your GitHub project
  5. Open a Pull Request on GitHub
  6. Discuss, and optionally continue committing
  7. The project owner merges or closes the Pull Request
  8. Sync the updated master back to your fork

Khi tạo Pull Request, GitHub cũng sẽ kiểm tra xem việc merge vào có tạo conflict hay không, nếu có thì người contributor nên sửa lại branch của mình, chứ không nên để maintainer phải sửa. Có 2 cách để làm điều này: rebase branch của mình về target branch hoặc là merge target branch về branch của mình. -> Nên chọn cách 2 để giảm thiểu lỗi.

“Don’t push your work until you’re happy with it”

Special Files

README

CONTRIBUTING: GitHub sẽ hiện cho contributor file này nếu họ tạo pull request

Q&A

  • Q: Copy a git repo without history
  • A: here

    git clone --depth <depth> -b <branch> <repo_url>

  • Q: Nên chia branch như thế nào ? Có những trường phái nào ?

Git stash là gì ?

Git clean ?

Git reset ? soft vs hard ?

Squashing Commits

Git submodules ?

Git credential storage ?

Git hook ? server-side and client-side ?

Làm thế nào để xóa 1 file chứa thông tin mật trong toàn bộ Git history ?

Note

Có các phần mình sẽ không liệt kê ở trong bài này dù có trong ở sách bao gồm:

  • Các vấn đề về xây dựng Git server: hầu như hiện nay đều sử dụng các full-featured server như GitLab, GitHub, BitBucket nên việc xây dựng Git server không quá cần thiết
  • Các kiến thức về modify Git history: Còn rất nhiều lệnh có thể modify history của Git, nhưng mình chỉ học một số cái cơ bản thôi. Ngoài ra, cũng không khuyến khích việc chỉnh sửa history (nhất là khi làm việc với team) nên mình sẽ không đi sâu về phần này.
  • Kiến thức về cách Git hoạt động: mình chỉ cần tiếp cận Git ở tầng abstract, thế nên cũng không có ý định đi sâu về phần này.

References

Contributor

Comments