Hướng dẫn

Pull request trong Git: là gì và cách tạo PR trên GitHub

Cập nhật: · Skillaria.Top

Pull request (PR) là đề nghị gộp nhánh của bạn vào nhánh chính trên GitHub: bạn trình bày thay đổi, đồng nghiệp thảo luận và kiểm tra, sau khi được chấp thuận nhánh được gộp. Trên GitLab, cơ chế tương tự được gọi là merge request (MR). Pull request là tính năng của nền tảng chứ không phải lệnh Git, và không liên quan đến lệnh git pull.

Pull request (PR) là đề nghị gộp thay đổi từ nhánh này vào nhánh khác, được tạo trên nền tảng như GitHub. Bạn đẩy nhánh lên server, mở PR, cả nhóm xem diff, bình luận trên từng dòng code, chạy kiểm tra tự động, và chỉ sau đó mới nhấn nút gộp. Trên GitLab, cơ chế tương tự được gọi là merge request (MR). Nếu hỏi ngắn gọn git pull request là gì, câu trả lời là: điểm kiểm soát giữa "tôi đã viết code" và "code đã vào main".

Git pull request là gì và tên gọi từ đâu

Cái tên hình thành theo lịch sử. Người viết thay đổi đề nghị chủ repository "kéo" (pull) nhánh của họ về, từ đó có tên pull request. GitLab chọn tên theo kết quả là merge request, tức yêu cầu gộp. Bitbucket và Azure DevOps dùng thuật ngữ pull request. Về bản chất, chúng giống nhau: nhánh nguồn, nhánh đích, mô tả, thảo luận, kiểm tra và nút gộp.

Điều cần hiểu rõ: pull request không phải một phần của Git. Trong Git không có đối tượng "PR", bạn không thể tạo nó bằng commit hay tìm thấy nó trong thư mục .git. PR là thực thể của nền tảng: GitHub lưu mô tả, bình luận và trạng thái kiểm tra ở phía mình, còn Git chỉ quản lý các nhánh và commit thuộc PR.

Trong trao đổi của lập trình viên, viết tắt PR hầu như luôn nghĩa là pull request: "gửi PR đi", "PR đang chờ review", "đã merge PR rồi". Với GitLab người ta nói MR.

Pull request và git pull: khác nhau thế nào

Hai khái niệm này hay bị nhầm vì tên giống nhau. Lệnh git pull tải commit mới từ repository remote về và gộp ngay vào nhánh hiện tại của bạn. Đây là thao tác cục bộ trong terminal. Pull request là một trang web trên GitHub, GitLab hoặc Bitbucket, chứa đề nghị gộp một nhánh vào nhánh khác.

git pullPull request
Nằm ở đâuLệnh Git trong terminalTrang trên GitHub, GitLab, Bitbucket
Làm gìLấy thay đổi từ server về máy bạnĐề nghị gộp nhánh của bạn vào nhánh khác hoặc nhánh chung
Ai tham giaChỉ bạnTác giả, người review, CI
Cần nền tảng không?Không, chỉ cần remoteCó

Không tồn tại lệnh "git pull request" trong Git. Có một tiện ích cũ là git request-pull, nhưng nó chỉ tạo nội dung email tóm tắt thay đổi để gửi đi, không liên quan đến PR trên GitHub.

Vì sao cần pull request

Khi dự án chỉ có một người làm, bạn có thể commit thẳng vào main. Khi có từ hai người trở lên, việc push trực tiếp lên nhánh chính bắt đầu gây ra build hỏng và mất công việc. Pull request giải quyết nhiều vấn đề cùng lúc:

  • review code — người thứ hai xem thay đổi trước khi nó vào main và bắt được lỗi mà tác giả bỏ sót;
  • kiểm tra tự động — GitHub Actions hoặc CI khác chạy test và linter trên mỗi PR, và trạng thái đỏ ngăn code lỗi được gộp;
  • lịch sử quyết định — sau một năm, từ PR bạn biết không chỉ đã đổi gì mà còn vì sao, kèm thảo luận;
  • đóng góp cho dự án của người khác — với mã nguồn mở bạn không có quyền ghi, và PR từ fork là cách duy nhất để đề xuất sửa lỗi;
  • bảo vệ nhánh — quy tắc bảo vệ nhánh trên GitHub có thể cấm push trực tiếp vào main và yêu cầu PR đã được chấp thuận.

