อะไรคือความแตกต่างระหว่าง "git merge" และ "git merge - no-ff"


977

ใช้gitk logฉันไม่สามารถเห็นความแตกต่างระหว่างทั้งสอง ฉันจะสังเกตเห็นความแตกต่างได้อย่างไร (ด้วยคำสั่ง git หรือเครื่องมือบางตัว)


3
ซ้ำเป็นไปได้ของGit Fast Forward VS ไม่มีข้างหน้าอย่างรวดเร็วผสาน


สำเนาที่เป็นไปได้ของGit fast forward VS ไม่มี forward forward fast
Avijit Gupta

คำตอบ:


1080

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

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

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


87
หากต้องการตอบคำถามของ OP โดยตรงมากขึ้น: พวกเขาไม่ได้แตกต่างกันเสมอไป แต่ถ้าเป็นเช่นนั้นมันชัดเจนจากgitkหรือgit log --graphว่าการผสานการกรอไปข้างหน้าไม่ได้สร้างการคอมมิชชันในขณะที่การส่งต่อที่ไม่ใช่กรอ
Cascabel

11
มันจะดีที่จะขยายเหตุผลในการหลีกเลี่ยง ff: ผู้เขียนกล่าวถึง "topology เฉพาะสาขา" หมายความว่าในกรณีที่ไม่มีการรวมพิเศษกระทำหน้าที่เป็นเครื่องหมายของการผสาน ข้อดีคือการรวมเครื่องหมายอย่างชัดเจนด้วยชื่อของผู้เขียนและการควบรวมกิจการ ข้อเสียคือประวัติศาสตร์ที่ไม่ใช่เชิงเส้นซึ่งดูเหมือนว่าชุดของลู่รถไฟ ผลอาจจิตวิทยาผลข้างเคียงของการผสาน แต่เป็นผู้ร่วมสมทบการสูญเสียความสนใจเนื่องจากกระบวนการตรวจสอบอีกต่อไป: blog.spreedly.com/2014/06/24/...
Vlad

6
มันจะยุติธรรมที่จะบอกว่า--no-ffจากคุณสมบัติในการพัฒนาหรือพัฒนาเพื่อ master คล้ายกับการรวมคำขอดึง?
merlinpatt

9
@merlinpatt แน่นอน หากคุณผสานคำขอดึงใน GitHub --no-ffมันไม่เทียบเท่า
Lily Ballard

1
นี่เป็นคำอธิบายที่ดี มนุษย์มีคำอธิบายคอมไพล์ที่ดีไม่เพียงพอสำหรับผู้คนที่จะเข้าใจว่าทำไมประวัติศาสตร์คอมไพล์ที่สะอาดจึงมีความสำคัญจริงๆ (รวมอยู่ด้วย!)
dudewad

1037

คำตอบกราฟิกสำหรับคำถามนี้

นี่คือไซต์ที่มีคำอธิบายที่ชัดเจนและภาพประกอบของการใช้git merge --no-ff:

ความแตกต่างระหว่าง git merge - no-ff และ git merge

จนกระทั่งฉันเห็นสิ่งนี้ฉันก็หายไปกับคอมไพล์อย่างสมบูรณ์ การใช้--no-ffช่วยให้ใครบางคนตรวจสอบประวัติเพื่อดูสาขาที่คุณเช็คเอาท์เพื่อทำงานอย่างชัดเจน (ลิงก์นั้นชี้ไปที่เครื่องมือสร้างภาพ "เครือข่าย" ของ GitHub) และนี่คืออีกหนึ่งการอ้างอิงที่ยอดเยี่ยมพร้อมภาพประกอบ การอ้างอิงนี้เติมเต็มสิ่งแรกที่มีความสำคัญมากกว่าผู้ที่ไม่คุ้นเคยกับ git


ข้อมูลพื้นฐานสำหรับมือใหม่เช่นฉัน

หากคุณเป็นเหมือนฉันไม่ใช่ Git-guru คำตอบของฉันที่นี่จะอธิบายถึงการจัดการการลบไฟล์ออกจากการติดตามของGit โดยไม่ต้องลบไฟล์เหล่านั้นออกจากระบบไฟล์ในตัวเครื่องซึ่งดูเหมือนว่าเอกสารไม่ดี แต่มักเกิดขึ้น สถานการณ์ newb อื่นกำลังได้รับรหัสปัจจุบันซึ่งยังสามารถหลบหลีกฉันได้


ตัวอย่างเวิร์กโฟลว์

ฉันอัปเดตแพ็คเกจไปยังเว็บไซต์ของฉันและต้องย้อนกลับไปที่บันทึกย่อของฉันเพื่อดูขั้นตอนการทำงานของฉัน ฉันคิดว่ามันมีประโยชน์ที่จะเพิ่มตัวอย่างในคำตอบนี้

