Chuyển đến nội dung chính

Git Stash Pop: Bảo toàn công việc khi chuyển nhánh

Tìm hiểu cách lưu công việc chưa commit khi chuyển nhánh, rồi khôi phục sau đó mà không mất dòng mã nào.
· 15 phút đọc

Khám phá cùng AI

Nếu bạn đang có dở dang một phần việc chưa sẵn sàng để commit nhưng cần đổi ngữ cảnh ngay bây giờ, đừng tạo commit "WIP". Cũng đừng copy-paste thay đổi của bạn vào một tệp văn bản như năm 2005. git stash là tất cả những gì bạn cần.

Lệnh này là cách quen thuộc để bảo toàn các thay đổi chưa commit trong khi bạn chuyển ngữ cảnh. Nó cho phép bạn lưu tạm thời các thay đổi, xử lý tác vụ khẩn trên nhánh khác, và khôi phục mọi thứ đúng vị trí bạn đang dang dở.

Trong bài viết này, tôi sẽ hướng dẫn bạn cách git stash pop hoạt động, khi nào nên dùng thay vì git stash apply, và cách khắc phục khi xung đột không thể tránh khỏi xuất hiện.

Nếu bạn mới dùng Git và thấy khái niệm stash còn mơ hồ, hãy đảm bảo nắm vững các kiến thức nền tảng trước. Các khóa học Introduction to Git và Introduction to GitHub của chúng tôi sẽ giúp bạn bắt đầu nhanh chóng.

Hiểu về Git Stash

Git stash là nơi lưu trữ tạm thời cho các thay đổi chưa commit.

Hãy nghĩ nó như clipboard cho công việc của bạn. Bạn có những thay đổi trong thư mục làm việc chưa sẵn sàng để commit, nhưng cần chuyển nhánh hoặc pull cập nhật từ remote. Thay vì làm mất công việc hoặc tạo commit WIP, bạn stash chúng lại.

Lệnh sẽ lưu các chỉnh sửa của bạn và đưa thư mục làm việc trở về khớp với commit HEAD.

Ý tưởng về stashing trong Git

Stashing giải quyết một vấn đề đơn giản: Git không cho phép bạn chuyển nhánh khi có thay đổi chưa commit.

Giả sử bạn đang làm một tính năng thì nhận được báo cáo lỗi. Bạn cần chuyển sang nhánh khác ngay, nhưng bạn có mã đang viết dở và các tệp đã chỉnh sửa. Commit thì không hợp lý vì công việc chưa hoàn thiện. Cưỡng bức checkout sẽ làm mất thay đổi của bạn.

Đó là lúc bạn stash.

Dưới đây là những tình huống phổ biến mà stashing có thể giúp bạn:

  1. Chuyển nhánh để xử lý việc khẩn: Bạn đang làm dở tính năng thì cần sửa lỗi production.
  2. Pull thay đổi từ remote: Đồng đội đã đẩy cập nhật và bạn cần pull, nhưng bạn có sửa đổi cục bộ.
  3. Tránh commit lộn xộn: Bạn chưa sẵn sàng commit, nhưng cần một thư mục làm việc sạch để rebase hoặc merge.
  4. Lưu mã thử nghiệm: Bạn muốn thử cách tiếp cận khác mà không làm mất công việc hiện tại

Stashing nhanh và có thể đảo ngược - không cần nghĩ thông điệp commit hay tên nhánh.

Kiến trúc stack của stash

Git quản lý các stash theo một ngăn xếp LIFO (vào sau, ra trước).

Mỗi lần bạn stash, Git tạo một mục mới ở đỉnh ngăn xếp. Stash gần nhất là stash@{0}, và các stash cũ dịch xuống: stash@{1}, stash@{2}, v.v.

Bên dưới, mỗi stash thực chất là một commit - chính xác hơn là một tập hợp commit. Git tạo cấu trúc commit đa-parent lưu trữ:

  1. Các thay đổi trong thư mục làm việc
  2. Các thay đổi đã stage (index)
  3. Các tệp chưa theo dõi (nếu bạn dùng cờ u)

Cấu trúc này có nghĩa stash không chỉ là snapshot. Chúng là các commit đầy đủ mà Git có thể merge trở lại vào thư mục làm việc của bạn sau này.

Bạn có thể tham chiếu stash theo hai cách - bằng chỉ số dùng cú pháp stash@{0}, hoặc dùng git stash list để tìm thông điệp bạn đã đặt và tham chiếu stash cụ thể đó. Chỉ số tiện cho stash gần đây, còn thông điệp hữu ích khi bạn có nhiều stash đã lưu.

Đây là ví dụ một ngăn xếp stash điển hình:

Hình 1 - Ví dụ đầu ra git stash list

Tiền tố WIP on nghĩa là bạn dùng git stash không kèm thông điệp. Tiền tố On nghĩa là bạn đã cung cấp thông điệp tùy chỉnh với git stash push -m.

