Hủy bỏ một stash pop trong Git


257

Tôi xuất hiện một stash và có một cuộc xung đột hợp nhất. Không giống như câu hỏi được liệt kê dưới dạng trùng lặp, tôi đã có một số thay đổi không được cam kết trong thư mục mà tôi muốn giữ. Tôi không chỉ muốn làm cho xung đột hợp nhất biến mất, mà còn để đưa thư mục của tôi trở lại trạng thái trước khi xuất hiện.

Tôi đã thử git merge --abort, nhưng git tuyên bố không có sự hợp nhất nào đang diễn ra. Có cách nào dễ dàng để hủy bỏ một pop mà không phá hủy những thay đổi ban đầu tôi có trong thư mục không?


Đối với những thay đổi không được cam kết của bạn: những thay đổi này đã có trong chỉ mục chưa?
jørgensen

Bạn có thể đăng phiên bản git bạn đang sử dụng.
Tinman


2
Câu trả lời được chấp nhận có vẻ phức tạp. Tôi nghĩ rằng đó là cách thực hành chung khá tốt để không bao giờ làm điều gì đó như cố gắng git stash poptrên một thư mục làm việc ô uế. Trong trường hợp đó bạn có thể chỉ đơn giản git reset --hardvà stash của bạn vẫn còn nguyên vẹn. (Đây ít nhiều là những gì chủ đề được liên kết của @ BradKoch gợi ý)
Steven Lu

1
@StevenLu, tôi đồng ý, nhưng vấn đề có thể xảy ra trong một thư mục hoạt động sạch nếu bạn đang xử lý các thay đổi để chuyển chúng sang một nhánh khác. Stash xung đột với các cam kết tồn tại trên nhánh mới không tồn tại trên nhánh cũ.
Jake Stevens-Haas

Câu trả lời:


55

Ok, tôi nghĩ rằng tôi đã làm việc "git stash không vui". Nó phức tạp hơn git apply --reversebởi vì bạn cần hành động hợp nhất ngược lại trong trường hợp có bất kỳ sự hợp nhất nào được thực hiện bởi git stash apply.

Hợp nhất ngược yêu cầu tất cả các thay đổi hiện tại được đẩy vào chỉ mục:

  • git add -u

Sau đó đảo ngược merge-recursiveđiều đó đã được thực hiện bởi git stash apply:

  • git merge-recursive stash@{0}: -- $(git write-tree) stash@{0}^1

Bây giờ bạn sẽ bị bỏ lại chỉ với những thay đổi không phải là stash. Họ sẽ ở trong chỉ số. Bạn có thể sử dụng git resetđể bỏ qua các thay đổi của bạn nếu bạn muốn.

Cho rằng git stash applythất bại ban đầu của bạn, tôi cho rằng điều ngược lại cũng có thể thất bại vì một số điều nó muốn hoàn tác đã không được thực hiện.

Dưới đây là một ví dụ cho thấy bản sao làm việc (thông qua git status) kết thúc sạch như thế nào :

 $ git status
# On branch trunk
nothing to commit (working directory clean)
 $ git stash apply
Auto-merging foo.c
# On branch trunk
# Changed but not updated:
#   (use "git add <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#       modified:   foo.c
#
no changes added to commit (use "git add" and/or "git commit -a")
 $ git add -u
 $ git merge-recursive stash@{0}: -- $(git write-tree) stash@{0}^1
Auto-merging foo.c
 $ git status
# On branch trunk
nothing to commit (working directory clean)

2
sau khi thực hiện việc này, các chỉnh sửa được lưu trữ trước đó sẽ bị mất vĩnh viễn hay chúng sẽ quay trở lại trong bản gốc?
Brian H.

7
git stash applykhông bao giờ làm rơi stash và khi hợp nhất thất bại thì nó git stash popvẫn giữ lại stash
Xerus

Những điều tồi tệ sẽ xảy ra nếu có xung đột hợp nhất trong bản gốc stash pop
3ocene

286

Trường hợp sử dụng của tôi: vừa thử bật lên nhánh sai và có xung đột. Tất cả những gì tôi cần là hoàn tác pop nhưng giữ nó trong danh sách stash để tôi có thể bật nó ra trên nhánh chính xác. Tôi đã làm điều này:

git reset HEAD --hard
git checkout my_correct_branch
git stash pop

Dễ dàng.


22
Vì vậy, stash mà bạn muốn ở trong danh sách stash cho đến khi bạn có một pop thành công?
Ryan Clark

52
Đây không phải là một câu trả lời cho câu hỏi ban đầu vì nó sẽ xóa sạch những thay đổi cục bộ không được cam kết.
Dmitry

13
@RyanClark Xem câu trả lời của DavidG bên dưới. Về cơ bản, vâng, nó vẫn nằm trong danh sách stash.
fkorsa

1
Tôi nghĩ rằng đại đa số người dùng thấy mình trong tình huống này sẽ thấy giải pháp này là lý tưởng. Tôi không thể tưởng tượng ra một tình huống mà tôi có những thay đổi không được cam kết trong thư mục làm việc của mình trước khi thử stash popvào nó. Nghe có vẻ như một công thức cho thảm họa.
Shadoninja

2
Đẹp. Sau khi đọc nó, tôi đã xem các tài liệu pop git stash và thông báo "Áp dụng trạng thái có thể thất bại với các xung đột; trong trường hợp này, nó không bị xóa khỏi danh sách stash." Vì vậy, đây là lý do tại sao stash có thể được bật lại sau khi thiết lập lại / thanh toán.
Brady Holt

48

Chỉnh sửa: Từ git help stash tài liệu trong phần pop:

Áp dụng nhà nước có thể thất bại với xung đột; trong trường hợp này, nó không bị xóa khỏi danh sách stash. Bạn cần giải quyết xung đột bằng tay và gọi git stash drop bằng tay sau đó.

Nếu tùy chọn --index được sử dụng, thì sẽ cố gắng khôi phục không chỉ các thay đổi của cây làm việc, mà cả các thay đổi của chỉ mục. Tuy nhiên, điều này có thể thất bại, khi bạn có xung đột (được lưu trữ trong chỉ mục, do đó bạn không còn có thể áp dụng các thay đổi như ban đầu).

Hãy thử sao chép tất cả repo của bạn vào một thư mục mới (để bạn có một bản sao của nó) và chạy:

git stash show và lưu sản lượng đó ở đâu đó nếu bạn quan tâm đến nó.

sau đó: git stash dropđể thả stash xung đột sau đó:git reset HEAD

Điều đó sẽ khiến repo của bạn ở trạng thái như trước đây (hy vọng, tôi vẫn chưa thể giải quyết vấn đề của bạn)

===

Tôi đang cố gắng khắc phục vấn đề của bạn nhưng tất cả những gì tôi nhận được khi sử dụng git stash poplà:

error: Your local changes to the following files would be overwritten by merge:
...
Please, commit your changes or stash them before you can merge.
Aborting

Trong một thư mục sạch:

git init
echo hello world > a
git add a & git commit -m "a"
echo hallo welt >> a
echo hello world > b
git add b & git commit -m "b"
echo hallo welt >> b
git stash
echo hola mundo >> a
git stash pop

Tôi không thấy git cố gắng hợp nhất các thay đổi của tôi, nó chỉ thất bại. Bạn có bất kỳ bước repro nào chúng tôi có thể làm theo để giúp bạn không?


Hãy thử thay đổi các tệp khác nhau.
asmeker

Cố gắng chuyển sang một tệp khác không hoạt động (xem các bước repro mới). Tôi chỉ không thể giải quyết vấn đề ...
DavidG

1
Giải pháp này không hoạt động nếu có những thay đổi không được cam kết trong thư mục làm việc.
ở đây

@here Đây không phải là một giải pháp thực sự, điều này chỉ để chứng minh rằng tôi không thể tái tạo vấn đề mà OP gặp phải và anh ấy đã không cung cấp bất kỳ bước nào để đến nơi anh ấy đang ở.
DavidG

1
Kịch bản thực tế là: 1) Thay đổi tệp A. 2) Thay đổi Stash 3) Thực hiện thay đổi xung đột trong tệp A và cam kết (ví dụ thay đổi cùng một dòng) 4) Thay đổi tệp B 5) Do 'git stash pop'. Bây giờ bạn có xung đột và thay đổi cục bộ. Nói chung, chúng sẽ ở trong các tệp khác nhau, nhưng bạn sẽ không bao giờ biết tệp nào được sửa đổi không xung đột từ stash và đó là những thay đổi không được xử lý cục bộ.
Dmitry

