Stashing chỉ dàn dựng thay đổi trong git - có thể?


366

Có cách nào để tôi có thể bỏ qua những thay đổi được dàn dựng của mình không? Kịch bản tôi gặp vấn đề là khi tôi làm việc với một số lỗi tại một thời điểm nhất định và có một số thay đổi chưa được thực hiện. Tôi muốn có thể tạo các tệp riêng lẻ này, tạo các tệp .patch của mình và bỏ chúng đi cho đến khi mã được phê duyệt. Bằng cách này, khi được phê duyệt, tôi có thể bỏ toàn bộ phiên (hiện tại) của mình, bật lỗi đó và đẩy mã.

Tôi đang đi về điều này sai cách? Tôi có hiểu nhầm làm thế nào git có thể làm việc theo những cách khác để đơn giản hóa quá trình của tôi?


Vâng, có lẽ bạn đang làm những điều sai trái để đi vào tình huống này. Vẫn là một câu hỏi hữu ích. Bạn thực sự nên stash hoặc nhánh trước khi bắt đầu sửa chữa tiếp theo. Câu trả lời, tiếp tuyến, stackoverflow.com/a/50692885 có lẽ là một cách tốt hơn để xử lý việc này trong git. Chơi xung quanh với stash thường làm những thứ kỳ lạ cho khu vực làm việc của tôi nếu tôi đã rút được cam kết từ thượng nguồn.
Samuel Åslund

Câu trả lời:


472

Có, điều đó là có thể với NHÂN ĐÔI NHÂN ĐÔI

  1. Giai đoạn tất cả các tập tin của bạn mà bạn cần để stash.
  2. Chạy đi git stash --keep-index. Lệnh này sẽ tạo ra một bản lưu với TẤT CẢ các thay đổi của bạn ( được dàn dựng và không được dàn dựng ), nhưng sẽ để lại các thay đổi theo giai đoạn trong thư mục làm việc của bạn (vẫn ở trạng thái được tổ chức).
  3. Chạy git stash push -m "good stash"
  4. Bây giờ bạn "good stash"đã CHỈ tập tin dàn dựng .

Bây giờ nếu bạn cần các tệp không được sắp xếp trước khi lưu trữ, chỉ cần áp dụng stash đầu tiên ( tệp được tạo bằng--keep-index ) và bây giờ bạn có thể xóa các tệp bạn đã lưu vào "good stash".

Thưởng thức


Nó ngăn chặn các thay đổi trong các mô hình con mặc dù chúng không được dàn dựng. Có cách nào để giái quyết vấn đề này không?
rluks

1
điều này bằng cách nào đó để lại tất cả các tập tin mới (thậm chí dàn dựng) ra.
Aurimas

8
@Aurimas, để stash các tập tin mới, bạn cần sử dụng công -utắc.
Gyromite

2
khi bạn áp dụng lại lần đầu tiên và nhận lại tất cả thay đổi trong khi bạn có thể chỉ quan tâm đến git stash apply --indextùy chọn sử dụng thay đổi của mình . Điều này sẽ cố gắng giữ trạng thái un (dàn dựng) của bạn. Bây giờ dễ dàng hơn để loại bỏ những thay đổi không mong muốn từ cây làm việc.
otomo

Mặc dù tôi không cần phải làm chính xác những gì câu trả lời này đã nói, nhưng biết về cờ --keep-index là rất hữu ích.
Aaron Krauss

128

Với git mới nhất, bạn có thể sử dụng --patchtùy chọn

git stash push --patch  

git stash save --patch   # for older git versions

Và git sẽ yêu cầu bạn cho mỗi thay đổi trong tệp của bạn để thêm hoặc không vào stash.
Bạn chỉ cần trả lời yhoặcn

UPD
Bí danh cho DOUBLE stash :

git config --global alias.stash-staged '!bash -c "git stash --keep-index; git stash push -m "staged" --keep-index; git stash pop stash@{1}"'

