Git là gì – Những khái niệm cơ bản khi làm việc trên Git

ngày 05-11-2018

1.1. Giới thiệu

 
Khi lập trình, sẽ có lúc bạn lỡ tay xoá một đoạn code vì nghĩ rằng đoạn code đó không phù hợp nữa, nhưng sau đó lại phát hiện là đoạn code đó bạn vẫn cần dùng. Bạn nghĩ mình có thể nhớ lại chính xác  những gì mình đã viết không? Thật sự là rất rất khó. Nhưng nếu bạn có dùng phần mềm quản lý phiên bản mã nguồn thì mọi việc sẽ trở nên đơn giản hơn rất nhiều vì phần mềm quản lý phiên bản mã nguồn sẽ cho phép bạn dễ dàng quay lại một phiên bản trước của tập tin đó. Có thể nói phần mềm quản lý mã nguồn là công cụ không thể thiếu đối với lập trình viên và một trong những phần mềm hỗ trợ quản lý phiên bản rất phổ biến hiện nay là Git. 
 
Git sẽ giúp người dùng lưu lại các phiên bản của những lần thay đổi mã nguồn để dễ dàng khôi phục lại phiên bản cũ mà không cần phải nhớ là mình đã chỉnh ở đâu, tất cả phiên bản bạn cần đều đã được sao lưu. 
 
Git sẽ giúp bạn:
  • Lưu lại được các phiên bản khác nhau của mã nguồn dự án phần mềm
  • Khôi phục lại mã nguồn từ một phiên bản bất kỳ
  • Dễ dàng so sánh giữa các phiên bản
  • Phát hiện được ai đã sửa phần nào làm phát sinh lỗi
  • Khôi phục lại tập tin bị mất
  • Dễ dàng thử nghiệm, mở rộng tính năng của dự án mà không làm ảnh hưởng đến phiên bản chính (master branch)
  • Giúp phối hợp thực hiện dự án trong nhóm một cách hiệu quả
 
Git là một hệ thống quản lý phiên bản phân tán (Distributed Version Control System – DVCS) ra đời vào năm 2005 và hiện được dùng rất phổ biến. So với các hệ thống quản lý phiên bản tập trung khi tất cả mã nguồn và lịch sử thay đổi chỉ được lưu một nơi là máy chủ thì trong hệ thống phân tán, các máy khách không chỉ "check out" phiên bản mới nhất của các tập tin mà là sao chép (mirror) toàn bộ kho mã nguồn (repository). Như vậy, nếu như máy chủ ngừng hoạt động, thì bạn hoàn toàn có thể lấy kho chứa từ bất kỳ máy khách nào để sao chép ngược trở lại máy chủ để khôi phục lại toàn bộ hệ thống. Mỗi checkout thực sự là một bản sao đầy đủ của tất cả dữ liệu của kho chứa từ máy chủ. 
 
Mô hình tổ chức dữ liệu phân tán trên Git
Mô hình tổ chức dữ liệu phân tán trên Git
 

1.2. Quy trình xử lý công việc (workflow) trên Git

 
Bạn lưu ý là bạn dùng Git để giúp bạn quản lý phiên bản mã nguồn, bạn không thể chỉnh code trong Git. Bạn vẫn phải thực hiện các công việc của mình trên môi trường làm việc với các chức năng tiện ích trên IDE của ngôn ngữ lập trình bạn đang làm việc. Thông thường, quy trình xử lý công việc trên Git sẽ như sau:
 
Local Operations
 
Trong đó:
 
Git GUI cho phép bạn xem được trạng thái trong working directory
Git GUI cho phép bạn xem được trạng thái trong working directory
 

1.3. Các khái niệm cơ bản 

 

1.3.1. Repository – Kho lưu trữ

 
Trong Git, Repository là nơi lưu trữ, quản lý tất cả những thông tin cần thiết (thư mục, tập tin, ảnh, video, bảng biểu, dữ liệu… ) cũng như các sửa đổi và lịch sử của toàn bộ dự án. Khi tạo mới repository, bạn nên tạo thêm tập tin README hoặc một tập tin thông tin giới thiệu về dự án của bạn. 
Bạn có thể có nhiều cách tổ chức cho repository, trong lập trình C# trên Visual Studio, bạn có thể lưu trữ một solution trong một kho, solution đó có thể chứa nhiều project.
Có hai loại repository, đó là local repository và remote repository.
Các thao tác xử lý giữa remote repository và local repository
Các thao tác xử lý giữa remote repository và local repository
 

1.3.2. Snapshot

 
Cơ chế lưu trữ phiên bản của Git là sau mỗi lần bạn thực hiện lưu trạng thái (commit) sẽ tạo ra một “ảnh chụp” (snapshot) lưu lại nội dung tất cả các tập tin, thư mục tại thời điểm đó rồi tạo tham chiếu tới snapshot đó. Để hiệu quả hơn, nếu như tập tin không có thay đổi, Git không lưu trữ tập tin đó lại mà chỉ tạo liên kết tới tập tin gốc đã tồn tại trước đó. Sau đó khi cần bạn hoàn toàn có thể khôi phục và sử dụng lại một snapshot, hay còn gọi là phiên bản nào đó. Đây cũng chính là lợi thế của Git khi nó không lưu dữ liệu mà sẽ lưu dạng snapshot, giúp tiết kiệm không gian lưu trữ. 
 
