Như một chút giải thích bổ sung, hãy lưu ý rằng git stash
thực hiện hai cam kết hoặc ba cam kết. Mặc định là hai; bạn nhận được ba nếu bạn sử dụng bất kỳ chính tả nào của --all
hoặc --include-untracked
các tùy chọn.
Hai hoặc ba cam kết này đặc biệt theo một cách quan trọng: chúng không có chi nhánh. Git định vị chúng thông qua tên đặc biệt stash
. 1 Tuy nhiên, điều quan trọng nhất là Git cho phép bạn — và khiến bạn — thực hiện với hai hoặc ba cam kết này. Để hiểu điều này, chúng ta cần xem xét những gì trong những cam kết đó.
Có gì bên trong kho
Mỗi cam kết có thể liệt kê một hoặc nhiều cam kết mẹ . Chúng tạo thành một biểu đồ, nơi các cam kết sau đó sẽ trỏ lại các biểu đồ trước đó. Kho lưu trữ thường chứa hai cam kết, mà tôi muốn gọi i
cho nội dung chỉ mục / khu vực dàn và w
cho nội dung cây công việc. Ngoài ra, hãy nhớ rằng mỗi cam kết giữ một ảnh chụp nhanh. Trong một cam kết bình thường, ảnh chụp nhanh này được tạo từ nội dung chỉ mục / khu vực dàn dựng. Vì vậy, i
commit trên thực tế là một commit hoàn toàn bình thường! Nó chỉ không nằm trên bất kỳ chi nhánh nào:
...--o--o--o <-- branch (HEAD)
|
i
Nếu bạn đang thực hiện một kho lưu trữ thông thường, git stash
mã tạo ra w
ngay bây giờ bằng cách sao chép tất cả các tệp cây công việc được theo dõi của bạn (vào một chỉ mục bổ trợ tạm thời). Git đặt cha mẹ đầu tiên của w
cam kết này để trỏ đến HEAD
cam kết và cha mẹ thứ hai để trỏ đến cam kết i
. Cuối cùng, nó thiết lập stash
để trỏ đến w
cam kết này :
...--o--o--o <-- branch (HEAD)
|\
i-w <-- stash
Nếu bạn thêm --include-untracked
hoặc --all
, Git sẽ thực hiện thêm một cam kết, u
giữa việc thực hiện i
và w
. Nội dung ảnh chụp nhanh u
là những tệp chưa được theo dõi nhưng không bị bỏ qua ( --include-untracked
) hoặc các tệp chưa được theo dõi ngay cả khi chúng bị bỏ qua ( --all
). Cam u
kết bổ sung này không có cha mẹ và sau đó khi được git stash
tạo w
, nó đặt cha mẹ thứ baw
của cam kết này , để bạn nhận được:u
...--o--o--o <-- branch (HEAD)
|\
i-w <-- stash
/
u
Tại thời điểm này, Git cũng xóa mọi tệp cây công việc nằm trong u
cam kết (sử dụng git clean
để làm điều đó).
Khôi phục kho lưu trữ
Khi bạn khôi phục kho lưu trữ, bạn có tùy chọn sử dụng --index
hoặc không sử dụng. Điều này cho git stash apply
(hoặc bất kỳ các lệnh nội bộ sử dụng apply
, chẳng hạn như pop
) rằng nó nên sử dụng các i
cam kết cố gắng để thay đổi chỉ số hiện tại của bạn. Việc sửa đổi này được thực hiện với:
git diff <hash-of-i> <hash-of-i's-parent> | git apply --index
(dù ít hay nhiều; có rất nhiều chi tiết tiêu cực cản trở ý tưởng cơ bản ở đây).
Nếu bạn bỏ qua --index
, git stash apply
hoàn toàn bỏ qua i
cam kết.
Nếu kho chỉ có hai cam kết, git stash apply
bây giờ có thể áp dụng w
cam kết. Nó thực hiện điều này bằng cách gọi git merge
2 (mà không cho phép nó cam kết hoặc coi kết quả như một hợp nhất thông thường), sử dụng cam kết ban đầu mà kho lưu trữ được tạo ( i
cha mẹ và w
cha mẹ đầu tiên của nó) làm cơ sở hợp nhất, w
như --theirs
cam kết và cam kết hiện tại (HEAD) của bạn là mục tiêu của hợp nhất. Nếu hợp nhất thành công, tất cả đều tốt - tốt, ít nhất Git cũng nghĩ như vậy - và git stash apply
bản thân nó thành công. Nếu bạn đã từng git stash pop
áp dụng kho lưu trữ, thì mã hiện tại sẽ giảm mức lưu trữ. 3 Nếu hợp nhất không thành công, Git tuyên bố áp dụng không thành công. Nếu bạn đã sử dụnggit stash pop
, mã vẫn lưu trữ và cung cấp tình trạng lỗi tương tự như đối với git stash apply
.
Nhưng nếu bạn có cam kết thứ ba — nếu có một u
cam kết trong kho mà bạn đang áp dụng — thì mọi thứ sẽ thay đổi! Không có tùy chọn nào để giả vờ rằng u
cam kết không tồn tại. 4 Git nhấn mạnh vào việc giải nén tất cả các tệp từu
cam kết đó vào cây công việc hiện tại. Điều này có nghĩa là các tệp không được tồn tại hoặc có cùng nội dung như trong bản u
cam kết.
Để điều đó xảy ra, bạn có thể sử dụng git clean
chính mình — nhưng hãy nhớ rằng các tệp không được theo dõi (bị bỏ qua hoặc không) không có sự tồn tại nào khác bên trong kho lưu trữ Git, vì vậy hãy đảm bảo rằng tất cả các tệp này đều có thể bị hủy! Hoặc, bạn có thể tạo một thư mục tạm thời và di chuyển các tệp đến đó để lưu giữ an toàn — hoặc thậm chí làm một thư mục khác git stash save -u
hoặc git stash save -a
vì những tệp đó sẽ chạy git clean
cho bạn. Nhưng điều đó chỉ để lại cho bạn một u
kho lưu trữ kiểu khác để giải quyết sau này.
1 Đây là trên thực tế refs/stash
. Điều này quan trọng nếu bạn đặt tên cho chi nhánh stash
: tên đầy đủ của chi nhánh là refs/heads/stash
, vì vậy chúng không mâu thuẫn. Nhưng đừng làm vậy: Git sẽ không bận tâm, nhưng bạn sẽ tự nhầm lẫn. :-)
2 Các git stash
mã thực sự sử dụng git merge-recursive
trực tiếp tại đây. Điều này là cần thiết vì nhiều lý do và cũng có tác dụng phụ là đảm bảo Git không coi nó như một hợp nhất khi bạn giải quyết xung đột và cam kết.
3 Đây là lý do tại sao tôi khuyên bạn nên tránh git stash pop
, ủng hộ git stash apply
. Bạn có cơ hội xem lại những gì đã được áp dụng và quyết định xem nó có thực sự được áp dụng đúng hay không. Nếu không, bạn vẫn có kho của mình , nghĩa là bạn có thể sử dụng git stash branch
để khôi phục mọi thứ một cách hoàn hảo. Vâng, giả sử thiếu u
cam kết khó chịu đó .
4 Thực sự nên có: git stash apply --skip-untracked
hoặc một cái gì đó. Cũng nên có một biến thể có nghĩa là thả tất cả các u
tệp cam kết đó vào một thư mục mới , ví dụ git stash apply --untracked-into <dir>
, có lẽ.
git stash show -p | git apply --3