Bây giờ bạn có thể giai đoạn các tập tin của bạn và sau đó chạy git stash-staged.
Kết quả là các tập tin theo giai đoạn của bạn sẽ được lưu vào stash .

Nếu bạn không muốn giữ các tập tin theo giai đoạn và muốn chuyển chúng vào stash. Sau đó, bạn có thể thêm một bí danh khác và chạy git move-staged:

git config --global alias.move-staged '!bash -c "git stash-staged;git commit -m "temp"; git stash; git reset --hard HEAD^; git stash pop"'

17
Về mặt kỹ thuật không trả lời câu hỏi - nhưng một kỹ thuật thực sự tốt đẹp đạt được sự chọn lọc.
alexreardon

6
Đồng ý, điều này không sao, nhưng ý tưởng ở đây với câu hỏi là TÔI ĐÃ THỰC HIỆN tất cả công việc này để dàn dựng những thay đổi mà tôi muốn làm gì đó (ban đầu rõ ràng là để cam kết, nhưng bây giờ muốn bỏ qua), không chỉ muốn làm lại từ đầu
Steven Lu

4
không hoạt động đối với các tệp mới được tạo (chỉ hoạt động trên các tệp đã sửa đổi)
Derek Liang

@DerekLiang: Các tệp mới được tạo hoàn toàn không được theo dõi. Có lẽ bạn nên kiểm tra -u|--include-untrackedtùy chọn củagit-stash
Eugen Konkov

2
Từ các tài liệu : " save : Tùy chọn này không được ủng hộ cho git stash đẩy . Nó khác với 'stash đẩy' ở chỗ nó không thể lấy pathspecs và bất kỳ đối số không tùy chọn nào tạo thành thông báo."
Borjovsky

53

Tôi đã tạo ra một kịch bản chỉ ghi lại những gì hiện đang dàn dựng và để lại mọi thứ khác. Điều này thật tuyệt vời khi tôi bắt đầu thực hiện quá nhiều thay đổi không liên quan. Đơn giản chỉ cần giai đoạn những gì không liên quan đến cam kết mong muốn và bỏ qua điều đó.

(Cảm ơn Bartłomiej cho điểm khởi đầu)

#!/bin/bash

#Stash everything temporarily.  Keep staged files, discard everything else after stashing.
git stash --keep-index

#Stash everything that remains (only the staged files should remain)  This is the stash we want to keep, so give it a name.
git stash save "$1"

#Apply the original stash to get us back to where we started.
git stash apply stash@{1}

#Create a temporary patch to reverse the originally staged changes and apply it
git stash show -p | git apply -R

#Delete the temporary stash
git stash drop stash@{1}

7
Tôi muốn thêm rằng bạn có thể biến tập lệnh thành lệnh git bằng cách theo dõi thediscoblog.com/blog/2014/03/29/custom-git-commands-in-3-steps
Petr

3
Điều đó thật tuyệt! Tôi đã điều chỉnh nó để nhắc người dùng mô tả stash nếu họ không nhập một dòng trên dòng lệnh: gist.github.com/brookinc/e2589a8c5ca33f804e4868f6bfc18282
brookinc

1
Điều này hoàn toàn không hoạt động với git 2.23.0.
thnee

Cảm ơn, tôi đã nâng cấp và biến nó thành bí danh ở đây: stackoverflow.com/a/60875067/430128 .
Raman

34

TL; DR Chỉ cần thêm -- $(git diff --staged --name-only)cho <pathspec>tham số git của bạn

Đây là một lót đơn giản:

git stash -- $(git diff --staged --name-only)

Và để thêm một tin nhắn đơn giản:

git stash push -m "My work in progress" -- $(git diff --staged --name-only)

Đã thử nghiệm trên v2.17.1v2.21.0.windows.1

Hạn chế:

  • Xin lưu ý rằng điều này sẽ bỏ qua mọi thứ, nếu bạn không có tập tin nào được dàn dựng.
  • Ngoài ra, nếu bạn có một tệp chỉ được dàn dựng một phần (tức là chỉ một số dòng thay đổi, được dàn dựng trong khi một số dòng thay đổi khác thì không), thì toàn bộ tệp sẽ bị xóa (bao gồm cả các dòng không được phân loại).

