Làm thế nào để giải quyết xung đột git stash mà không cam kết?


495

Khi được hỏi trong câu hỏi này , tôi cũng muốn biết cách giải quyết xung đột git stash popmà không cần thêm tất cả các sửa đổi vào một cam kết (giống như "git stash pop" mà không có xung đột nào).

Cách tiếp cận hiện tại của tôi rất không hay vì tôi làm theo cách này:

git stash pop -> CONFLICT
git stash drop
[resolve conflict]
[add conflict files]
git reset HEAD <all files that are in commit-mode>

[Cập nhật] Một cách để tái tạo nó:

mkdir foo; cd foo; git init
echo "1" > one
echo "2" > two
git add -A; git commit -m "first"
echo "1.1" > one
echo "2.1" > two
git stash
echo "2.2" > two
git commit -a -m "second"
echo "Only this file would stay in HEAD without the conflict" > third
git add third
git stash pop
git status

2016-06-27: Đã thêm một tệp mới gọi là 'thứ ba' vào ví dụ để cho thấy các cách giải quyết như giải pháp từ scy chỉ hoạt động đối với các Head trống nhưng không khắc phục vấn đề ban đầu mà HEAD không có cùng nội dung như cho một git stash popkhông có xung đột.


Vì vậy, bạn đã git addgiải quyết các tệp xung đột, sắp xếp chúng một cách hiệu quả trong chỉ mục và bạn muốn không có chúng trong chỉ mục của chúng tôi?
Romain

Vâng đúng vậy. Tôi chỉ muốn hành vi git stash popcó khi không có xung đột xảy ra (nhưng với thông báo tập tin nào cần được hợp nhất).
Sven

2
Có vẻ như câu trả lời cho điều này là ở đây: stackoverflow.com/questions/3945826/git-stash-questions . Trong câu trả lời được chọn, trên bình luận thứ 4, Adam giải thích tại sao git làm điều này.
Patrick

@Patrick Cảm ơn bạn về thông tin này - vì vậy có vẻ như sẽ không có giải pháp khả dụng vì "theo thiết kế"
Sven

Câu trả lời:


509

Đừng làm theo những câu trả lời khác

Vâng, bạn có thể theo dõi họ :). Nhưng tôi không nghĩ rằng thực hiện một cam kết và sau đó đặt lại chi nhánh để loại bỏ cam kết đó và các cách giải quyết tương tự được đề xuất trong các câu trả lời khác là cách rõ ràng để giải quyết vấn đề này.

Dung dịch sạch

Giải pháp sau đây có vẻ sạch sẽ hơn đối với tôi và nó cũng được đề xuất bởi chính Git - hãy thử thực thi git statustrong kho lưu trữ với một xung đột:

Unmerged paths:
  (use "git reset HEAD <file>..." to unstage)
  (use "git add <file>..." to mark resolution)

Vì vậy, hãy làm những gì Git gợi ý (mà không thực hiện bất kỳ cam kết vô ích nào):

  1. Thủ công (hoặc sử dụng một số công cụ hợp nhất , xem bên dưới) giải quyết (các) xung đột.
  2. Sử dụng git resetđể đánh dấu (các) xung đột là đã được giải quyết và bỏ qua các thay đổi. Bạn có thể thực hiện nó mà không cần bất kỳ tham số nào và Git sẽ xóa mọi thứ khỏi chỉ mục. Bạn không phải thực hiện git addtrước.
  3. Cuối cùng, loại bỏ stash với git stash drop, bởi vì Git không làm điều đó khi xung đột.

Được dịch sang dòng lệnh:

$ git stash pop

# ...resolve conflict(s)

$ git reset

$ git stash drop

Giải thích về hành vi mặc định

Có hai cách đánh dấu xung đột khi được giải quyết: git addgit reset. Mặc dù git resetđánh dấu các xung đột là đã được giải quyết và xóa các tệp khỏi chỉ mục, nhưng git addcũng đánh dấu các xung đột là đã được giải quyết, nhưng giữ các tệp trong chỉ mục.

Thêm tệp vào chỉ mục sau khi xung đột được giải quyết là có chủ đích. Bằng cách này, bạn có thể phân biệt các thay đổi so với bản gốc trước đó và các thay đổi bạn đã thực hiện sau khi xung đột được giải quyết. Nếu bạn không thích nó, bạn luôn có thể sử dụng git resetđể xóa mọi thứ khỏi chỉ mục.

Hợp nhất công cụ

Tôi đặc biệt khuyên bạn nên sử dụng bất kỳ công cụ hợp nhất 3 chiều nào để giải quyết xung đột, ví dụ: KDiff3 , Meld , v.v., thay vì thực hiện thủ công. Nó thường tự giải quyết tất cả hoặc phần lớn các xung đột. Đó là tiết kiệm thời gian rất lớn !