เวิร์กโฟลว์ของคำสั่ง git ของฉัน:

git checkout -b contact-form
(do your work on "contact-form")
git status
git commit -am  "updated form in contact module"
git checkout master
git merge --no-ff contact-form
git branch -d contact-form
git push origin master

ด้านล่าง:การใช้งานจริงรวมถึงคำอธิบาย
หมายเหตุ: เอาต์พุตด้านล่างถูกตัดออก คอมไพล์ค่อนข้าง verbose

$ git status
# On branch master
# Changed but not updated:
#   (use "git add/rm <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#       modified:   ecc/Desktop.php
#       modified:   ecc/Mobile.php
#       deleted:    ecc/ecc-config.php
#       modified:   ecc/readme.txt
#       modified:   ecc/test.php
#       deleted:    passthru-adapter.igs
#       deleted:    shop/mickey/index.php
#
# Untracked files:
#   (use "git add <file>..." to include in what will be committed)
#
#       ecc/upgrade.php
#       ecc/webgility-config.php
#       ecc/webgility-config.php.bak
#       ecc/webgility-magento.php

สังเกตเห็น 3 สิ่งจากด้านบน:
1) ในผลลัพธ์คุณสามารถเห็นการเปลี่ยนแปลงจากการอัพเกรดแพ็คเกจ ECC รวมถึงการเพิ่มไฟล์ใหม่
2) แจ้งให้ทราบด้วยว่ามีสองไฟล์ (ไม่ใช่ใน/eccโฟลเดอร์) ฉันลบโดยไม่ขึ้นกับการเปลี่ยนแปลงนี้ แทนที่จะสับสนกับการลบไฟล์เหล่านั้นด้วยeccฉันจะทำการแยกcleanupสาขาในภายหลังเพื่อสะท้อนการลบไฟล์เหล่านั้น
3) ฉันไม่ได้ทำตามขั้นตอนการทำงานของฉัน! ฉันลืมเกี่ยวกับคอมไพล์ในขณะที่ฉันพยายามที่จะทำงาน ecc อีก

ด้านล่าง: แทนที่จะรวมทุกอย่างgit commit -am "updated ecc package"ตามปกติแล้วฉันแค่ต้องการเพิ่มไฟล์ใน/eccโฟลเดอร์ ไฟล์ที่ถูกลบเหล่านั้นไม่ได้เป็นส่วนหนึ่งของฉันโดยเฉพาะgit addแต่เนื่องจากพวกเขามีการติดตามในคอมไพล์แล้วฉันต้องลบพวกเขาออกจากการกระทำของสาขานี้:

$ git checkout -b ecc
$ git add ecc/*
$ git reset HEAD passthru-adapter.igs
$ git reset HEAD shop/mickey/index.php
Unstaged changes after reset:
M       passthru-adapter.igs
M       shop/mickey/index.php

$ git commit -m "Webgility ecc desktop connector files; integrates with Quickbooks"

$ git checkout master
D       passthru-adapter.igs
D       shop/mickey/index.php
Switched to branch 'master'
$ git merge --no-ff ecc
$ git branch -d ecc
Deleted branch ecc (was 98269a2).
$ git push origin master
Counting objects: 22, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (14/14), done.
Writing objects: 100% (14/14), 59.00 KiB, done.
Total 14 (delta 10), reused 0 (delta 0)
To git@github.com:me/mywebsite.git
   8a0d9ec..333eff5  master -> master



สคริปต์สำหรับการดำเนินการอัตโนมัติด้านบน

หลังจากใช้กระบวนการนี้มากกว่า 10 ครั้งต่อวันฉันได้เขียนสคริปต์แบบแบตช์เพื่อเรียกใช้คำสั่งดังนั้นฉันจึงสร้างgit_update.sh <branch> <"commit message">สคริปต์ที่เหมาะสมสำหรับการทำตามขั้นตอนข้างต้น นี่คือที่มาสำคัญสำหรับสคริปต์นั้น

แทนที่จะgit commit -amฉันเลือกไฟล์จากรายการ "แก้ไข" ที่สร้างผ่านgit statusแล้ววางไฟล์ในสคริปต์นี้ สิ่งนี้เกิดขึ้นเพราะฉันทำการแก้ไขหลายสิบครั้ง แต่ต้องการชื่อสาขาที่หลากหลายเพื่อช่วยจัดกลุ่มการเปลี่ยนแปลง


11
คุณยังสามารถลบสาขาได้อย่างปลอดภัยหลังจากคุณรวมกับ--no-ffตัวเลือกหรือไม่
Andy Fleming

17
@ ผู้ออกแบบซื้อใช่คุณสามารถลบสาขาเก่าได้อย่างปลอดภัย คิดว่าสาขา es เป็นเพียงตัวชี้ไปยังการกระทำที่เฉพาะเจาะจง
Zyphrax

2
ฉันพบว่าข้อความนี้จากหน้าที่ลิงก์มามีประโยชน์: ไม่ต้อง - ไม่มี -ff "มันเป็นไปไม่ได้ที่จะเห็นจากประวัติ Git ซึ่งวัตถุการกระทำร่วมกันได้ใช้คุณสมบัติ - คุณจะต้องอ่านข้อความบันทึกทั้งหมดด้วยตนเอง"
Lorne Laliberte

ภาพหนึ่งภาพดีกว่าหนึ่งพันคำเสมอ!
rupps

1
กราฟิกไม่แสดงสาขาฟีเจอร์ในภาพที่สองทำไมล่ะ? มันยังคงมีอยู่ใช่ไหม
ADJenks

269

ผสานกลยุทธ์

Explicit Merge : สร้างการผสานใหม่ (นี่คือสิ่งที่คุณจะได้รับถ้าคุณใช้--no-ff)

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

กรอไปข้างหน้าอย่างรวดเร็ว:ส่งต่ออย่างรวดเร็วโดยไม่ต้องสร้างการคอมมิตใหม่:

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

Rebase : สร้างระดับฐานใหม่:

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

สควอช:บดขยี้หรือบีบ (บางสิ่ง) ด้วยแรงเพื่อให้แบน:

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


11
กราฟิกที่น่าสนใจ แต่ไม่ได้แสดงกรณี --no-ff จริง ๆ และด้วยเหตุนี้จึงไม่ค่อยตอบคำถามที่นี่
Vib

22
สิ่งแรกคือ "การผสานอย่างชัดเจน" มีชื่อว่าที่นี่ว่า "การผสาน" เป็นการผสานที่ไม่ใช่ ff
bkribbs

3
กราฟิกนั้นช่วยฉันได้มากจริงๆ
exexzian

2
ไม่ตอบคำถามต่อพูด แต่ภาพนี้น่าทึ่งมากอัปมงคล!
SovietFrontier

3
ดังนั้นความแตกต่างระหว่าง Rebase และการผสานการกรอไปข้างหน้าคืออะไร
ThanosFisherman

217

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

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


3
ลูกศรสีเขียวหมายถึงอะไรกับลูกศรสีดำ
rtconner

11
ลูกศรสีเขียวแสดงลิงค์ระหว่างการกระทำและผู้ปกครองที่การกระทำมีมากกว่าหนึ่งผู้ปกครอง การกระทำปกติ (สีดำ) มีผู้ปกครองเพียงคนเดียว
แดเนียลสมิ ธ

9
@DanielSmith คุณจะวาดกราฟที่สวยงามได้อย่างไร
chancyWu

4
@chancyWu กับ Adobe Illustrator
Daniel Smith

ดูเหมือนว่าใน บริษัท ของฉันคำขอดึงทั้งหมดจะรวมเข้ากับตัวเลือก --no-ff ภายใต้สถานการณ์นั้นมันยังคงมีเหตุผลที่จะทำการ rebase ก่อนที่ฉันจะสร้างคำขอการดึงหรือไม่
Nickpick

37

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


คุณช่วยโปรดตรวจสอบขั้นตอนการทำงานในคำตอบของฉันด้านบน: นี่หมายความว่าฉันต้องการความมุ่งมั่นเพิ่มเติมนอกเหนือจากที่ฉันได้รับตอนนี้หรือไม่? ขอบคุณ
Chris K

3
@ChrisK git merge --no-ff eccคุณจะมีการรวมเพิ่มเติมที่กระทำในgit logสาขาหลัก นั่นเป็นเทคนิคไม่จำเป็นในกรณีที่อาจารย์ชี้ไปที่บรรพบุรุษโดยตรงของการกระทำ ecc อยู่ แต่โดยการระบุตัวเลือก --no-ff คุณกำลังบังคับให้สร้างการผสานที่กระทำ มันจะมีชื่อ:Merge branch 'ecc'
Ankur Agarwal

สรุปยอดเยี่ยม! :)
Eduard

4

แฟล็ก --no-ff ทำให้การผสานสร้างวัตถุการยอมรับใหม่เสมอแม้ว่าการผสานจะสามารถทำได้ด้วยการกรอไปข้างหน้า วิธีนี้จะช่วยหลีกเลี่ยงการสูญเสียข้อมูลเกี่ยวกับการดำรงอยู่ในอดีตของสาขาฟีเจอร์และกลุ่มต่างๆเข้าด้วยกันทั้งหมดยอมรับว่าได้เพิ่มฟีเจอร์นี้เข้าด้วยกัน

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