6
Tôi nghĩ rằng đây là lựa chọn tốt nhất cho tình huống được mô tả: dễ hiểu và không có phép thuật đen liên quan!
Luis

1
Điều này là khá gọn gàng. Tôi cuối cùng đã tạo ra một bí danh từ nó!
Kalpesh Panchal

giữ bình chọn cho họ 😉
Somo S.

@KalpeshPanchal bạn có thể chia sẻ bí danh của mình không? Tôi không chắc làm thế nào để thoát khỏi nó, vì vậy nó không diễn giải chính xác.
Igor Nadj


15

Để hoàn thành điều tương tự ...

  1. Giai đoạn chỉ các tập tin bạn muốn làm việc trên.
  2. git commit -m 'temp'
  3. git add .
  4. git stash
  5. git reset HEAD~1

Bùng nổ. Các tập tin bạn không muốn được lưu trữ. Các tập tin bạn muốn đã sẵn sàng cho bạn.


3
Đây dễ dàng là câu trả lời hay nhất và dễ nhớ nhất
Kevin

9

Trong kịch bản này, tôi thích tạo các nhánh mới cho từng vấn đề. Tôi sử dụng tiền tố temp / vì vậy tôi biết rằng tôi có thể xóa các nhánh này sau.

git checkout -b temp/bug1

Giai đoạn các tập tin sửa lỗi1 và cam kết chúng.

git checkout -b temp/bug2

Sau đó, bạn có thể chọn cherry từ các nhánh tương ứng theo yêu cầu và gửi yêu cầu kéo.


2
Mặc dù âm thanh stash lạ mắt là tốt để biết về, trong thực tế, điều này có vẻ như là một cách tiếp cận tôi ít có khả năng để bork.
ryanjdillon

1
Sử dụng "git cherry-pick tmpCommit" để nhận lại cam kết tạm thời với một cam kết hợp nhất hoặc "git merge tmpCommit" + "git reset HEAD ^" để nhận các thay đổi mà không cần cam kết.
Samuel Åslund

1
Vì câu trả lời này cho thấy đôi khi tốt hơn là hỏi trực tiếp những gì bạn muốn đạt được thay vì làm thế nào để đạt được nó với kỹ thuật nhất định. Các nhánh tạm thời và cherry-pick rất tiện dụng trong các tình huống phức tạp.
Guney Ozsan

nếu bạn dàn dựng một phần tệp, bạn sẽ cần phải thay đổi các thay đổi của mình trước khi quay trở lại nhánh ban đầu và bật lại chúng
jan-glx

6

Tại sao bạn không cam kết thay đổi cho một lỗi nhất định và tạo một bản vá từ cam kết đó và tiền thân của nó?

# hackhackhack, fix two unrelated bugs
git add -p                   # add hunks of first bug
git commit -m 'fix bug #123' # create commit #1
git add -p                   # add hunks of second bug
git commit -m 'fix bug #321' # create commit #2

Sau đó, để tạo các bản vá thích hợp, sử dụng git format-patch:

git format-patch HEAD^^

Điều này sẽ tạo ra hai tệp: 0001-fix-bug-123.patch0002-fix-bug-321.patch

Hoặc bạn có thể tạo các nhánh riêng biệt cho từng lỗi, để bạn có thể hợp nhất hoặc khởi động lại các bản sửa lỗi riêng lẻ hoặc thậm chí xóa chúng, nếu chúng không hoạt động.


2

git stash --keep-index là một giải pháp tốt ... ngoại trừ nó không hoạt động chính xác trên các đường dẫn đã bị xóa, đã được sửa trong Git 2.23 (quý 3 năm 2019)

Xem cam kết b932f6a (16 tháng 7 năm 2019) của Thomas Gummerer ( tgummerer) .
(Được hợp nhất bởi Junio ​​C Hamano - gitster- trong cam kết f8aee85 , ngày 25 tháng 7 năm 2019)