Stash chỉ tồn tại trong kho cục bộ của bạn - chúng không được push lên remote. Điều này giữ cho công việc tạm thời ở đúng nơi nó thuộc về: trên máy của bạn, sẵn sàng khôi phục khi cần.

Tạo, Quản lý và Kiểm tra Stash

Lệnh git stash cơ bản là đủ để bắt đầu, nhưng khá "lười". Bạn sẽ có một đống mục "WIP on branch-name" mà không nói lên được gì bên trong. Sáu tuần sau, bạn sẽ không nhớ stash nào chứa bản vá lỗi và stash nào chứa phần refactor dở dang.

Giờ tôi sẽ chỉ bạn cách làm đúng ngay từ đầu.

Tạo stash với metadata phù hợp

Cách đơn giản nhất để stash là gõ git stash:

git stash

Lệnh này lưu thay đổi của bạn, nhưng thông điệp "WIP on main" không mấy thông tin. Bạn không thể biết bên trong có gì nếu không kiểm tra.

Thay vào đó, chạy git stash push -m kèm một thông điệp mô tả:

git stash push -m "Updated file.txt"

Giờ danh sách stash của bạn thực sự có ý nghĩa:

Hình 2 - Git stash có và không có thông điệp

Sự khác biệt là quá rõ. Một mục nói chính xác bạn đã làm gì, còn mục kia bắt bạn phải đoán.

Hãy viết thông điệp như đang để lại ghi chú cho chính mình trong tương lai. "Thêm logic auth", "Sửa lỗi navbar", hoặc "Thử nghiệm thiết kế API mới" luôn tốt hơn các thông điệp WIP chung chung.

Bao gồm tệp chưa theo dõi hoặc bị bỏ qua

Theo mặc định, git stash chỉ lưu các tệp đã được theo dõi - các tệp Git đã biết.

Các tệp chưa theo dõi vẫn ở trong thư mục làm việc. Điều này hay khiến mọi người bất ngờ khi stash, chuyển nhánh, và thấy các tệp mới vẫn còn đó.

Đây là điều xảy ra:

echo "new_feature.py" > new_feature.py
git stash

Tệp không được stash vì Git chưa theo dõi nó:

Hình 3 - Cố stash một tệp chưa theo dõi

Để xử lý, dùng cờ -u để stash cả tệp chưa theo dõi:

git stash push -u -m "Added a Python file for a new cool feature"

Lệnh này stash cả tệp đã theo dõi lẫn chưa theo dõi. Thư mục làm việc của bạn trở nên hoàn toàn sạch.

Hình 4 - Stash một tệp chưa theo dõi

Mẹo: Bạn có thể thêm cờ -a để bao gồm cả các tệp bị .gitignore bỏ qua. Nó sẽ stash các tệp đã theo dõi, chưa theo dõi, và các tệp trong .gitignore. Bạn hiếm khi cần điều này, trừ khi đang debug vấn đề build hoặc muốn lưu lại snapshot hoàn toàn sạch của mọi thứ.

Đây là một cheat sheet để bạn biết khi nào dùng từng cờ:

  1. Không có cờ: Bạn chỉ thay đổi các tệp đã tồn tại

  2. u flag: Bạn đã thêm tệp mới chưa được theo dõi

  3. a flag: Bạn cần lưu mọi thứ, bao gồm đầu ra build hoặc tệp tạm

Phần lớn thời gian, bạn sẽ dùng -u. Đây là lựa chọn an toàn nhất khi bạn tạo tệp mới trong quá trình phát triển.

Stash một phần hoặc có chọn lọc

Bạn không nhất thiết phải stash mọi thứ trong thư mục làm việc.

Giả sử bạn đã sửa hai lỗi, nhưng chỉ muốn stash một lỗi. Hoặc bạn có thay đổi ở năm tệp, nhưng chỉ ba tệp liên quan đến tính năng muốn lưu.

Bạn có thể stash các tệp cụ thể với pathspec:

git stash push -m "New files" new_file.txt new_file_2.txt

Lệnh này chỉ stash new_file.txt và new_file_2.txt. Mọi thứ khác vẫn ở thư mục làm việc.

Hình 5 - Chỉ stash các tệp được chọn

Bạn cũng có thể dùng wildcard:

git stash push -m "Stash all txt files" *.txt

Hoặc dùng --patch để stash tương tác:

git stash push --patch

Git duyệt qua từng thay đổi và hỏi bạn có muốn stash không. Bạn trả lời y (yes), n (no), hoặc s (tách hunk này thành phần nhỏ hơn).

Hữu ích khi bạn có thay đổi liên quan và không liên quan trộn trong cùng một tệp. Bạn stash phần công việc tính năng và để lại mã debug.

Đây là giao diện nhắc tương tác:

Hình 6 - Ví dụ patch

Liệt kê và kiểm tra stash

Như bạn đã thấy, bạn có thể chạy git stash list để xem mọi thứ đã stash:

Hình 7 - Liệt kê stash

Stash gần nhất luôn là stash@{0}. Các stash cũ dịch xuống chỉ số.

