2010-01-03 25 views
457

Tôi có bố trí kho lưu trữ sau đây:Làm thế nào để anh đào chọn một loạt các cam kết và hợp nhất thành một chi nhánh khác

  • thạc sĩ ngành (sản xuất)
  • hội nhập
  • làm việc

Những gì tôi muốn đạt được là anh đào chọn một loạt các cam kết từ các chi nhánh làm việc và hợp nhất nó vào nhánh hội nhập. Tôi khá mới để git và tôi không thể tìm ra cách chính xác làm điều này (anh đào chọn phạm vi cam kết trong một hoạt động không phải là sáp nhập) mà không gây rối các kho lưu trữ lên. Bất kỳ con trỏ hay suy nghĩ về điều này? Cảm ơn!

+0

http://www.draconianoverlord.com/2013/09/07/no-cherry-picking.html (không phải blog của tôi) –

Trả lời

598

Khi nói đến một loạt các cam kết, chọn hoa anh đào không thực tế.

Như mentioned below bởi Keith Kim, Git 1.7.2+ giới thiệu khả năng anh đào-chọn một loạt các cam kết (nhưng bạn vẫn cần phải nhận thức được các consequence of cherry-picking for future merge)

git cherry-pick" learned to pick a range of commits
(e.g. " cherry-pick A..B " and " cherry-pick --stdin "), so did " git revert "; these do not support the nicer sequencing control " rebase [-i] " has, though.

damiancomments và cảnh báo với chúng tôi:

In the " cherry-pick A..B " form, A should be older than B.
If they're the wrong order the command will silently fail.

Nếu bạn muốn chọn phạm vi B qua D (bao gồm) sẽ là B^..D.
Xem "Git create branch from range of previous commits?" làm minh họa.

Như Jubobs đề cập in the comments:

This assumes that B is not a root commit; you'll get an " unknown revision " error otherwise.

Lưu ý: như của Git 2.9.x/2.10 (Q3 2016), bạn có thể cherry-chọn một loạt các cam kết trực tiếp trên một chi nhánh trẻ mồ côi (trống đầu): xem "How to make existing branch an orphan in git".


câu trả lời gốc (January 2010)

Một rebase --onto sẽ tốt hơn, nơi bạn phát lại phạm vi nhất định cam kết trên đầu trang của chi nhánh tích hợp của bạn, như Charles Bailey described here.
(cũng có, tìm kiếm "Đây là cách bạn sẽ cấy ghép một chi nhánh chủ đề dựa trên một chi nhánh khác" trong git rebase man page, để xem một ví dụ thực tế của git rebase --onto)

Nếu chi nhánh hiện tại của bạn là tích hợp:

# Checkout a new temporary branch at the current location 
git checkout -b tmp 

# Move the integration branch to the head of the new patchset 
git branch -f integration last_SHA-1_of_working_branch_range 

# Rebase the patchset onto tmp, the old location of integration 
git rebase --onto tmp first_SHA-1_of_working_branch_range~1 integration 

Điều đó sẽ phát lại tất cả mọi thứ giữa:

  • sau khi mẹ của first_SHA-1_of_working_branch_range (vì thế ~1): cam kết đầu tiên bạn muốn phát lại
  • lên đến "integration" (mà chỉ vào cam kết cuối cùng bạn muốn phát lại, từ chi nhánh working)

để "tmp" (mà chỉ vào nơi integration được chỉ trước đó)

Nếu có bất kỳ mâu thuẫn khi một trong những cam kết là tái hiện lại:

  • hoặc giải quyết nó và chạy "git rebase --continue".
  • hoặc bỏ bản vá này, và thay vào đó chạy "git rebase --skip"
  • hoặc hủy bỏ tất cả các điều với một "git rebase --abort" (và đưa trở lại các chi nhánh integration trên cành tmp)

Sau đó rebase --onto, integration sẽ quay trở lại cam kết cuối cùng của nhánh hội nhập (nghĩa là "tmp" chi nhánh + tất cả các khoản cam kết được phát lại)

Với lựa chọn anh đào hoặc rebase --onto, đừng quên nó có hậu quả trên phân lớp con nt hợp nhất, như described here.


Một tinh khiết "cherry-pick" Giải pháp là discussed here, và sẽ bao gồm một cái gì đó như:

If you want to use a patch approach then "git format-patch|git am" and "git cherry" are your options.
Currently, git cherry-pick accepts only a single commit, but if you want to pick the range B through D that would be B^..D in git lingo, so

git rev-list --reverse --topo-order B^..D | while read rev 
do 
    git cherry-pick $rev || break 
done 