Cách tạo pull request trên GitHub từ nhánh

Đây là kịch bản chính cho nhóm mà mọi thành viên đều có quyền ghi vào repository. Công việc diễn ra trên nhánh tính năng, và main chỉ thay đổi qua PR.

Bước 1. Tạo nhánh từ main mới nhất

git switch main
git pull
git switch -c feature/login-form

Đặt tên nhánh rõ ràng: feature/…, fix/…, hoặc mã số công việc. Người review hiểu ngay sẽ bàn về điều gì.

Bước 2. Tạo commit

git add src/login.js
git commit -m "Thêm form đăng nhập"

Một PR cho một công việc. PR nhỏ khoảng 100–300 dòng được kiểm tra nhanh và kỹ, còn PR 3000 dòng thường được duyệt mà không ai thực sự đọc.

Bước 3. Đẩy nhánh lên server (git push cho pull request)

git push -u origin feature/login-form

Cờ -u liên kết nhánh cục bộ với nhánh remote, nên về sau chỉ cần git push. Phản hồi của GitHub sẽ tự gợi ý liên kết để tạo PR:

remote: Create a pull request for 'feature/login-form' on GitHub by visiting:
remote:      https://github.com/team/app/pull/new/feature/login-form

Bước 4. Mở PR trên giao diện web

Mở liên kết từ terminal, hoặc mở repository trên GitHub. Sau khi vừa push, sẽ có banner với nút "Compare & pull request". Cách khác là tab "Pull requests" rồi nút "New pull request".

  1. Kiểm tra cặp nhánh: base là nơi gộp về (thường là main), compare là nhánh của bạn.
  2. Viết tiêu đề: đã làm gì, có thể ở dạng mệnh lệnh hoặc tóm tắt ngắn.
  3. Trong mô tả, giải thích vì sao, cách kiểm tra, và những gì còn nằm ngoài phạm vi. Nếu repository có mẫu PR, các ô sẽ được điền sẵn.
  4. Ở cột bên phải, chỉ định người review (Reviewers), người phụ trách (assignee), và nhãn nếu nhóm có dùng.
  5. Nhấn "Create pull request", hoặc chọn bản nháp (xem bên dưới).

Tạo PR từ terminal: GitHub CLI

Nếu không muốn chuyển sang trình duyệt, có tiện ích chính thức gh. Đây không phải lệnh của Git mà là chương trình riêng của GitHub, nhưng rất tiện cho PR:

gh pr create --base main --title "Thêm form đăng nhập" --body "Closes #42"
gh pr list
gh pr checkout 57
gh pr merge 57 --squash

Với câu hỏi "các lệnh git pull request", câu trả lời trung thực là: Git quản lý nhánh, commit và push, còn PR được tạo trên nền tảng, qua trình duyệt, gh, hoặc API.

Cách gửi pull request tới repository của người khác qua fork

Trong các dự án mã nguồn mở, bạn không có quyền ghi. Vì vậy trước tiên hãy tạo fork, tức bản sao repository trong tài khoản GitHub của bạn. Bạn làm việc trên bản fork đó rồi đề xuất thay đổi cho bản gốc.

  1. Trên trang dự án, nhấn "Fork". Repository github.com/you/project sẽ xuất hiện trong tài khoản của bạn.
  2. Clone fork của bạn và thêm bản gốc làm remote thứ hai, thường đặt tên upstream.
  3. Tạo nhánh, commit, và đẩy nhánh lên fork của bạn.
  4. Trên GitHub, mở PR: base repository là dự án gốc, head repository là fork và nhánh của bạn.
