วิธีเชอร์รี่เลือกช่วงของความมุ่งมั่นและรวมเข้ากับสาขาอื่นได้อย่างไร


640

ฉันมีเลย์เอาต์ที่เก็บต่อไปนี้:

  • สาขาหลัก (การผลิต)
  • บูรณาการ
  • การทำงาน

สิ่งที่ฉันต้องการบรรลุคือการเลือกเชอร์รี่ที่หลากหลายจากสาขาการทำงานและรวมเข้ากับสาขาการรวม ฉันค่อนข้างใหม่กับคอมไพล์และฉันไม่สามารถหาวิธีการทำเช่นนี้ได้อย่างแน่นอน (การเลือกช่วงเชอร์รี่ของการกระทำในการดำเนินการหนึ่งไม่ใช่การรวม) โดยไม่ทำให้ที่เก็บข้อมูลสับสน คำแนะนำหรือความคิดเกี่ยวกับเรื่องนี้? ขอบคุณ!


1
draconianoverlord.com/2013/09/07/no-cherry-picking.html (ไม่ใช่บล็อกของฉัน)
Christophe Roussy

คำตอบ:


808

เมื่อพูดถึงภารกิจที่หลากหลาย คือ ก็ไม่จริง

ในฐานะที่ระบุไว้ด้านล่างโดยคี ธ คิม , Git 1.7.2+ แนะนำความสามารถในการเชอร์รี่เลือกช่วงของการกระทำ ( แต่คุณยังคงต้องตระหนักถึงผลที่ตามมาของเชอร์รี่ยกสำหรับการผสานอนาคต )

เชอร์รี่รับของคอมไพล์" เรียนรู้ที่จะรับช่วงของการกระทำ
(เช่น ' cherry-pick A..B' และ ' cherry-pick --stdin') เพื่อให้ได้' git revert'เหล่านี้ไม่สนับสนุนการควบคุมลำดับดีกว่า' rebase [-i]' มี แต่

เดเมียน แสดงความคิดเห็นและเตือนเรา:

ใน " cherry-pick A..B" รูปแบบควรจะเก่ากว่าA Bหากพวกเขากำลังผิดลำดับคำสั่งเงียบจะล้มเหลว

หากคุณต้องการที่จะเลือกช่วงBผ่านD(รวม)B^..Dที่จะเป็น
ดูที่ " Git สร้างสาขาจากช่วงที่ทำก่อนหน้านี้? " เป็นภาพประกอบ

ในฐานะที่เป็นJubobsกล่าวถึงในความคิดเห็น :

นี่ถือว่าBไม่ใช่การกระทำที่รูท; คุณจะได้รับunknown revisionข้อผิดพลาด ""

หมายเหตุ: ตั้งแต่ Git 2.9.x / 2.10 (ไตรมาสที่ 3 ปี 2559) คุณสามารถเลือกช่วงของการกระทำโดยตรงในสาขาเด็กกำพร้า (หัวว่าง): ดู " วิธีทำให้สาขาที่มีอยู่เป็นเด็กกำพร้าในคอมไพล์ "


คำตอบเดิม (มกราคม 2010)

rebase --ontoจะดีกว่าที่คุณเล่นในช่วงที่กำหนดของการกระทำที่ด้านบนของสาขาบูรณาการของคุณเป็นชาร์ลส์เบลีย์อธิบายไว้ที่นี่
(เช่นเดียวกับค้นหา "นี่คือวิธีที่คุณจะปลูกสาขาหัวข้อตามสาขาหนึ่งไปยังอีก" ในหน้า man rebase gitเพื่อดูตัวอย่างการปฏิบัติของgit rebase --onto)

หากสาขาปัจจุบันของคุณคือการรวม:

# 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

ที่จะเล่นซ้ำทุกสิ่งระหว่าง:

  • หลังจากที่ผู้ปกครองของfirst_SHA-1_of_working_branch_range(จึง~1): คนแรกที่กระทำคุณต้องการที่จะเล่นซ้ำ
  • มากถึง " integration" (ซึ่งชี้ไปที่การส่งมอบครั้งสุดท้ายที่คุณต้องการเล่นซ้ำจากworkingสาขา)

ถึง " tmp" (ซึ่งชี้ไปยังตำแหน่งที่integrationชี้ก่อน)

