Git căn bản

Viết mã là một công việc phức tạp. Do đó, để quản lý hiệu quả mã nguồn, lập trình viên dùng một chương trình gọi là version control system (VCS). Sau này, khi cần thiết, họ dễ dàng xem lại những thay đổi đã thực hiện. Nếu trong trường hợp có lỗi xảy ra, họ có thể truy tìm nguyên nhân qua những thông tin được lưu lại bởi VCS. Nếu không có VCS thì việc quản lý code sẽ trở thành cơn ác mộng. Chương trình VCS thì có rất nhiều, đa phần là phần mềm thương mại. Tuy nhiên, trong những năm gần đây, cái tên Git nổi lên như cồn đã gần như qua mặt các ông lớn VCS.

Git là đứa con của Linus Torvalds. Trong quá trình thiết kế nhân Linux, ông không hài lòng với VCS đang dùng nên đã tạo ra Git. Đến nay, Git đã trưởng thành và phổ biến trong giới công nghệ nguồn mở. Những dịch vụ lưu trữ code sử dụng Git như GitHub đã trở nên một phần không thể thiếu trong công việc hàng ngày của lập trình viên.

Nói đến GitHub, không ít lần bạn được dẫn đến trang này để download hoặc “clone” mã nguồn về. Đây cũng chính là ưu điểm của Git. Khi clone một repo (viết tắt của repository, hiểu đơn giản là một thư mục chứa các file code), ta sẽ sao chép tất cả thay đổi trong repo từ thay đổi đầu tiên cho tới thay đổi cuối cùng. Với tính năng này, ta có thể quay ngược thời gian để xem diện mạo của dự án trông ra sao vào hôm qua, tháng trước, hoặc lúc khởi tạo. Nói cách khác, mọi thông tin thay đổi trong các file đều được Git lưu lại.

Một trong những ưu điểm khác của Git là nó không bắt buộc kết nối tới máy chủ trung tâm để sử dụng. Trong các VCS truyền thống, ta phải kết nối tới máy chủ lưu trữ code để tải file cần dùng, sau đó đăng file này trở lại. Tất cả lập trình viên cùng làm trong dự án đều kết nối đến server này. Tuy nhiên, với Git, clone một repo đồng nghĩa với sao chép toàn bộ repo về máy. Do vậy, mỗi repo clone về máy chính là một bản sao lưu của dự án. Lỡ như repo ở trung tâm bị trục trặc, ta có thể dùng repo đã tải về máy để phục hồi lại.

Nếu bạn là lập trình viên thì việc học Git trở nên thiết yếu hơn bao giờ hết, đặc biệt là khi làm việc trong một nhóm.

Cài đặt Git

Đầu tiên, ta vào trang web của Git và download gói cài đặt phù hợp với hệ điều hành đang dùng. Việc cài đặt này hết sức đơn giản trong môi trường Windows và Mac OS, tuy nhiên trong Linux thì hơi phức tạp. Nếu cần, bạn có thể tham khảo thông tin về cài đặt cho Linux trong phần hỗ trợ của trang web.

Sau khi cài đặt xong, ta chạy chương trình bằng icon Git Bash. Một cửa sổ dòng lệnh hiện ra, ta gõ:

git --version

Nếu thấy tên và phiên bản của Git hiện ra thì ta biết đã cài đặt thành công. Lệnh này cũng được dùng để kiểm tra phiên bản của Git đang sử dụng.

Cấu hình Git

Trước khi sử dụng Git, ta phải cấu hình nó bằng cách cung cấp thông tin cơ bản. Mục đích của việc này là để khi lưu lại (commit) những thay đổi trong file code, Git sẽ đính kèm thông tin về người đã thực hiện việc lưu trữ này. Trong cửa sổ dòng lệnh, ta gõ 2 câu lệnh sau:

git config --global user.name "Hieu Sensei"
git config --global user.email "[email protected]"

Bạn thay phần tên và email sao cho phù hợp với thông tin cá nhân của mình.

Khởi tạo

Đầu tiên ta phải tạo một thư mục mới. Thư mục này đóng vai trò là nơi chứa dự án đang thực hiện. Ta có thể tạo thư mục mới trong cửa sổ dòng lệnh bằng:

mkdir folder

Sau khi tạo thư mục mới, ta di chuyển vào trong thư mục đó. Để làm việc này ta dùng dòng lệnh:

cd folder

Tiếp theo, để khai báo cho Git biết rằng đây chính là thư mục mà ta muốn Git quản lý những thay đổi:

git init

Lúc này, ta sẽ nhận được thông báo là một thư mục tên .git đã được thêm vào dự án. Thư mục này mặc định sẽ ẩn và đây chính là nơi mà mọi thông tin về dự án sẽ được Git lưu trữ.

Theo dõi tập tin

Để kiểm tra tình trạng hiện tại của các tập tin trong thư mục, ta dùng lệnh sau:

git status

Lệnh này sẽ liệt kê những tập tin mà ta chưa đưa vào trạng thái đang được theo dõi, những tập tin đang trong trạng thái đó, và những tập tin vừa được ta chỉnh sửa.

Trước khi tiếp tục, tôi muốn nói đôi điều về thuật ngữ. Trong tiếng Anh, Git dùng thuật ngữ Staging Area để nói đến cái mà tôi gọi là “danh sách theo dõi”. Thực ra đây là cách dịch đơn giản tôi sử dụng nhằm giúp bạn dễ hiểu tính năng của nó. Ngoài ra, từ Stage còn được dùng như một động từ để chỉ hành động “đưa một tập tin vào danh sách theo dõi”.

Để thêm một tập tin nào đó vào danh sách theo dõi, ta dùng lệnh sau:

git add file.txt

Nếu trong thư mục ta có nhiều tập tin và ta muốn thêm tất cả các tập tin này vào danh sách theo dõi của Git, ta dùng lệnh sau:

git add .

Ở đây, dấu chấm nghĩa là thư mục hiện hành.

Commit

Khái niệm commit có vẻ khó hiểu với những người mới bắt đầu sử dụng Git. Thực ra, đây chỉ là thuật ngữ chỉ việc lưu lại những thay đổi trong tập tin đã được đưa vào danh sách theo dõi. Ta cũng có thể hiểu đơn giản commit chính là Save.

Để commit những thay đổi trong tập tin, ta dùng lệnh sau:

git commit