git clone git@github.com:you/project.git
cd project
git remote add upstream https://github.com/owner/project.git
git switch -c fix/typo-readme
git commit -am "Sửa lỗi chính tả trong README"
git push -u origin fix/typo-readme

Trước khi bắt đầu, hãy đọc file CONTRIBUTING.md. Thường file này mô tả quy tắc viết commit, test, và việc có cần tạo issue trước hay không. Giữ fork luôn cập nhật bằng git fetch upstream và git rebase upstream/main trong nhánh của bạn, để PR không bị lạc hậu so với bản gốc.

Merge request trên GitLab: là gì và cách tạo

Merge request trên GitLab là bản tương đương đầy đủ của pull request. Tên gọi và chi tiết giao diện khác nhau, nhưng quy trình thì giống: nhánh, push, yêu cầu gộp, review, rồi merge.

Bạn có thể tạo MR bằng vài cách:

  • qua liên kết GitLab in ra trong terminal sau khi git push một nhánh mới;
  • trong mục Merge requests của dự án, bấm "New merge request" và chọn nhánh nguồn và nhánh đích;
  • ngay khi push, bằng push options của GitLab, không cần mở trình duyệt.
git push -o merge_request.create -o merge_request.target=main origin feature/login-form

Thuật ngữ trên GitLab hơi khác: phê duyệt gọi là approve, người chịu trách nhiệm gộp là assignee, và thay cho GitHub Actions, GitLab dùng GitLab CI/CD với file .gitlab-ci.yml. Bản nháp MR cũng có, được gọi là Draft.

Review pull request

Người review mở tab "Files changed" và xem diff. Có thể để bình luận trên bất kỳ dòng nào, hoặc đề xuất sửa sẵn (suggestion) để tác giả áp dụng chỉ với một cú nhấp. Các bình luận được gom thành một review và gửi cùng một kết luận:

  • Comment — chỉ là góp ý, không có quyết định;
  • Approve — thay đổi được chấp thuận;
  • Request changes — cần chỉnh sửa thêm, chưa nên gộp.

Một review tốt xem xét logic, khả năng đọc hiểu, test và các trường hợp biên, không tranh luận về sở thích. Định dạng code nên giao cho linter trong CI. Tác giả nên trả lời từng bình luận: đã sửa, đã thảo luận, hoặc giữ nguyên kèm giải thích.

Cách kiểm tra PR của người khác trên máy (git checkout pull request)

Đôi khi bạn cần chạy code trên máy mình. Nếu nhánh nằm trong cùng repository, chỉ cần git fetch và git switch. Với PR từ fork, GitHub cung cấp tham chiếu đặc biệt pull/<số>/head:

git fetch origin pull/57/head:pr-57
git switch pr-57

Trên GitLab, tham chiếu tương ứng có tên merge-requests/<số>/head. Hoặc đơn giản dùng gh pr checkout 57 nếu đã cài GitHub CLI.

Cách cập nhật pull request sau khi có góp ý review

PR gắn với nhánh, không gắn với commit cụ thể. Chỉ cần tạo commit mới trên cùng nhánh rồi đẩy lên, PR sẽ tự cập nhật. Không cần tạo yêu cầu mới.

git commit -am "Chỉnh sửa theo góp ý review"
git push

Git rebase pull request: lấy main mới nhất

Trong lúc PR chờ review, main tiếp tục tiến lên. Nếu GitHub báo nhánh đã cũ hoặc có xung đột, hãy cập nhật nhánh. Cách rebase cho lịch sử thẳng hàng:

git fetch origin
git rebase origin/main
# giải quyết xung đột, git add, git rebase --continue
git push --force-with-lease