Nếu bạn đã viết thông điệp tốt, bạn có thể lướt danh sách và biết chính xác nội dung từng stash. Nếu không, bạn sẽ phải kiểm tra từng cái một.

Dùng git stash show để xem tóm tắt:

Hình 8 - Tóm tắt stash

Lệnh này hiển thị tệp nào thay đổi và bao nhiêu dòng được thêm hoặc xóa.

Ngoài ra, dùng git stash show -p để xem toàn bộ khác biệt:

Hình 9 - Toàn bộ khác biệt trong stash

Cờ -p hiển thị thay đổi mã thực tế, giống như git diff. Đây là cách bạn xác nhận mình sẽ pop đúng stash trước khi chạy git stash pop.

Thực chất Git Stash Pop làm gì

Giờ hãy nói về git stash pop thực sự làm gì và vì sao bạn nên dùng.

Hành vi cơ bản và cú pháp

Lệnh git stash pop làm hai việc: áp dụng các thay đổi đã stash và sau đó xóa stash đó khỏi ngăn xếp.

Cú pháp cơ bản:

git stash pop

Lệnh này pop stash gần nhất (stash@{0}) và xóa nó khỏi danh sách.

Bạn cũng có thể pop một stash cụ thể bằng cách tham chiếu chỉ số của nó:

git stash pop stash@{1}

Lệnh này áp dụng stash@{1} và xóa nó, nhưng có một điểm - các stash còn lại sẽ dịch lên. Cái trước là stash@{2} sẽ thành stash@{1}, v.v.

Hình 10 - Pop một stash cụ thể

Git xử lý một lần stash pop như thế nào

Khi bạn chạy git stash pop, Git thực hiện một lần merge ba chiều ở hậu trường.

Nó so sánh ba trạng thái:

  1. Commit gốc (nơi bạn tạo stash)
  2. Commit HEAD hiện tại
  3. Các thay đổi trong stash

Nếu merge thành công không có xung đột, Git áp dụng thay đổi và xóa stash. Thư mục làm việc của bạn trông đúng như trước khi stash.

Đây là ví dụ quy trình làm việc đơn giản:

# You're working on a feature
echo "new feature code" >> feature.py
git stash push -m "Half-finished feature work"

# Switch to new branch and make unrelated changes
git checkout bugfix
echo "bug fix" >> bugfix.py
git add bugfix.py
git commit -m "Fix critical bug"

# Go back to your other branch
git checkout main

# Pop your stashed work
git stash pop

Nếu Git không thể merge sạch, bạn sẽ gặp xung đột. Stash vẫn ở trong ngăn xếp vì pop chưa hoàn tất thành công. Bạn sẽ thấy ký hiệu xung đột trong các tệp, giống như khi merge conflict thông thường.

Khi có xung đột, bạn cần tự xử lý, stage các tệp đã sửa và commit. Stash không tự biến mất, nên bạn cần chạy git stash drop sau khi đã giải quyết xong.

Áp dụng stash giữa các nhánh

Stash không gắn với nhánh cụ thể nào.

Bạn có thể tạo stash trên bugfix, chuyển sang main, và pop stash đó tại đây. Git không quan tâm stash đến từ đâu - nó chỉ cố áp dụng thay đổi vào nhánh bạn đang đứng.

Điều này mở ra các quy trình hữu ích:

  1. Di chuyển công việc giữa các nhánh: Giả sử bạn bắt đầu code trên nhánh sai. Thay vì reset và mất việc, bạn stash lại, chuyển sang nhánh đúng, và pop tại đó.

  2. Tái tổ chức tác vụ: Bạn đã stash mã thử nghiệm một tuần trước. Giờ muốn thử nó trên nhánh khác xem có hợp hơn không.

  3. Kiểm thử thay đổi trong các ngữ cảnh khác nhau: Bạn có một bản vá lỗi đã stash, và muốn thử trên nhiều nhánh trước khi commit. Hãy dùng git stash apply thay vì pop để giữ stash lại, thử trên các nhánh khác nhau, rồi pop ở nhánh bạn muốn commit. Sẽ nói thêm về apply ngay sau.

Miễn là các thay đổi không xung đột với nhánh hiện tại, stash sẽ áp dụng sạch - đó là ý chính bạn cần nhớ.

Git Stash Pop so với Git Stash Apply

Các lập trình viên thường băn khoăn nên dùng git stash pop hay git stash apply.

Khác biệt chính: tính tồn tại của stash

Khác biệt chủ yếu rất đơn giản: pop xóa stash sau khi áp dụng, còn apply giữ stash lại trong ngăn xếp.

Với pop, bạn nói "Tôi xong với stash này - áp dụng và bỏ đi." Với apply, bạn nói "Có thể tôi sẽ cần lại sau."

Điều này ảnh hưởng đến quy trình của bạn theo hai cách:

  1. An toàn: Nếu có sự cố trong quá trình pop và bạn cần khôi phục, stash đã biến mất. Với apply, stash vẫn ở trong ngăn xếp như một bản dự phòng.

  2. Dọn dẹp: Dùng pop giúp danh sách stash sạch tự động. Dùng apply nghĩa là bạn phải tự chạy git stash drop sau đó để xóa các stash không còn cần.