Lúc này, Git sẽ tự động chạy Vim, một chương trình soạn thảo văn bản trong cửa sổ dòng lệnh, và yêu cầu nhập ghi chú về những thay đổi đã thực hiện. Ta chỉ cần ghi chú ngắn gọn vào dòng đầu tiên trong của sổ này (hoặc bất kì dòng nào không bắt đầu bằng dấu #). Sau đó ta bấm phím Escape, rồi gõ vào:

:wq

Lúc này, Vim sẽ lưu lại những thay đổi và thoát.

Tuy nhiên, thao tác này quá bất tiện nên ta có thể dùng lệnh sau để vừa commit vừa ghi chú trên cùng một dòng lệnh:

git commit -m 'Add your comment here'

Nên nhớ rằng trước khi ta commit thì ta phải dùng lệnh add để báo cho Git biết rằng đây là tập tin mà ta sẽ commit trong lần commit sắp tới. Lệnh add là lệnh đa chức năng. Ta có thể dùng nó để thêm tập tin mới vào trong danh sách theo dõi hoặc cũng có thể dùng nó để báo cho Git biết trong lần commit tiếp theo thì sẽ commit những tập tin nào.

Tuy nhiên, nếu chỉnh sửa nhiều tập tin và mỗi lần như thế ta phải gõ lệnh add thì quá mệt mỏi. Do đó, Git đã cung cấp cho chúng một cách để có thể commit toàn bộ tập tin đã nằm trong danh sách theo dõi bằng lệnh:

git commit -a

Ngoài ra, ta cũng có thể gom flag -a với flag -m để ghi chú cho commit trong cùng một dòng lệnh:

git commit -am 'message'

Ignore

Trong thư mục dự án, không phải tập tin nào ta cũng muốn Git theo dõi. Có những tập tin như tập tin tạm, được tạo ra bởi IDE hoặc hệ điều hành, ta hoàn toàn không muốn lưu tâm đến. Do vậy khi commit, ta phải gọi lệnh add cho từng tập tin. Để khắc phục nhược điểm này, ta có thể khai báo một danh sách những tập tin cần làm ngơ (ignore). Git sẽ dựa vào danh sách này để biết nên bỏ qua file nào khi commit cả thư mục.

Đầu tiên, bạn hãy tạo một tập tin mới có tên .gitignore nằm trong thư mục dự án. Trong tập tin này, ta sẽ liệt kê các tập tin ta không muốn Git commit. Giả sử bạn muốn Git bỏ qua các tập tin có đuôi .txt thì hãy thêm vào trong file .gitignore dòng sau:

*.txt 

Từ giờ, khi add toàn bộ thư mục, Git sẽ làm ngơ với tất cả file text.

Lệnh diff

Để so sánh sự khác nhau giữa một tập tin trong thư mục hiện hành và trong danh sách theo dõi, ta dùng lệnh:

git diff file

Git sẽ hiển thị những dòng nào trong tập tin đã được thêm vào bằng dấu cộng phía trước. Còn những dòng bị loại bỏ thì có dấu trừ.

Trong trường hợp ta muốn so sánh giữa phiên bản đang ở trong danh sách theo dõi (stage area) và phiên bản trong lần commit cuối cùng, ta sử dụng lệnh:

git diff --staged file

Ta cũng có thể thay --stage bằng --cached để có cùng kết quả như trên.

Tương tự, ta cũng có thể xem sự khác nhau giữa phiên bản tập tin trong thư mục hiện hành và phiên bản tập tin đó trong lần commit cuối cùng:

git diff HEAD file

Ở đây, HEAD là từ khóa chỉ lần commit cuối cùng.

Ta cũng có thể không cần ghi tên file trong các câu lệnh trên. Nếu như vậy, Git sẽ hiện những thay đổi của tất cả tập tin trong thư mục.

Lệnh log

Để xem lịch sử commit, ta dùng lệnh:

git log

Git sẽ hiển thị một danh sách những commit trong quá khứ. Để hiển thị thêm thông tin trong mỗi commit, ta dùng:

git log --stat

Lần này, dưới mỗi commit, Git sẽ cho biết có bao nhiêu tập tin được thay đổi, bao nhiêu dòng được thêm vào và bao nhiêu dòng được loại bỏ.

Tuy nhiên, nếu danh sách commit quá dài thì sẽ khó xem. Ta có thể dùng lệnh sau để thu gọn danh sách lại, chỉ hiển thị dòng ghi chú của ta trong từng commit:

git log --oneline

Ta cũng có thể hiển thị các commit dưới dạng biểu đồ trong trường hợp có nhiều nhánh (branch, sẽ được đề cập ở phần sau):

git log --graph

Sau khi thực hiện lệnh trên, ta thấy ở sát mép trái có một biểu đồ dạng ASCII hiện ra chỉ rõ mối liên hệ giữa các nhánh và commit.

Git cũng cho phép ta kết hợp nhiều câu lệnh thành một, ví dụ:

git log --oneline --graph

Lần này, Git vừa hiển thị một dòng, vừa hiển thị biểu đồ. Để “làm đẹp” cho danh sách kết quả, ta dùng lệnh:

git log --pretty="%h, %cn, %cr"

Trong đó, chuỗi nằm sau cờ (flag) –pretty bao gồm những ký tự định dạng giúp Git biết được sẽ hiển thị kết quả như thế nào. Ta có thể tham khảo danh sách các ký tự này trong Documentation của Git.

Ngoài ra, nếu bạn tinh ý thì sẽ nhận thấy rằng khi cài đặt Git có kèm theo cho ta một giao diện đồ họa. Để chạy giao diện đồ họa này, ta dùng lệnh:

gitk

Mọi thứ ta làm trong giao diện dòng lệnh đều có thể thực hiện trong giao diện đồ họa. Tuy nhiên, tôi sẽ không hướng dẫn sử dụng giao diện đồ họa bởi vì trong thực tế hầu như ta chẳng bao giờ dùng đến nó.

Nhánh (Branch)

Nhánh cho phép ta thoải mái thử nghiệm các ý tưởng mới với dự án. Với nhánh, ta có thể thay đổi, vọc phá mà không sợ gây ảnh hưởng đến dự án. Sau đó, nếu ưng ý với kết quả, ta có thể hợp nhất (merge) nó với dự án. Còn nếu không, ta có thể bỏ cả nhánh và dự án không hề bị ảnh hưởng.

Nếu bây giờ chạy lệnh git status, ta sẽ thấy ở dòng trên cùng cho biết ta đang ở nhánh master (# On branch master). Đây là nhánh mặc định của Git.

Để xem danh sách các nhánh hiện hành, ta dùng lệnh:

git branch

Một danh sách các nhánh sẽ hiện ra. Dấu sao đánh dấu nhánh mà ta đang ở. Để tạo một nhánh mới, ta thêm tên của nhánh vào sau lệnh git branch:

git branch branch_name

Sau khi tạo nhánh mới, ta phải chuyển qua nhánh đó mới có thể sử dụng được:

git checkout branch_name

Giờ đây, ta có thể làm việc trên nhánh mới vừa tạo. Lưu ý rằng những commit thực hiện ở nhánh mới hoàn toàn không ảnh hưởng gì đến nhánh chính (gọi là master) cho đến khi ta ra lệnh hợp nhất (merge).

Để quay về nhánh chính, ta dùng lệnh:

git checkout master

Ta cũng có thể kết hợp cả 2 lệnh tạo nhánh và chuyển qua nhánh đó trong cùng một dòng:

git checkout -b branch_name

Ngoài ra, để xem toàn bộ commit của tất cả các nhánh thay vì chỉ một nhánh như ta đã biết, ta dùng lệnh:

git log --all

Để thêm tên nhánh vào trong danh sách kết quả cho dễ xem, ta thêm vào –decorate

git log --all --decorate

Hợp nhất nhánh (Merge)

Sau khi ta đã hài lòng với những thử nghiệm đã thực hiện trong một nhánh, giờ là lúc để ta hợp nhất với nhánh master. Để làm điều này, ta dùng câu lệnh:

git merge branch_name

Sau đó, Git yêu cầu ta nhập vào ghi chú cho lần commit này. Mỗi lần ta hợp nhất nhánh thì Git thực hiện commit luôn cho ta.

Sau khi đã hợp nhất với nhánh master, ta nên xóa nhánh kia đi. Để xóa nhánh, ta dùng lệnh:

git branch -d branch_name

Đẩy mã lên GitHub

GitHub cung cấp kho chứa code miễn phí trực tuyến. Ta có thể chia sẻ cả thư mục dự án cho mọi người sử dụng. Nếu dùng tài khoản miễn phí, các dự án đăng tải sẽ được công khai. Nếu muốn được riêng tư, ta phải thanh toán chi phí hàng tháng cho GitHub.

Để dùng GitHub, ta phải đăng ký một tài khoản. Sau khi đăng ký xong, ta sẽ được đưa vào trang quản lý. Ở trang này, ta bấm nút New Repository để tạo repo mới. Trong trang kế tiếp, ta nhập vào tên ở ô Repository name và thêm vài dòng mô tả nếu cần thiết. Cuối cùng, ta chọn chế độ Public (Private chỉ dành cho tài khoản trả phí) và bấm nút Create Repository.

Ở trang tiếp theo, GitHub hướng dẫn các câu lệnh cần thiết để thiết lập và đăng tải file code lên repo vừa tạo. GitHub hướng dẫn thực hiện trong 2 trường hợp:

  1. Nếu chưa có thư mục dự án thì ta làm theo các bước hướng dẫn để thiết lập Git trong thư mục này.
  2. Nếu đã có một thư mục dự án rồi thì ta chỉ cần thực hiện “push” các tập tin lên GitHub.

Trong cả 2 trường hợp, câu lệnh quan trọng nhất là git remote và git push. Lệnh git remote khai báo đường dẫn tới repo trên GitHub. Lệnh git push tiến hành “đẩy” file trong thư mục lên repo GitHub đã xác định ở git remote.

Sau khi thực hiện lệnh push, Git yêu cầu nhập username và password để đăng nhập GitHub. Khi đăng nhập thành công, Git sẽ tiến hành đẩy nội dung trong thư mục dự án lên repo. Bây giờ, ta có thể vào trong kho trên GitHub để xem các tập tin của mình.

Tham quan GitHub

GitHub là một kho chứa mã khổng lồ và ta có thể tìm thấy hầu hết các dự án nguồn mở ở đây. Chỉ cần gõ tên của một dự án nổi tiếng (ví dụ như jQuery) thì sẽ thấy ngay một danh sách kết quả.

Để truy cập vào kho chứa của dự án, ta bấm chuột vào dòng kết quả tương ứng. Lúc này, một danh sách liệt kê các tập tin trong dự án sẽ hiện ra. Ta cũng có thể xem nội dung tập tin bằng cách bấm vào nó. Phía trên, ta có thể thấy một vài thông tin về repo như số lần commit, tổng số nhánh và tổng số những người tham gia vào dự án.

Phía bên phải là một loạt các thẻ. Hiện tại ta đang ở thẻ Code. Phía dưới ta sẽ thấy dòng HTTPS, đây chính là địa chỉ mà ta sẽ sử dụng để sao chép (clone) repo về máy. Dưới cùng là nút Download ZIP. Ta bấm nút này để tải về repo dưới dạng zip, phù hợp cho những ai không biết sử dụng Git (hi vọng đó không phải là bạn).

Thẻ Pull Requests là nơi góp ý với chủ repo về những đề xuất thay đổi. Để sử dụng tính năng này, trước tiên ta Fork repo về tài khoản. Sau đó, ta chỉnh sửa file code. Tiếp đến ta gửi một Pull Request cho chủ repo, và họ sẽ nhận được một email thông báo. Nếu đồng ý những thay đổi ta đề xuất, họ có thể tích hợp những thay đổi này vào repo gốc.

Phía trên cùng ta sẽ thấy nút Fork. Tính năng này cho phép tạo một bản sao của repo và lưu nó vào tài khoản của ta. Sau khi bấm nút Fork, ta sẽ thấy bản sao của kho nằm trong danh sách My Repositories.

Kho chứa từ xa (Remote Repository)

Là lập trình viên, trước sau gì ta cũng phải làm việc với kho chứa từ xa. Do đó, làm quen với các câu lệnh để thực hiện công việc này là rất cần thiết.

Ta đã biết câu lệnh mà GitHub yêu cầu sử dụng để kết nối đến kho từ xa. Câu lệnh này có dạng như sau:

git remote add origin https://github.com/user/repo.git

Trong đó, origin là tên ta đặt cho repo (Git khuyến cáo nên dùng origin), còn phần URL phía sau chính là đường dẫn đến kho từ xa.

Tiếp theo, ta sẽ dùng câu lệnh remote:

git remote

Câu lệnh này liệt kê tất cả các kho từ xa mà ta đã khai báo. Nếu bạn thực hiện câu lệnh remote add như trên thì Git sẽ hiển thị kết quả là origin.

Khi ta muốn đẩy mã lên kho từ xa, ta dùng lệnh:

git push origin master

Ở đây, master là tên nhánh mà ta muốn push. Sau khi cho chạy lệnh này, ta hãy kiên nhẫn chờ một chút để Git kết nối tới kho từ xa qua mạng.

Trong trường hợp ta muốn cập nhật repo trên máy với những commit mới nhất trên repo từ xa, ta sẽ dùng lệnh:

git pull

Câu lệnh này thực hiện 2 việc. Thứ nhất, nó sẽ lấy những commit mới nhất từ repo từ xa về máy. Thứ hai, nó sẽ tự động hợp nhất nhánh của repo từ xa vừa tải về với nhánh của repo nội bộ. Đó cũng chính là điểm bất tiện của phương pháp này. Do vậy, ta nên sử dụng câu lệnh sau:

git fetch

Câu lệnh này sẽ tải những commit về nhưng không hợp nhất với nhánh ở repo trong máy. Tiếp theo, để thực hiện lệnh hợp nhất, ta dùng lệnh sau:

git merge origin/master

Lúc này, lịch sử commit của repo nội bộ sẽ được đồng bộ với repo từ xa. Ta có thể kiểm tra lại điều này bằng lệnh git push và nhận được kết quả Everything up-to-date.

Cuối cùng, tôi muốn giới thiệu lệnh clone. Đây là lệnh dùng để sao chép toàn bộ repo từ xa về máy:

git clone https://github.com/user/repo.git

Lời kết

Với những câu lệnh mà tôi trình bày trong bài, bạn đã có đủ kiến thức cơ bản để làm việc với Git. Theo quan sát của tôi, Git và GitHub vẫn còn khá xa lạ với phần lớn lập trình viên. Nếu bạn thuộc nhóm người này, đây là lúc thích hợp nhất để bắt đầu làm quen với Git và GitHub.