32
@kamalpal dường như là cần thiết khi git stash popthất bại với xung đột.
Emile Bergeron

21
@kamalpal vâng, Git thậm chí còn thông báo cho bạn rằng stash đã không bị bỏ trong trường hợp xung đột. Và câu hỏi là về trường hợp như vậy, vì vậy bạn thực sự cần phải thực thigit stash drop trừ khi bạn muốn giữ bí mật đó.
David Ferenczy Rogožan

@ DavidFerenczyRogožan Git đã không thông báo cho tôi rằng tất cả đều không bỏ mục stash. Phiên bản 2.17.1 tại đây.
Robert Siemer

298

Giả sử bạn có kịch bản này trong đó bạn thực hiện các thay đổi của mình để lấy từ nguồn gốc. Có thể vì những thay đổi cục bộ của bạn chỉ debug: truenằm trong một số tệp cài đặt. Bây giờ bạn kéo và ai đó đã giới thiệu một thiết lập mới ở đó, tạo ra một cuộc xung đột.

git status nói:

# On branch master
# Unmerged paths:
#   (use "git reset HEAD <file>..." to unstage)
#   (use "git add/rm <file>..." as appropriate to mark resolution)
#
#   both modified:      src/js/globals.tpl.js
no changes added to commit (use "git add" and/or "git commit -a")

Được chứ. Tôi quyết định đi theo những gì Git đề xuất: Tôi đã giải quyết xung đột và cam kết:

vim src/js/globals.tpl.js
# type type type …
git commit -a -m WIP   # (short for "work in progress")

Bây giờ bản sao làm việc của tôi ở trạng thái tôi muốn, nhưng tôi đã tạo một cam kết mà tôi không muốn có. Làm cách nào để thoát khỏi cam kết đó mà không sửa đổi bản sao làm việc của tôi? Đợi đã, có một lệnh phổ biến cho điều đó!

git reset HEAD^

Bản sao làm việc của tôi không bị thay đổi, nhưng cam kết WIP đã biến mất. Đó chính xác là những gì tôi muốn! (Lưu ý rằng tôi không sử dụng --softở đây, bởi vì nếu có những tính năng tự động sáp nhập tập tin trong stash của bạn, họ được tự động dàn dựng và do đó bạn muốn kết thúc với những tập tin này được dàn dựng lại sau reset.)

Nhưng còn một điều nữa: Trang hướng dẫn git stash popnhắc nhở chúng ta rằng "Áp dụng trạng thái 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 dropthủ công sau đó." Vì vậy, đó chính xác là những gì chúng ta làm bây giờ:

git stash drop

Và thực hiện.


34
Có rất nhiều sự xấu xí vốn có khi cố tình phải thiết lập lại cam kết ^ ... cho một cái gì đó chỉ ảnh hưởng đến cây làm việc.

6
Tại sao không chỉ giải quyết các xung đột và sau đó git add <resolved conflict files>theo sau git reset HEAD?
BoltzmannBrain

Cảm ơn đề xuất nhưng điều này không khắc phục được vấn đề ban đầu rằng đây không phải là hành vi giống như git stash popkhông có xung đột. Chỉ cần thêm một tệp khác vào CHÍNH trước khi thực hiện xung đột git stash popvà hơn là bạn git commit -a -m WIPcũng sẽ thêm tệp mới vào cam kết. Nhưng không có xung đột, chỉ có tệp mới sẽ ở trong CHÍNH chứ không phải các git stash poptệp.
Sven

7
Tôi không nghĩ cần phải cam kết trước rồi hoàn tác. Chỉ cần đặt lại từ Dawid Ferenczy câu trả lời sẽ làm tương tự
vladkras

3
Đối với người dùng windows, phần ^này được sử dụng như một phần tiếp theo đặc biệt và sẽ khiến bạn ngồi ở vị trí khác? nhắc thay vì thực hiện lệnh. Thay vào đó sử dụng : git reset --soft HEAD~1. Xem how-do-i-xóa-unpushed-git-commits?
mrfelis

87

Thay vì thêm các thay đổi bạn thực hiện để giải quyết xung đột, bạn có thể sử dụng git reset HEAD fileđể giải quyết xung đột mà không cần dàn dựng các thay đổi của mình.

Bạn có thể phải chạy lệnh này hai lần, tuy nhiên. Một lần để đánh dấu xung đột là đã được giải quyết và một lần để làm sáng tỏ những thay đổi được dàn dựng bởi thói quen giải quyết xung đột.

