Nội dung chính
Git pull là một câu lệnh quan trọng trong Git giúp lập trình viên đồng bộ mã nguồn với kho lưu trữ từ xa. Git pull giúp việc cộng tác trong nhóm được dễ dàng và thuận tiện hơn.
Đọc bài viết này để hiểu rõ hơn về:
- Git Pull là gì?
- Nguyên lý hoạt động của Git Pull
- Cú pháp và trường hợp sử dụng
- Các tùy chọn nâng cao của Git Pull
- Phân biệt giữa Git pull và Git fetch
Git là gì?
Git là một hệ thống quản lý phiên bản phân tán được sử dụng rộng rãi hiện nay. Ngoài khả năng quản lý lịch sử mã nguồn một cách hiệu quả, Git còn cung cấp các tính năng giúp nâng cao khả năng cộng tác trong dự án.
Mỗi người tham gia vào dự án Git đều được xem là một kho lưu trữ từ xa cùng với một kho lưu trữ chính (thông thường sẽ sử dụng các dịch vụ như GitHub, GitLab,…). Khi làm việc, mỗi thành viên cần cập nhật các thay đổi từ kho lưu trữ cục bộ lên kho lưu trữ từ xa, đồng thời đồng bộ các dữ liệu mới từ các thành viên khác về máy cá nhân. Lệnh git pull chính là công cụ quan trọng giúp đồng bộ hóa dữ liệu này một cách nhanh chóng và hiệu quả, đảm bảo mã nguồn của các thành viên luôn nhất quán và cập nhật.
Đọc thêm các bài viết tổng quan về Git:
- Git là gì: Định nghĩa, Thuật ngữ cơ bản và Cách cài đặt
- Git vs GitHub: Các điểm khác nhau và Cách kết hợp
- Tổng hợp 20+ các lệnh Git cơ bản cần biết
Git Pull là gì?
Git pull là một lệnh được sử dụng để lấy và tải các thay đổi mới nhất từ kho lưu trữ từ xa, sau đó cập nhật ngay lập tức kho lưu trữ cục bộ để đồng bộ với nội dung đó. Việc hợp nhất các thay đổi từ xa vào kho lưu trữ cục bộ là một tác vụ phổ biến trong quy trình làm việc hợp tác dựa trên Git.
Cú pháp của Git Pull:
git pull <tên kho lưu trữ> <tên nhánh>
Trong đó:
- <tên kho lưu trữ>: Tên của kho lưu trữ từ xa (thường là origin) khi bạn thực hiện kết nối bằng lệnh git remote add.
- <tên nhánh>: Tên của nhánh từ xa mà bạn muốn lấy về và hợp nhất vào nhánh hiện tại.
Trong trường hợp không chỉ định tên nhánh, Git sẽ căn cứ vào cấu hình sẵn có để xác định nhánh từ xa cần cập nhật. Thông thường Git sẽ tự động hiểu là bạn đang muốn đồng bộ dữ liệu với nhánh hiện tại đang làm việc ở máy cá nhân, hoặc dựa vào thiết lập khi bạn thực hiện lệnh kết nối:
git branch --set-upstream-to=<tên kho lưu trữ>/<tên nhánh>
Ví dụ: Nếu bạn đang làm việc trên nhánh develop từ kho lưu trữ với tên origin, và cần cập nhật các commit mới được bổ sung từ thành viên khác, thì bạn cần chạy lệnh:
git pull origin develop
Nguyên lý hoạt động của Git Pull
Lệnh git pull thực chất là sự kết hợp của hai lệnh khác là git fetch và git merge:
- Ở giai đoạn đầu tiên, git pull sẽ thực hiện công việc của lệnh git fetch, tải xuống các thay đổi mới nhất từ kho lưu trữ từ xa về cho nhánh cục bộ được chỉ định.
- Sau đó, git pull sẽ chuyển sang quy trình hợp nhất của lệnh git merge. Một commit hợp nhất mới sẽ được tạo và HEAD sẽ được cập nhật để trỏ đến commit mới này trên nhánh hiện tại.
Giả sử bạn đang làm việc trên nhánh main của một dự án và có những thay đổi vừa được đẩy lên nhánh main của kho lưu trữ từ xa bởi thành viên khác. Bây giờ để cập nhật những thay đổi này cho máy cục bộ, bạn có thể thực hiện git pull như sau:
git pull origin main
Kết quả:
- Các thay đổi từ origin/main được tải về máy cục bộ.
- Hợp nhất các thay đổi từ xa vào nhánh cục bộ main.
Việc này cũng có thể được thực hiện riêng lẻ bởi Git fetch và Git merge như sau:
Đầu tiên, tải các thay đổi từ xa bằng git fetch.
git fetch origin main
Kết quả: Các thay đổi được tải về nhưng chưa áp dụng vào nhánh cục bộ.
Bước tiếp theo, hợp nhất các thay đổi vào nhánh cục bộ bằng git merge.
git merge origin/main
Kết quả: Các thay đổi từ origin/main được hợp nhất vào nhánh cục bộ
Các trường hợp sử dụng Git Pull
Cập nhật mã nguồn mới nhất từ kho lưu trữ từ xa
Khi bạn và các thành viên khác cùng làm việc trên một nhánh, bạn sẽ cần phải thường xuyên đồng bộ dữ liệu mới từ các thành viên khác để cập nhật mã nguồn và tránh xảy ra xung đột khi đẩy commit lên kho lưu trữ từ xa:
git pull <tên kho lưu trữ> <tên nhánh>
Ví dụ: Khi bạn đang làm việc trên nhánh develop và muốn cập nhật các commit mới từ thành viên khác về máy cá nhân, bạn có thể thực hiện lệnh sau:
git pull origin develop
Cập nhật nhánh trước khi phát triển tính năng mới
Thông thường, trước khi bắt đầu tách nhánh để phát triển một tính năng mới, bạn nên cập nhật nhánh cha để đảm bảo mã nguồn ở trạng thái mới nhất:
git checkout <nhánh cha> git pull origin <nhánh cha> git checkout -b <nhánh tính năng>
Ví dụ: bạn đang chuẩn bị phát triển 1 tính năng đăng nhập người dùng, lúc này bạn cần thực hiện các bước sau:
- Chuyển sang nhánh gốc (giả sử là main)
- Cập nhật mã nguồn mới nhất cho nhánh main
- Tạo và chuyển sang nhánh tính năng mới
git checkout main git pull origin main git checkout -b feat/login
Cập nhật nhánh trước khi tạo lệnh Pull Request
Sau khi hoàn tất công việc phát triển tính năng mới và bạn muốn tạo Pull Request (PR) trên kho lưu trữ từ xa (GitHub, GitLab,…) để hợp nhất mã mới này vào nhánh gốc.
Lúc này bạn nên cập nhật nhánh làm việc của mình với các thay đổi mới nhất từ nhánh gốc (ví dụ là main hoặc develop) để đảm bảo rằng PR của bạn có mã nguồn mới nhất và tránh xung đột. Dưới đây là quy trình cụ thể:
git checkout main git pull origin main git checkout feat/login git merge main git push origin feat/login
Trong đó:
- Chuyển trạng thái làm việc sang nhánh main
- Sử dụng git pull để cập nhật mã nguồn mới nhất cho nhánh main
- Chuyển về nhánh tính năng
- Sử dụng git merge (hoặc git rebase) để hợp nhất các thay đổi mới (nếu có) từ nhánh main sang nhánh tính năng
- Đẩy toàn bộ thay đổi từ nhánh tính năng lên kho lưu trữ từ xa để thực hiện tạo Pull Request
Các tùy chọn nâng cao của Git Pull
Giả sử chúng ta đang có nhánh main ở kho lưu trữ từ xa với các commits sau:
A -- B -- C -- D (origin/main)
Nhánh cục bộ có 1 commit mới:
A -- B (main) -- E (local)
Như vậy, ban đầu cả nhánh origin/main và main đều có commits A và B, tuy nhiên sau quá trình làm việc, nhánh origin/main được bổ sung commits C và D từ nhánh khác, còn nhánh main ở cục bộ cũng được bổ sung thêm commit E.
Những tùy chọn bên dưới sẽ có những hành động cụ thể như sau:
–rebase (hoặc viết tắt -r)
Sử dụng chức năng rebase thay cho chức năng merge trong git pull.
Đặt các commit cục bộ sau các commit mới nhất từ kho lưu trữ từ xa, tạo lịch sử commit gọn gàng hơn.
Cú pháp:
git pull --rebase
Sử dụng ở ví dụ trên, ta có:
git pull --rebase origin main
Lúc này Git sẽ tải commit từ xa (A, B, C, D) và đưa commit E (cục bộ) lên đầu lịch sử commit (thay vì tạo commit merge như hành vi mặc định). Nhánh main ở cục bộ sẽ có commits là:
A -- B -- C -- D -- E
–depth=
Giới hạn chỉ lấy về một số lượng commit nhất định, giúp tiết kiệm thời gian và dung lượng nếu bạn chỉ cần cập nhật nhanh.
Cú pháp:
git pull --depth=<số commit>
Sử dụng ở ví dụ trên, ta có:
git pull --depth=2 origin main
Lúc này Git chỉ tải về commit C và D. sau đó thực hiện merge vào nhánh main như hành vi mặc định.
–verbose
Để hiển thị chi tiết các thay đổi khi pull.
Cú pháp:
git pull --verbose
Ví dụ khi sử dụng:
git pull --verbose origin main
Git sẽ thực hiện hành động pull các commits từ nhánh origin/main đồng thời hiển thị nội dung chi tiết của từng bước thực hiện trong terminal.
–tags
Dùng để cập nhật các tags từ kho lưu trữ từ xa.
Cú pháp:
git pull --tags
Ví dụ để tải toàn bộ tags từ kho lưu trữ từ xa, bạn thực hiện:
git pull --tags
Lúc này Git sẽ tải về tất cả các tags từ kho lưu trữ từ xa nếu chúng chưa có ở kho cục bộ.
–ff-only
Chỉ cho phép pull khi có thể fast-forward (*), tránh tạo commit merge không cần thiết. Nếu không thể fast-forward, lệnh sẽ thất bại.
(*) fast-forward nghĩa là nhánh cục bộ không có commit nào mới so với nhánh trên kho lưu trữ từ xa và chỉ cần cập nhật để theo kịp các commit mới từ remote. Git sẽ chỉ cập nhật con trỏ của nhánh cục bộ đến commit mới nhất của nhánh trên remote mà không tạo commit merge.
Cú pháp:
git pull --ff-only
Sử dụng ở ví dụ trên, ta có:
git pull --ff-only origin main
Lúc này lệnh sẽ thất bại, bởi vì Git không thể thực hiện fast-forward vì nhánh cục bộ đã có thêm commit riêng là E.
Nếu trong trường hợp nhánh cục bộ không có commit E, việc git pull sẽ được thực hiện thành công, và nhánh main cục bộ sẽ có các commits:
A -- B -- C -- D
So sánh Git Fetch vs Git Pull
Git pull và Git fetch đều là các lệnh dùng để cập nhật mã nguồn mới nhất từ kho lưu trữ từ xa về kho lưu trữ cục bộ, nhưng giữa Git Fetch vs Git Pull có sự khác biệt lớn trong cách hoạt động và mục đích sử dụng.
Dưới đây là so sánh chi tiết giữa hai lệnh Git Fetch vs Git Pull:
Tiêu chí | Git fetch | Git Pull |
Cú pháp | git fetch origin main | git pull origin main |
Mục đích | Cập nhật dữ liệu mới từ remote repository nhưng không thay đổi nhánh hiện tại | Lấy về các thay đổi từ remote repository và ngay lập tức hợp nhất (merge) các thay đổi đó vào nhánh hiện tại của bạn |
Hoạt động | Tải về toàn bộ các commits, tags, và các nhánh mới từ remote repository mà chưa có trong kho lưu trữ cục bộ | Mặc định chỉ lấy về commit mới và hợp nhất nó vào nhánh được chỉ định.
Không tự động tải về nhánh mới |
Trường hợp sử dụng | Khi muốn xem trước các thay đổi từ remote
Khi muốn tải về các nhánh mới từ remote |
Khi muốn đồng bộ mã nguồn giữa nhánh cục bộ và nhánh remote một cách nhanh chóng |
Tùy chọn thêm | Không tạo ra commit mới | Có thể dùng –rebase thay vì merge hoặc một số tùy chọn khác |
Các câu hỏi thường gặp về Git Pull
“git pull -r” có chức năng gì?
Git pull -r là cú pháp rút gọn của git pull –rebase, đây là tùy chọn đặc biệt khi sử dụng git pull.
Theo mặc định, git pull sẽ thực hiện 2 công việc tuần tự là git fetch (tải các thay đổi mới nhất từ remote repository) và git merge (hợp nhất các thay đổi đó vào nhánh hiện tại). Điều này sẽ tạo ra một commit merge khi nhánh cục bộ đang có những commit khác.
Tùy chọn -r (–rebase) sẽ thay thế hoạt động merge bằng hoạt động rebase, giúp đặt các commit cục bộ sau các commit mới nhất từ kho lưu trữ từ xa, tạo lịch sử commit gọn gàng hơn.
Tại sao nên sử dụng “git pull –rebase”?
Theo mặc định, lệnh git pull sẽ sử dụng merge để hợp nhất các thay đổi từ remote repository. Điều này có thể tạo ra các commit merge không cần thiết. Lúc này bạn có thể sử dụng lệnh “git pull –rebase” để thay thế hành động merge bằng hành động rebase.
Git pull –rebase sẽ giúp cho lịch sử commit được gọn gàng và sạch sẽ, tránh các commit merge không cần thiết. Ngoài ra, các commit của nhánh cục bộ được áp dụng tuần tự sau các commit mới từ remote repository, điều này giúp hạn chế các xung đột mã nguồn, cũng như giúp việc xử lý xung đột (nếu có) được thuận tiện hơn.
Tại sao nên thường xuyên chạy lệnh git pull khi làm việc?
Việc thường xuyên chạy lệnh git pull khi làm việc với Git, đặc biệt là trong các dự án nhóm là rất quan trọng và cần thiết. Sau đây là một số lí do cho điều này:
- Đồng bộ mã nguồn với nhóm làm việc của bạn, tránh cho mã nguồn ở kho lưu trữ cục bộ bị lỗi thời và không đầy đủ dữ liệu
- Giảm thiểu xung đột (conflicts): Bởi vì mã nguồn thường xuyên được cập nhật, nên sẽ giảm thiểu đáng kể tỉ lệ xung đột mã nguồn khi bạn thực hiện merge hoặc rebase với nhánh khác
Cách khắc phục xung đột khi sử dụng git pull?
Trong quá trình thực hiện git pull, bạn cũng có thể bắt gặp các xung đột mã nguồn. Khi đó bạn sẽ gặp các thông báo như:
Auto-merging <file> CONFLICT (content): Merge conflict in <file> Automatic merge failed; fix conflicts and then commit the result.
Lúc này, bạn có thể sử dụng lệnh git status để kiểm tra các tập tin nào đang bị xung đột và mở tập tin đó lên bằng các trình soạn thảo như vscode, sublime text,… để giải quyết. Git sẽ đánh dấu phần mã bị xung đột như sau:
<<<<<<< HEAD # Đây là thay đổi trên nhánh cục bộ ... ======= # Đây là thay đổi từ remote ... >>>>>>> main
Trong đó:
- <<<<<<< HEAD: Phần mã nguồn trên nhánh cục bộ.
- =======: Ranh giới giữa phần mã cục bộ và remote.
- >>>>>>> main: Phần mã từ remote repository.
Sau đó bạn có thể quyết định chọn một trong ba hướng xử lý:
- Giữ lại phần mã cục bộ, xóa bỏ phần mã từ remote repository
- Giữ lại phần mã từ remote repository, xóa bỏ phần mã cục bộ
- Kết hợp cả hai
Lưu ý: Nếu đây là nội dung mã của người khác, bạn nên thảo luận với họ trước khi đưa ra quyết định, điều này giúp giảm tỷ lệ thiếu dữ liệu và hạn chế lỗi xảy ra.
Sau khi lựa chọn phần mã phù hợp, hãy thêm tập tin đó vào staging area và hoàn tất quá trình hợp nhất bằng lệnh commit.
Phân biệt git pull vs git pull origin main
Khi sử dụng git pull, nếu không chỉ định thêm đối tượng:
- Git sẽ kiểm tra nhánh từ xa nào được liên kết (upstream branch) với nhánh cục bộ hiện tại và thực hiện pull từ nhánh đó.
- Upstream branch thường được thiết lập với lệnh git branch –set-upstream-to
Nếu như nhánh không được liên kết, khi thực hiện Git pull, chúng ta cần bổ sung phần “origin <tên nhánh>” nhằm mục đích chỉ định rõ ràng kho lưu trữ từ xa và nhánh từ xa đang cần được thực hiện git pull. Ngoài ra, điều này cũng sẽ bỏ qua cấu hình upstream branch của nhánh hiện tại.
Những lưu ý khi sử dụng Git Pull
- Cần hiểu rõ hành vi của git pull là tải các thay đổi và hợp nhất nó vào nhánh hiện tại, điều này cũng sẽ tạo ra commit merge. Từ đó có thể lựa chọn –rebase hoặc –ff-only cho những trường hợp cần thiết.
- Cấu hình upstream branch để giảm độ phức tạp của câu lệnh pull
- Nên sử dụng git pull thường xuyên để đồng bộ mã nguồn giữa kho lưu trữ từ xa và kho lưu trữ cục bộ. Từ đó giúp giảm thiểu khả năng xung đột (conflict) xảy ra.
- Sử dụng git stash (lưu tạm) trước khi thực hiện pull giúp tránh lỗi do thay đổi chưa commit và giúp giải quyết xung đột (nếu có) dễ dàng hơn.
Tổng kết
Việc đồng bộ mã nguồn giữa kho lưu trữ từ xa và kho lưu trữ cục bộ là điều cần thiết trong quá trình làm việc với Git, đặc biệt là trong các nhóm dự án. Lệnh Git Pull chính là một công cụ quan trọng để thực hiện điều này.
Qua bài viết trên, ITViec hi vọng đã giúp bạn nắm vững được kiến thức cơ bản đến nâng cao về nguyên lý hoạt động, cũng như cách sử dụng Git Pull. Hãy luôn ghi nhớ rằng bạn nên thực hiện git pull thường xuyên để đảm bảo quy trình làm việc được diễn ra thuận lợi, cải thiện hiệu suất làm việc trong nhóm.