Sau rebase, hash của commit thay đổi, nên push thông thường sẽ bị từ chối và bạn cần push cưỡng bức. Dùng --force-with-lease: lệnh này từ chối ghi đè nhánh nếu có commit lạ được thêm vào. Nếu nhiều người cùng làm trên nhánh đó, an toàn hơn là gộp main bằng git merge origin/main, vì cách này không viết lại lịch sử.

Cách gộp pull request: merge, squash và rebase

Khi review đã được chấp thuận và kiểm tra đã xanh, PR có thể được gộp. GitHub đưa ra ba cách, và quản trị viên repository có thể chỉ cho phép một số trong đó.

CáchKết quả trên mainPhù hợp khi
Create a merge commitTất cả commit của nhánh cộng thêm một merge commitCần lịch sử đầy đủ và muốn thấy những commit nào đi cùng nhau
Squash and mergeMột commit chứa toàn bộ thay đổi của PRNhánh có nhiều commit nhỏ kiểu "sửa lỗi chính tả", và log của main cần sạch
Rebase and mergeCác commit của nhánh lần lượt, không có merge commitCommit gọn gàng, mỗi commit đều có ý nghĩa riêng

Merge pull request qua giao diện về bản chất giống git merge, chỉ là chạy trên server. Sau khi gộp, GitHub đề nghị xóa nhánh. Hãy đồng ý, vì nhánh đó không còn cần thiết. Dọn dẹp trên máy của bạn:

git switch main
git pull
git branch -d feature/login-form

Cũng có thể gộp PR thủ công từ terminal: gộp nhánh vào main cục bộ rồi push. GitHub sẽ nhận ra commit của nhánh đã nằm trong base và đánh dấu PR là merged. Tuy nhiên cách này bỏ qua kiểm tra và quy tắc bảo vệ nhánh, nên trong nhóm người ta thường gộp bằng nút.

Auto-merge và merge queue

Nếu kiểm tra chạy lâu, bạn có thể bật auto-merge: PR sẽ tự gộp khi mọi điều kiện đã đạt. Trong các repository lớn, người ta dùng merge queue, hàng đợi kiểm tra mỗi PR trên nền của các PR đã xếp trước, để main không bị hỏng vì các thay đổi xung đột với nhau.

Bản nháp pull request (Draft)

Bản nháp là PR chưa sẵn sàng để review. Không thể gộp nó, và người review không nhận yêu cầu kiểm tra. Bản nháp hữu ích để cho thấy hướng đi sớm, chạy CI, hoặc hỏi ý kiến về kiến trúc. Trên GitHub, khi tạo PR hãy chọn tùy chọn bản nháp trong menu của nút tạo, hoặc chạy gh pr create --draft. Khi công việc xong, nhấn "Ready for review". Trên GitLab, bản nháp được đánh dấu bằng tiền tố Draft: trong tiêu đề MR.

Liên kết pull request với issue

Nếu trong mô tả PR bạn viết từ khóa cùng số công việc, sau khi PR được gộp vào nhánh mặc định, GitHub sẽ tự đóng issue đó:

Closes #42
Fixes #17
Resolves #8

Các từ close, fix, resolve hoạt động ở nhiều dạng (closes, fixed, resolves). Chỉ nhắc #42 mà không có từ khóa thì sẽ tạo liên kết nhưng không đóng công việc. Trên GitLab, Closes #42 và các cụm tương tự cũng hoạt động.

Có xóa được pull request hay merge request không

Thành viên thông thường không thể xóa PR trên GitHub. Chỉ có thể đóng nó bằng nút "Close pull request". PR đã đóng vẫn còn trong lịch sử và có thể mở lại. Hãy xóa nhánh sau khi đóng một cách riêng. Muốn xóa hẳn PR chỉ có thể nhờ bộ phận hỗ trợ GitHub, ví dụ khi có bí mật bị đưa vào đó.