Đa số lập trình viên mặc định dùng pop vì sạch sẽ hơn. Nhưng apply tốt hơn khi bạn chưa chắc stash sẽ hoạt động sạch trên nhánh hiện tại, hoặc khi muốn thử cùng thay đổi trên nhiều nhánh.

Tính dự đoán, rủi ro, và dịch chuyển chỉ số

Đây là chỗ pop trở nên rắc rối - chỉ số stash dịch chuyển khi bạn pop từ giữa ngăn xếp.

Giả sử bạn có ba stash:

stash@{0}: Feature work
stash@{1}: Bug fix
stash@{2}: Experimental code

Nếu bạn pop stash@{1}, các stash còn lại dịch lên:

stash@{0}: Feature work
stash@{1}: Experimental code

Điều này có thể gây vấn đề nếu bạn viết script Git hoặc chạy nhiều lệnh pop liên tiếp. Bạn pop stash@{1}, rồi thử pop stash@{2}, nhưng chỉ số đó không còn tồn tại.

Lệnh apply không gặp vấn đề này. Chỉ số chỉ dịch chuyển khi bạn rõ ràng chạy git stash drop. Bạn có thể apply các stash theo bất kỳ thứ tự nào mà không lo ngăn xếp thay đổi ngoài ý muốn.

Xử lý xung đột giống hệt nhau

Cả hai lệnh xử lý xung đột theo cách giống hệt nhau.

Nếu Git không thể merge sạch các thay đổi trong stash, bạn sẽ thấy ký hiệu xung đột trong tệp. Khác biệt là điều gì xảy ra với stash:

  1. Với pop: Stash vẫn ở trong ngăn xếp vì pop chưa hoàn tất

  2. Với apply: Stash vẫn ở trong ngăn xếp (vốn dĩ là như vậy)

Trong cả hai trường hợp, bạn cần tự sửa xung đột, stage các tệp đã giải quyết bằng git add, và xóa stash bằng git stash drop (nếu dùng apply).

Quy trình xử lý xung đột là giống nhau - điểm khác biệt duy nhất là stash có được xóa tự động khi thành công hay không.

Khi nào chọn mỗi lệnh

Dùng git stash pop khi:

  1. Bạn áp dụng một stash một lần và xong với nó
  2. Bạn muốn danh sách stash được dọn tự động
  3. Bạn làm việc trên một nhánh và không cần stash ở nơi khác

Dùng git stash apply khi:

  1. Bạn muốn thử cùng thay đổi trên nhiều nhánh
  2. Bạn chưa chắc stash sẽ áp dụng sạch
  3. Bạn đang viết script Git và cần chỉ số ổn định, dự đoán được
  4. Bạn muốn có lưới an toàn nếu có sự cố

Xử lý Xung đột khi Stash Pop

Xung đột xảy ra khi Git không thể merge sạch các thay đổi trong stash. Phần này, tôi sẽ chỉ bạn cách xử lý.

Khi nào có xung đột và vì sao

Xung đột xảy ra trong quá trình merge ba chiều mà Git thực hiện khi bạn pop một stash.

Git so sánh ba phiên bản mã của bạn:

  1. Commit gốc nơi bạn tạo stash
  2. Commit HEAD hiện tại
  3. Các thay đổi trong stash

Nếu cùng dòng bị thay đổi ở nhánh hiện tại và trong stash, Git không biết giữ phiên bản nào. Đó là xung đột.

Nguyên nhân phổ biến của xung đột khi pop stash:

  1. Bạn chỉnh sửa cùng một tệp ở cả hai nhánh: Bạn stash thay đổi vào một tệp, chuyển nhánh, rồi lại chỉnh tệp đó trước khi pop
  2. Người khác đã push thay đổi: Bạn stash công việc, pull từ remote, và các cập nhật đó đụng vào cùng đoạn mã
  3. Bạn rebase hoặc cherry-pick commit: Commit gốc mà Git kỳ vọng không còn nữa, dẫn tới lệch merge

Nói chung, stash để càng lâu thì khả năng gặp xung đột khi pop càng cao.

Quy trình xử lý xung đột

Khi xung đột xảy ra, Git tạm dừng pop và đánh dấu xung đột trong tệp:

Tệp của bạn giờ có các ký hiệu xung đột:

Mọi thứ giữa <<<<<<< Updated upstream và ======= là mã hiện tại của bạn. Mọi thứ giữa ======= và >>>>>>> Stashed changes là từ stash.

Bạn có thể làm như sau để sửa:

  1. Mở tệp bị xung đột và quyết định giữ phiên bản nào (hoặc kết hợp cả hai)

  2. Xóa các ký hiệu xung đột (<<<<<<<, =======, >>>>>>>)

  3. Stage tệp đã giải quyết bằng git add

  4. Xóa stash thủ công bằng git stash drop - nhớ rằng nó sẽ không biến mất tự động.

