Giới thiệu các khái niệm cơ bản của git

Tiếp nối bài trước, trong bài này mình sẽ giới thiệu và đề cập đến một số khái niệm cơ bản của git.

1. Repository

Repository hay được gọi tắt là Repo là nơi chứa tất cả những thông tin cần thiết để duy trì và quản lý các sửa đổi và lịch sử của toàn bộ project.

Git có 2 loại là remote repository và local repository.

Local repository
là repository bố trí trên máy local của lập trình viên, dành cho một người dùng sử dụng.

Tất cả dữ liệu của Repo đều được chứa trong thư mục bạn đang làm việc dưới dạng folder ẩn có tên là .git.

Để khởi tạo một local repository bạn dùng câu lệnh:

$ git init

sau khi khởi tạo bạn có thể thấy thư mục .git được sinh ra trong folder vừa tạo (ảnh ví dụ)

Hiện tại, sau khi khởi tạo, local repo của mình là tutorial chưa có file mới, mình thử thêm file_1.txt, file_2.txt, file_3.txt:

image

Sau đó mình sẽ thử commit và push lên 1 github repo (mình sẽ giải thích ở dưới)

Remote repository
là repository để chia sẻ giữa nhiều người và bố trí trên server chuyên dụng.
Lúc này trên github của mình đã có remote repository là tutorial với 3 file file_1.txt, file_2.txt, file_3.txt đã tạo lúc trước.

Về cơ bản bạn có thể hình dung như trong ảnh dưới về mối quan hệ giữa local repository và remote repository.

image
Như ta thấy, trên máy tính của Developer A - một local repository dành cho Developer A thực hiện các thao tác với project sau đó được đồng bộ lên Remote Repository chung. Sau đó Developer B cũng có thể đồng bộ thông tin từ remote repo về local repo trên máy tình của Developer B.

2. Branch

Đây là thế mạnh của git. Với git, việc quản lý branch - nhánh rất dễ dàng. Mỗi nhánh trong Git gần giống như một workspace. Việc nhảy vào một nhánh để làm việc trong đó tương tự việc chuyển qua workspace mới và cũng có thể quay lại workspace cũ một cách nhanh chóng.

Branch được dùng để phát triển tính năng mới mà không làm ảnh hưởng đến code hiện tại.

      A---B---C new_feature
     /
D---E---F---G master

Như trong ví dụ, master là nhánh “mặc định” của repository. Khi thực hiện một tính năng mới để tránh ảnh hưởng đến code ở branch master, bạn tạo một nhánh mới là new_feature.

      A---B---C new_feature
     /         \
D---E---F---G---H master

khi thực hiện xong sẽ hợp nhất lại với nhánh master. Việc hợp nhất 2 nhánh lại được gọi là merge. Lúc này, ở tại điểm H trên nhánh master, code của new_feature đã được cập nhật.

Để dễ hiểu hơn, mình sẽ đưa thêm ra ví dụ sau:
Như tutorial local repository ở trên, ta đã có 3 file: file_1.txt, file_2.txt, file_3.txt và branch hiện tại là master.

Sau đó mình sẽ tạo 1 nhánh là add_new_file bằng câu lệnh

$ git checkout -b branch_name

và add 2 file file_4.txt, file_5.txt

Lúc này 2 tại branch add_new_file (như đang được hiển thị) đã có 2 file file_4.txt, file_5.txt

Bây giờ mình sẽ quay trở lại branch master ban đầu sau khi đã commit 2 file mới tại branch add_new_file:

Lúc này, ta sẽ thấy tại branch master sẽ không có 2 file file_4.txt, file_5.txt vừa được thêm. Điều đó giúp thực hiện một tính năng mới sẽ không ảnh hưởng đến code ở branch master khi chưa được merge.

3. Snapshot

Như trong bài giới thiệu về git mình có đề cập đến snapshot là ảnh chụp toàn bộ nội dung tất cả các file tại thời điểm.

4. Commit
Như trong bài trước, mình có chia sẻ về trạng thái committed.

committed: tức là git đã snapshot lại file ở Staging Area phía trên với 1 unique index trong Git directory.

Hành động chuyển trạng thái sang committed là commit. Hay commit là hành động record lại thay đổi trong repository của bạn.

Khi thực hiện commit, trong repository sẽ tạo ra commit đã ghi lại sự khác biệt các file từ trạng thái đã commit lần trước với trạng thái hiện tại.

Mỗi commit đều có yêu cầu phải có commit message, để giải thích commit này là bạn đã làm gì trong này.

Các commit này có dạng 40 ký tự alphabet

035072b999a2e9b54126f061f3c3f39a93d48a44

được băm từ thông tin commit.

Để check trạng thái các file đã sẵn sàng commit chưa các bạn dùng lệnh

$ git status

Như trong bài giới thiệu về git mình đã đề cập đến, ở đây file_5.txt vẫn đang trong trạng thái untracked nên chưa thể commit.

Để thực hiện commit, trước tiên mình sẽ đổi trạng thái file sang staged bằng cách

$ git add file_5.txt

hoặc add tất cả các file untracked/modified bằng câu lệnh add -A (--all)

$ git add -A

sau đó là sử dụng câu lệnh commit

$ git commit -m "commit message"

và để thấy commit mà chúng ta đã commit, các bạn sử dụng câu lệnh

$ git log