หากมีข้อขัดแย้งใด ๆ เมื่อหนึ่งในข้อผูกพันเหล่านั้นถูกเล่นซ้ำ:

  • แก้ปัญหาและเรียกใช้ " git rebase --continue"
  • หรือข้ามแพทช์นี้และรัน " git rebase --skip" แทน
  • หรือยกเลิกทุกสิ่งด้วย " git rebase --abort" (และนำintegrationสาขากลับมาที่tmpสาขา)

หลังจากนั้นrebase --onto, integrationจะกลับไปที่สุดท้ายกระทำของสาขาบูรณาการ (นั่นคือ " tmpสาขา" + ทุกกระทำเล่นซ้ำ)

ด้วยเชอร์รี่แคะหรือrebase --ontoไม่ลืมที่จะมีผลกระทบในการผสานต่อมาเป็นอธิบายไว้ที่นี่


มีcherry-pickการกล่าวถึงวิธีแก้ปัญหาที่บริสุทธิ์และจะเกี่ยวข้องกับสิ่งต่อไปนี้:

หากคุณต้องการใช้วิธีแก้ไข "git format-patch | git am" และ "git cherry" เป็นตัวเลือกของคุณ
ปัจจุบันgit cherry-pickยอมรับเพียงคนเดียวกระทำ แต่ถ้าคุณต้องการที่จะรับช่วงBผ่านDที่จะเป็นB^..Dในคอมไพล์ศัพท์แสงดังนั้น

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

แต่อย่างไรก็ตามเมื่อคุณต้องการ "เล่นซ้ำ" ช่วงคำว่า "เล่นซ้ำ" ควรผลักดันให้คุณใช้rebaseคุณสมบัติ "" ของ Git


1
หากคุณมีข้อผูกพันที่มีผู้ปกครองที่ต้องการ-mตัวเลือกคุณจะจัดการข้อผูกพันเหล่านั้นได้อย่างไร หรือมีวิธีการกรองการกระทำเหล่านี้หรือไม่
สิงหาคม

@aug -mควรจัดการกับพวกมันให้คุณโดยเลือกการฉีดที่อ้างอิงโดย-mพารามิเตอร์ที่คุณเลือกสำหรับการเก็บเชอร์รี่นี้
VonC

สิ่งคือถ้าคุณเป็นเชอร์รี่เลือกช่วงของการกระทำมันจะเชอร์รี่เลือกผู้ปกครองกระทำอย่างถูกต้อง แต่เมื่อมันกระทบการกระทำปกติมันล้มเหลวและบอกว่ากระทำไม่ได้ผสาน ฉันเดาว่าคำถามของฉันดีกว่าที่เป็นวลีที่ว่าจะทำให้มันผ่าน-mตัวเลือกได้เมื่อผู้ปกครองตกลงเมื่อช่วงเชอร์รี่เลือกรับ ตอนนี้ถ้าฉันผ่าน-mเหมือนgit cherry-pick a87afaaf..asfa789 -m 1มันใช้กับทุกการกระทำภายในช่วง
สิงหาคม

@aug แปลกฉันไม่ได้ทำซ้ำปัญหา รุ่น git ของคุณคืออะไรและอะไรคือข้อความแสดงข้อผิดพลาดที่คุณเห็น?
VonC

ฉันใช้ git เวอร์ชั่น 2.6.4 อยู่แล้ว (Apple Git-63) ข้อผิดพลาดที่ผมเห็นจะเป็นสิ่งที่ชอบerror: Commit 8fcaf3b61823c14674c841ea88c6067dfda3af48 is a merge but no -m option was given.ฉันรู้จริงคุณสามารถเพียงgit cherry-pick --continueและมันจะดี ( แต่มันจะไม่รวมถึงผู้ปกครองกระทำ)
สิงหาคม

136

ในฐานะของ git v1.7.2 cherry pick สามารถยอมรับช่วงของการกระทำ:

git cherry-pickเรียนรู้ที่จะเลือกช่วงของการกระทำ (เช่นcherry-pick A..Bและcherry-pick --stdin) ทำเช่นนั้นgit revert; rebase [-i]แม้ว่าสิ่งเหล่านี้จะไม่สนับสนุนการควบคุมการเรียงลำดับที่ดีกว่า


