Git ผสานกับการบังคับเขียนทับ


110

ฉันมีสาขาที่เรียกว่าdemoฉันต้องรวมกับmasterสาขา ฉันได้ผลลัพธ์ที่ต้องการด้วยคำสั่งต่อไปนี้:

git pull origin demo
git checkout master
git pull origin master
git merge demo
git push origin master

ข้อกังวลเดียวของฉันคือหากมีปัญหาในการผสานฉันต้องการบอกgitให้เขียนทับการเปลี่ยนแปลงในmasterสาขาโดยไม่ต้องแจ้งให้ฉันรวม โดยพื้นฐานแล้วการเปลี่ยนแปลงในdemoสาขาควรเขียนทับการเปลี่ยนแปลงในmasterสาขาโดยอัตโนมัติ

ฉันมองไปรอบ ๆ มีตัวเลือกมากมาย แต่ฉันไม่ต้องการใช้โอกาสในการรวม


git push -f origin master
MD XF

4
@MDXF: ฉันอาจจะผิด แต่ฉันไม่ควรใช้-fตัวเลือกกับmergeคำสั่งไม่ใช่ด้วยpushคำสั่ง
OpenStack

คุณสามารถลองทั้งสองอย่างและดูว่าอะไรเหมาะกับคุณ
MD XF

ดูลิงก์ด้านล่างสำหรับวิธีแก้ปัญหาการเขียนทับ: stackoverflow.com/a/42454737/984471
Manohar Reddy Poreddy

คำตอบ:


114

ไม่เกี่ยวข้องจริงๆคำตอบนี้ แต่ฉันคลองgit pullที่เพิ่งวิ่งตามมาด้วยgit fetch git mergeคุณกำลังทำการผสานสามครั้งซึ่งจะทำให้ Git ของคุณเรียกใช้การดำเนินการดึงข้อมูลสามครั้งเมื่อการดึงข้อมูลเพียงครั้งเดียวคือทั้งหมดที่คุณต้องการ ดังนั้น:

git fetch origin   # update all our origin/* remote-tracking branches

git checkout demo         # if needed -- your example assumes you're on it
git merge origin/demo     # if needed -- see below

git checkout master
git merge origin/master

git merge -X theirs demo   # but see below

git push origin master     # again, see below

การควบคุมการผสานที่ยุ่งยากที่สุด

git merge -X theirsส่วนที่น่าสนใจมากที่สุดคือที่นี่ ในฐานะที่เป็นroot545 ตั้งข้อสังเกตที่-Xตัวเลือกจะถูกส่งผ่านไปยังกลยุทธ์การผสานและทั้งสองเริ่มต้นrecursiveกลยุทธ์และทางเลือกที่resolveจะใช้กลยุทธ์-X oursหรือ-X theirs(หนึ่งหรืออื่น ๆ แต่ไม่ใช่ทั้งสอง) อย่างไรก็ตามเพื่อให้เข้าใจว่าพวกเขาทำอะไรคุณต้องรู้ว่า Git ค้นหาและปฏิบัติต่อผสานความขัดแย้งได้อย่างไร

ความขัดแย้งในการผสานอาจเกิดขึ้นภายในไฟล์1บางไฟล์เมื่อเวอร์ชันพื้นฐานแตกต่างจากทั้งเวอร์ชันปัจจุบัน (หรือที่เรียกว่าโลคัล, HEAD หรือ--ours) และเวอร์ชันอื่น (เรียกอีกอย่างว่ารีโมตหรือ--theirs) ของไฟล์เดียวกันนั้น นั่นคือการผสานได้ระบุการแก้ไขสามครั้ง (สามข้อผูกพัน): ฐานของเราและของพวกเขา เวอร์ชัน "ฐาน" มาจากฐานการผสานระหว่างคอมมิตของเราและคอมมิตดังที่พบในกราฟคอมมิต (สำหรับข้อมูลเพิ่มเติมเกี่ยวกับเรื่องนี้โปรดดูโพสต์ StackOverflow อื่น ๆ ) จากนั้น Git ได้พบการเปลี่ยนแปลง 2 ชุด: "สิ่งที่เราทำ" และ "สิ่งที่พวกเขาทำ" การเปลี่ยนแปลงเหล่านี้ (โดยทั่วไป) จะพบในทีละบรรทัดโดยเป็นข้อความเท่านั้นพื้นฐาน. Git ไม่มีความเข้าใจอย่างแท้จริงเกี่ยวกับเนื้อหาของไฟล์ เป็นเพียงการเปรียบเทียบข้อความแต่ละบรรทัด