stash: sửa lỗi xử lý các tệp đã xóa bằng --keep-index

git stash push --keep-index được cho là giữ tất cả các thay đổi đã được thêm vào chỉ mục, cả trong chỉ mục và trên đĩa.

Hiện tại điều này không hoạt động chính xác khi một tệp bị xóa khỏi chỉ mục.
Thay vì giữ nó bị xóa trên đĩa, ** - keep-index hiện khôi phục tệp. **

Khắc phục hành vi đó bằng cách sử dụng ' git checkout' trong chế độ không lớp phủ có thể khôi phục trung thực chỉ mục và cây làm việc.
Điều này cũng đơn giản hóa mã.

Lưu ý rằng điều này sẽ ghi đè lên các tệp không bị theo dõi nếu tệp không được theo dõi có cùng tên với một tệp đã bị xóa trong chỉ mục.


2

Stash chỉ là chỉ số (thay đổi theo giai đoạn) trong Git là khó khăn hơn nó nên được. Tôi đã tìm thấy câu trả lời của @ Joe hoạt động tốt và biến một biến thể nhỏ của nó thành bí danh này:

stash-index = "!f() { \
  git stash push --quiet --keep-index -m \"temp for stash-index\" && \
  git stash push \"$@\" && \
  git stash pop --quiet stash@{1} && \
  git stash show -p | git apply -R; }; f"

Nó đẩy cả những thay đổi theo giai đoạn và không được dàn dựng thành một ngăn tạm thời, chỉ để lại những thay đổi theo giai đoạn. Sau đó, nó đẩy các thay đổi theo giai đoạn vào stash, đó là stash mà chúng ta muốn giữ. Các đối số được truyền cho bí danh, chẳng hạn như --message "whatever"sẽ được thêm vào lệnh stash này. Cuối cùng, nó bật stash tạm thời để khôi phục trạng thái ban đầu và loại bỏ stash tạm thời, và cuối cùng "loại bỏ" các thay đổi được lưu trữ từ thư mục làm việc thông qua một ứng dụng vá ngược.

Đối với vấn đề ngược lại của stashing chỉ là những thay đổi chưa được đặt ra (bí danh stash-working) xem câu trả lời này .


1

Có nhất thiết phải làm việc trên một số lỗi cùng một lúc không? Và bởi "cùng một lúc", ý tôi là "có các tệp được chỉnh sửa cho nhiều lỗi cùng một lúc." Bởi vì trừ khi bạn thực sự cần điều đó, tôi chỉ làm việc với một lỗi tại một thời điểm trong môi trường của bạn. Bằng cách đó, bạn có thể sử dụng các nhánh & rebase cục bộ, điều mà tôi thấy dễ dàng hơn nhiều so với việc quản lý một stash / giai đoạn phức tạp.

Giả sử chủ nhân đang cam kết B. Bây giờ hãy làm việc với lỗi # 1.

git checkout -b bug1

Bây giờ bạn đang ở nhánh bug1. Thực hiện một số thay đổi, cam kết, chờ xem xét mã. Đây là địa phương, vì vậy bạn không ảnh hưởng đến bất kỳ ai khác và nó đủ dễ để tạo một bản vá từ git diffs.

A-B < master
   \
    C < bug1

Bây giờ bạn đang làm việc trên bug2. Quay về làm chủ với git checkout master. Tạo một chi nhánh mới git checkout -b bug2,. Thực hiện thay đổi, cam kết, chờ xem xét mã.

    D < bug2
   /
A-B < master
   \
    C < bug1

Hãy giả vờ rằng người khác cam kết E & F trên chủ trong khi bạn đang chờ xem xét.

    D < bug2
   /
A-B-E-F < master
   \
    C < bug1

Khi mã của bạn đã được phê duyệt, bạn có thể khởi động lại để làm chủ với các bước sau:

git checkout bug1
git rebase master
git checkout master
git merge bug1

Điều này sẽ dẫn đến kết quả như sau:

    D < bug2
   /
A-B-E-F-C' < master, bug1