Trên GitLab, MR có thể được đóng bởi tác giả hoặc thành viên có quyền đủ, còn xóa thì do chủ dự án hoặc quản trị viên thực hiện. Nếu mật khẩu hay token lỡ nằm trong PR, chỉ đóng PR là chưa đủ: hãy thu hồi bí mật ngay, vì các commit đã từng nằm trên server.

Lỗi thường gặp với pull request

  • PR từ main. Bạn commit vào main cục bộ rồi mở PR từ đó. PR sau sẽ kéo theo cả thay đổi cũ. Luôn làm việc trên nhánh riêng.
  • Nhánh base sai. PR mở vào develop trong khi cần main, hoặc ngược lại. Có thể đổi base bằng nút Edit cạnh tiêu đề PR.
  • File thừa trong diff. Kết quả build, node_modules, .env. Thêm chúng vào .gitignore và gỡ khỏi index bằng git rm --cached.
  • PR quá lớn. Hãy chia công việc thành nhiều PR liên tiếp.
  • Push thường sau rebase. Gặp lỗi rejected (non-fast-forward). Hãy dùng git push --force-with-lease, không dùng --force.
  • Xung đột. Đừng xử lý mù quáng trên trình soạn thảo web. Hãy kéo main về máy, giải quyết xung đột và chạy test.

Pull request trong git flow và các quy trình khác

Pull request phù hợp với mọi quy trình làm việc nhóm. Trong GitHub Flow mọi thứ đơn giản: nhánh từ main, PR, review, gộp, triển khai. Trong git flow, PR được mở vào develop từ các nhánh tính năng, còn nhánh release và hotfix cũng đi qua PR vào main. Trong trunk-based development, PR ngắn và tồn tại vài giờ, không phải vài ngày. Dù quy trình là gì, nguyên tắc vẫn vậy: thay đổi vào nhánh chung chỉ đi qua pull request.

Kết luận

Pull request là cơ chế của nền tảng, không phải lệnh Git: nhánh được đẩy lên server cộng với trang để thảo luận, review và kiểm tra. Trên GitLab nó được gọi là merge request, còn trong trao đổi hằng ngày người ta nói PR hoặc MR. Hãy tạo PR từ nhánh riêng, giữ nó nhỏ gọn, cập nhật bằng push vào cùng nhánh, và chọn cách gộp phù hợp với lịch sử dự án. Nhánh tính năng, fork và pull request được trình bày thực hành trong khóa học Git.

Câu hỏi thường gặp

Cần bao nhiêu người review cho một pull request?

Thường một hoặc hai người là đủ. Số lượt chấp thuận tối thiểu được đặt trong quy tắc bảo vệ nhánh, và nếu chưa đủ, nút gộp sẽ không khả dụng.

Có thể tạo pull request mà không cần fork không?

Có, nếu bạn có quyền ghi vào repository: tạo nhánh ngay trong repository đó và mở PR từ nhánh ấy. Chỉ cần fork khi bạn không có quyền ghi.

Phải làm gì nếu PR lâu không ai xem?

Nhắc người review một cách lịch sự trong bình luận hoặc trên chat của nhóm. Ở các dự án mã nguồn mở, việc chờ có thể mất vài tuần. Hãy kiểm tra PR đã tuân thủ CONTRIBUTING.md chưa.

Có thể mở pull request giữa hai nhánh trong fork của chính mình không?

Có, trên trang so sánh hãy chọn fork của bạn làm cả base lẫn head. Cách này tiện để tự review thay đổi trước khi gửi sang dự án gốc.

Vì sao PR hiển thị các commit mà tôi không viết?

Rất có thể nhánh được tạo từ main chưa cập nhật, hoặc base được chọn sai. Hãy rebase lên main mới nhất hoặc đổi base trong cài đặt PR.

Học chủ đề này qua video

Chủ đề này được giảng chi tiết kèm thực hành trong khóa học “Git с нуля до уверенного разработчика”.

Đến khóa học