16

Tôi đã luôn luôn sử dụng

git reset --merge

Tôi không thể nhớ nó đã từng thất bại.


@GauravPaliwal, tôi sẽ cho tôi xem lần sau git reset. Bạn có biết nếu nó giống hệt chức năng git reset --merge?
Kenn Sebesta

5

Nếu bạn không phải lo lắng về bất kỳ thay đổi nào khác mà bạn đã thực hiện và bạn chỉ muốn quay lại cam kết cuối cùng, thì bạn có thể làm:

git reset .
git checkout .
git clean -f

4

OK, tôi nghĩ rằng tôi đã tìm được một luồng công việc sẽ đưa bạn trở lại nơi bạn cần (như thể bạn chưa thực hiện pop).

HÃY BACKUP TRƯỚC KHI !! Tôi không biết liệu điều này có hiệu quả với bạn hay không, vì vậy hãy sao chép toàn bộ repo của bạn chỉ trong trường hợp nó không hoạt động.

1) Khắc phục các sự cố hợp nhất và khắc phục tất cả các xung đột bằng cách chọn tất cả các thay đổi xuất phát từ bản vá (trong hình con rùa, điều này hiển thị dưới dạng một.REMOETE (của chúng)).

git mergetool

2) Cam kết những thay đổi này (chúng sẽ được thêm thông qua lệnh mergetool). Cung cấp cho nó một thông điệp cam kết "hợp nhất" hoặc một cái gì đó bạn nhớ.

git commit -m "merge"

3) Bây giờ bạn vẫn sẽ có các thay đổi chưa được xử lý cục bộ mà bạn đã bắt đầu ban đầu, với một cam kết mới từ bản vá (chúng ta có thể thoát khỏi điều này sau). Bây giờ cam kết thay đổi chưa được phân loại của bạn

git add .
git add -u .
git commit -m "local changes"

4) Đảo ngược các bản vá. Điều này có thể được thực hiện với lệnh sau:

git stash show -p | git apply -R

5) Cam kết những thay đổi này:

git commit -a -m "reversed patch"

6) Loại bỏ các cam kết vá / unpatch

git rebase -i HEAD^^^

từ đây, xóa hai dòng có 'hợp nhất' và 'bản vá đảo ngược' trong đó.

7) Nhận lại các thay đổi chưa được chỉnh sửa của bạn và hoàn tác cam kết 'thay đổi cục bộ'

git reset HEAD^

Tôi đã chạy qua nó với một ví dụ đơn giản và nó đưa bạn trở lại nơi bạn muốn - trực tiếp trước khi stash được bật lên, với những thay đổi cục bộ của bạn và với stash vẫn có sẵn để bật.


Nếu điều đó không hiệu quả, hy vọng nó sẽ giúp bạn đi gần hết! :-)
agentgonzo

git stash show -p | git apply -RĐiều đó không hiệu quả nếu git stash applythực hiện bất kỳ sự hợp nhất thực sự. Xem câu trả lời của tôi ...
Ben Jackson

3

Tôi đã giải quyết điều này theo một cách hơi khác. Đây là những gì đã xảy ra.

Đầu tiên, tôi xuất hiện trên nhánh sai và có xung đột. Stash vẫn còn nguyên nhưng chỉ số đã được giải quyết xung đột, chặn nhiều lệnh.

Một đơn giản đã git reset HEADhủy bỏ giải quyết xung đột và để lại những thay đổi không được cam kết (và KHÔNG ĐƯỢC PHÉP ).

Một số git co <filename>hoàn nguyên chỉ mục về trạng thái ban đầu. Cuối cùng, tôi chuyển đổi chi nhánh với git co <branch-name>và chạy một cái mới git stash pop, giải quyết mà không có xung đột.


2

Một vài ý tưởng:

  • Sử dụng git mergetoolđể phân chia các tập tin hợp nhất thành các phần gốc và phần mới. Hy vọng rằng một trong số đó là tệp có các thay đổi không chính xác trong đó.

  • Áp dụng khác biệt của stash theo chiều ngược lại, để hoàn tác những thay đổi đó. Có lẽ bạn sẽ phải tự tách ra các tệp có xung đột hợp nhất (hy vọng thủ thuật trên sẽ có hiệu quả).