Sau đó, bạn có thể đẩy, xóa chi nhánh bug1 cục bộ của mình và tắt đi. Một lỗi tại một thời điểm trong không gian làm việc của bạn, nhưng với việc sử dụng các nhánh cục bộ, kho lưu trữ của bạn có thể xử lý nhiều lỗi. Và điều này tránh được một điệu nhảy / stash phức tạp.

Trả lời câu hỏi của ctote trong các bình luận:

Chà, bạn có thể quay lại stash cho mỗi lỗi và chỉ hoạt động với một lỗi tại một thời điểm. Atleast giúp bạn tiết kiệm các vấn đề dàn dựng. Nhưng đã thử điều này, cá nhân tôi thấy nó thật rắc rối. Stash là một chút lộn xộn trong một biểu đồ nhật ký git. Và quan trọng hơn, nếu bạn làm hỏng cái gì đó, bạn không thể hoàn nguyên. Nếu bạn có một thư mục làm việc bẩn và bạn bật stash, bạn không thể "hoàn tác" cửa sổ bật lên đó. Khó hơn nhiều để làm hỏng các cam kết hiện có.

Vì vậy git rebase -i.

Khi bạn rebase một nhánh lên nhánh khác, bạn có thể thực hiện nó một cách tương tác (cờ -i). Khi bạn làm điều này, bạn có tùy chọn để chọn những gì bạn muốn làm với mỗi cam kết. Pro Git là một cuốn sách tuyệt vời cũng trực tuyến ở định dạng HTML và có một phần hay về việc đánh lại & bẹp:

http://git-scm.com/book/ch6-4.html

Tôi sẽ đánh cắp nguyên văn ví dụ của họ để thuận tiện. Giả sử bạn có lịch sử cam kết sau đây và bạn muốn rebase & squash bug1 lên master:

    F < bug2
   /
A-B-G-H < master
   \
    C-D-E < bug1

Đây là những gì bạn sẽ thấy khi bạn gõ git rebase -i master bug1

pick f7f3f6d changed my name a bit
pick 310154e updated README formatting and added blame
pick a5f4a0d added cat-file
#
# Commands:
#  p, pick = use commit
#  e, edit = use commit, but stop for amending
#  s, squash = use commit, but meld into previous commit
#
# If you remove a line here THAT COMMIT WILL BE LOST.
# However, if you remove everything, the rebase will be aborted.
#

Để xóa tất cả các cam kết của một nhánh thành một cam kết duy nhất, hãy giữ cam kết đầu tiên là "chọn" và thay thế tất cả các mục "chọn" tiếp theo bằng "squash" hoặc đơn giản là "s". Bạn cũng sẽ có cơ hội thay đổi thông điệp cam kết.

pick f7f3f6d changed my name a bit
s 310154e updated README formatting and added blame
s a5f4a0d added cat-file
#
# Commands:
#  p, pick = use commit
#  e, edit = use commit, but stop for amending
#  s, squash = use commit, but meld into previous commit

Vì vậy, yeah, squashing là một chút đau đớn, nhưng tôi vẫn sẽ khuyên bạn nên sử dụng quá nhiều stash.