Git lưu trữ dữ liệu dưới dạng ảnh chụp (snapshot) của mã nguồn dự án theo thời gian
Git lưu trữ dữ liệu dưới dạng ảnh chụp (snapshot) của mã nguồn dự án theo thời gian
 

1.3.3. Commit

 
Commit là thao tác báo cho hệ thống biết bạn muốn lưu lại trạng thái hiện hành, ghi nhận lại lịch sử các xử lý như đã thêm, xóa, cập nhật các file hay thư mục nào đó trên repository.
Khi thực hiện commit, trong repository sẽ ghi lại sự khác biệt từ lần commit trước với trạng thái hiện tại. Các commit ghi nối tiếp với nhau theo thứ tự thời gian do đó chỉ cần theo vết các commit thì có thể biết được lịch sử thay đổi trong quá khứ.
 
 
Khi bạn thực hiện commit, hệ thống đều yêu cầu bạn phải nhập vào commit message để ghi chú tóm tắt là trong lần commit này là bạn đã thực hiện những thay đổi nào, có ý nghĩa thế nào,…
 

1.3.4. Clone

 
Nếu bạn muốn có một bản sao của một kho chứa Git có sẵn, có thể là một dự án mà bạn tham gia – thì bạn hãy thực hiện clone. Đâ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. Và một điểm nữa là nếu ổ cứng máy chủ bị hư, bạn có thể sử dụng bất kỳ bản sao trên bất kỳ máy khách nào để khôi phục lại trạng thái của máy chủ.
 
Lịch sử các thay đổi trên repository khi clone về máy cục bộ
Lịch sử các thay đổi trên repository khi clone về máy cục bộ
 
Bạn có thể clone từ bất kỳ kho chứa nào, nó sẽ sao chép luôn các thiết lập về repository và sẽ tự động tạo một master branch trên máy tính của bạn. 
Có lưu ý ở đây là trên 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.
 

1.3.5. Push

 
Lệnh push được sử dụng để đưa nội dung kho lưu trữ cục bộ lên server. Push là cách bạn chuyển giao các commit từ kho lưu trữ cục bộ của bạn lên server.
 
 

1.3.6. Fetch 

 
Khi ta thực hiện fetch, lệnh này sẽ truy cập vào repository trên server và kéo toàn bộ dữ liệu mà bạn chưa có từ repository trên server về. Sau đó, bạn có thể tích hợp dữ liệu vào branch bất kỳ lúc nào.
 

1.3.7. Pull 

 
Lệnh này sẽ tự động lấy toàn bộ dữ liệu từ repository trên server và gộp vào cái branch hiện tại bạn đang làm việc.
 