Tôi đã không kiểm tra một trong hai thứ này, vì vậy tôi không biết chắc chắn chúng sẽ hoạt động.


1

Tôi có thể sao chép sạch git stash poptrên thư mục "bẩn", với những thay đổi không được cam kết, nhưng chưa bật sẽ tạo ra xung đột hợp nhất.

Nếu xung đột hợp nhất, số tiền bạn cố gắng áp dụng không biến mất, bạn có thể thử kiểm tra git show stash@{0}(tùy chọn với --ourshoặc --theirs) và so sánh với git statisgit diff HEAD. Bạn sẽ có thể thấy những thay đổi đến từ việc áp dụng stash.


1

Nếu DavidG chính xác rằng nó không bật stash vì xung đột hợp nhất, thì bạn chỉ cần dọn dẹp thư mục làm việc của mình. Nhanh chóng git commitmọi thứ bạn quan tâm. (Bạn có thể resethoặc squashcam kết sau nếu bạn chưa hoàn thành.) Sau đó, với mọi thứ bạn quan tâm về an toàn, git resetmọi thứ khác sẽ git stash popđổ vào thư mục làm việc của bạn.


1

Nếu không có thay đổi theo giai đoạn trước git stash pop, như trong câu hỏi, thì hai lệnh sau sẽ hoạt động.

git diff --name-only --cached | xargs git checkout --ours HEAD
git ls-tree stash@{0}^3 --name-only | xargs rm

Việc đầu tiên đảo ngược bất kỳ sự hợp nhất từ ​​stash, thành công hay không. Thứ hai xóa bất kỳ tập tin không bị theo dõi nào được giới thiệu bởi stash.

Từ man git stash: The working directory must match the index. Mà @DavidG chỉ ra, stash popsẽ thất bại nếu bất kỳ xung đột tệp sửa đổi hiện tại không được chỉnh sửa. Vì vậy, chúng ta không cần phải lo lắng về việc giải quyết xung đột hợp nhất ngoài việc quay lại HEAD. Bất kỳ tệp sửa đổi nào còn lại sau đó không liên quan đến bản gốc và đã được sửa đổi trướcstash pop

Nếu có những thay đổi theo giai đoạn, tôi không rõ liệu chúng ta có thể dựa vào các lệnh tương tự hay không và bạn có thể muốn thử kỹ thuật của @Ben Jackson hay không. Gợi ý đánh giá cao ..

Đây là một thiết lập thử nghiệm cho tất cả các trường hợp khác nhau https://gist.github.com/here/4f3af6dafdb4ca15e804

# Result:
# Merge succeeded in m (theirs)
# Conflict in b
# Unstaged in a
# Untracked in c and d

# Goal:
# Reverse changes to successful merge m
# Keep our version in merge conflict b
# Keep our unstaged a
# Keep our untracked d
# Delete stashed untracked c

Tôi nghĩ rằng đây là câu trả lời đúng nhất cho đến nay. Rất tốt nghĩ ra.
Đại lý Thứ Sáu ngày

0

Sử dụng git reflogđể liệt kê tất cả các thay đổi được thực hiện trong lịch sử git của bạn. Sao chép một id hành động và loạigit reset ACTION_ID


2
Git không tạo mục nhập reflog trước khi bạn bật (bạn cần phải có cam kết)
Casebash

-1

Tôi đang đăng ở đây hy vọng rằng những người khác của tôi tìm thấy câu trả lời của tôi hữu ích. Tôi đã gặp một vấn đề tương tự khi tôi cố gắng thực hiện một stash pop trên một nhánh khác với nhánh mà tôi đã lấy từ đó. Trong trường hợp của tôi, tôi không có tệp nào không được cam kết hoặc trong chỉ mục nhưng vẫn gặp trường hợp xung đột hợp nhất (trường hợp tương tự như @pid). Như những người khác đã chỉ ra trước đây, pop gash stash thất bại thực sự đã giữ lại stash của tôi, sau đó, một git reset nhanh chóng ĐẦU TIÊN cộng với quay trở lại chi nhánh ban đầu của tôi và thực hiện stash từ đó đã giải quyết vấn đề của tôi.

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.