การเปลี่ยนแปลงเหล่านี้คือสิ่งที่คุณเห็นในgit diffผลลัพธ์และเช่นเคยมีบริบทด้วยเช่นกัน เป็นไปได้ว่าสิ่งที่เราเปลี่ยนแปลงนั้นอยู่คนละบรรทัดกับสิ่งที่เปลี่ยนแปลงเพื่อให้ดูเหมือนว่าการเปลี่ยนแปลงจะไม่ชนกัน แต่บริบทก็เปลี่ยนไปเช่นกัน (เช่นเนื่องจากการเปลี่ยนแปลงของเราอยู่ใกล้กับด้านบนหรือด้านล่างของไฟล์ เพื่อให้ไฟล์หมดในเวอร์ชันของเรา แต่ในไฟล์นั้นพวกเขาได้เพิ่มข้อความเพิ่มเติมที่ด้านบนหรือด้านล่าง)

หากการเปลี่ยนแปลงเกิดขึ้นในบรรทัดที่แตกต่างกันตัวอย่างเช่นเราเปลี่ยนcolorเป็นcolourบรรทัดที่ 17 และเปลี่ยนfredเป็นbarneyบรรทัด 71 ดังนั้นจะไม่มีข้อขัดแย้ง Git เพียงแค่ทำการเปลี่ยนแปลงทั้งสองอย่าง หากการเปลี่ยนแปลงเกิดขึ้นในบรรทัดเดียวกัน แต่เป็นการเปลี่ยนแปลงที่เหมือนกัน Git จะใช้สำเนาการเปลี่ยนแปลง 1 ชุด เฉพาะในกรณีที่การเปลี่ยนแปลงอยู่ในบรรทัดเดียวกัน แต่เป็นการเปลี่ยนแปลงที่แตกต่างกันหรือกรณีพิเศษของบริบทรบกวนคุณจะได้รับการแก้ไข / แก้ไขความขัดแย้ง

-X oursและ-X theirsตัวเลือกบอก Git วิธีการแก้ปัญหาความขัดแย้งนี้โดยการเลือกเพียงหนึ่งในสองการเปลี่ยนแปลง: เราหรือพวกเขา ตั้งแต่คุณบอกว่าคุณกำลังผสานdemo(พวกเขา) ลงในmaster(ของเรา) และต้องการการเปลี่ยนแปลงจากที่คุณต้องการdemo-X theirs

-Xอย่างไรก็ตามการสมัครโดยสุ่มสี่สุ่มห้าเป็นสิ่งที่อันตราย เพียงเพราะการเปลี่ยนแปลงของเราไม่ขัดแย้งกันทีละบรรทัดไม่ได้หมายความว่าการเปลี่ยนแปลงของเราจะไม่ขัดแย้งกัน! ตัวอย่างคลาสสิกหนึ่งเกิดขึ้นในภาษาที่มีการประกาศตัวแปร เวอร์ชันพื้นฐานอาจประกาศตัวแปรที่ไม่ได้ใช้:

int i;

ในเวอร์ชันของเราเราจะลบตัวแปรที่ไม่ได้ใช้เพื่อให้คำเตือนของคอมไพเลอร์หายไปและในเวอร์ชันของพวกเขาจะเพิ่มลูปในภายหลังโดยใช้iเป็นตัวนับลูป หากเรารวมการเปลี่ยนแปลงทั้งสองเข้าด้วยกันโค้ดผลลัพธ์จะไม่คอมไพล์อีกต่อไป -Xตัวเลือกที่ช่วยไม่ได้ที่นี่ตั้งแต่การเปลี่ยนแปลงที่อยู่บนเส้นที่แตกต่างกัน