1
Cảm ơn cho bài viết chi tiết! Điều này giải quyết rất nhiều vấn đề của tôi chắc chắn - vấn đề duy nhất tôi thấy là nhóm hiện tại của chúng tôi đã yêu cầu chúng tôi giữ tất cả việc giao hàng cho một cam kết duy nhất. :(
MrDuk

1
Nếu họ không cần hoặc muốn lịch sử làm việc của bạn trong repo sản xuất thì tốt: hãy làm cho lịch sử tổng thể theo dõi của bạn ít hơn bằng cách áp dụng các khác biệt thay vì sáp nhập các chi nhánh. Bạn có thể tìm ra cách giữ một nhánh chính được trang trí có lịch sử hợp nhất thực tế và thực hiện công việc thực sự của bạn từ đó, theo cách đó sẽ dễ dàng tự động tạo ra các khác biệt chính xác.
jthill

2
Lưu ý rằng git checkout master; git checkout -b bug2có thể rút ngắn thành git checkout -b bug2 master. Điều tương tự cũng áp dụng cho git checkout bug1; git rebase master; git checkout master; git merge bug1, giống hệt với git rebase master bug1; git push . bug1:master(được cho là, pushmánh khóe không rõ ràng)
knittl

1
Tôi đã đưa ra một hướng dẫn cho việc bỏ qua ở trên trong câu trả lời chính để tôi có thể sử dụng định dạng ưa thích
Mike Monkiewicz

6
Tôi đã từ chối vì điều này không trả lời câu hỏi ban đầu. Tôi đang ở trong một chi nhánh đang làm việc gì đó và tôi đã thực hiện một thay đổi mà tôi nghĩ nên cam kết riêng với chi nhánh tích hợp. Tất cả những gì tôi muốn làm là giai đoạn thay đổi và bỏ qua nó để tôi có thể chuyển sang một chi nhánh khác và cam kết riêng, thay vì chi nhánh "công việc đang tiến hành" hiện tại của tôi. (Cảnh báo, git rign phía trước.) Thật vô lý khi điều này rất khó thực hiện; Tôi phải tưởng tượng rằng đây là một sự xuất hiện phổ biến . (Làm việc trong một chi nhánh và phát hiện ra một thay đổi nhanh chóng cần thực hiện và quên chuyển đổi trước.)
jpmc26

0

Trong số các ý kiến ​​của bạn cho câu trả lời của Mike Monkiewicz, tôi đề nghị sử dụng một mô hình đơn giản hơn: Sử dụng các nhánh phát triển thông thường, nhưng sử dụng tùy chọn squash của hợp nhất để có được một cam kết trong nhánh chính của bạn:

git checkout -b bug1    # create the development branch
* hack hack hack *      # do some work
git commit
* hack hack hack *
git commit
* hack hack hack *
git commit
* hack hack hack *
git commit
git checkout master     # go back to the master branch
git merge --squash bug1 # merge the work back
git commit              # commit the merge (don't forget
                        #    to change the default commit message)
git branch -D bug1      # remove the development branch

Ưu điểm của thủ tục này là bạn có thể sử dụng luồng công việc git bình thường.


Tôi không thể thấy câu trả lời này có thể giúp ích như thế nào. Nó không liên quan đến câu hỏi ban đầu.
frapen

0

TL; DR ;git stash-staged

Sau khi tạo bí danh:

git config --global alias.stash-staged '!bash -c "git stash -- \$(git diff --staged --name-only)"'

Ở đây git difftrả về danh sách các --stagedtập tin --name-only
Và sau đó chúng tôi vượt qua danh sách này pathspecđể bắt đầu git stash.

Từ man git stash:

git stash [--] [<pathspec>...]

<pathspec>...
   The new stash entry records the modified states only for the files
   that match the pathspec. The index entries and working tree
   files are then rolled back to the state in HEAD only for these
   files, too, leaving files that do not match the pathspec intact.


-1

Để cắt một thay đổi ngẫu nhiên, đặc biệt là xóa nhiều tệp, hãy làm như sau:

git add <stuff to keep> && git stash --keep-index && git stash drop

nói cách khác, hãy bỏ rác và ném nó đi cùng với đống rác đó.

Đã thử nghiệm trong phiên bản git 2.17.1


một downvote mà không có một bình luận là không hữu ích cho tôi cũng như người đọc tiếp theo ... zaenks anon cục cằn. Mặc dù tôi có thể tưởng tượng một vấn đề với lớp lót này: Một người nên hết sức cẩn thận đừng quên thêm tất cả các thay đổi mong muốn vào chỉ mục, nếu không những thay đổi quan trọng đó cũng sẽ bị xóa. Nhưng một lần nữa, việc sử dụng không cẩn thận bất kỳ công cụ cli nào có thể rất nguy hiểm đối với thời gian và công việc quý giá của một người trong trường hợp xấu nhất.
wmax
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.