Một phần anh đào chọn một cam kết với Git


501

Tôi đang làm việc trên 2 nhánh khác nhau: phát hànhphát triển .

Tôi nhận thấy tôi vẫn cần tích hợp một số thay đổi đã được cam kết cho nhánh phát hành trở lại vào nhánh phát triển .

Vấn đề là tôi không cần tất cả các cam kết, chỉ một số người trong một số tệp nhất định, vì vậy đơn giản

git cherry-pick bc66559

không lừa

Khi tôi làm một

git show bc66559

Tôi có thể thấy khác biệt nhưng không thực sự biết một cách hay để áp dụng một phần vào cây làm việc hiện tại của mình.

Câu trả lời:


792

Điều cốt lõi mà bạn sẽ muốn ở đây là git add -p( -plà một từ đồng nghĩa với --patch). Điều này cung cấp một cách tương tác để kiểm tra nội dung, cho phép bạn quyết định xem mỗi hunk có nên vào hay không và thậm chí cho phép bạn chỉnh sửa bản vá theo cách thủ công nếu cần.

Để sử dụng kết hợp với cherry-pick:

git cherry-pick -n <commit> # get your patch, but don't commit (-n = --no-commit)
git reset                   # unstage the changes from the cherry-picked commit
git add -p                  # make all your choices (add the changes you do want)
git commit                  # make the commit!

(Cảm ơn Tim Henigan đã nhắc nhở tôi rằng git-cherry-pick có tùy chọn --no-commit và cảm ơn Felix Rabe vì đã chỉ ra rằng bạn cần đặt lại! , bạn có thể sử dụng git reset <path>...để hủy bỏ các tệp đó.)

Tất nhiên bạn có thể cung cấp các đường dẫn cụ thể đến add -pnếu cần thiết. Nếu bạn đang bắt đầu với một bản vá, bạn có thể thay thế cherry-pickbằng apply.


Nếu bạn thực sự muốn một git cherry-pick -p <commit>(tùy chọn đó không tồn tại), bạn có thể sử dụng

git checkout -p <commit>

Điều đó sẽ khác với cam kết hiện tại so với cam kết bạn chỉ định và cho phép bạn áp dụng các hunk từ riêng lẻ đó. Tùy chọn này có thể hữu ích hơn nếu cam kết mà bạn thực hiện có xung đột hợp nhất trong một phần của cam kết mà bạn không quan tâm. (Tuy nhiên, lưu ý checkoutkhác với cherry-pick: checkoutcố gắng áp dụng <commit>hoàn toàn nội dung, cherry-pickáp dụng khác biệt của cam kết được chỉ định từ cha mẹ của nó. Điều này có nghĩa là checkoutcó thể áp dụng nhiều hơn chỉ cam kết đó, có thể nhiều hơn bạn muốn.)


Trên thực tế, nếu tôi làm theo lời khuyên với git 1.7.5.4, 'git add -p' nói 'Không thay đổi' vì tất cả đã có trong chỉ mục. Tôi cần thực hiện 'git reset HEAD' trước 'git add' - có cách nào để tránh thiết lập lại đó với một số tùy chọn không?
Felix Rabe

@FelixRabe: Tôi thực sự ngạc nhiên khi đến một lúc nào đó cherry-pick -ndường như không để lại các thay đổi được tổ chức - quy ước chắc chắn là --no-commitcác tùy chọn dừng ngay trước khi cam kết, tức là với tất cả các thay đổi được tổ chức. Tôi sẽ thêm thiết lập lại vào câu trả lời.
Cascabel

1
@Blixt Nghĩa là, nếu các cam kết ở nhánh khác nhưng không phải là nhánh hiện tại của bạn là ABCDEF, git checkout -p <F>thì bạn không chỉ cần thay đổi từ F, nó sẽ giúp bạn ABCDEF hoàn toàn hòa trộn với nhau và cho phép bạn sắp xếp phần nào bạn muốn . Giảm bớt một số thay đổi từ F là một nỗi đau. Mặt khác, git cherry-pick -n <F>bạn chỉ nhận được các thay đổi từ F - và nếu một số thay đổi đó xung đột, nó sẽ giúp bạn biết một cách hữu ích để bạn có thể tìm ra cách hợp nhất đúng cách.
Cascabel

Tôi gặp vấn đề với điều này khi thay đổi là một bổ sung tập tin. Việc git resetnày sẽ loại bỏ các tập tin theo giai đoạn và add -psẽ chỉ nói 'không có gì để thêm'.
Ian Grainger