Có thể nên có một chế độ thiết lập lại đồng thời thực hiện cả hai điều này, mặc dù hiện tại không có chế độ nào.


2
Chế độ đặt lại là chế độ tôi tìm kiếm - các cách giải quyết khác giống như cách tôi đã mô tả và không thực tế cho hơn 5 tệp.
Sven

25
Và sử dụng "git stash drop" sau đó để hoàn thành "git stash pop".
David Liu

2
Mặc dù câu hỏi không yêu cầu điều này một cách rõ ràng, nhưng có thể hữu ích khi cập nhật câu trả lời để bao gồm "git stash drop" vì stash không được tự động bỏ trong trường hợp có xung đột.
Abhishek Pathak

29
git checkout stash -- .

đã làm cho tôi.

Lưu ý : điều này có thể nguy hiểm vì nó không cố gắng hợp nhất các thay đổi từ bản gốc vào bản sao làm việc của bạn, nhưng ghi đè lên nó bằng các tệp được lưu trữ thay thế. Vì vậy, bạn có thể mất những thay đổi không cam kết của bạn.


Điều này giúp ích khi "git pull --autostash" giới thiệu các cam kết hợp nhất không mong muốn và ngăn kiểm tra git -. ghi đè vô điều kiện xung đột từ stash
Alec Istomin

11
git add .
git reset

git add . sẽ giai đoạn TẤT CẢ các tệp nói với git rằng bạn đã giải quyết xung đột

git reset sẽ bỏ qua TẤT CẢ các tệp được sắp xếp mà không tạo ra một cam kết


Đây thực sự không phải là một câu trả lời tồi, nó khá giống như git add -usau đógit reset
ebob

4

Có vẻ như đây có thể là câu trả lời mà bạn đang tìm kiếm, tôi chưa thử cá nhân này, nhưng có vẻ như nó có thể làm điều khó khăn. Với lệnh này, GIT sẽ cố gắng áp dụng các thay đổi như trước đây, mà không cố gắng thêm tất cả chúng cho cam kết.

git stash apply --index

đây là lời giải thích đầy đủ:

http://git-scm.com/book/en/Git-Tools-Stashing


Cảm ơn về gợi ý này, nhưng điều này sẽ không giúp ích khi tôi đã làm git stash pop- hoặc có cách nào để hoàn nguyên điều này và làm gì git stash apply --indexkhi tôi phát hiện ra điều đó git stash popsẽ xảy ra xung đột?
Sven

Tôi đã thêm một ví dụ về cách sản xuất cái này - hãy tưởng tượng bạn đang chỉnh sửa hơn 10 tệp, vì vậy bạn không biết bạn đã sửa đổi cái nào trong số chúng.
Sven

3
Nếu bạn nhìn vào phần dưới của bài đăng này TẠI ĐÂY, nó nói rằng nếu bạn chạy git stash popvà kết thúc bằng xung đột, stash sẽ không bị xóa ... vì vậy bạn có thể chạy git reset --hardđể hoàn tác pop và sau đó thử giải pháp tôi đề xuất.
Marco Ponti

Chỉ cần thử điều này và nó không hoạt động sau khi bạn có một tệp trong trạng thái xung đột. Ngay cả khi bạn khắc phục xung đột bằng tay.
Sam3k

2

git stash branchsẽ hoạt động, tạo ra một chi nhánh mới cho bạn, kiểm tra cam kết bạn đã thực hiện khi bạn sắp xếp công việc của mình, áp dụng lại công việc của bạn ở đó và sau đó bỏ stash nếu áp dụng thành công. kiểm tra cái này


2

Cách nhanh nhất tôi đã tìm thấy là giải quyết xung đột, sau đó làm git add -uvà sau đó làm git reset HEAD, điều đó thậm chí không liên quan đến một cam kết.


1

Theo các câu hỏi git stash , sau khi sửa chữa xung đột, git add <file>là hành động đúng đắn.

Đó là sau khi đọc bình luận này , tôi hiểu rằng những thay đổi sẽ tự động được thêm vào chỉ mục (theo thiết kế). Đó là lý do tại sao git add <file>hoàn thành quá trình giải quyết xung đột.


-1

Đây không phải là cách tốt nhất để làm điều đó nhưng nó hoạt động:

$ git stash apply
$ >> resolve your conflict <<
$ >> do what you want to do with your code <<
$ git checkout HEAD -- file/path/to/your/file

Câu trả lời này có vẻ sai đối với tôi, vì nó sẽ loại bỏ tất cả các thay đổi file/path/to/your/file, đó không phải là những gì OP yêu cầu, AFAIU
oromoiluig
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.