1.3.8. Branch - Nhánh

 
Nhánh là khái niệm rất hay trong Git, với nhánh bạn có thể tách riêng các tính năng của dự án, thử nghiệm các tính năng mới hay cũng có thể dùng nhánh để khắc phục, hoàn chỉnh lỗi nào đó của dự án,… Khi bắt đầu khởi tạo một repository hoặc clone một repository, bạn sẽ có một nhánh (branch) chính tên là master, đây là branch chứa toàn bộ các mã nguồn chính trong repository. Từ nhánh master này, trong quá trình thực hiện dự án bạn có thể rẽ thêm nhiều nhánh khác tùy theo nhu cầu thực tế. Tất cả các nhánh đều được hệ thống lưu lại lịch sử các lần commit trên nhánh và bạn hoàn toàn quay lại mốc commit nào mà mình muốn.
 
 
Một lưu ý nhỏ chỗ này là tất cả thao tác fetch, push hoặc pull mặc định thực hiện trên nhánh hiện hành, nên bạn lưu ý nhánh mình đang thao tác là nhánh master hay nhánh nào để tránh sai sót.
Giả sử bạn đang làm việc trên một dự án đã có một số commit từ trước như hình sau:
 
 
Bạn quyết định sẽ nâng cấp dự án qua việc giải quyết vấn đề số #53 (cách đặt mã số vấn đề theo quy ước của dự án) nên bạn sẽ tạo một nhánh mới iss53 để cho lần nâng cấp đó, bạn sẽ giải quyết vấn đề #53. Bạn sẽ tạo nhánh mới iss53, vậy là từ lần commit C2 trong repository của bạn sẽ có 2 nhánh là masteriss53
 
 
Bạn tiếp tục làm việc trên nhánh iss53 và sau đó thực hiện commit ở C3.
 
 
Bây giờ bạn nhận được thông báo rằng có một vấn đề với dự án và bạn cần khắc phục nó ngay lập tức. Bạn có vấn đề mình đang giải quyết (iss53) nhưng chưa xong và lỗi mới phát hiện, cần khắc phục. Với Git, bạn không cần phải tốn quá nhiều công sức để khôi phục lại các thay đổi trước khi bạn làm iss53 để vá lỗi rồi làm tiếp iss53. Hoàn toàn độc lập, tất cả những gì bạn cần phải làm là chuyển lại nhánh master và lấy lại mã lệnh ở thời điểm mình cần.
Tuy nhiên, trước khi làm điều này, bạn nên lưu ý rằng nếu thư mục làm việc hoặc khu vực tổ chức có chứa các thay đổi chưa được commit mà xung đột với nhánh bạn đang làm việc, Git sẽ không cho phép bạn chuyển nhánh. Tốt nhất là bạn nên ở trạng thái làm việc "sạch" (đã commit hết) trước khi chuyển nhánh. 
Bạn sẽ quay lại thời điểm commit mình cần, giả sử C2, lúc này thư mục làm việc của dự án giống hệt như trước khi bạn bắt đầu giải quyết vấn đề #53 và bạn có thể tập trung vào việc sửa lỗi. Điểm quan trọng cần ghi nhớ ở đây là Git sẽ khôi phục lại thư mục làm việc của bạn giống như snapshot của lần commit C2. Git sẽ tự động thêm, xóa và sửa các tập tin sao cho đảm bảo rằng thư mục làm việc của bạn giống như lần commit C2.
Để sửa lỗi, bạn tạo nhánh mới hotfix dựa trên nhánh master để giải quyết các lỗi mới phát sinh và bạn giải quyết xong vấn đề với lần commit C4.
 
 
Bạn có thể chạy để kiểm tra và chắc chắn rằng bản vá lỗi hoạt động đúng theo ý bạn muốn rồi sau đó tích hợp nó lại nhánh chính master để triển khai với thao tác merge. Để merge, bạn sẽ chuyển sang nhánh master trước rồi thực hiện merge nhánh master với nhánh hotfix. Sau khi merge những cập nhật mới của bạn bây giờ ở trong snapshot của commit  C4 được trỏ tới bởi nhánh master và bạn có thể mang đi triển khai thực tế phiên bản này. 
 
 
Vậy là xong, lúc này bạn cần xóa nhánh hotfix đi vì bạn không còn cần tới nó nữa, nhánh master đã trỏ đến lần commit bạn cần là C4. Bạn đã triển khai xong bản vá lỗi quan trọng và sẵn sàng quay lại với công việc bị gián đoạn trước đó. Bạn sẽ chuyển sang nhánh iss53 mà bạn đang làm việc trước đó để tiếp tục giải quyết vấn đề #53
 
 
Như vậy với Git, bạn hoàn toàn chủ động trong các giai đoạn của dự án mà không làm ảnh hưởng đến tiến độ chung cũng như công việc đang thực hiện của người khác. Ngoài cách dùng merge để tích hợp những thay đổi từ một nhánh vào một nhánh khác bạn còn có thể sử dụng rebase
Rebase cũng là cách để bạn tích hợp các thay đổi từ một nhánh này sang nhánh khác. Nhưng so với merge là gộp nhánh, tức là lấy snapshot mới nhất của mỗi branch rồi kết hợp lại với nhau. Như vậy, mỗi khi bạn merge một feature branch về master branch thì đơn giản là nó sẽ tạo ra một merge commit ở master branch. Trong khi đó nếu chúng ta sử dụng rebase, ví dụ như cần tích hợp feature branch vào nhánh master thì nó sẽ đem tất cả các thay đổi, các commit từ nhánh feature vào nhánh branch hay nói cách khác là nó sẽ sao chép tất cả các thay đổi từ nhánh feature đặt lên trước nhánh master lần lượt theo thứ tự.
 
 
Vậy thì khi nào bạn dùng rebase và khi nào bạn dùng merge? Rõ ràng bạn sẽ thấy lịch sử trên git của merge nhìn rối hơn, đặc biệt là khi bạn có nhiều nhánh con trong khi rebase thì bạn cho một lịch sử dễ nhìn hơn. Tuy nhiên, merge là cách gộp nhánh an toàn, đặc biệt khi có nhiều người tham gia cùng dự án. Nó sẽ lưu lại thật sự tất cả những gì đã diễn ra trong quá trình thực hiện dự án.  Trong khi đó, nếu bạn sử dụng rebase chủ quan, không đúng cách thì có thể mất commit trên nhánh của người khác .
Bạn mến,
Vậy là bạn đã có được những khái niệm cơ bản về Git. Nếu bạn muốn có được hướng dẫn đầy đủ hơn về Git và GitHub, chúng tôi sẽ gửi tặng bạn ebook Cẩm nang hướng dẫn Git và GitHub.
 
Nhận ebook Hướng dẫn sử dụng Git-GitHub
 
Marketing by
 
CHƯƠNG TRÌNH ĐÀO TẠO