36

Giả sử những thay đổi bạn muốn nằm ở đầu chi nhánh bạn muốn thay đổi từ đó, hãy sử dụng thanh toán git

cho một tệp duy nhất:

git checkout branch_that_has_the_changes_you_want path/to/file.rb

cho nhiều tập tin chỉ chuỗi daisy:

git checkout branch_that_has_the_changes_you_want path/to/file.rb path/to/other_file.rb

5
Điều đó sao chép toàn bộ tập tin: không chỉ những thay đổi.
Danh sách Jeremy

1
Câu trả lời này, giống như bất kỳ câu trả lời nào khác ở đây cho đến nay, có một nhược điểm lớn: nó không bảo vệ tác giả ban đầu của sự thay đổi, thay vào đó là cam kết dưới tên của bạn. Nếu thay đổi là tốt, bạn đang đánh cắp tín dụng của ai đó, nếu thay đổi là xấu, bạn sẽ tự thiêu. Dường như không có cách nào để có git cherry-pick -p, và xấu hổ là nó vẫn không ở đó.
pfalcon

15

Dựa trên câu trả lời của Mike Monkiewicz, bạn cũng có thể chỉ định một hoặc nhiều tệp để kiểm tra từ sha1 / chi nhánh được cung cấp.

git checkout -p bc66559 -- path/to/file.java 

Điều này sẽ cho phép bạn tương tác chọn các thay đổi bạn muốn áp dụng cho phiên bản hiện tại của tệp.


5
Đây là vấn đề nếu phiên bản hiện tại của tệp khác biệt đáng kể so với phiên bản đó. Bạn sẽ được nhắc nhở với nhiều thay đổi không liên quan (không xuất hiện trong cam kết). Ngoài ra, những thay đổi bạn thực sự muốn có thể xuất hiện dưới dạng "hình thức ngụy trang" như một sự khác biệt so với hiện tại, không phải là sự khác biệt ban đầu. Có thể là sự khác biệt ban đầu mà bạn muốn xung đột và phải được hợp nhất chính xác; bạn sẽ không có cơ hội đó ở đây.
Kaz

1

Nếu bạn muốn chỉ định danh sách các tệp trên dòng lệnh và hoàn thành toàn bộ công việc trong một lệnh nguyên tử duy nhất, hãy thử:

git apply --3way <(git show -- list-of-files)

--3way: Nếu một bản vá không áp dụng sạch, Git sẽ tạo xung đột hợp nhất để bạn có thể chạy git mergetool. Bỏ qua --3waysẽ khiến Git từ bỏ các bản vá không áp dụng sạch.


0

Nếu "chọn cherry một phần" có nghĩa là "trong các tệp, chọn một số thay đổi nhưng loại bỏ các phần khác", thì có thể thực hiện bằng cách đưa vào git stash:

  1. Làm anh đào đầy đủ chọn.
  2. git reset HEAD^ để chuyển đổi toàn bộ cam kết chọn anh đào thành các thay đổi làm việc không được tổ chức.
  3. Bây giờ git stash save --patch: tương tác chọn vật liệu không mong muốn để stash.
  4. Git khôi phục các thay đổi được lưu trữ từ bản sao làm việc của bạn.
  5. git commit
  6. Vứt bỏ đống rác của những thay đổi không mong muốn : git stash drop.

Mẹo: nếu bạn cung cấp cho stash các thay đổi không mong muốn một tên: git stash save --patch junksau đó nếu bạn quên thực hiện (6) bây giờ, sau này bạn sẽ nhận ra stash cho nó là gì.


Nếu bạn chỉ chọn cherry, sau đó đặt lại ... bạn sẽ không có gì để bỏ. Như đã nói ở trên, bạn phải thực hiện một lựa chọn anh đào với --no-commit hoặc thiết lập lại - HÃY ĐẦU TIÊN ~. Lưu ý rằng bạn đang xử lý tiêu cực (chọn những gì bạn không muốn). Các giải pháp được chấp nhận ở trên cho phép một cách tiếp cận tích cực hoặc tiêu cực.
Pierre-Olivier Vares

0

Sử dụng git format-patchđể cắt ra một phần của cam kết mà bạn quan tâm và git amáp dụng nó cho một chi nhánh khác

git format-patch <sha> -- path/to/file
git checkout other-branch
git am *.patch
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.