Chiến lược thay thế

Nếu bạn không muốn xử lý xung đột ở nhánh hiện tại, hãy dùng git stash branch.

Lệnh này tạo một nhánh mới từ commit nơi bạn tạo stash, rồi áp dụng stash tại đó:

git stash branch temp-feature

Git tạo nhánh tên temp-feature, checkout nó, và pop stash của bạn lên đó. Vì nhánh bắt đầu từ đúng commit nơi bạn stash, sẽ không có xung đột. Tôi sẽ bàn kỹ hơn ở phần riêng.

Bạn cũng có thể hủy một lần pop thất bại với git reset --merge:

git stash pop

# Conflicts appear

git reset --merge

Lệnh này hoàn tác nỗ lực pop và đưa thư mục làm việc về trạng thái trước khi pop. Stash vẫn ở trong ngăn xếp.

Hủy bỏ các thao tác stash đang diễn ra

Nếu bạn đã bắt đầu pop hoặc apply và muốn quay lại hoàn toàn, bạn có vài lựa chọn.

Với xung đột bạn chưa bắt đầu sửa, chạy lệnh sau:

git reset --merge

Lệnh này hủy merge và dọn dẹp thư mục làm việc. Mọi thứ trở về như trước khi bạn chạy lệnh stash.

Với xung đột bạn đã sửa một phần, chạy lệnh này:

git reset --hard HEAD

Lệnh này loại bỏ toàn bộ thay đổi trong thư mục làm việc, gồm cả các sửa bạn đã làm. Dùng khi bạn đã làm rối và muốn bắt đầu lại.

Hãy nhớ lệnh này sẽ hủy vĩnh viễn công việc chưa commit. Đảm bảo bạn thực sự muốn bỏ hết trước khi chạy.

Sau khi hủy, stash của bạn vẫn ở trong ngăn xếp (trừ khi bạn dùng pop và nó đã thành công trước khi gặp xung đột). Bạn có thể thử lại sau, hoặc dùng git stash branch để tiếp cận sạch hơn.

Dọn Dẹp Stash

Stash tích tụ rất nhanh trong các dự án dài hạn. Bạn stash công việc để sửa lỗi, rồi quên nó. Bạn tạo các stash thử nghiệm mà không bao giờ pop. Sáu tháng sau, bạn có 20 stash và không biết nửa trong số đó chứa gì.

Điều này khiến việc tìm stash bạn cần trở nên khó hơn. Bạn sẽ phải chạy git stash show -p cho từng cái để biết bên trong có gì.

Rất tốn thời gian, nên hãy nhớ dọn dẹp stash thường xuyên.

Xóa từng stash riêng lẻ

Dùng git stash drop để xóa một stash cụ thể:

git stash drop stash@{2}

Lệnh này xóa stash@{2} khỏi ngăn xếp. Các stash còn lại dịch lên - cái trước là stash@{3} sẽ thành stash@{2}.

Bạn cũng có thể xóa stash gần nhất mà không cần chỉ số:

git stash drop

Lệnh này mặc định xóa stash@{0}.

Theo nhiều năm làm việc với Git, những lý do hàng đầu khiến tôi xóa stash là khi:

  1. Bạn nhận ra không còn cần các thay đổi nữa
  2. Bạn có các stash trùng lặp (xảy ra thường xuyên hơn bạn nghĩ)
  3. Một stash quá cũ đến mức bạn không nhớ nó dùng để làm gì

Nếu chưa chắc có nên xóa stash, hãy dùng git stash show -p để kiểm tra trước. Cẩn tắc vô ưu.

Xóa toàn bộ ngăn xếp stash

Bạn có thể dùng git stash clear để xóa tất cả stash cùng lúc:

git stash clear

Hãy nhớ rằng thao tác này là vĩnh viễn. Bạn không thể hoàn tác hoặc khôi phục stash sau đó.

Chỉ dùng git stash clear khi bạn hoàn toàn chắc chắn không cần bất kỳ công việc nào đã stash.

Phần lớn thời gian, xóa từng stash riêng lẻ là tốt hơn để tránh xóa nhầm thứ bạn cần.

Tạo Nhánh từ Stash

Đôi khi giải pháp sạch nhất là biến một stash thành nhánh riêng. Điều này tránh xung đột merge và cho công việc đã stash một chỗ ở đúng nghĩa.

Dùng git stash branch

Lệnh git stash branch tạo nhánh mới và áp dụng stash của bạn lên đó:

Diễn ra như sau:

  1. Git tạo nhánh mới tên feature-experiment từ commit nơi bạn tạo stash

  2. Git checkout nhánh mới đó

  3. Git pop stash lên nhánh

  4. Git xóa stash khỏi ngăn xếp (giống như pop)

Vì nhánh bắt đầu từ đúng commit với stash, sẽ không có xung đột. Các thay đổi luôn áp dụng sạch.

Bạn cũng có thể dùng một stash cụ thể thay vì cái gần nhất:

git stash branch new-feature stash@{2}

Các trường hợp sử dụng phổ biến

