Cách chọn cherry nhiều lượt


875

Tôi có hai chi nhánh. Cam kết alà người đứng đầu của một, trong khi người kia có b, c, d, eftrên đầu trang của a. Tôi muốn di chuyển c, d, efđến chi nhánh đầu tiên mà không cam kết b. Sử dụng cherry nhặt nó rất dễ dàng: kiểm tra chi nhánh đầu tiên anh đào tự tay chọn từng người một cđể fvà rebase chi nhánh thứ hai vào đầu tiên. Nhưng có cách nào để chọn tất cả anh đào c- ftrong một lệnh không?

Dưới đây là một mô tả trực quan của kịch bản (cảm ơn JJD ):

nhập mô tả hình ảnh ở đây


3
cuộc nổi loạn mà bạn đề cập không thực sự phù hợp cho câu hỏi phải không? (Tôi hiểu rằng bạn có thể muốn bdựa vào fsau này, nhưng điều đó không liên quan gì đến việc hái anh đào.)
Superole

Câu trả lời:


1282

Git 1.7.2 đã giới thiệu khả năng cherry chọn một loạt các cam kết. Từ ghi chú phát hành :

git cherry-pickđã học cách chọn một loạt các cam kết (ví dụ cherry-pick A..Bcherry-pick --stdin), cũng vậy git revert; những cái này không hỗ trợ điều khiển trình tự đẹp hơn rebase [-i], mặc dù.

Để chọn cherry tất cả các cam kết từ cam kết Ađến cam kết B(nơi Acũ hơn B), hãy chạy:

git cherry-pick A^..B

Nếu bạn muốn bỏ qua chính A, hãy chạy:

git cherry-pick A..B

(Tín dụng cho damian, JB Rainsberger và sschaef trong các bình luận)


249
Ở dạng "cherry-pick A..B", A nên lớn hơn B. Nếu chúng sai thứ tự, lệnh sẽ âm thầm thất bại.
damian

294
Ngoài ra, điều này sẽ không chọn A, mà là tất cả mọi thứ sau A lên đến và bao gồm cả B.
JB Rainsberger

455
Để bao gồm A chỉ gõgit cherry-pick A^..B
kiritsuku

17
Nếu bạn có git 1.7.1 hoặc sớm hơn và có thể không cập nhật, bạn có thể khá nhanh chóng cherry-chọn chúng theo thứ tự bằng cách chạy git cherry-pick f~3rồi git cherry-pick f~2, vv lên đến git cherry-pick f(nhấn mũi tên lên được lệnh trước đó vì vậy tôi có thể nhanh chóng thay đổi số và chạy nó, nên giống nhau trong hầu hết các bảng điều khiển).
David Mason

19
Có thể là tốt để biết rằng cú pháp này cũng hoạt động với tên chi nhánh. git cherry-pick master..somebranchsẽ chọn tất cả các xác nhận trên somebranch kể từ khi chủ (giả sử đã được chuyển sang chủ) và áp dụng chúng cho chi nhánh hiện tại của bạn.
Tor Klingberg

103

Cách đơn giản nhất để làm điều này là với ontotùy chọn rebase. Giả sử rằng nhánh mà hiện tại kết thúc ađược gọi là mybranch và đây là nhánh mà bạn muốn di chuyểnc - flên.

# checkout mybranch
git checkout mybranch

# reset it to f (currently includes a)
git reset --hard f

# rebase every commit after b and transplant it onto a
git rebase --onto a b

1
Cảm ơn bạn! Bạn cũng có thể thêm git checkout secondbranch && git rebase mybranchcâu trả lời đầy đủ
tig

1
Câu trả lời này đã giúp tôi rất nhiều để hiểu được cam kết trong kịch bản này. Và: bạn cũng có thể sử dụng rebasechế độ tương tác. Cảm ơn, @Charles!
Oliver

1
Cái hay của phương pháp này là bạn có thể sử dụng --interactiveđể loại bỏ một số cam kết khỏi chuỗi hoặc sắp xếp lại chúng trước khi "chọn anh đào". +1
Michael Merickel

Đây là một lệnh tuyệt vời, một chút khó khăn để quấn đầu bạn, nhưng nó hoạt động kỳ diệu.
Valerio

Thật thú vị khi bạn phải đưa ra cam kết rằng bạn không muốn phản đối như một trong những đối số ( btrong ví dụ này) nhưng vâng, điều này đã làm việc cho tôi.
asontu

85