103
โปรดทราบว่าcherry-pick A..Bจะไม่ได้รับการส่งมอบ A (คุณจะต้องA~1..Bทำเช่นนั้น) และหากมีข้อขัดแย้งใด ๆ คอมไพล์จะไม่ดำเนินการต่อโดยอัตโนมัติเช่นเดียวกับการรีบูต (อย่างน้อย 1.7.3.1)
Gabe Moothart

3
นอกจากนี้ยังเป็นเรื่องที่ดีที่git cherry-pick A..B Cจะไม่ทำงานเหมือนที่คุณคาดหวังไว้อย่างไร้เดียงสา มันจะไม่เลือกทุกอย่างที่อยู่ในระยะA..BและคอมมิทC! การทำเช่นนี้คุณจะต้องแยกออกเป็นสองสายแรกแล้วgit cherry-pick A..B git cherry-pick Cดังนั้นเมื่อใดก็ตามที่คุณมีช่วงคุณต้องดำเนินการแยกต่างหาก
MicroVirus

42

สมมติว่าคุณมี 2 สาขา

"branchA": รวมการกระทำที่คุณต้องการคัดลอก (จาก "commitA" ถึง "commitB"

"branchB": สาขาที่คุณต้องการโอนการส่งมอบจาก "branchA"

1)

 git checkout <branchA>

2) รับ ID ของ "commitA" และ "commitB"

3)

git checkout <branchB>

4)

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

5) ในกรณีที่คุณมีข้อขัดแย้งให้แก้ไขและพิมพ์

git cherry-pick --continue

เพื่อดำเนินการตามกระบวนการเชอร์รี่


ทำงานเหมือนมีเสน่ห์! ขอบคุณมาก!
Snukone

28

คุณแน่ใจหรือไม่ว่าไม่ต้องการรวมสาขาเข้าด้วยกัน หากสาขาที่ทำงานมีข้อผูกพันล่าสุดที่คุณไม่ต้องการคุณสามารถสร้างสาขาใหม่ด้วย HEAD ณ จุดที่คุณต้องการ

ตอนนี้ถ้าคุณอยากจะทำเชอร์รี่อย่างหลากหลายไม่ว่าด้วยเหตุผลใดวิธีที่ยอดเยี่ยมในการทำเช่นนี้คือเพียงดึงแพตช์เซ็ตแล้วนำไปใช้กับสาขาการรวมสาขาใหม่ของคุณ:

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

นี่คือสิ่งที่สิ่งที่ git-rebase ทำอยู่แล้ว แต่ไม่จำเป็นต้องเล่นเกม คุณสามารถเพิ่ม--3wayการgit-amถ้าคุณต้องการที่จะผสาน ตรวจสอบให้แน่ใจว่าไม่มีไฟล์ * .patch อื่น ๆ อยู่ในไดเรกทอรีที่คุณทำเช่นนี้หากคุณทำตามคำแนะนำคำต่อคำ ...


1
โปรดทราบว่าเช่นเดียวกับช่วงการแก้ไขอื่น ๆ จำเป็นต้องA^รวมAไว้ด้วย
Gino Mempin

9

ฉันห่อโค้ดของ VonCไว้ในสคริปต์ทุบตีสั้น ๆgit-multi-cherry-pickเพื่อให้ทำงานได้ง่าย:

#!/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 

ขณะนี้ฉันใช้สิ่งนี้ขณะที่ฉันสร้างประวัติโครงการที่มีทั้งรหัสบุคคลที่สามและการปรับแต่งที่ผสมกันในลำต้น svn เดียวกัน ตอนนี้ฉันแยกรหัสหลักของบุคคลที่สามโมดูลของบุคคลที่สามและการปรับแต่งเป็นสาขา git ของตัวเองเพื่อความเข้าใจที่ดีขึ้นของการปรับแต่งในอนาคต git-cherry-pickมีประโยชน์ในสถานการณ์นี้เนื่องจากฉันมีต้นไม้สองต้นในที่เก็บเดียวกัน แต่ไม่มีบรรพบุรุษที่ใช้ร่วมกัน


3