Mẹo: thêm option --oneline để nhìn log dễ dàng hơn. Hãy thử và cảm nhận sự khác biệt.

5. Clone

Clone là hành động thực hiện tải một bản sao có sẵn của một remote repository server nào đó (có thể là dự án bạn tham gia). Đây là điểm khác biệt của Git so với một số hệ thống quản lý phiên bản mã nguồn khác vì clone là tạo ra một bản sao của gần như tất cả những gì của repository mà máy chủ đang lưu trữ. Bạn sẽ có được tất cả lịch sử đã xảy ra trên repository và hoàn toàn có thể quay lại, undo lại từ bất kỳ thời điểm commit nào.

Có thêm 1 điểm nữa, GitHub bạn có một cách khác để sao chép kho từ người khác là bạn thực hiện fork trên repository bạn cần. Điểm khác của fork là bạn có thể đóng góp thêm vào repository gốc bằng cách thực hiện pull request. Khi chủ sở hữu của repository nơi bạn fork nhận được yêu cầu sẽ xem xét chỉnh sửa của bạn, nếu thấy hay sẽ tiến hành merge nội dung chỉnh sửa của bạn vào source gốc.

Trong ví dụ về clone, mình dùng câu lệnh với giao thức ssh. Ví dụ: Server có kết nối ssh: git@github.com, trên đó lưu một Repo ở đường dẫn hapo-nghialuu/tutorial.git, thì có thể copy về bằng lệnh.

$ git clone git@github.com:hapo-nghialuu/tutorial.git

Thông thường, việc clone này các bạn chỉ cần copy ssh link trên remote repo như github và thực hiện clone.

6. Push

Push là hành động đưa nội dung của local_repo lên remote_repo. Push là cách bạn chuyển giao các commit từ local_repo của bạn lên remote_repo.
Cấu trúc lệnh push cơ bản

$ git push remote_name branch_name

Như ở trên mình có thực hiện push

mình đã tiến hành push một local branch là add_new_file lên remote server và lúc này trên remote server tên branch mặc định của remote branch lúc này cũng được tạo là add_new_file. Ngoài ra, mình còn 1 lưu ý là để push từ local repo lên remote repo thì bạn cần set liên kết giữa chúng. Trong ví dụ của mình đó là origin, để xem local repo liên kết đến remote repo nào, bạn dùng:

Ở đây origin của mình đang được gắn với ssh link(liên kết) là

git@github.com:hapo-nghialuu/tutorial.git

ở trong remote repo tutorial mà mình đã tạo ở trên.

Ngoài ra các bạn cũng có thể thay đổi trong local project của mình bằng câu lệnh add và remove remote

$ git remote add remote_name link_remote
$ git remote rm remote_name

7. Pull và Fetch

Trong Git, Git Pull và Git Fetch là có chức năng tương đồng với nhau. Cả hai đều được sử dụng để tải xuống dữ liệu mới từ một remote_repo. Tuy nhiên, Git Fetch thường được coi là một phiên bản an toàn hơn của Git Pull.

  • Khi thực hiện fetch cùng với remote_origin, Git sẽ tải về dữ liệu của tất cả các branch của repository trên remote server nằm tại địa chỉ quy định bởi remote_origin và cập nhật dữ liệu này với dữ liệu của cách branch phía dưới máy local.
    Tuy nhiên git fetch không cập nhật dữ liệu của working directory(Thư mục làm việc). Điều này có nghĩa là nếu như có bất cứ commit nào trên remote server thì chúng cũng không ảnh hưởng tới các tập tin, thư mục của bạn.

  • Khi thực pull cùng với remote_originbranch_name

$ git pull remote_origin branch_name

Git sẽ áp thực hiện việc fetch dữ liệu của Git repository tại nhánh branch_name từ server nằm tại địa chỉ quy định bởi remote_origin và áp dụng (merge) các thay đổi này vào thư mục và tập tin ở working directory. Git pull sẽ có thể gây ra xung đột (conflict) trong khi merge.

8. Bài tập thực hành

Bài tập 1:
Hãy tạo 1 local repository git trên local - máy tính của mình (project chỉ cần chứa 1 vài file đơn giản). Sau đó hãy tạo 1 remote repository trên github và đồng bộ local lên remote.

Bài tập 2: clone project tại địa chỉ về local. Sau đó, tạo 1 branch mới, thực hiện thay đổi các file trên branch đó, rồi commit và push lên remote repository. Ngoài ra hãy show tên commit vào tạo dưới dạng hash (băm).

Bài tập 3:
là phần tiếp nối của bài tập 2 - trong repo đã clone ở trên thực hiện xóa remote tên là origin hiện tại. sau đó tạo 1 remote mới là upstream.

Bài tập 4:
là phần tiếp theo của bài tập 2 và 3.
Bạn checkout sang branch branch_pratice_1 thực hiện:

$ git fetch $remote_name
$ git pull $remote_name master

để thấy sự khác biệt giữa fetch và pull.

Trên đây, mình vừa Giới thiệu các khái niệm cơ bản của git. Trong bài tiếp theo của series này mình sẽ giới thiệu về 1 workflow cơ bản khi làm việc của git.
(Link bài tiếp theo của series tại đây)

Ngoài ra, các bạn có thể xem lại bài trước của series này, Giới thiệu các config option cho git thường sử dụng để củng cố thêm kiến thức về git nhé.
(Link bài trước của series tại đây)