Hoặc một lớp lót được yêu cầu:

git rebase --onto a b f

5
Nếu chỉ vì sự ngắn gọn của nó, đây là câu trả lời tốt nhất.
Nate Chandler

9
Được nâng cấp nhưng sẽ khiến bạn ở trạng thái CHÍNH tách rời nếu f là một cam kết (trái ngược với một chi nhánh) - bạn nên chỉnh sửa để thêm một chi nhánh như trong câu trả lời dưới đây
Mr_and_Mrs_D

68

Bạn có thể sử dụng kết hợp nối tiếp git rebasegit branchđể áp dụng một nhóm các cam kết lên một nhánh khác. Như đã được đăng bởi wolfc , lệnh đầu tiên thực sự sao chép các xác nhận. Tuy nhiên, thay đổi không hiển thị cho đến khi bạn thêm tên chi nhánh vào cam kết cao nhất của nhóm.

Vui lòng mở hình ảnh trong một tab mới ...

Quy trình làm việc

Để tóm tắt các lệnh ở dạng văn bản:

  1. Mở gitk như một quy trình độc lập bằng cách sử dụng lệnh : gitk --all &.
  2. Chạy đi git rebase --onto a b f.
  3. Nhấn F5vào gitk . Không có gì thay đổi. Nhưng khôngHEAD đánh dấu.
  4. Chạy git branch selection
  5. Nhấn F5vào gitk . Chi nhánh mới với cam kết của nó xuất hiện.

Điều này sẽ làm rõ mọi thứ:

  • Cam kết alà đích đến mới của nhóm.
  • Cam kết b là cam kết trước cam kết đầu tiên của nhóm (độc quyền).
  • Cam kết flà cam kết cuối cùng của nhóm (bao gồm).

Sau đó, bạn có thể sử dụng git checkout feature && git reset --hard bđể xóa các cam kết cđến ftừfeature chi nhánh.

Ngoài câu trả lời này, tôi đã viết một bài đăng blog mô tả các lệnh trong một kịch bản khác sẽ giúp sử dụng chung nó.


2
Nếu mybranch (a..f cam kết) không còn cần thiết nữa thì điều này có thể được đơn giản hóa thành: git rebase --onto a b mybranchvà btw - chương trình nào thực hiện những hình ảnh git tiện lợi đó?
Mr_and_Mrs_D

2
@Mr_and_Mrs_D Cảm ơn bình luận của bạn. Tôi nghĩ rằng tôi đã sử dụng cacoo.com để vẽ những bức tranh.
JJD

48

Để áp dụng các nhận xét của JB Rainsberger và sschaef để trả lời cụ thể câu hỏi ... Để sử dụng phạm vi chọn cherry trong ví dụ này:

git checkout a
git cherry-pick b..f

hoặc là

git checkout a
git cherry-pick c^..f

3
Tôi sử dụng git 2.7.0.windows.1và nhận thấy rằng khi tôi cố gắng chọn phạm vi cam kết, mọi thứ đều ổn nhưng git không cho bạn biết bất cứ nơi nào bạn phải làm git cherry-pick --continue | --abort | --quittrước khi bạn cố gắng cam kết / chọn lại anh đào. Vì vậy, nếu bạn chọn phạm vi cam kết, bạn sẽ cần chạy git cherry-pick --continuemỗi khi bạn sẵn sàng (giải quyết xung đột hoặc tương tự) với một cam kết từ phạm vi đã cho.
kuskmen

Tôi đã làm chính xác như vậy, nhưng gây tử vong: Không thể tìm thấy 'a..b'
Amit Karnik

Tôi không biết mình sai ở đâu nhưng khi tôi thực hiện 'git cherry-pick c ^ .. f' về phía tôi, điều này bao gồm cả cam kết f nhưng không phải là cam kết c. Nhưng khi tôi đọc ở khắp mọi nơi, nó được cho là định nghĩa c và f là bao gồm. Hoặc là tôi sai?
Samuel

@Samuel vâng, đúng vậy. Các ^sau khi c thực sự có nghĩa là "cam kết trước khi c", đó là b trong trường hợp này. Đây là lý do tại sao c^..fđồng nghĩa với b..f. Hãy thử làm git log c^..fvà bạn sẽ thấy các cam kết từ c đến f, giống hệt như bạn đã làmgit log b..f
Andy

41

Nếu bạn có các sửa đổi chọn lọc để hợp nhất, giả sử A, C, F, J từ A, B, C, D, E, F, G, H, I, J cam kết, chỉ cần sử dụng lệnh dưới đây:

git cherry-pick ACFJ


1
đẹp và đơn giản
spinup

21
git rev-list --reverse b..f | xargs -n 1 git cherry-pick

2
Hoạt động hoàn hảo nếu không có xung đột, nếu không thì "rebase on" có thể dễ dàng hơn vì bạn sẽ không phải tìm ra nơi nó đã dừng và áp dụng lại các bản vá còn lại.
Ruslan Kabalin

6
Vui lòng thêm ý kiến ​​giải thích những gì đang làm
Mr_and_Mrs_D

7
vì không ai giải thích ... danh sách git rev in tất cả các bản sửa đổi từ nhánh b sang f (đảo ngược) để khi mỗi dòng (hàm băm cam kết) được chuyển theo thứ tự, anh đào sẽ chọn từng bản lên TRÊN Git hiện tại. tức làgit cherry-pick {hash of c}; git cherry-pick {hash of d}; ...
coderatchet

8

Để chọn cherry từ một id xác nhận cho đến đầu cành, bạn có thể sử dụng:

git cherry-pick commit_id^..branch_name


Đây đã là một phần của câu trả lời stackoverflow.com/a/31640427/96823
tig

1
Câu trả lời này thực sự khác biệt và hữu ích với tôi. Nó chỉ định tên chi nhánh, không phải SHA cam kết cuối cùng.
Subtletree

7

Một biến thể khác đáng được đề cập là nếu bạn muốn các ncam kết cuối cùng từ một nhánh, ~cú pháp có thể hữu ích:

git cherry-pick some-branch~4..some-branch

Trong trường hợp này, lệnh trên sẽ chọn 4 lần xác nhận cuối cùng từ một nhánh được gọi some-branch(mặc dù bạn cũng có thể sử dụng hàm băm cam kết thay cho tên nhánh)


1
Câu trả lời RẤT hữu ích, cảm ơn bạn! :)
Jacek Dziurdzikowski 11/12/19

3

Trên thực tế, cách đơn giản nhất để làm điều đó có thể là:

  1. ghi lại cơ sở hợp nhất giữa hai nhánh: MERGE_BASE=$(git merge-base branch-a branch-b)
  2. chuyển tiếp nhanh hoặc đẩy lùi nhánh cũ lên nhánh mới hơn
  3. khởi động lại nhánh kết quả vào chính nó, bắt đầu từ cơ sở hợp nhất từ ​​bước 1 và xóa thủ công các cam kết không mong muốn:

    git rebase ${SAVED_MERGE_BASE} -i
    

    Ngoài ra, nếu chỉ có một vài cam kết mới, hãy bỏ qua bước 1 và chỉ cần sử dụng

    git rebase HEAD^^^^^^^ -i
    

    trong bước đầu tiên, sử dụng đủ ^để di chuyển qua cơ sở hợp nhất.

Bạn sẽ thấy một cái gì đó như thế này trong rebase tương tác:

pick 3139276 commit a
pick c1b421d commit b
pick 7204ee5 commit c
pick 6ae9419 commit d
pick 0152077 commit e
pick 2656623 commit f

Sau đó xóa các dòng b (và bất kỳ dòng nào khác bạn muốn)


1
git format-patch --full-index --binary --stdout range... | git am -3

42
Vui lòng thêm ý kiến ​​giải thích những gì đang làm
Mr_and_Mrs_D

0

Đây là một tập lệnh sẽ cho phép bạn chọn nhiều cam kết liên tiếp chỉ bằng cách cho tập lệnh biết nguồn nào và các nhánh đích cho các lượt chọn anh đào và số lần xác nhận:

https://gist.github.com/nickboldt/99ac1dc4eb4c9ff003a1effef2eb2d81

Để chọn cherry từ chi nhánh của bạn thành chủ (sử dụng chi nhánh hiện tại làm nguồn):

./gcpl.sh -m

Để chọn cherry 5 lần cam kết mới nhất từ ​​chi nhánh 6.19.x của bạn thành chủ:

./gcpl.sh -c 5 -s 6.19.x -t master

Tại sao tập lệnh này lại cần thiết khi bạn có thể làm điều đó với Git trực tiếp? ...
code_dredd

Ít gõ hơn và tập lệnh của tôi sẽ tự động chọn các cam kết cho bạn, vì vậy bạn không phải chọn cherry theo từng SHA. Dù sao, YMMV.
nickboldt
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.