1. Remote Repository là gì?
Remote Repository trong Git là một phiên bản của dự án của bạn được lưu trữ ở một vị trí khác, thường là trên một máy chủ. Điều này cho phép nhiều người cùng làm việc trên cùng một dự án mà không làm ảnh hưởng đến nhau.
Khi bạn làm việc với Git, hầu hết các thao tác (như commit, xem lịch sử, …) đều được thực hiện trên Local Repository, tức là phiên bản của dự án được lưu trữ trên máy tính của bạn.
Tuy nhiên, khi bạn muốn chia sẻ công việc của mình với người khác, hoặc muốn làm việc trên nhiều máy khác nhau (ví dụ, ở văn phòng và ở nhà), bạn sẽ cần sử dụng Remote Repository.
Một Git Server là một máy chủ có cài đặt dịch vụ Git, cho phép bạn tạo ra các Repository. Ví dụ, GitHub là một Git Server, nơi bạn có thể tạo ra một Remote Repository. Bạn có thể clone Remote Repository này về máy của mình để tạo ra một Local Repository, làm việc trên nó, và sau đó cập nhật lại lên Remote Repository khi cần.
GitHub là một cách nhanh chóng để tạo ra một Repository. Nếu bạn không muốn sử dụng GitHub, bạn cũng có thể tạo ra một Git Server riêng của mình.
2. Cài đặt để có Git Server.
Để cài đặt Git Server, bạn cần thực hiện hai bước chính: cài đặt Git trên máy chủ (server) và cài đặt SSH để người dùng có thể kết nối đến máy chủ thông qua SSH. Sử dụng SSH Key là một phương pháp an toàn để xác thực người dùng.
2.1. Cài đặt Git trên Server.
Trên máy chủ Linux, bạn có thể cài đặt Git bằng lệnh sau: sudo apt install git-all
. Đây là lệnh dùng để cài đặt Git trên các hệ điều hành dựa trên Debian như Ubuntu.
Trên máy chủ Linux CentOS, bạn cần sử dụng lệnh sudo yum install git
. CentOS sử dụng hệ thống quản lý gói yum, do đó lệnh cài đặt sẽ khác so với Ubuntu.
Trên Windows Server, bạn cần tải phiên bản Git phù hợp từ trang chủ của Git tại địa chỉ: https://git-scm.com/downloads. Sau khi tải về, bạn chỉ cần chạy bộ cài đặt.
Sau khi cài đặt Git, bạn có thể kiểm tra xem Git đã được cài đặt thành công hay chưa bằng cách gõ lệnh git --version
trên cửa sổ dòng lệnh (terminal). Nếu Git đã được cài đặt thành công, lệnh này sẽ trả về phiên bản của Git.
2.2. Cài đặt SSH.
Bạn sẽ cần cài đặt và cấu hình SSH trên máy chủ của mình để cho phép người dùng kết nối đến máy chủ thông qua SSH. Việc này thường bao gồm việc cài đặt dịch vụ SSH, tạo SSH keys cho mỗi người dùng và cấu hình quyền truy cập SSH.
Nếu bạn làm việc với nhiều máy chủ khác nhau chứa Remote Repository, bạn có thể thiết lập thông số riêng cho mỗi máy chủ và người dùng trên Windows. Thông số này bao gồm cổng kết nối, file SSH Key, và được khai báo trong file C:\Users\UserName\.ssh\config
.
Ví dụ về cấu hình trong file config
:
Host domain.com - username
User username
Hostname domain.com
PreferredAuthentications publickey
IdentityFile "C:\sshkey\id_rsa1"
Host 192.168.1.10 - username
User Administrator
Hostname 192.168.1.10
Port 2222
PreferredAuthentications publickey
IdentityFile "C:\sshkey\id_rsa2"
Với cấu hình trên:
- Khi Git truy cập đến máy chủ có địa chỉ
domain.com
với tên đăng nhập làusername
, nó sẽ sử dụng cổng mặc định 22 và file SSH Key lưu tạiC:\sshkey\id_rsa1
. - Khi Git truy cập đến máy chủ có địa chỉ
192.168.1.10
với tên đăng nhập làAdministrator
, nó sẽ sử dụng cổng 2222 và file SSH Key lưu tạiC:\sshkey\id_rsa2
.
Cấu hình này giúp Git biết cách kết nối đến từng máy chủ cụ thể.
3. Tạo Remote Repository.
Để tạo một Remote Repository, bạn cần thực hiện các bước sau:
Tạo một tài khoản người dùng trên hệ thống Linux (hoặc Windows) để sử dụng cho Git và cho phép kết nối đến máy chủ qua SSH. Ví dụ, để tạo một người dùng mới trên Linux CentOS với tên là gitusername
, bạn có thể sử dụng các lệnh sau:
sudo adduser gitusername
sudo passwd gitusername
Sau khi thực hiện các lệnh này, hệ thống sẽ có một người dùng mới với tên là gitusername
và thư mục home mặc định của người dùng này là /home/gitusername/
.
Đăng nhập vào hệ thống với người dùng bạn muốn sử dụng. Ví dụ, trên Linux, bạn có thể sử dụng các lệnh sau để chuyển sang người dùng gitusername
và di chuyển đến thư mục home của người dùng này:
su gitusername
cd ~
Tạo một thư mục để chứa Remote Repository. Ví dụ, để tạo một thư mục có tên là myproject
, bạn có thể sử dụng các lệnh sau:
mkdir myproject
cd myproject
Sử dụng lệnh git init --bare
để thiết lập thư mục myproject
là nơi lưu trữ Remote Repository. Lệnh này sẽ khởi tạo một kho lưu trữ Git trống trong thư mục /home/gitusername/myproject/
.
git init --bare
Sau khi thực hiện các bước này, bạn sẽ có một Remote Repository với địa chỉ là gitusername@domain.com:myproject.git
(hoặc gitusername@IP:myproject.git
nếu bạn sử dụng địa chỉ IP). Remote Repository này sẵn sàng để các máy khách clone và cập nhật.
4. Thiết lập để làm việc với Remote Repository.
Để làm việc với một Remote Repository, bạn cần thực hiện các bước sau từ Local Repository của mình:
Kiểm tra xem Local Repository của bạn đang được liên kết với Remote Repository nào bằng lệnh git remote
. Nếu chưa có Remote Repository nào được thiết lập, lệnh này sẽ không trả về kết quả.
Để thêm một Remote Repository, sử dụng lệnh git remote add
với cú pháp như sau:
git remote add <name_remote> <addr_remote>
Trong đó, <name_remote>
là tên bạn muốn đặt cho Remote Repository (ví dụ: origin
, abc
, xyz
, …) và <addr_remote>
là địa chỉ của Remote Repository. Ví dụ:
git remote add origin gitusername@domain.com:myproject.git
Lệnh này sẽ thiết lập một Remote Repository có tên là origin
với địa chỉ kết nối là gitusername@domain.com:myproject.git
trong Local Repository của bạn.
Để kiểm tra lại, bạn có thể sử dụng lệnh git remote -v
. Lệnh này sẽ liệt kê tất cả các Remote Repository đã được thiết lập trong Local Repository của bạn.
Vì Remote Repository mới được khởi tạo trên máy chủ, bạn cần đẩy (push) dữ liệu đầu tiên lên nó. Để làm điều này, sử dụng lệnh git push
với tham số là tên của Remote Repository (origin
) và nhánh cần cập nhật (master
). Ví dụ:
git push origin master
Từ đây, Remote Repository đã sẵn sàng để hoạt động. Bạn có thể cập nhật dữ liệu lên nó (push), lấy dữ liệu về (pull, fetch), …
Nếu bạn muốn clone Remote Repository về máy để tạo một Local Repository mới, bạn có thể sử dụng lệnh git clone
. Ví dụ:
git clone gitusername@domain.com:myproject.git
Lệnh này sẽ clone Remote Repository về máy của bạn và tự động thiết lập dự án với Remote Repository có tên là origin
.
5. Một số lệnh cơ bản làm việc với Remote Repository.
Dưới đây là một số lệnh cơ bản khi làm việc với Remote Repository:
git fetch
: Lệnh này lấy dữ liệu mới từ Remote Repository mà Local Repository của bạn chưa có (ví dụ, dữ liệu do người khác mới đưa lên). Lệnh này không ảnh hưởng đến code hiện tại của dự án trên Local Repository, nó chỉ lấy về thông tin về các file chưa có.
git fetch <remote_name>
git pull
: Lệnh này lấy toàn bộ dữ liệu mới từ Remote Repository và cập nhật Local Repository của bạn để giống với Remote. Nếu Local Repository chưa commit và push code cũ, có thể bị mất.
git pull <remote_name> <branch_name>
git push
: Lệnh này cập nhật dữ liệu từ Local Repository lên Remote Repository.
git push <remote_name> <branch_name>
git remote -v
: Lệnh này hiển thị thông tin về các Remote Repository đã được thiết lập trong Local Repository của bạn.
git remote add
: Lệnh này thêm một Remote Repository vào Local Repository của bạn.
git remote add <remote_name> <url>
git remote show
: Lệnh này hiển thị thông tin chi tiết về một Remote Repository cụ thể. Cú pháp:
git remote show <remote_name>
git remote rename
: Lệnh này đổi tên một Remote Repository trong Local Repository của bạn. Cú pháp:
git remote rename <old_name> <new_name>
6. Sự khác nhau của git fetch
và git pull
.
Git fetch
và git pull
đều là lệnh để lấy thông tin từ Remote Repository về Local Repository, nhưng chúng có sự khác biệt quan trọng:
Như sơ đồ trên thì ta thấy
git fetch
: Lệnh này lấy thông tin mới từ Remote Repository và cập nhật vào Local Repository, nhưng nó không thay đổi code trong thư mục làm việc của bạn. Điều này có nghĩa là, nếu có bất kỳ thay đổi nào trên Remote Repository mà bạn chưa có, git fetch
sẽ lấy chúng về nhưng không tự động hợp nhất (merge) chúng vào code hiện tại của bạn. Điều này cho phép bạn xem xét các thay đổi trước khi hợp nhất chúng.
git pull
: Ngược lại, git pull
không chỉ lấy thông tin mới từ Remote Repository mà còn tự động hợp nhất chúng vào thư mục làm việc của bạn. Điều này có nghĩa là, nếu có bất kỳ thay đổi nào trên Remote Repository mà bạn chưa có, git pull
sẽ lấy chúng về và tự động hợp nhất chúng vào code hiện tại của bạn.
7. Thực hành.
7.1. Tạo dự án theo sơ đồ dưới đây.
Đầu tiên chúng ta cần phải tạo một Local Repository ở máy trạm cụ thể như sau:
7.2. Xây dựng trên máy Computer, ví dụ là Computer A.
Chuẩn bị folder cho dự án.
shell> mkdir myproject
shell> cd myproject/
Khởi tạo Repository.
shell> git init
Initialized empty Git repository in /Users/hoanghd/My Drive/git/bai6/myproject/.git/
Tạo commit đầu tiên C1 tại nhánh main.
shell> echo "Noi dung A" > a.txt
shell> git add .
shell> git commit -m"C1"
[main (root-commit) 7de3cce] C1
1 file changed, 1 insertion(+)
create mode 100644 a.txt
shell> git log --oneline
7de3cce (HEAD -> main) C1
Tạo commit thứ 2 C2 tại nhánh main.
shell> echo "Noi dung B" > b.txt
shell> git add .
shell> git commit -m"C2"
[main add27da] C2
1 file changed, 1 insertion(+)
create mode 100644 b.txt
shell> git log --oneline
add27da (HEAD -> main) C2
7de3cce C1
Chuyển qua nhánh Beta và tạo ra 1 commit đầu tiên ở nhánh này với tên là B1.
shell> git branch beta
shell> git checkout beta
Switched to branch 'beta'
shell> echo "Noi dung cap nhat boi Beta" > add-by-beta.txt
shell> git add .
shell> git commit -m"B1"
[beta af9135c] B1
1 file changed, 1 insertion(+)
create mode 100644 add-by-beta.txt
shell> git log --oneline
af9135c (HEAD -> beta) B1
add27da (main) C2
7de3cce C1
Chuyển qua nhánh main và tạo một commit mới với tên M1.
shell> git checkout main
Switched to branch 'main'
shell> echo "Cap nhat boi Main" > add-by-main.txt
shell> git add .
shell> git commit -m"M1"
[main 7690035] M1
1 file changed, 1 insertion(+)
create mode 100644 add-by-main.txt
shell> git log --oneline
7690035 (HEAD -> main) M1
add27da C2
7de3cce C1
Cùng xem lại các commit trên nhánh Beta như sau:
shell> git log --oneline beta
0f3da3d (beta) B1
80c5409 C2
481e3f4 C1
7.3. Khởi tạo Remote Repo.
Bây giờ mình sẽ xây dựng một máy chủ chạy Linux (ví dụ là Ubuntu) để lưu trữ các kho chứa Git. Server này có địa chỉ IP là 192.168.13.231/23.
shell> ifconfig eth0
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 192.168.13.231 netmask 255.255.254.0 broadcast 192.168.13.255
inet6 fe80::c8cb:27ff:fe0f:ee64 prefixlen 64 scopeid 0x20<link>
ether ca:cb:27:0f:ee:64 txqueuelen 1000 (Ethernet)
RX packets 52673566 bytes 70583988355 (70.5 GB)
RX errors 0 dropped 29148 overruns 0 frame 0
TX packets 40211215 bytes 34862186236 (34.8 GB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
Server này bắt buộc phải có Git nhé, ví dụ mình đang sử dụng Git phiên bản 2.25.1 chạy trên Ubuntu 20.04.6 LTS như dưới.
shell> lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description: Ubuntu 20.04.6 LTS
Release: 20.04
Codename: focal
shell> git version
git version 2.25.1
Bạn sẽ cần cài đặt và cấu hình SSH trên máy chủ này.
Đầu tiên mình tạo tài khoản mới sử dụng cho Git và sao chép các file sshkey nằm trong thư mục ~/.ssh/ (lý do là mình sử dụng file sshkey đã được tạo từ trước cho tài khoản root) sang thư mục /home/hoanghd/ để sử dụng cho tài khoản hoanghd.
useradd hoanghd
cp -R ~/.ssh/ /home/hoanghd/
cd /home/hoanghd/
Như bạn thấy thì sau khi copy xong .ssh thì thư mục này đang thuộc sở hữu của tài khoản root.
shell> ls -al /home/hoanghd/
total 12
drwxr-xr-x 3 root root 4096 Feb 5 04:14 .
drwxr-xr-x 3 root root 4096 Feb 5 04:10 ..
drwxr-xr-x 2 root root 4096 Feb 5 04:14 .ssh
Tiến hành phân quyền cho thư mục .ssh vừa copy cho từ tài khoản root sang cho tài khoản hoanghd.
shell> chown -R hoanghd:hoanghd .ssh/
shell> ls -al
total 12
drwxr-xr-x 3 root root 4096 Feb 5 04:14 .
drwxr-xr-x 3 root root 4096 Feb 5 04:10 ..
drwxr-xr-x 2 hoanghd hoanghd 4096 Feb 5 04:14 .ssh
Bây giờ bạn có thể remote vào server này thông qua sshkey với username là hoanghd.
ssh hoanghd@192.168.13.231
Mình tạo thư mục repos/myproject
trong thư mục home của hoanghd
và sau đó di chuyển vào thư mục này.
mkdir /home/hoanghd/repos
cd /home/hoanghd/repos
mkdir myproject
cd myproject
Sử dụng lệnh git init --bare
để thiết lập thư mục myproject
là nơi lưu trữ Remote Repository. Lệnh này sẽ khởi tạo một kho lưu trữ Git trống trong thư mục /home/gitusername/myproject/
.
shell> git init --bare
Initialized empty Git repository in /home/hoanghd/repos/myproject/
Bây giờ ở thư mục này sẽ xuất hiện cấu trúc thư mục và file chứa các thành phần dữ liệu của Git Server.
shell> ls -al
total 40
drwxrwxr-x 7 hoanghd hoanghd 4096 Feb 5 06:19 .
drwxrwxr-x 3 hoanghd hoanghd 4096 Feb 5 06:18 ..
-rw-rw-r-- 1 hoanghd hoanghd 23 Feb 5 06:19 HEAD
drwxrwxr-x 2 hoanghd hoanghd 4096 Feb 5 06:19 branches
-rw-rw-r-- 1 hoanghd hoanghd 66 Feb 5 06:19 config
-rw-rw-r-- 1 hoanghd hoanghd 73 Feb 5 06:19 description
drwxrwxr-x 2 hoanghd hoanghd 4096 Feb 5 06:19 hooks
drwxrwxr-x 2 hoanghd hoanghd 4096 Feb 5 06:19 info
drwxrwxr-x 4 hoanghd hoanghd 4096 Feb 5 06:19 objects
drwxrwxr-x 4 hoanghd hoanghd 4096 Feb 5 06:19 refs
7.4. Lệnh git remote.
Treen máy Local bạn kiểm tra xem Local Repository của bạn đang được liên kết với Remote Repository nào bằng lệnh git remote
. Nếu chưa có Remote Repository nào được thiết lập, lệnh này sẽ không trả về kết quả.
git remote
Thêm remote.
git remote add abc hoanghd@192.168.13.231:/home/hoanghd/repos/myproject/
Kiểm tra thông tin remote.
shell> git remote -v
abc hoanghd@192.168.13.231:/home/hoanghd/repos/myproject/ (fetch)
abc hoanghd@192.168.13.231:/home/hoanghd/repos/myproject/ (push)
Xóa remote, bạn dùng lệnh git remote rm <tên remote>
và nếu bạn sử dụng lệnh git remote -v
bạn sẽ không còn thấy dữ liệu đầu ra nữa.
git remote rm abc
git remote -v
7.5. Upload dữ liệu từ local lên server.
Mình tiến hành thêm lại remote server vào máy local với tên mới là origin.
shell> git remote add origin hoanghd@192.168.13.231:/home/hoanghd/repos/myproject/
shell> git remote -v
origin hoanghd@192.168.13.231:/home/hoanghd/repos/myproject/ (fetch)
origin hoanghd@192.168.13.231:/home/hoanghd/repos/myproject/ (push)
Cách push dữ liệu vào Remote Server, bạn sử dụng lệnh git push -u <repo_name> <branch_name>
như sau:
shell> git push -u origin main
Enumerating objects: 9, done.
Counting objects: 100% (9/9), done.
Delta compression using up to 12 threads
Compressing objects: 100% (5/5), done.
Writing objects: 100% (9/9), 717 bytes | 717.00 KiB/s, done.
Total 9 (delta 0), reused 0 (delta 0), pack-reused 0
To 192.168.13.231:/home/hoanghd/repos/myproject/
* [new branch] main -> main
branch 'main' set up to track 'origin/main'.
Lệnh git push -u origin main
được sử dụng để đẩy các thay đổi từ nhánh “main” của Local Repository lên Remote Repository được gọi là “origin”.
Cụ thể, các phần của lệnh này có nghĩa như sau:
git push
: Lệnh này được sử dụng để đẩy các thay đổi từ Local Repository lên Remote Repository.-u
: Tùy chọn này, còn được gọi là--set-upstream
, được sử dụng để thiết lập liên kết giữa nhánh hiện tại của Local Repository và nhánh tương ứng trên Remote Repository. Sau khi liên kết này được thiết lập, bạn có thể sử dụnggit push
vàgit pull
mà không cần chỉ định tên nhánh.origin
: Đây là tên mặc định mà Git gán cho Remote Repository từ đó bạn đã clone. Trong hầu hết các trường hợp, “origin” là Remote Repository chính mà bạn làm việc.main
: Đây là tên của nhánh mà bạn muốn đẩy các thay đổi lên. Trong trường hợp này, đó là nhánh “main”.
Hiển thị lịch sử commit của nhánh hiện tại dưới dạng một dòng cho mỗi commit. Kết quả cho thấy có ba commit, với commit mới nhất có mã là “7690035” và được gắn nhãn “M1”. “HEAD -> main, origin/main” cho biết commit này là commit mới nhất (“HEAD”) trên nhánh “main” của Local Repository và nhánh “main” của Remote Repository “origin”.
shell> git log --oneline
7690035 (HEAD -> main, origin/main) M1
add27da C2
7de3cce C1
Liệt kê tất cả các nhánh trong Local Repository và kết quả cho thấy có hai nhánh: “beta” và “main”. Dấu sao (*) trước “main” cho biết đây là nhánh hiện tại.
shell> git branch
beta
* main
Liệt kê tất cả các nhánh trong Local Repository và Remote Repository và kết quả cho thấy có hai nhánh trong Local Repository (“beta” và “main”) và một nhánh trong Remote Repository “origin” (“main”).
shell> git branch -a
beta
* main
remotes/origin/main
Hoặc chúng ta có thể push với tham số –all để push tất cả các nhánh vào Remote Server.
shell> git push -u origin --all
Enumerating objects: 4, done.
Counting objects: 100% (4/4), done.
Delta compression using up to 12 threads
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 314 bytes | 314.00 KiB/s, done.
Total 3 (delta 0), reused 0 (delta 0), pack-reused 0
To 192.168.13.231:/home/hoanghd/repos/myproject/
* [new branch] beta -> beta
branch 'main' set up to track 'origin/main'.
branch 'beta' set up to track 'origin/beta'.
Liệt kê tất cả các nhánh trong cả Local Repository và Remote Repository. Kết quả của lệnh cho thấy có hai nhánh trong Local Repository của bạn, đó là “beta” và “main”. Dấu sao (*) trước “main” cho biết đây là nhánh hiện tại mà bạn đang làm việc.
Ngoài ra, kết quả cũng cho thấy có hai nhánh trong Remote Repository “origin”, đó là “beta” và “main”. Những nhánh này được lưu trữ trên máy chủ từ xa và bạn có thể lấy các thay đổi từ chúng hoặc đẩy các thay đổi lên chúng.
shell> git branch -a
beta
* main
remotes/origin/beta
remotes/origin/main
Tại nhánh main, tiến hành thay đổi nội dung file b.txt.
shell> echo -e "\nSu doi boi Main" >> b.txt
shell> git status
On branch main
Your branch is up to date with 'origin/main'.
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: b.txt
no changes added to commit (use "git add" and/or "git commit -a")
Tiến hành sử dụng git add .
và thực hiện thêm commit mới có tên M2.
shell> git add .
shell> git commit -m"M2"
[main 4c141f3] M2
1 file changed, 2 insertions(+)
Và đây là kết quả khi sử dụng lệnh git log –oneline, lệnh hiển thị lịch sử commit của nhánh hiện tại dưới dạng một dòng cho mỗi commit.
shell> git log --oneline
4c141f3 (HEAD -> main) M2
7690035 (origin/main) M1
add27da C2
7de3cce C1
Kết quả của lệnh cho thấy có bốn commit:
- “4c141f3 (HEAD -> main) M2”: Đây là commit mới nhất trên nhánh “main” của Local Repository. “HEAD -> main” cho biết commit này là commit mới nhất (“HEAD”) trên nhánh “main”. “M2” là thông điệp commit.
- “7690035 (origin/main) M1”: Đây là commit trước đó trên nhánh “main” của Remote Repository “origin”. “M1” là thông điệp commit.
- “add27da C2”: Đây là commit trước đó nữa. “C2” là thông điệp commit.
- “7de3cce C1”: Đây là commit cũ nhất trong lịch sử hiển thị. “C1” là thông điệp commit.
Lưu ý rằng “origin/main” không còn là commit mới nhất, điều này cho thấy có thể bạn đã thực hiện một hoặc nhiều commit mà chưa đẩy lên Remote Repository.
Chúng ta sẽ thực hiện push sự thay đổi của nhánh main vào Remote Server.
shell> git push -u origin main
Enumerating objects: 5, done.
Counting objects: 100% (5/5), done.
Delta compression using up to 12 threads
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 267 bytes | 267.00 KiB/s, done.
Total 3 (delta 1), reused 0 (delta 0), pack-reused 0
To 192.168.13.231:/home/hoanghd/repos/myproject/
7690035..4c141f3 main -> main
branch 'main' set up to track 'origin/main'.
Lệnh git log --oneline
sẽ hiển thị lịch sử commit của nhánh hiện tại dưới dạng một dòng cho mỗi commit. Kết quả của lệnh cho thấy có bốn commit và “origin/main” nằm ở commit mới nhất, điều này cho thấy bạn đã đẩy tất cả các thay đổi lên Remote Repository và Local Repository của bạn đang được cập nhật với Remote Repository.
shell> git log --oneline
4c141f3 (HEAD -> main, origin/main) M2
7690035 M1
add27da C2
7de3cce C1
Sử dụng lệnh git push --delete origin beta
để xóa nhánh “beta” từ Remote Repository “origin”. Kết quả cho thấy nhánh “beta” đã được xóa thành công từ Remote Repository.
shell> git push --delete origin beta
To 192.168.13.231:/home/hoanghd/repos/myproject/
- [deleted] beta
shell> git branch -a
beta
* main
remotes/origin/main
Để thêm lại nhánh beta vào Remote Server bạn có thể push dữ liệu với tham số –all hoặc gõ tên nhánh cần push như dưới.
shell> git push -u origin --all
Enumerating objects: 4, done.
Counting objects: 100% (4/4), done.
Delta compression using up to 12 threads
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 314 bytes | 314.00 KiB/s, done.
Total 3 (delta 0), reused 0 (delta 0), pack-reused 0
To 192.168.13.231:/home/hoanghd/repos/myproject/
* [new branch] beta -> beta
branch 'main' set up to track 'origin/main'.
branch 'beta' set up to track 'origin/beta'.
shell> git branch -a
beta
* main
remotes/origin/beta
remotes/origin/main
8. Lệnh git clone.
Phần này mình sử dụng một máy Linux chạy Ubuntu 20.04 với IP là 192.168.13.232 sử dụng git version 2.25.1.
shell> ifconfig eth0
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 192.168.13.232 netmask 255.255.254.0 broadcast 192.168.13.255
inet6 fe80::18b9:e0ff:fe0f:d419 prefixlen 64 scopeid 0x20<link>
ether 1a:b9:e0:0f:d4:19 txqueuelen 1000 (Ethernet)
RX packets 133888545 bytes 66697908797 (66.6 GB)
RX errors 0 dropped 529196 overruns 0 frame 0
TX packets 34728592 bytes 48396037081 (48.3 GB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
shell> lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description: Ubuntu 20.04.6 LTS
Release: 20.04
Codename: focal
shell> git version
git version 2.25.1
Mình sẽ config lại thông tin user.name và user.email để chúng ta có thể dễ dàng phân biệt những commit nào được thực hiện trên máy nào.
git config --global user.name "hoanghd-13.232"
git config --global user.email "hoanghd164@gmail.com"
Xác minh lại thiết lập của git config
.
shell> git config --list
user.name=hoanghd-13.232
user.email=hoanghd164@gmail.com
Sử dụng lệnh git clone <ip_remote>:<path_folder_remote>
để clone source code về máy Local. Nếu bạn gặp lỗi “warning: remote HEAD refers to nonexistent ref, unable to checkout.”, nó thường xuất hiện khi bạn cố gắng clone một repository mà nhánh mặc định (thường là “master” hoặc “main”) không tồn tại.
shell> git clone 192.168.13.231:/home/hoanghd/repos/myproject/
Cloning into 'myproject'...
remote: Enumerating objects: 15, done.
remote: Counting objects: 100% (15/15), done.
remote: Compressing objects: 100% (9/9), done.
remote: Total 15 (delta 2), reused 0 (delta 0)
Receiving objects: 100% (15/15), done.
Resolving deltas: 100% (2/2), done.
warning: remote HEAD refers to nonexistent ref, unable to checkout.
Để giải quyết vấn đề này thì từ máy local clone xuống bị lỗi “warning: remote HEAD refers to nonexistent ref, unable to checkout.
“, bạn hãy vào thư mục vừa clone xong.
cd myproject/
Kiểm tra các nhánh có trong repository chúng ta thấy có 2 nhánh remotes/origin/beta
và remotes/origin/main
.
shell> git branch -a
remotes/origin/beta
remotes/origin/main
Nếu bạn muốn tạo một nhánh mới tương ứng với nhánh “main” trên Remote Repository “origin”, bạn nên sử dụng lệnh sau:
shell> git checkout -b main origin/main
Branch 'main' set up to track remote branch 'main' from 'origin'.
Switched to a new branch 'main'
Lệnh này sẽ tạo một nhánh mới có tên là “main” trong Local Repository của bạn, sau đó chuyển đổi (checkout) sang nhánh mới này. Nhánh này sẽ theo dõi nhánh “main” trên Remote Repository “origin”, nghĩa là Git sẽ biết rằng nhánh này tương ứng với nhánh “main” trên “origin”.
Lệnh git branch -a
hiển thị tất cả các nhánh trong cả Local Repository và Remote Repository.
git branch -a
* main
remotes/origin/beta
remotes/origin/main
Kết quả của lệnh cho thấy:
- “* main”: Đây là nhánh hiện tại mà bạn đang làm việc trong Local Repository. Dấu sao (*) chỉ ra nhánh hiện tại.
- “remotes/origin/beta”: Đây là nhánh “beta” trên Remote Repository “origin”.
- “remotes/origin/main”: Đây là nhánh “main” trên Remote Repository “origin”.
Lưu ý rằng chỉ có nhánh “main” tồn tại trong Local Repository của bạn, trong khi có hai nhánh “beta” và “main” tồn tại trên Remote Repository “origin”.
9. Lệnh git fetch.
git fetch
: Lệnh này sẽ cập nhật thông tin từ kho chứa từ xa (remote repository) về kho chứa cục bộ (local repository) của bạn, nhưng không thực hiện việc hợp nhất (merge) hoặc thay đổi gì đến mã nguồn hiện tại.
Lệnh git fetch
được sử dụng trong trường hợp này để cập nhật thông tin từ kho chứa từ xa (remote repository) về kho chứa cục bộ (local repository) của bạn.
Khi bạn làm việc với một dự án có nhiều người cùng tham gia và cùng đẩy (push) thay đổi lên kho chứa từ xa, thông tin về kho chứa từ xa trên máy của bạn sẽ không còn cập nhật. Điều này có nghĩa là, có thể có những nhánh mới được tạo ra, hoặc những thay đổi mới trên các nhánh hiện có mà bạn không hề biết.
Bằng cách sử dụng git fetch
, bạn sẽ cập nhật thông tin về kho chứa từ xa mà không làm ảnh hưởng đến mã nguồn hiện tại của bạn. Điều này giúp bạn có cái nhìn tổng quan về tất cả các thay đổi đã diễn ra trên kho chứa từ xa, và từ đó, bạn có thể quyết định xem có nên chuyển đổi sang nhánh mới hoặc hợp nhất thay đổi từ nhánh khác vào nhánh hiện tại của bạn hay không.
Như ví dụ này trên máy local 192.168.12.28 mình sử dụng git fetch để cập nhật thông tin mới trước khi chuyển sang nhánh beta. Nếu bạn không sử dụng git fetch thì có khả năng máy local sẽ không thấy nhánh beta để chuyển sang.
shell> git fetch
shell> git branch -a
* main
remotes/origin/beta
remotes/origin/main
shell> git checkout beta
Branch 'beta' set up to track remote branch 'beta' from 'origin'.
Switched to a new branch 'beta'
shell> git branch
* beta
main
Bây giờ mình sẽ thử tạo thêm 1 file mới ở nhánh main và tiến hành thêm 1 commit mới xem chuyện gì xảy ra nhé.
Đầu tiên mình chuyển sang main bằng lệnh git checkout.
shell> git checkout main
Already on 'main'
Your branch is up to date with 'origin/main'.
Mình sẽ cho bạn thấy hiện tại repo của mình ở Server và Local đang ở nhánh main và dừng lại ở commit M2.
shell> git log --oneline
4c141f3 (HEAD -> main, origin/main) M2
7690035 M1
add27da C2
7de3cce C1
Giờ mình tiến hành thêm một file mới c.txt và tạo một commit M3 tại nhánh main này.
shell> echo 'Noi dung C' > c.txt
shell> git status
On branch main
Your branch is up to date with 'origin/main'.
Untracked files:
(use "git add <file>..." to include in what will be committed)
c.txt
nothing added to commit but untracked files present (use "git add" to track)
shell> git add .
shell> git commit -m"M3"
[main 74136ce] M3
1 file changed, 1 insertion(+)
create mode 100644 c.txt
Giờ bạn sẽ thấy nhánh main tại Local đã có thêm 1 commit mới là M3 còn nhánh main tại Server đang dừng lại ở commit M2.
shell> git log --oneline
74136ce (HEAD -> main) M3
4c141f3 (origin/main) M2
7690035 M1
add27da C2
7de3cce C1
Giờ mình tiến hành push repo tại local lên nhánh main của server.
shell> git push origin main
Enumerating objects: 4, done.
Counting objects: 100% (4/4), done.
Delta compression using up to 12 threads
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 266 bytes | 266.00 KiB/s, done.
Total 3 (delta 1), reused 0 (delta 0), pack-reused 0
To 192.168.13.231:/home/hoanghd/repos/myproject/
4c141f3..74136ce main -> main
Và kết quả bạn sẽ thấy hiện tại nhanh main cả repo server và local đều đã dừng lại ở nhánh M3, điều này cho thấy chúng ta đã push dữ liệu từ local lên server thành công.
shell> git log --oneline
74136ce (HEAD -> main, origin/main) M3
4c141f3 M2
7690035 M1
add27da C2
7de3cce C1
Giờ mình chuyển sang máy Local 192.168.13.232 và chuyển sang nhánh main, chúng ta sẽ kiểm tra trạng thái của commit nhé.
shell> git checkout main
Switched to branch 'main'
Your branch is up to date with 'origin/main'.
shell> git log --oneline
4c141f3 (HEAD -> main, origin/main) M2
7690035 M1
add27da C2
7de3cce C1
Với kết quả này chúng ta thấy nhánh main đã có commit M3 nhưng mà ở máy local này lại chỉ cập nhật được ở nhánh M2 ở cả local lẫn remote. Điều này có nghĩa là thông tin ở máy local này chưa được cập nhật mới.
Để cập nhật thông tin ở máy remote cho máy local chúng ta sử dụng lệnh git fetch <remote_servername>
.
shell> git fetch origin
remote: Enumerating objects: 4, done.
remote: Counting objects: 100% (4/4), done.
remote: Compressing objects: 100% (2/2), done.
remote: Total 3 (delta 1), reused 0 (delta 0)
Unpacking objects: 100% (3/3), 246 bytes | 246.00 KiB/s, done.
From 192.168.13.231:/home/hoanghd/repos/myproject
4c141f3..74136ce main -> origin/main
Kiểm tra trạng thái commit tại máy local chúng ta vẫn thấy dừng lại ở commit M2.
shell> git log --oneline
4c141f3 (HEAD -> main) M2
7690035 M1
add27da C2
7de3cce C1
Kiểm tra trạng thái của repo bằng lệnh git status bạn nhận được thông báo Your branch is behind 'origin/main' by 1 commit, and can be fast-forwarded.
, tức là nhánh ‘main’ cục bộ của bạn đang lạc hậu so với nhánh ‘main’ trên kho chứa từ xa (origin) 1 commit. Điều này có nghĩa là, đã có 1 commit được push lên nhánh ‘main’ trên kho chứa từ xa mà bạn chưa cập nhật về máy cục bộ.
Bạn có thể sử dụng lệnh git pull
để cập nhật những thay đổi này. Sử dụng theo cú pháp git pull <remote_servername> <tên_nhánh>
hoặc git pull <remote_servername> --all
nếu muốn cập nhật tất cả các nhánh.
Ở đây mình chỉ muốn cập nhật cho nhánh main nên mình sẽ chạy lệnh như dưới.
shell> git pull origin main
From 192.168.13.231:/home/hoanghd/repos/myproject
* branch main -> FETCH_HEAD
Updating 4c141f3..74136ce
Fast-forward
c.txt | 1 +
1 file changed, 1 insertion(+)
create mode 100644 c.txt
Lệnh git pull origin main
được sử dụng để cập nhật những thay đổi từ nhánh ‘main’ trên kho chứa từ xa (origin) về nhánh hiện tại trên kho chứa cục bộ của bạn.
Dưới đây là giải thích về thông báo bạn nhận được:
From 192.168.13.231:/home/hoanghd/repos/myproject
: Đây là địa chỉ của kho chứa từ xa (origin) mà bạn đang cập nhật.* branch main -> FETCH_HEAD
: Git đã tải về những thay đổi từ nhánh ‘main’ trên kho chứa từ xa và lưu chúng vào FETCH_HEAD, một tham chiếu tạm thời.Updating 4c141f3..74136ce
: Git đã cập nhật mã nguồn từ commit có mã hash là 4c141f3 lên commit có mã hash là 74136ce. Mỗi commit trong git đều có một mã hash duy nhất để định danh.Fast-forward
: Đây là một loại hợp nhất trong git, khi nhánh hiện tại không có thay đổi nào so với nhánh được hợp nhất, git chỉ cần “dịch chuyển” (fast-forward) nhánh hiện tại lên commit mới nhất của nhánh được hợp nhất.c.txt | 1 +
: File ‘c.txt’ đã được thêm vào và có 1 dòng được thêm vào.1 file changed, 1 insertion(+)
: Tổng cộng có 1 file bị thay đổi với 1 dòng được thêm vào.create mode 100644 c.txt
: File ‘c.txt’ đã được tạo mới với quyền truy cập là 100644 (đọc và viết cho người sở hữu, và chỉ đọc cho người dùng khác).
Giờ bạn show lại lịch sử commit của nhánh main bạn sẽ thấy nhánh main tại local và remote đều đã dừng lại ở commit M3.
shell> git log --oneline
74136ce (HEAD -> main, origin/main) M3
4c141f3 M2
7690035 M1
add27da C2
7de3cce C1
Để xem thông tin chi tiết hơn, bạn có thể sử dụng lệnh git log.
shell> git log
commit 74136ced9b06df060bf88a4be932d44d2596f6e5 (HEAD -> main, origin/main)
Author: hoanghd <hoanghd164@gmail.com>
Date: Wed Feb 7 21:13:25 2024 +0700
M3
commit 4c141f3c03b77d7f5c0555fa3161fcdadebdc357
Author: hoanghd <hoanghd164@gmail.com>
Date: Mon Feb 5 15:30:56 2024 +0700
M2
commit 76900353bacac6bf3ccec22623ed50cab2e5701d
Author: hoanghd <hoanghd164@gmail.com>
Date: Mon Feb 5 10:45:32 2024 +0700
M1
commit add27dad80f18ae9800e29f1a8e69c6e4d098fa1
Author: hoanghd <hoanghd164@gmail.com>
Date: Mon Feb 5 10:44:31 2024 +0700
C2
commit 7de3cce1d5bde33d44f396fc2f20785c2666179a
Author: hoanghd <hoanghd164@gmail.com>
Date: Mon Feb 5 10:43:57 2024 +0700
C1
Bây giờ chúng ta hãy thử nghiệm một ví dụ khác như sau.
Thực hiện tạo một commit mới ở máy gitlocal-192.168.12.28.
shell> echo -e "\nSua doi boi gitlocal-192.168.12.28" >> c.txt
shell> git add .
shell> git commit -m"M4"
[main 28310f3] M4
1 file changed, 2 insertions(+)
Kiểm tra trạng thái commit chúng ta thấy nhánh main của máy local đã có commit M4 tuy nhiên máy remote đang dừng lại ở commit M3.
shell> git log --oneline
28310f3 (HEAD -> main) M4
74136ce (origin/main) M3
4c141f3 M2
7690035 M1
add27da C2
7de3cce C1
Chúng ta cứ để nguyên và chuyển sang máy gitlocal-192.168.13.232 và tạo một commit mới như dưới.
shell> echo -e "\nSua doi boi gitlocal-192.168.13.232" >> c.txt
shell> git add .
shell> git commit -m"M4 - gitlocal-192.168.13.232"
[main 4211e90] M4 - gitlocal-192.168.13.232
1 file changed, 2 insertions(+)
Kiểm tra trạng thái của commit chúng ta thấy local đã có commit M4 – gitlocal-192.168.13.232 còn remote đang dừng lại ở commit M3.
shell> git log --oneline
4211e90 (HEAD -> main) M4 - gitlocal-192.168.13.232
74136ce (origin/main) M3
4c141f3 M2
7690035 M1
add27da C2
7de3cce C1
Tiến hành push dữ liệu lên nhành main của remote và kiểm tra lại trạng thái commit bạn sẽ thấy cả local và remote đều đã được cập nhật commit M4 – gitlocal-192.168.13.232.
shell> git push origin main
Enumerating objects: 5, done.
Counting objects: 100% (5/5), done.
Delta compression using up to 8 threads
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 309 bytes | 309.00 KiB/s, done.
Total 3 (delta 1), reused 0 (delta 0)
To 192.168.13.231:/home/hoanghd/repos/myproject/
74136ce..4211e90 main -> main
shell> git status
On branch main
Your branch is up to date with 'origin/main'.
nothing to commit, working tree clean
shell> git log --oneline
4211e90 (HEAD -> main, origin/main) M4 - gitlocal-192.168.13.232
74136ce M3
4c141f3 M2
7690035 M1
add27da C2
7de3cce C1
Giờ ta quay lại máy gitlocal-192.168.12.28 cập nhật lại trạng thái repo.
shell> git fetch origin
remote: Enumerating objects: 5, done.
remote: Counting objects: 100% (5/5), done.
remote: Compressing objects: 100% (2/2), done.
remote: Total 3 (delta 1), reused 0 (delta 0)
Unpacking objects: 100% (3/3), 289 bytes | 289.00 KiB/s, done.
From 192.168.13.231:/home/hoanghd/repos/myproject
74136ce..4211e90 main -> origin/main
Kiểm tra lại trạng thái commit thì commit của local vẫn dừng lại ở commit M4 của local.
shell> git log --oneline
28310f3 (HEAD -> main) M4
74136ce M3
4c141f3 M2
7690035 M1
add27da C2
7de3cce C1
Kiểm tra lại trạng thái của repo bạn sẽ thấy thông báo Your branch and 'origin/main' have diverged, and have 1 and 1 different commits each, respectively.
, cho biết nhánh ‘main’ cục bộ của bạn và nhánh ‘main’ trên kho chứa từ xa (origin) đã tách ra và có những commit khác nhau. Cụ thể, nhánh cục bộ của bạn có 1 commit mà nhánh từ xa không có, và ngược lại, nhánh từ xa cũng có 1 commit mà nhánh cục bộ của bạn không có. Điều này thường xảy ra khi bạn và người khác cùng làm việc và commit thay đổi trên cùng một nhánh.
shell> git status
On branch main
Your branch and 'origin/main' have diverged,
and have 1 and 1 different commits each, respectively.
(use "git pull" to merge the remote branch into yours)
nothing to commit, working tree clean
Từ máy gitlocal-192.168.12.28 sử dụng lệnh dưới để kiểm tra trạng thái commit của origin/main chúng ta cũng thấy local của gitlocal-192.168.13.232 đang dừng lại ở commit M4 – gitlocal-192.168.13.232.
Và tại máy local thì dừng lại ở commit M4 và chưa push dữ liệu ở commmit M4 này lên remote.
shell> git log --oneline main
28310f3 (HEAD -> main) M4
74136ce M3
4c141f3 M2
7690035 M1
add27da C2
7de3cce C1
shell> git log --oneline origin/main
4211e90 (origin/main) M4 - gitlocal-192.168.13.232
74136ce M3
4c141f3 M2
7690035 M1
add27da C2
7de3cce C1
Trong trường hợp này khi chúng ta pull dữ liệu về thì bản chất chúng ta sẽ thực hiện công việc gộp nhánh. Chúng ta sẽ đứng từ máy gitlocal-192.168.12.28 để thực hiện gộp nhánh main từ local với nhánh main của máy gitlocal-192.168.13.232.
shell> git merge origin/main
Auto-merging c.txt
CONFLICT (content): Merge conflict in c.txt
Automatic merge failed; fix conflicts and then commit the result.
Bạn sẽ nhận được thông báo xung đột dữ liêu khi gộp nhánh.
Chúng ta thực hiện commit mới.
shell> git status
On branch main
Your branch and 'origin/main' have diverged,
and have 1 and 1 different commits each, respectively.
(use "git pull" to merge the remote branch into yours)
You have unmerged paths.
(fix conflicts and run "git commit")
(use "git merge --abort" to abort the merge)
Unmerged paths:
(use "git add <file>..." to mark resolution)
both modified: c.txt
no changes added to commit (use "git add" and/or "git commit -a")
shell> git add .
shell> git commit -m"M5 - Merge"
[main 13507ed] M5 - Merge
Chúng ta kiểm tra lại trạng thái của repo và trạng thái commit, chúng ta sẽ thấy đã có commit mới M5 và nó cũng đã lấy về commit mới đó là M4 – gitlocal-192.168.13.232.
shell> git status
On branch main
Your branch is ahead of 'origin/main' by 2 commits.
(use "git push" to publish your local commits)
nothing to commit, working tree clean
shell> git log --oneline
13507ed (HEAD -> main) M5 - Merge
4211e90 (origin/main) M4 - gitlocal-192.168.13.232
28310f3 M4
74136ce M3
4c141f3 M2
7690035 M1
add27da C2
7de3cce C1
Thực hiện push dữ liệu từ máy gitlocal-192.168.12.28 lên remote.
shell> git push origin main
Enumerating objects: 10, done.
Counting objects: 100% (10/10), done.
Delta compression using up to 12 threads
Compressing objects: 100% (5/5), done.
Writing objects: 100% (6/6), 584 bytes | 584.00 KiB/s, done.
Total 6 (delta 2), reused 0 (delta 0), pack-reused 0
To 192.168.13.231:/home/hoanghd/repos/myproject/
4211e90..13507ed main -> main
Thông báo Your branch is up to date with ‘origin/main’ cho biết repo của local và remote đã giống nhau.
shell> git status
On branch main
Your branch is up to date with 'origin/main'.
nothing to commit, working tree clean
Quay trở lại máy gitlocal-192.168.13.232 chúng ta cập nhật trạng thái repo.
shell> git fetch
remote: Enumerating objects: 10, done.
remote: Counting objects: 100% (10/10), done.
remote: Compressing objects: 100% (5/5), done.
remote: Total 6 (delta 2), reused 0 (delta 0)
Unpacking objects: 100% (6/6), 564 bytes | 564.00 KiB/s, done.
From 192.168.13.231:/home/hoanghd/repos/myproject
4211e90..13507ed main -> origin/main
Mặc dù bạn nhận được thông báo nothing to commit, working tree clean
, tuy nhiên bạn cũng nhận được thông báo Your branch is behind 'origin/main' by 2 commits, and can be fast-forwarded
, thông báo này cho biết commit commit tại máy local đang đi sau 2 commit của máy remote
shell> git status
On branch main
Your branch is behind 'origin/main' by 2 commits, and can be fast-forwarded.
(use "git pull" to update your local branch)
nothing to commit, working tree clean
Giờ bạn hãy tiến hành pull dữ liệu mới về và kiểm tra lại trạng thái của repo và trạng thái của commit, bạn sẽ thấy kết quả như dưới.
shell> git pull origin main
From 192.168.13.231:/home/hoanghd/repos/myproject
* branch main -> FETCH_HEAD
Updating 4211e90..13507ed
Fast-forward
c.txt | 1 +
1 file changed, 1 insertion(+)
shell> git status
On branch main
Your branch is up to date with 'origin/main'.
nothing to commit, working tree clean
shell> git log --oneline
13507ed (HEAD -> main, origin/main) M5 - Merge
4211e90 M4 - gitlocal-192.168.13.232
28310f3 M4
74136ce M3
4c141f3 M2
7690035 M1
add27da C2
7de3cce C1
Nếu bạn sử dụng lệnh git log bạn cũng sẽ thấy chi tiết các commit được thực hiện bởi username nào, thời gian commit,…
shell> git log
commit 13507ed5a817dc25c826b90bb5a07d1bb9dd4186 (HEAD -> main, origin/main)
Merge: 28310f3 4211e90
Author: hoanghd <hoanghd164@gmail.com>
Date: Wed Feb 7 22:28:41 2024 +0700
M5 - Merge
commit 4211e9093ba8a3ab2160835ad65c676eda86ca34
Author: hoanghd-13.232 <hoanghd164@gmail.com>
Date: Wed Feb 7 15:09:47 2024 +0000
M4 - gitlocal-192.168.13.232
commit 28310f3955b7d20a5324c912b16afcbdf8c8d8b7
Author: hoanghd <hoanghd164@gmail.com>
Date: Wed Feb 7 22:05:42 2024 +0700
M4
commit 74136ced9b06df060bf88a4be932d44d2596f6e5
Author: hoanghd <hoanghd164@gmail.com>
Date: Wed Feb 7 21:13:25 2024 +0700
M3
commit 4c141f3c03b77d7f5c0555fa3161fcdadebdc357
Author: hoanghd <hoanghd164@gmail.com>
Date: Mon Feb 5 15:30:56 2024 +0700
M2
commit 76900353bacac6bf3ccec22623ed50cab2e5701d
Author: hoanghd <hoanghd164@gmail.com>
Date: Mon Feb 5 10:45:32 2024 +0700
M1
commit add27dad80f18ae9800e29f1a8e69c6e4d098fa1
Author: hoanghd <hoanghd164@gmail.com>
Date: Mon Feb 5 10:44:31 2024 +0700