Tạo nhánh từ stash rất phù hợp cho các tính năng thử nghiệm. Giả sử bạn đã stash một số mã thử nghiệm cách đây một tuần, và giờ muốn phát triển nó đàng hoàng mà không làm rối công việc hiện tại.

Bạn có một nhánh sạch để làm việc với thử nghiệm. Nếu hiệu quả, bạn merge lại. Nếu không, bạn xóa nhánh và tiếp tục.

Nó cũng tuyệt cho các lần rebase dài. Thay vì xử lý khả năng xung đột trong quá trình rebase, bạn có thể stash mọi thứ và tạo nhánh từ đó.

git stash push -m "WIP before rebase"
git rebase main
git stash branch temp-work

Giờ bạn có thể xem lại các thay đổi đã stash riêng biệt và quyết định cách tích hợp chúng sau khi rebase.

Tạo nhánh cũng hữu ích để tách biệt các thay đổi lớn hoặc lộn xộn. Hãy tưởng tượng bạn có một stash khổng lồ với thay đổi ở 15 tệp, một nửa xung đột với nhánh hiện tại. Chỉ cần tạo nhánh mới - không cần vật lộn.

Bạn có một nhánh riêng để xem lại mọi thứ một cách bình tĩnh, commit đúng chuẩn, rồi merge lại với lịch sử rõ ràng.

Kỹ thuật Nâng cao và Phương án Khôi phục

Đến giờ bạn đã quen với những điều cơ bản của git stash, nên tôi sẽ chỉ bạn vài khái niệm nâng cao hơn.

Làm việc với các mục stash cụ thể

Bạn có thể pop hoặc apply bất kỳ stash nào trong ngăn xếp, không chỉ cái gần nhất.

Chỉ cần tham chiếu stash bằng chỉ số để lấy đúng thứ bạn cần:

git stash pop stash@{3}
git stash apply stash@{1}

Điều này cho phép bạn lấy stash cũ hơn mà không đụng tới những cái mới. Khi bạn pop một stash ở giữa, mọi chỉ số phía dưới sẽ dịch lên. Cái trước là stash@{4} sẽ thành stash@{3}, v.v. Nếu bạn đang viết script Git hoặc pop nhiều stash liên tiếp, việc dịch chỉ số này có thể làm hỏng lệnh của bạn.

Giải pháp là dùng apply thay vì pop khi bạn cần chỉ số dự đoán được:

git stash apply stash@{2}

# Drop it manually when you're done
git stash drop stash@{2}

Cách này cho bạn toàn quyền kiểm soát khi nào ngăn xếp thay đổi. Bạn apply stash, xác nhận mọi thứ hoạt động đúng, rồi tự tay drop. Chỉ số chỉ dịch khi bạn yêu cầu rõ ràng.

Khôi phục trạng thái staged

Theo mặc định, git stash pop và git stash apply khôi phục thay đổi của bạn nhưng không giữ lại những gì đã stage.

Giả sử bạn sửa ba tệp, và đã stage hai tệp để commit trước khi stash. Khi pop stash đó, cả ba tệp quay lại dưới dạng thay đổi chưa stage. Git không còn biết bạn muốn commit tệp nào.

Giải pháp là dùng cờ --index để khôi phục trạng thái staging đúng như trước:

git stash pop --index

Lệnh này tái tạo chính xác working tree và index trước khi stash. Tệp nào đã stage sẽ vẫn stage. Tệp nào chưa stage vẫn chưa stage. Mọi thứ về đúng chỗ.

Bạn sẽ cần cờ này khi:

  1. Bạn đang chuẩn bị một commit được stage cẩn thận và không thể để mất thông tin staging đó

  2. Bạn làm việc với kịch bản staging phức tạp, như khi dùng git add -p để stage chỉ một số hunk

  3. Bạn muốn thư mục làm việc trông đúng như trước khi stash, không ngoại lệ

Phần lớn thời gian, bạn sẽ không cần cờ này. Bạn pop stash, xem những gì trở lại, và stage những gì cần.

Khôi phục khi mắc lỗi với stash

Rồi sẽ có lúc bạn drop nhầm một stash cần thiết hoặc lỡ clear cả ngăn xếp.

Nếu điều đó xảy ra, reflog của git có thể cứu bạn.

Reflog theo dõi mọi thay đổi tham chiếu trong kho của bạn, bao gồm stash. Khi bạn tạo hoặc drop một stash, Git ghi lại trong reflog. Ngay cả sau khi drop, các commit đó vẫn tồn tại một thời gian như đối tượng không thể truy cập.

Bạn có thể tìm stash đã mất bằng cách tìm commit không thể truy cập:

git fsck --unreachable | grep commit

Tìm commit không thể truy cập

Giờ bạn có thể kiểm tra từng commit để tìm stash của mình:

git show 066ded985342e585bc8e75c074add99ec1950f50

Xem diff và thông điệp commit để xác định đâu là stash đã mất. Khi tìm thấy, hãy khôi phục nó:

git stash apply 066ded985342e585bc8e75c074add99ec1950f50

Lệnh này áp dụng stash đã mất vào thư mục làm việc như thể nó chưa từng bị drop.

Tốt hơn nữa, hãy ngăn lỗi ngay từ đầu bằng cách làm theo các thực hành tốt sau:

  1. Viết thông điệp stash có mô tả để bạn biết chính xác mình đang drop gì. "Sửa lỗi đăng nhập" tốt hơn "WIP" khi bạn nhìn vào danh sách năm stash.

  2. Dùng git stash show -p trước khi drop để xác nhận đó là stash đúng. Mất năm giây để kiểm tra còn hơn năm phút để khôi phục.

  3. Dọn ngăn xếp stash thường xuyên để nó không bừa bộn. Ngăn xếp với ba stash liên quan dễ điều hướng hơn nhiều so với 15 mục cũ.

  4. Dùng apply thay vì pop khi bạn chưa 100% chắc muốn xóa stash. Bạn luôn có thể drop sau khi đã xác minh mọi thứ hoạt động.

Reflog có thể giúp khôi phục, nhưng khá mất công. Chỉ cần nhìn vào hình trên để thấy tôi sẽ phải kiểm tra thủ công bao nhiêu commit - và đây chỉ là một dự án nhỏ tạo ra cho bài viết này.

Thực hành Tốt và Hướng dẫn Quy trình

Dùng stash hiệu quả nghĩa là làm theo các mô thức giúp ngăn lỗi và giữ quy trình của bạn sạch sẽ. Tôi sẽ chỉ bạn cách tiếp cận ưa thích của mình.

Mô thức quy trình được khuyến nghị

Quy trình stash an toàn nhất theo mẫu đơn giản: stash kèm thông điệp, chuyển ngữ cảnh, rồi pop khi bạn sẵn sàng tiếp tục.

# Work on feature-branch
git stash push -m "Feature work in progress"

git checkout main
# Do urgent work on main

# Go back
git checkout feature-branch
git stash pop

Cách này phù hợp cho hầu hết tình huống. Bạn bảo toàn công việc, xử lý gián đoạn, và khôi phục mọi thứ sạch sẽ.

Một mô thức phổ biến khác là quy trình "kiểm tra trước khi pop". Bạn liệt kê các stash, xem bên trong có gì, rồi pop cái phù hợp.

git stash list
git stash show -p stash@{1}
git stash pop stash@{1}

Bước xác minh bổ sung này ngăn bạn pop nhầm stash hoặc áp dụng công việc đã lỗi thời.

Một số lập trình viên thích lựa chọn "commit miễn cưỡng" thay cho stashing. Thay vì stash, họ tạo commit tạm với thông điệp như "WIP - not ready" rồi amend hoặc reset sau. Cách này hữu ích nếu bạn không thoải mái với stash hoặc muốn công việc được theo dõi trong lịch sử nhánh. Điểm trừ là bạn có thể để lại lịch sử commit lộn xộn nếu quên dọn.

Tích hợp với công cụ phát triển

Hầu hết GUI Git hiển thị stash trực quan và cho phép pop hoặc apply chỉ bằng một cú nhấp.

Các công cụ như GitKraken, Sourcetree, và GitHub Desktop liệt kê stash trong thanh bên. Bạn có thể xem thông điệp, xem diff, và apply hoặc drop stash mà không cần gõ lệnh. Điều này hữu ích khi bạn có nhiều stash và muốn so sánh chúng cạnh nhau.

Phần mở rộng Git của VS Code hiển thị stash trong bảng điều khiển source control. Bạn có thể nhấp phải để pop, apply, hoặc drop ngay trong trình soạn thảo.

Alias shell giúp stash nhanh hơn nếu bạn thích dòng lệnh. Thêm các lệnh này vào .bashrc hoặc .zshrc:

alias gst='git stash'
alias gstp='git stash pop'
alias gstl='git stash list'
alias gsts='git stash show -p'

Giờ đây gst sẽ stash công việc của bạn, gstp pop nó, gstl liệt kê các stash, và gsts hiển thị diff. Bạn tiết kiệm được vài lần gõ mỗi lần.

Vệ sinh stash và cân nhắc cho nhóm

Stash là cục bộ trên máy bạn - chúng không bao giờ được push lên kho từ xa.

Điều này có nghĩa đồng đội không thể thấy stash của bạn, và bạn cũng không thể truy cập chúng từ máy khác. Nếu cần chia sẻ công việc đang làm, đừng stash. Hãy dùng patch hoặc nhánh tạm.

Để chia sẻ công việc qua patch, chạy:

git stash show -p > my-work.patch
# Send the patch file to your teammate
# They apply it with: git apply my-work.patch

Giờ hãy gửi tệp patch cho đồng đội, và họ sẽ áp dụng bằng cách chạy git apply my-work.patch.

Để chia sẻ công việc qua nhánh tạm, chạy:

git checkout -b temp/work-in-progress
git add .
git commit -m "WIP - authentication feature"
git push origin temp/work-in-progress