หากคุณมีชุดทดสอบอัตโนมัติสิ่งที่สำคัญที่สุดที่ต้องทำคือเรียกใช้การทดสอบหลังจากรวมเข้าด้วยกัน คุณสามารถทำสิ่งนี้ได้หลังจากตกลงและแก้ไขในภายหลังหากจำเป็น หรือคุณสามารถทำมันก่อนที่จะกระทำโดยการเพิ่ม--no-commitกับgit mergeคำสั่ง เราจะฝากรายละเอียดทั้งหมดนี้ไว้ในโพสต์อื่น ๆ


1นอกจากนี้คุณยังอาจได้รับความขัดแย้งเกี่ยวกับการดำเนินการ "ทั้งไฟล์" เช่นบางทีเราแก้ไขการสะกดคำในไฟล์ (เพื่อให้เรามีการเปลี่ยนแปลง) และลบไฟล์ทั้งหมด (เพื่อให้มี ลบ). Git จะไม่แก้ไขข้อขัดแย้งเหล่านี้ด้วยตัวเองโดยไม่คำนึงถึง-Xข้อโต้แย้ง


ทำการผสานน้อยลงและ / หรือผสานและ / หรือใช้ rebase อย่างชาญฉลาด

มีสามการผสานในลำดับคำสั่งของเราทั้งสอง ประการแรกคือการนำorigin/demoเข้าสู่ท้องถิ่นdemo(ของคุณใช้git pullซึ่งหาก Git ของคุณเก่ามากจะไม่สามารถอัปเดตได้origin/demoแต่จะให้ผลลัพธ์ที่เหมือนกัน) ประการที่สองคือการนำorigin/masterเข้าmasterมา

มันไม่ชัดเจนกับผมที่มีการปรับปรุงและdemo / หรือ masterหากคุณเขียนโค้ดของคุณเองในdemoสาขาของคุณเองและคนอื่น ๆ กำลังเขียนโค้ดและผลักดันไปยังdemoสาขาoriginนั้นการผสานขั้นแรกนี้อาจมีข้อขัดแย้งหรือทำให้เกิดการผสานจริง บ่อยกว่านั้นควรใช้ rebase แทนการผสานเพื่อรวมงาน (ยอมรับว่าเป็นเรื่องของรสนิยมและความคิดเห็น) ในกรณีนี้คุณอาจต้องการใช้git rebaseแทน ในทางกลับกันถ้าคุณไม่เคยทำอะไรด้วยตัวเองคุณก็demoไม่จำเป็นต้องมีdemoสาขา หรือหากคุณต้องการทำสิ่งนี้โดยอัตโนมัติ แต่สามารถตรวจสอบอย่างรอบคอบเมื่อมีการกระทำที่ทั้งคุณและคนอื่นสร้างขึ้นคุณอาจต้องการใช้git merge --ff-only origin/demo: สิ่งนี้จะส่งต่ออย่างรวดเร็วของคุณdemoเพื่อให้ตรงกับการอัปเดตorigin/demoหากเป็นไปได้และจะล้มเหลวทันทีหากไม่ได้ (ณ จุดนี้คุณสามารถตรวจสอบการเปลี่ยนแปลงทั้งสองชุดและเลือกการผสานจริงหรือฐานใหม่ตามความเหมาะสม)

นี้ตรรกะเดียวกันกับmasterถึงแม้ว่าคุณจะกำลังทำผสานบน เพื่อให้คุณแน่นอนจะต้องมีmaster masterอย่างไรก็ตามแม้ว่าคุณจะชอบที่คุณต้องการให้การผสานล้มเหลวหากไม่สามารถทำได้เป็นการไม่ผสานไปข้างหน้าอย่างรวดเร็วดังนั้นสิ่งนี้ก็ควรจะเป็นgit merge --ff-only origin/masterเช่นกัน