Nhưng dù sao, khi bạn cần phải "phát lại" một loạt các cam kết, từ " phát lại "sẽ đẩy bạn sử dụng tính năng" rebase "của Git.

+0

Nếu bạn có cam kết có cha mẹ yêu cầu tùy chọn '-m', thì làm cách nào bạn xử lý những cam kết đó? Hoặc có cách nào để lọc ra các cam kết này không? – aug

+0

@aug '-m' là nghĩa vụ phải xử lý chúng cho bạn, bằng cách chọn đường chính được tham chiếu bởi tham số' -m' mà bạn đã chọn cho việc chọn anh đào này. – VonC

+0

Điều này là nếu bạn đang anh đào chọn một loạt các cam kết, nó sẽ cherry chọn phụ huynh cam kết một cách chính xác nhưng sau đó khi nó chạm một cam kết bình thường, nó không thành công và nói cam kết không phải là một hợp nhất. Tôi đoán câu hỏi của tôi là tốt hơn phrased làm thế nào để làm cho nó vượt qua tùy chọn '-m' chỉ khi nó chạm vào một phụ huynh cam kết khi anh đào-hái phạm vi cam kết? Ngay bây giờ nếu tôi chuyển '-m' như' git cherry-pick a87afaaf..asfa789 -m 1' thì nó áp dụng cho tất cả các commit trong phạm vi. – aug

22

Bạn có chắc chắn không muốn hợp nhất các chi nhánh không? Nếu nhánh đang hoạt động có một số cam kết gần đây mà bạn không muốn, bạn có thể tạo một nhánh mới với một HEAD tại điểm mà bạn muốn.

Bây giờ, nếu bạn thực sự muốn anh đào-chọn một loạt các cam kết, vì lý do gì, một cách thanh lịch để làm điều này là chỉ cần kéo một nghĩa patchset và áp dụng nó vào chi nhánh hội nhập mới của bạn:

git format-patch A..B 
git checkout integration 
git am *.patch 

Đây thực chất là những gì git-rebase đang thực hiện, nhưng không cần chơi trò chơi. Bạn có thể thêm --3way vào git-am nếu bạn cần hợp nhất. Đảm bảo không có tệp * .patch nào khác đã có trong thư mục nơi bạn thực hiện việc này, nếu bạn làm theo hướng dẫn đúng nguyên văn ...

7

tôi quấn VonC's code vào một kịch bản bash ngắn, git-multi-cherry-pick, để dễ dàng chạy:

#!/bin/bash 

if [ -z $1 ]; then 
    echo "Equivalent to running git-cherry-pick on each of the commits in the range specified."; 
    echo ""; 
    echo "Usage: $0 start^..end"; 
    echo ""; 
    exit 1; 
fi 

git rev-list --reverse --topo-order $1 | while read rev 
do 
    git cherry-pick $rev || break 
done 

Tôi hiện đang sử dụng này như tôi xây dựng lại lịch sử của một dự án mà có cả mã của bên thứ 3 và các tùy chỉnh trộn cùng nhau trong cùng một svn trunk. Bây giờ tôi chia tách mã bên thứ ba cốt lõi, các mô-đun bên thứ 3 và các tùy chỉnh thành các nhánh git của riêng họ để hiểu rõ hơn về các tùy chỉnh trong tương lai. git-cherry-pick hữu ích trong trường hợp này vì tôi có hai cây trong cùng một kho lưu trữ, nhưng không có tổ tiên được chia sẻ.

112

Tính đến git v1.7.2 cherry chọn có thể chấp nhận một loạt các cam kết:

git cherry-pick learned to pick a range of commits (e.g. cherry-pick A..B and cherry-pick --stdin), so did git revert ; these do not support the nicer sequencing control rebase [-i] has, though.

+78

Lưu ý rằng 'cherry-pick A..B' sẽ không nhận được commit A (bạn sẽ cần' A ~ 1..B' cho điều đó), và nếu có bất kỳ xung đột nào, git sẽ không tự động tiếp tục như rebase (ít nhất là 1.7.3.1) –

+2

Cũng rất đáng lưu ý rằng 'git cherry-pick A..BC' không hoạt động như bạn mong đợi, ngây thơ. Nó sẽ không chọn mọi thứ trong phạm vi 'A..B' và commit' C'! Để làm điều này, bạn cần chia thành hai dòng, đầu tiên là 'git cherry-pick A..B' và sau đó là 'git cherry-pick C'. Vì vậy, bất cứ khi nào bạn có một phạm vi, bạn cần phải thực hiện nó một cách riêng biệt. – MicroVirus

0