Đồng đội có thể pull nhánh, xem xét công việc, và tiếp tục từ đó.

Dù bạn chọn cách nào làm mặc định, luôn duy trì vệ sinh stash tốt. Điều này sẽ tránh nhầm lẫn và giúp ngăn xếp cục bộ dễ quản lý. Dưới đây là vài thực hành tốt:

  1. Dùng tên mô tả cho mỗi stash. "Thêm logic xác thực người dùng" cho biết bên trong có gì. "WIP on feature-branch" thì không.

  2. Dọn stash hàng tuần. Chạy git stash list và drop mọi thứ cũ hơn một tuần. Nếu bảy ngày rồi bạn chưa cần, có lẽ sẽ không cần nữa.

  3. Đừng để số lượng stash vượt quá năm. Nhiều hơn năm stash nghĩa là bạn đang dùng chúng như kho lưu trữ dài hạn thay vì tạm thời. Hãy pop, commit công việc, hoặc drop.

  4. Nếu bạn làm việc theo nhóm và ai đó hỏi, "Đoạn mã bạn đang làm đâu?", đừng bao giờ trả lời, "Trong stash của tôi." Stash là riêng tư và tạm thời - không dùng để chia sẻ hay lưu trữ công việc quan trọng.

Kết luận

Giờ bạn đã biết git stash pop hoạt động thế nào và khi nào nên dùng.

Cốt lõi, stashing rất đơn giản: lưu công việc chưa commit, chuyển ngữ cảnh, và khôi phục sau. Nhưng chi tiết mới quan trọng. Biết khi nào dùng pop so với apply, cách xử lý xung đột, và khi nào tạo nhánh từ stash sẽ giúp bạn tiết kiệm hàng giờ bực bội.

Điều quan trọng nhất cần nhớ là stash là lưu trữ tạm thời, không phải sao lưu vĩnh viễn. Hãy dùng thông điệp mô tả, dọn dẹp thường xuyên, và đừng để ngăn xếp vượt quá năm mục. Nếu cần chia sẻ công việc hoặc giữ lâu dài, hãy commit lên một nhánh.

Nếu bạn thấy hữu ích và muốn giỏi Git hơn, điểm dừng tiếp theo nên là các khóa Intermediate Git và Intermediate GitHub Concepts của chúng tôi.

Dario Radečić

Tác giả

Dario Radečić

Chuyên gia Khoa học Dữ liệu Cấp cao, làm việc tại Croatia. Tác giả Công nghệ Hàng đầu với hơn 700 bài viết đã xuất bản, đạt trên 10 triệu lượt xem. Tác giả cuốn sách Tự động hóa Machine Learning với TPOT.

FAQs

Git stash pop là gì và khi nào nên dùng?

git stash pop áp dụng các thay đổi đã stash gần nhất vào thư mục làm việc của bạn và xóa stash đó khỏi ngăn xếp. Dùng khi bạn đã tạm lưu công việc chưa commit để chuyển nhánh hoặc pull cập nhật, và giờ sẵn sàng khôi phục chúng. Rất phù hợp để xử lý gián đoạn mà không cần tạo commit "WIP" lộn xộn.

Sự khác nhau giữa git stash pop và git stash apply là gì?

git stash pop áp dụng các thay đổi trong stash và xóa stash khỏi ngăn xếp, trong khi git stash apply giữ lại stash sau khi áp dụng. Dùng pop khi bạn đã xong với stash và muốn giữ ngăn xếp gọn gàng. Dùng apply khi bạn muốn thử cùng thay đổi trên nhiều nhánh hoặc giữ stash làm bản dự phòng.

Làm thế nào xử lý xung đột merge khi dùng git stash pop?

Khi xảy ra xung đột trong lúc pop stash, Git đánh dấu các tệp xung đột bằng ký hiệu xung đột và tạm dừng thao tác. Mở các tệp xung đột, tự xử lý bằng cách chọn phần cần giữ, rồi stage tệp đã sửa bằng git add. Stash vẫn ở trong ngăn xếp sau khi xung đột xảy ra, nên bạn cần drop thủ công bằng git stash drop khi hoàn tất.

Tôi có thể khôi phục một stash đã lỡ xóa không?

Có, bạn có thể khôi phục các stash đã xóa bằng reflog của Git. Chạy git fsck --unreachable | grep commit để tìm commit không thể truy cập, sau đó dùng git show với từng hash để xác định stash đã mất. Khi tìm thấy, chạy git stash apply <commit-hash> để khôi phục.

Tôi nên dùng git stash hay tạo commit tạm thời?

Dùng git stash cho các lần chuyển ngữ cảnh nhanh khi bạn sẽ sớm quay lại công việc, như sửa lỗi khẩn trên nhánh khác. Tạo commit tạm nếu bạn muốn công việc được theo dõi trong lịch sử nhánh hoặc nếu bạn không thoải mái với stash. Stash là cục bộ và tạm thời, còn commit là vĩnh viễn và có thể push lên remote.

Chủ đề

Liên quan