demoพูดเถอะว่าคุณไม่เคยทำกระทำของคุณเองบน ในกรณีนี้เราสามารถทิ้งชื่อdemoทั้งหมดได้:

git fetch origin   # update origin/*

git checkout master
git merge --ff-only origin/master || die "cannot fast-forward our master"

git merge -X theirs origin/demo || die "complex merge conflict"

git push origin master

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


นี้เป็นหนึ่งในคำอธิบายที่ดีที่สุดของอะไรในดังนั้น โดยเฉพาะอย่างยิ่งการแยกย่อยและปัญหาตัวอย่างกับ-X oursเมื่อเทียบกับ-X theirsเป็นประโยชน์อย่างยิ่ง ขอบคุณ!
Jason R Stevens CFA

31

ฉันมีปัญหาที่คล้ายกันซึ่งฉันต้องการแทนที่ไฟล์ใด ๆ ที่มีการเปลี่ยนแปลง / ขัดแย้งกับสาขาอื่นอย่างมีประสิทธิภาพ

git merge -s ours branchวิธีการแก้ปัญหาที่ผมพบคือการใช้

โปรดทราบว่าตัวเลือกที่เป็นและไม่ได้-s หมายถึงการใช้เป็นกลยุทธ์การผสานระดับบนสุดจะเป็นการใช้ตัวเลือกกับกลยุทธ์การผสานซึ่งไม่ใช่สิ่งที่ฉัน (หรือเรา) ต้องการในกรณีนี้-X-sours-Xoursrecursive

ขั้นตอนoldbranchสาขาที่คุณต้องการเขียนทับnewbranchอยู่ที่ไหน

  • git checkout newbranch ตรวจสอบสาขาที่คุณต้องการเก็บไว้
  • git merge -s ours oldbranch รวมอยู่ในสาขาเก่า แต่เก็บไฟล์ทั้งหมดของเราไว้
  • git checkout oldbranch ตรวจสอบสาขาที่คุณต้องการเขียนทับ
  • get merge newbranch รวมในสาขาใหม่เขียนทับสาขาเก่า

หมายเหตุ: ไม่มีวิธีแก้ปัญหาอื่นใดที่ใช้ได้ผลสำหรับฉันดังนั้นฉันจึงโพสต์คำตอบของฉัน
Niall Mccormack

1
มันไม่ได้ผลสำหรับฉัน มันแก้ไขข้อขัดแย้ง (แก้ไขไฟล์ที่ขัดแย้งกัน) แต่ไฟล์ไม่ถูกรวมเข้าด้วยกัน และไม่สามารถรวมไม่ได้
c-an

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

ขอบคุณ @NiallMccormack ที่โพสต์คำตอบนี้ช่วยเราได้มาก!
Raphael_S

21

วิธีการผสานนี้จะเพิ่มการกระทำที่ทับmasterซ้อนfeatureกันโดยไม่บ่นเกี่ยวกับความขัดแย้งหรือเรื่องไร้สาระอื่น ๆ

ป้อนคำอธิบายภาพที่นี่

ก่อนที่คุณจะสัมผัสอะไร

git stash
git status # if anything shows up here, move it to your desktop

ตอนนี้เตรียมมาสเตอร์

git checkout master
git pull # if there is a problem in this step, it is outside the scope of this answer

รับfeatureทุกแต่งตัว

git checkout feature
git merge --strategy=ours master

ไปฆ่า

git checkout master
git merge --no-ff feature

1
git push ให้เสร็จ
Kochchy

git-scm.com/docs/git-merge#Documentation/git-merge.txt-ours ใช้งานได้เมื่อการกระทำไม่ได้รวมเข้าด้วยกันอย่างหมดจด Changes from the other tree that do not conflict with our side are reflected in the merge resultแต่วิธีนี้จะไม่ทำงานเสมอที่จะพูดแหล่งที่มา มันไม่ได้ผลสำหรับฉันเนื่องจากฉันได้รวมคอมมิตอย่างหมดจดในสาขาของฉันซึ่งถูกเขียนทับ มีวิธีอื่น ๆ ?
NiharGht