ตัวเลือกทั้งหมดข้างต้นจะแจ้งให้คุณแก้ไขข้อขัดแย้งในการรวม หากคุณกำลังรวมการเปลี่ยนแปลงที่เกิดขึ้นกับทีมเป็นเรื่องยากที่จะแก้ไขข้อขัดแย้งในการผสานจากผู้พัฒนาและดำเนินการต่อ อย่างไรก็ตาม "git merge" จะทำการผสานในหนึ่งช็อต แต่คุณไม่สามารถผ่านช่วงของการแก้ไขเป็นอาร์กิวเมนต์ได้ เราต้องใช้คำสั่ง "git diff" และ "git Apply" เพื่อทำช่วงการรวมของ revs ฉันสังเกตว่า "git ใช้" จะล้มเหลวหากไฟล์แพทช์แตกต่างกันสำหรับไฟล์มากเกินไปดังนั้นเราต้องสร้างแพตช์ต่อไฟล์แล้วจึงนำไปใช้ โปรดทราบว่าสคริปต์จะไม่สามารถลบไฟล์ที่ถูกลบในสาขาต้นทาง นี่เป็นกรณีที่ไม่ค่อยเกิดขึ้นคุณสามารถลบไฟล์ดังกล่าวจากสาขาเป้าหมายได้ด้วยตนเอง สถานะทางออกของ "git Apply" ไม่ใช่ศูนย์หากไม่สามารถใช้งาน patch ได้

ด้านล่างเป็นสคริปต์

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"

2

ฉันได้ทดสอบแล้วเมื่อหลายวันก่อนหลังจากอ่านคำอธิบายที่ชัดเจนของ Vonc

ขั้นตอนของฉัน

เริ่มต้น

  • สาขาdev: ABCDEFGHIJ
  • สาขาtarget: ABCD
  • ฉันไม่ต้องการEหรือH

ขั้นตอนในการคัดลอกฟีเจอร์ที่ไม่มีขั้นตอน E และ H ในสาขา dev_feature_wo_E_H

  • git checkout dev
  • git checkout -b dev_feature_wo_E_H
  • git rebase --interactive --rebase-merges --no-ff Dที่ฉันใส่dropด้านหน้าEและHในตัวแก้ไข rebase
  • แก้ไขข้อขัดแย้งดำเนินการต่อและ commit

ขั้นตอนในการคัดลอกสาขาdev_feature_wo_E_Hตามเป้าหมาย

  • git checkout target
  • git merge --no-ff --no-commit dev_feature_wo_E_H
  • แก้ไขข้อขัดแย้งดำเนินการต่อและ commit

ข้อสังเกตบางส่วน

  • ฉันทำอย่างนั้นเพราะมากเกินไปcherry-pickในวันก่อน
  • git cherry-pick ทรงพลังและเรียบง่าย แต่

    • มันสร้างการกระทำที่ซ้ำกัน
    • และเมื่อฉันต้องการmergeฉันต้องแก้ไขข้อขัดแย้งของการกระทำครั้งแรกและการทำซ้ำดังนั้นสำหรับหนึ่งหรือสองcherry-pickมันก็โอเคที่ "เชอร์รี่ - แคะ" แต่สำหรับมันมากเกินไป verbose และสาขาจะซับซ้อนเกินไป
  • ในใจของฉันขั้นตอนที่ฉันทำมีความชัดเจนยิ่งกว่า git rebase --onto

1
โพสต์ที่ดี upvoted มันทำให้ผมนึกถึงstackoverflow.com/a/38418941/6309 ฉันซาร์ข้อบกพร่องของด้านหลังเชอร์รี่เลือกในปี 2012: stackoverflow.com/a/13524494/6309
VonC

1

ตัวเลือกอื่นอาจจะรวมกับกลยุทธ์ของเราเพื่อกระทำก่อนช่วงและจากนั้นผสาน 'ปกติ' กับกระทำสุดท้ายของช่วงนั้น (หรือสาขาเมื่อมันเป็นคนสุดท้าย) ดังนั้นสมมติว่ามีเพียง 2345 และ 3456 การกระทำของอาจารย์ที่จะรวมเข้ากับสาขาคุณลักษณะ:

ต้นแบบ:
1234
2345
3456
4567

ในสาขาคุณลักษณะ:

git merge -s ours 4567
คอมไพล์รวม 2345
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.