สรุป
ตามค่าเริ่มต้นgit pullสร้างการรวมการกระทำที่เพิ่มเสียงรบกวนและความซับซ้อนให้กับประวัติรหัส นอกจากนี้pullทำให้ง่ายต่อการไม่คิดว่าการเปลี่ยนแปลงของคุณอาจได้รับผลกระทบจากการเปลี่ยนแปลงที่เข้ามา
git pullคำสั่งมีความปลอดภัยตราบใดที่มันดำเนินการผสานข้างหน้าอย่างรวดเร็ว หากgit pullมีการกำหนดค่าให้ทำการผสานการกรอไปข้างหน้าเท่านั้นและเมื่อไม่สามารถผสานการกรอไปข้างหน้าได้ Git จะออกจากข้อผิดพลาด วิธีนี้จะให้โอกาสคุณในการศึกษาความมุ่งมั่นที่เข้ามาคิดเกี่ยวกับวิธีที่พวกเขาจะส่งผลกระทบต่อการกระทำในท้องถิ่นของคุณและตัดสินใจแนวทางการดำเนินการที่ดีที่สุด
ด้วย Git 2.0 และใหม่กว่าคุณสามารถเรียกใช้:
git config --global pull.ff only
เพื่อเปลี่ยนพฤติกรรมเริ่มต้นเป็นเพียงการส่งต่ออย่างรวดเร็ว ด้วยเวอร์ชัน Git ระหว่าง 1.6.6 ถึง 1.9.x คุณจะต้องติดนิสัยในการพิมพ์:
git pull --ff-only
อย่างไรก็ตามด้วย Git ทุกรุ่นฉันแนะนำให้ตั้งgit upชื่อแทนดังนี้:
git config --global alias.up '!git remote update -p; git merge --ff-only @{u}'
และใช้แทนgit up git pullฉันชอบชื่อแทนนี้มากกว่าgit pull --ff-onlyเพราะ:
- ใช้ได้กับ Git ทุกรุ่น (ที่ไม่ใช่โบราณ)
- มันดึงสาขาต้นน้ำทั้งหมด (ไม่ใช่เฉพาะสาขาที่คุณกำลังทำงานอยู่) และ
- มันล้าง
origin/*สาขาเก่าที่ไม่มีต้นน้ำอีกต่อไป
มีปัญหากับ git pull
git pullไม่เลวถ้าใช้อย่างถูกต้อง การเปลี่ยนแปลงล่าสุดของ Git ทำให้การใช้งานgit pullอย่างถูกต้องง่ายขึ้นแต่น่าเสียดายที่พฤติกรรมเริ่มต้นของธรรมดาgit pullมีปัญหาหลายประการ:
- มันแนะนำความไม่เชิงเส้นที่ไม่จำเป็นในประวัติศาสตร์
- มันทำให้ง่ายต่อการรื้อฟื้นคอมมิตโดยไม่ตั้งใจซึ่งถูกรีบูทโดยเจตนาโดยไม่ตั้งใจ
- มันปรับเปลี่ยนไดเรกทอรีการทำงานของคุณในรูปแบบที่ไม่แน่นอน
- การหยุดสิ่งที่คุณกำลังทำอยู่ชั่วคราวเพื่อทบทวนงานของคนอื่นนั้นน่ารำคาญ
git pull
- มันทำให้ยากที่จะ rebase อย่างถูกต้องไปยังสาขาระยะไกล
- มันไม่ได้ล้างสาขาที่ถูกลบใน repo ระยะไกล
ปัญหาเหล่านี้อธิบายโดยละเอียดยิ่งขึ้นด้านล่าง
ประวัติความเป็นมาไม่เชิงเส้น
โดยค่าเริ่มต้นgit pullคำสั่งจะเทียบเท่ากับการทำงานตามgit fetch git merge @{u}หากมีการกระทำ unpushed ในพื้นที่เก็บข้อมูลในท้องถิ่นเป็นส่วนหนึ่งของการผสานgit pullสร้างผสานกระทำ
ไม่มีอะไรเลวร้ายเกี่ยวกับการรวมการกระทำ แต่พวกเขาอาจเป็นอันตรายและควรได้รับการปฏิบัติด้วยความเคารพ:
- การรวมคอมมิชชันนั้นยากที่จะตรวจสอบโดยเนื้อแท้ เพื่อให้เข้าใจว่าการผสานกำลังทำอะไรคุณต้องเข้าใจความแตกต่างของพ่อแม่ทุกคน ความแตกต่างทั่วไปไม่ได้ถ่ายทอดข้อมูลหลายมิตินี้อย่างดี ในทางตรงกันข้ามชุดของการกระทำปกตินั้นง่ายต่อการตรวจสอบ
- การแก้ไขความขัดแย้งผสานเป็นเรื่องยุ่งยากและความผิดพลาดมักจะไม่ถูกตรวจจับเป็นเวลานานเนื่องจากการคอมมิทผสานนั้นยากต่อการตรวจสอบ
- การรวมกันสามารถแทนที่ผลของการกระทำปกติได้อย่างเงียบ ๆ รหัสดังกล่าวไม่ได้เป็นผลรวมของความมุ่งมั่นที่เพิ่มขึ้นอีกต่อไปซึ่งนำไปสู่ความเข้าใจผิดเกี่ยวกับสิ่งที่เปลี่ยนแปลงไปจริง ๆ
- การรวมกระทำอาจขัดขวางแผนการรวมอย่างต่อเนื่องบางอย่าง (เช่นสร้างอัตโนมัติเฉพาะเส้นทางพาเรนต์แรกภายใต้อนุสัญญาที่สันนิษฐานว่าผู้ปกครองคนที่สองชี้ไปที่งานที่ยังไม่เสร็จสมบูรณ์)
แน่นอนว่ามีเวลาและสถานที่สำหรับการผสาน แต่การเข้าใจเมื่อการรวมควรและไม่ควรใช้สามารถปรับปรุงประโยชน์ของที่เก็บของคุณ
โปรดทราบว่าจุดประสงค์ของ Git คือเพื่อให้ง่ายต่อการแบ่งปันและใช้งานการวิวัฒนาการของ codebase ไม่ใช่เพื่อบันทึกประวัติอย่างแม่นยำในขณะที่มันคลี่ออกมา (ถ้าคุณไม่เห็นด้วยให้พิจารณาrebaseคำสั่งและเหตุผลที่สร้างขึ้น) การรวมที่สร้างโดยgit pullไม่สื่อความหมายที่เป็นประโยชน์ต่อผู้อื่น - พวกเขาเพียงแค่บอกว่ามีคนอื่นเกิดขึ้นที่จะผลักดันไปยังที่เก็บก่อนที่จะทำการเปลี่ยนแปลงของคุณ เหตุใดการผสานเหล่านี้จึงเกิดขึ้นถ้าพวกเขาไม่ได้มีความหมายต่อผู้อื่นและอาจเป็นอันตรายได้
เป็นไปได้ที่จะกำหนดค่าgit pullให้รีบูทแทนที่จะรวม แต่ยังมีปัญหา (อธิบายในภายหลัง) แต่git pullควรจะกำหนดค่าที่จะทำเพียงผสานข้างหน้าอย่างรวดเร็ว
การคืนสู่พันธะที่ถูกปฏิเสธ
สมมติว่ามีใครบางคน rebases สาขาและแรงผลักดันมัน โดยทั่วไปไม่ควรเกิดขึ้น แต่บางครั้งก็มีความจำเป็น (เช่นเพื่อลบไฟล์บันทึก 50GiB ที่เข้ามาแล้วผลักโดยไม่ตั้งใจ) การผสานที่ทำโดยgit pullจะรวมเวอร์ชันใหม่ของสาขาอัปสตรีมลงในเวอร์ชันเก่าที่ยังคงมีอยู่ในที่เก็บในเครื่องของคุณ หากคุณดันผลลัพธ์ออกมา pitch forks and torches จะเริ่มเข้ามา
บางคนอาจแย้งว่าปัญหาที่แท้จริงคือการปรับปรุงกำลัง ใช่โดยทั่วไปจะแนะนำให้หลีกเลี่ยงการกดทับเมื่อทำได้ แต่บางครั้งก็หลีกเลี่ยงไม่ได้ นักพัฒนาจะต้องเตรียมที่จะจัดการกับการอัพเดทแรงเพราะบางครั้งพวกเขาจะเกิดขึ้น git pullวิธีนี้ไม่สุ่มสี่สุ่มห้าผสานในกระทำเก่าผ่านสามัญ
การปรับเปลี่ยนไดเรกทอรีการทำงานที่น่าประหลาดใจ
ไม่มีวิธีใดที่จะคาดเดาได้ว่าไดเรกทอรีหรือดัชนีการทำงานจะมีหน้าตาเป็นอย่างไรจนกระทั่งgit pullเสร็จสิ้น อาจมีข้อขัดแย้งที่ผสานที่คุณต้องแก้ไขก่อนที่คุณจะสามารถทำสิ่งอื่นได้มันอาจแนะนำไฟล์บันทึก 50GiB ในไดเรกทอรีทำงานของคุณเพราะมีคนผลักมันโดยไม่ตั้งใจอาจเปลี่ยนชื่อไดเรกทอรีที่คุณกำลังทำงานอยู่เป็นต้น
git remote update -p(หรือgit fetch --all -p) อนุญาตให้คุณดูความมุ่งมั่นของผู้อื่นก่อนที่คุณจะตัดสินใจที่จะรวมหรือปฏิเสธการอนุญาตให้คุณจัดทำแผนก่อนดำเนินการ
ความยากลำบากในการตรวจสอบภาระผูกพันของผู้อื่น
สมมติว่าคุณกำลังทำการเปลี่ยนแปลงบางอย่างและมีคนอื่นต้องการให้คุณตรวจทานบางสิ่งที่พวกเขาเพิ่งผลัก git pullการทำงานของการผสาน (หรือการรีบูต) แก้ไขไดเรกทอรีการทำงานและดัชนีซึ่งหมายความว่าไดเรกทอรีการทำงานและดัชนีของคุณต้องสะอาด
คุณสามารถใช้git stashแล้วgit pullแต่คุณจะทำอย่างไรเมื่อคุณตรวจสอบเสร็จแล้ว? ในการกลับไปยังที่ที่คุณอยู่คุณต้องเลิกทำการผสานที่สร้างโดยgit pullใช้ที่เก็บของ
git remote update -p(หรือgit fetch --all -p) ไม่ได้ปรับเปลี่ยนไดเรกทอรีการทำงานหรือดัชนีดังนั้นจึงปลอดภัยที่จะเรียกใช้เมื่อใดก็ได้ - แม้ว่าคุณจะมีการจัดฉากและ / หรือการเปลี่ยนแปลงที่ไม่มีการจัดฉาก คุณสามารถหยุดสิ่งที่คุณกำลังทำและทบทวนการกระทำของคนอื่นโดยไม่ต้องกังวลกับการหยุดชะงักหรือทำสิ่งที่คุณทำอยู่ git pullไม่ได้ให้ความยืดหยุ่นแก่คุณ
การรีบูทเข้าสู่สาขาระยะไกล
รูปแบบการใช้ Git ทั่วไปคือการทำgit pullเพื่อนำการเปลี่ยนแปลงล่าสุดตามด้วยgit rebase @{u}เพื่อกำจัดการคอมมิชชันที่git pullแนะนำ ก็พอที่ทั่วไปที่ Git มีตัวเลือกการกำหนดค่าบางอย่างเพื่อลดขั้นตอนเหล่านี้ไปยังขั้นตอนเดียวโดยบอกgit pullจะดำเนินการ rebase แทนการผสาน (ดูbranch.<branch>.rebase, branch.autosetuprebaseและpull.rebaseตัวเลือก)
น่าเสียดายที่ถ้าคุณมีการรวมที่ไม่ได้กดการกระทำที่คุณต้องการรักษาไว้ (เช่นการรวมการรวมสาขาของฟีเจอร์ที่ถูกผลักเข้าไปmaster) ไม่ว่าจะเป็นการ rebase-pull ( git pullพร้อมการbranch.<branch>.rebaseตั้งค่าเป็นtrue) หรือการผสานดึง ( git pullพฤติกรรมเริ่มต้น) ตามด้วย rebase จะทำงานได้ นี่เป็นเพราะgit rebaseกำจัดการรวม (ทำให้เป็นเส้นตรง DAG) โดยไม่มี--preserve-mergesตัวเลือก การดำเนินการดึง rebase ไม่สามารถกำหนดค่าเพื่อรักษาการผสานและการผสานดึงตามด้วยgit rebase -p @{u}จะไม่กำจัดการผสานที่เกิดจากการผสานดึง ปรับปรุง: Git v1.8.5 เพิ่มและgit pull --rebase=preserve git config pull.rebase preserveสาเหตุเหล่านี้git pullจะต้องทำgit rebase --preserve-mergesหลังจากดึงความมุ่งมั่นต้นน้ำ (ขอบคุณfunkasterสำหรับเฮดอัพ!)
ล้างสาขาที่ถูกลบ
git pullไม่ตัดสาขาการติดตามระยะไกลที่สอดคล้องกับสาขาที่ถูกลบออกจากที่เก็บระยะไกล ตัวอย่างเช่นถ้ามีคนลบสาขาfooจาก repo origin/fooระยะไกลคุณจะยังคงเห็น
สิ่งนี้นำไปสู่การที่ผู้ใช้ทำการคืนชีพสาขาที่ถูกฆ่าโดยไม่ตั้งใจเพราะพวกเขาคิดว่าพวกเขายังคงทำงานอยู่
ทางเลือกที่ดีกว่า: ใช้git upแทนgit pull
แทนที่จะgit pullแนะนำให้สร้างและใช้git upนามแฝงต่อไปนี้:
git config --global alias.up '!git remote update -p; git merge --ff-only @{u}'
นามแฝงนี้ดาวน์โหลดการกระทำล่าสุดทั้งหมดจากสาขาต้นน้ำทั้งหมด (การตัดกิ่งที่ตายแล้ว) และพยายามที่จะส่งต่อสาขาท้องถิ่นอย่างรวดเร็วไปยังการกระทำล่าสุดในสาขาอัปสตรีม หากประสบความสำเร็จแสดงว่าไม่มีการกระทำในท้องถิ่นดังนั้นจึงไม่มีความเสี่ยงในการรวมความขัดแย้ง กรอไปข้างหน้าจะล้มเหลวหากมีการกระทำในท้องถิ่น (ไม่ได้รับการชำระ) ให้โอกาสคุณในการตรวจสอบข้อผูกพันต้นน้ำก่อนที่จะดำเนินการ
สิ่งนี้ยังคงปรับเปลี่ยนไดเร็กตอรี่การทำงานของคุณในรูปแบบที่ไม่แน่นอน, แต่ถ้าคุณไม่มีการเปลี่ยนแปลงในเครื่อง ไม่เหมือนgit pullว่าgit upจะไม่ส่งคุณไปยังพรอมต์ที่คาดหวังให้คุณแก้ไขข้อขัดแย้งในการผสาน
ตัวเลือกอื่น: git pull --ff-only --all -p
ต่อไปนี้เป็นทางเลือกสำหรับgit upนามแฝงข้างต้น:
git config --global alias.up 'pull --ff-only --all -p'
เวอร์ชันนี้git upมีพฤติกรรมเหมือนกับgit upนามแฝงก่อนหน้ายกเว้น:
- ข้อความแสดงข้อผิดพลาดเป็นความลับอีกเล็กน้อยถ้าสาขาในประเทศของคุณไม่ได้กำหนดค่าด้วยสาขาต้นน้ำ
- มันขึ้นอยู่กับคุณสมบัติที่ไม่มีเอกสาร (
-pอาร์กิวเมนต์ซึ่งถูกส่งผ่านไปfetch) ที่อาจเปลี่ยนแปลงใน Git เวอร์ชันในอนาคต
หากคุณใช้ Git 2.0 หรือใหม่กว่า
ด้วย Git 2.0 และใหม่กว่าคุณสามารถกำหนดค่าgit pullเพื่อทำการผสานการกรอไปข้างหน้าอย่างรวดเร็วโดยค่าเริ่มต้น:
git config --global pull.ff only
สาเหตุนี้git pullจะทำหน้าที่เหมือนgit pull --ff-onlyแต่ก็ยังไม่สามารถดึงข้อมูลกระทำต้นน้ำหรือทำความสะอาดออกเก่าสาขาดังนั้นฉันยังคงต้องการorigin/*git up