Một lựa chọn khác có thể là để hợp nhất với chúng ta chiến lược cam kết trước khi phạm vi và sau đó một 'bình thường' hợp nhất với cam kết cuối cùng của phạm vi đó (hoặc chi nhánh khi nó là người cuối cùng). Vì vậy, giả sử chỉ 2345 và 3456 cam kết của tổng thể để được sáp nhập vào chi nhánh tính năng:

 
master: 
1234 
2345 
3456 
4567 

tại chi nhánh tính năng:

 
git merge -s ours 4567 
git merge 2345 
2

Tất cả các tùy chọn ở trên sẽ nhắc bạn để giải quyết xung đột nhập. Nếu bạn đang hợp nhất các thay đổi đã cam kết cho một nhóm, rất khó để giải quyết các xung đột hợp nhất từ ​​các nhà phát triển và tiếp tục. Tuy nhiên, "git merge" sẽ thực hiện hợp nhất trong một cảnh nhưng bạn không thể vượt qua một loạt các sửa đổi làm đối số. chúng tôi phải sử dụng lệnh "git diff" và "git apply" để thực hiện phạm vi hợp nhất các vòng quay. Tôi đã quan sát thấy rằng "git apply" sẽ thất bại nếu tập tin vá lỗi có quá nhiều tập tin, vì vậy chúng ta phải tạo một bản vá cho mỗi tập tin và sau đó áp dụng. Lưu ý rằng tập lệnh sẽ không thể xóa các tệp bị xóa trong nhánh nguồn. Đây là trường hợp hiếm hoi, bạn có thể xóa thủ công các tệp đó khỏi nhánh đích. Trạng thái thoát của "git apply" không phải là số không nếu nó không thể áp dụng các bản vá, tuy nhiên nếu bạn sử dụng tùy chọn -3way nó sẽ rơi trở lại 3 cách hợp nhất và bạn không phải lo lắng về sự thất bại này.

Dưới đây là tập lệnh.

enter code here 



    #!/bin/bash 

    # This script will merge the diff between two git revisions to checked out branch 
    # Make sure to cd to git source area and checkout the target branch 
    # Make sure that checked out branch is clean run "git reset --hard HEAD" 


    START=$1 
    END=$2 

    echo Start version: $START 
    echo End version: $END 

    mkdir -p ~/temp 
    echo > /tmp/status 
    #get files 
    git --no-pager diff --name-only ${START}..${END} > ~/temp/files 
    echo > ~/temp/error.log 
    # merge every file 
    for file in `cat ~/temp/files` 
    do 
     git --no-pager diff --binary ${START}..${END} $file > ~/temp/git-diff 
     if [ $? -ne 0 ] 
     then 
#  Diff usually fail if the file got deleted 
     echo Skipping the merge: git diff command failed for $file >> ~/temp/error.log 
     echo Skipping the merge: git diff command failed for $file 
     echo "STATUS: FAILED $file" >> /tmp/status 
     echo "STATUS: FAILED $file" 
    # skip the merge for this file and continue the merge for others 
     rm -f ~/temp/git-diff 
     continue 
     fi 

     git apply --ignore-space-change --ignore-whitespace --3way --allow-binary-replacement ~/temp/git-diff 

     if [ $? -ne 0 ] 
     then 
# apply failed, but it will fall back to 3-way merge, you can ignore this failure 
     echo "git apply command filed for $file" 
     fi 
     echo 
     STATUS=`git status -s $file` 


     if [ ! "$STATUS" ] 
     then 
# status is null if the merged diffs are already present in the target file 
     echo "STATUS:NOT_MERGED $file" 
     echo "STATUS: NOT_MERGED $file$" >> /tmp/status 
     else 
#  3 way merge is successful 
     echo STATUS: $STATUS 
     echo "STATUS: $STATUS" >> /tmp/status 
     fi 
    done 

    echo GIT merge failed for below listed files 

    cat ~/temp/error.log 

    echo "Git merge status per file is available in /tmp/status" 
1

Giả sử rằng bạn có 2 chi nhánh,

"branchA": bao gồm các cam kết mà bạn muốn sao chép (từ "commitA" thành "commitB"

"branchB": chi nhánh mà bạn muốn cam kết sẽ được chuyển từ "branchA"

1)

git checkout <branchA> 

2) có được ID của "comm ITA" và "commitB"

3)

git checkout <branchB> 

4)

git cherry-pick <commitA>^..<commitB> 

5) Trong trường hợp bạn có một cuộc xung đột, giải quyết nó và gõ

git cherry-pick --continue 

để tiếp tục quá trình chọn anh đào.

Các vấn đề liên quan