11

คุณสามารถลองใช้ตัวเลือก "ของเรา" ในการผสานคอมไพล์

git ผสานสาขา -X ของเรา

ตัวเลือกนี้บังคับให้คนที่ขัดแย้งกันได้รับการแก้ไขโดยอัตโนมัติอย่างหมดจดโดยเลือกเวอร์ชันของเรา การเปลี่ยนแปลงจากต้นไม้อีกต้นที่ไม่ขัดแย้งกับฝ่ายเราจะสะท้อนให้เห็นผลการผสาน สำหรับไฟล์ไบนารีเนื้อหาทั้งหมดจะถูกนำมาจากฝั่งของเรา


3
นี่จะเป็นการย้อนกลับไปตามที่ OP กล่าวว่าเขาต้องการให้demoเวอร์ชันนี้เป็นที่ต้องการแม้ว่าเขาจะรวมเข้าmasterด้วยกันก็ตาม ก็มี-X theirsเช่นกัน แต่ไม่ชัดเจนว่านี่คือสิ่งที่ OP ต้องการจริงๆ
ฉีกขาด

คุณยังไม่ได้อ่านวิธีทั้งหมด บันทึกของคุณอธิบายถึงสิ่งที่oursทำเมื่อใช้บนแบ็กเอนด์ด้วย-sตัวเลือก เมื่อใช้oursกับ-X: มันจะทิ้งทุกสิ่งที่ต้นไม้อื่น ๆ ทำโดยการประกาศว่าประวัติศาสตร์ของเรามีทุกสิ่งที่เกิดขึ้นในนั้น ที่มา
jimasun

2

เมื่อฉันลองใช้-X theirsและสวิตช์คำสั่งอื่น ๆ ที่เกี่ยวข้องฉันยังคงได้รับการผสานรวม ฉันคงเข้าใจไม่ถูกต้อง ทางเลือกหนึ่งที่เข้าใจง่ายคือการลบสาขาแล้วติดตามอีกครั้ง

git branch -D <branch-name>
git branch --track <branch-name> origin/<branch-name>

นี่ไม่ใช่การ "รวม" อย่างแน่นอน แต่นี่คือสิ่งที่ฉันกำลังมองหาเมื่อเจอคำถามนี้ ในกรณีของฉันฉันต้องการดึงการเปลี่ยนแปลงจากสาขาระยะไกลที่ถูกผลัก


0

คำสั่งเหล่านี้จะช่วยในการเขียนทับรหัสของdemoสาขาลงในmaster

git fetch --all

ดึงdemoสาขาของคุณในท้องถิ่น

git pull origin demo

ตอนนี้ชำระเงินไปที่masterสาขา สาขานี้จะถูกเปลี่ยนทั้งหมดโดยใช้รหัสบนdemoสาขา

git checkout master

อยู่ในmasterสาขาและเรียกใช้คำสั่งนี้

git reset --hard origin/demo

reset หมายความว่าคุณจะรีเซ็ตสาขาปัจจุบัน

--hard เป็นค่าสถานะที่หมายความว่าจะถูกรีเซ็ตโดยไม่เพิ่มความขัดแย้งในการผสาน

origin/demoจะเป็นสาขาที่จะถือว่าเป็นรหัสที่จะเขียนทับmasterสาขาปัจจุบันอย่างมีประสิทธิภาพ

ผลลัพธ์ของคำสั่งดังกล่าวจะแสดงข้อความคอมมิตล่าสุดของคุณในorigin/demoหรือdemoสาขา ป้อนคำอธิบายภาพที่นี่

จากนั้นในท้ายที่สุดบังคับให้กดรหัสบนmasterสาขาไปยัง repo ระยะไกลของคุณ

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