ในกรณีของฉันฉันมีmy-pluginพื้นที่เก็บข้อมูลและmain-projectพื้นที่เก็บข้อมูลและฉันอยากจะแกล้งทำเป็นว่าmy-pluginเคยพัฒนาในไดเรกทอรีย่อยของpluginsmain-project
โดยทั่วไปฉันจะเขียนประวัติของที่my-pluginเก็บเพื่อให้การพัฒนาทั้งหมดเกิดขึ้นในplugins/my-pluginไดเรกทอรีย่อย จากนั้นฉันเพิ่มประวัติการพัฒนาmy-pluginลงในmain-projectประวัติศาสตร์และรวมต้นไม้สองต้นเข้าด้วยกัน เนื่องจากไม่มีplugins/my-pluginไดเรกทอรีอยู่ในที่main-projectเก็บจึงเป็นการรวมที่ไม่ขัดแย้งกันเล็กน้อย ที่เก็บผลลัพธ์มีประวัติทั้งหมดจากโครงการดั้งเดิมและมีสองราก
TL; DR
$ cp -R my-plugin my-plugin-dirty
$ cd my-plugin-dirty
$ git filter-branch -f --tree-filter "zsh -c 'setopt extended_glob && setopt glob_dots && mkdir -p plugins/my-plugin && (mv ^(.git|plugins) plugins/my-plugin || true)'" -- --all
$ cd ../main-project
$ git checkout master
$ git remote add --fetch my-plugin ../my-plugin-dirty
$ git merge my-plugin/master --allow-unrelated-histories
$ cd ..
$ rm -rf my-plugin-dirty
รุ่นยาว
ก่อนอื่นให้สร้างสำเนาของที่my-pluginเก็บเพราะเรากำลังจะเขียนประวัติของที่เก็บนี้อีกครั้ง
ตอนนี้ไปที่รูทของที่my-pluginเก็บเช็คสาขาหลักของคุณ (อาจmaster) และรันคำสั่งต่อไปนี้ แน่นอนคุณควรแทนที่my-pluginและpluginsชื่อจริงของคุณคืออะไร
$ git filter-branch -f --tree-filter "zsh -c 'setopt extended_glob && setopt glob_dots && mkdir -p plugins/my-plugin && (mv ^(.git|plugins) plugins/my-plugin || true)'" -- --all
ตอนนี้สำหรับคำอธิบาย git filter-branch --tree-filter (...) HEADรันคำสั่งในทุกการกระทำที่สามารถเข้าถึงได้จาก(...) HEADโปรดทราบว่าสิ่งนี้ทำงานโดยตรงกับข้อมูลที่เก็บไว้สำหรับการกระทำแต่ละครั้งดังนั้นเราจึงไม่ต้องกังวลเกี่ยวกับแนวคิดของ "ไดเรกทอรีการทำงาน", "ดัชนี", "การจัดเตรียม" และอื่น ๆ
ถ้าคุณเรียกใช้filter-branchคำสั่งที่ล้มเหลวก็จะทิ้งบางไฟล์ใน.gitไดเรกทอรีและครั้งต่อไปที่คุณพยายามที่filter-branchจะบ่นเกี่ยวกับเรื่องนี้จนกว่าคุณจะจัดหาตัวเลือกในการ-ffilter-branch
สำหรับคำสั่งจริงฉันไม่มีโชคมากที่bashจะทำสิ่งที่ฉันต้องการดังนั้นแทนที่จะใช้zsh -cเพื่อzshเรียกใช้งานคำสั่ง ครั้งแรกที่ฉันตั้งค่าextended_globตัวเลือกซึ่งเป็นสิ่งที่ช่วยให้^(...)ไวยากรณ์ในmvคำสั่งเช่นเดียวกับglob_dotsตัวเลือกซึ่งช่วยให้ฉันเลือก dotfiles (เช่น.gitignore) ด้วย glob ( ^(...))
ต่อไปฉันใช้mkdir -pคำสั่งเพื่อสร้างทั้งสองpluginsและplugins/my-pluginในเวลาเดียวกัน
ในที่สุดฉันใช้คุณสมบัติzsh"ลบ glob" ^(.git|plugins)เพื่อจับคู่ไฟล์ทั้งหมดในไดเรกทอรีรากของที่เก็บยกเว้น.gitและmy-pluginโฟลเดอร์ที่สร้างขึ้นใหม่ (การยกเว้น.gitอาจไม่จำเป็นที่นี่ แต่การลองย้ายไดเรกทอรีไปยังตัวเองถือเป็นข้อผิดพลาด)
ในที่เก็บของฉันการคอมมิชชันเริ่มต้นไม่ได้รวมไฟล์ใด ๆ ดังนั้นmvคำสั่งส่งคืนข้อผิดพลาดในการคอมมิทเริ่มต้น ดังนั้นฉันจึงเพิ่มรายการ|| trueที่git filter-branchจะไม่ยกเลิก
--allตัวเลือกที่บอกfilter-branchจะเขียนประวัติศาสตร์สำหรับทุกสาขาในพื้นที่เก็บข้อมูลและเพิ่มเติม--เป็นสิ่งจำเป็นที่จะบอกgitจะตีความว่ามันเป็นส่วนหนึ่งของรายการตัวเลือกสำหรับสาขาที่จะเขียนแทนการเป็นตัวเลือกให้กับfilter-branchตัวเอง
ตอนนี้ไปที่ที่main-projectเก็บของคุณแล้วดูสาขาที่คุณต้องการรวม เพิ่มสำเนาที่my-pluginเก็บโลคัลของคุณ (ที่แก้ไขประวัติ) เป็นรีโมตmain-projectด้วย:
$ git remote add --fetch my-plugin $PATH_TO_MY_PLUGIN_REPOSITORY
ตอนนี้คุณจะมีต้นไม้ที่ไม่เกี่ยวข้องสองต้นในประวัติการกระทำของคุณซึ่งคุณสามารถเห็นภาพได้อย่างชัดเจนโดยใช้:
$ git log --color --graph --decorate --all
เพื่อรวมพวกเขาใช้:
$ git merge my-plugin/master --allow-unrelated-histories
โปรดทราบว่าใน Git pre-2.9.0 --allow-unrelated-historiesไม่มีตัวเลือกอยู่ หากคุณกำลังใช้รุ่นใดรุ่นหนึ่งเหล่านี้เพียงละเว้นตัวเลือก: ข้อความแสดงข้อผิดพลาดที่--allow-unrelated-historiesป้องกันไม่ให้ถูกยังเพิ่มใน 2.9.0
คุณไม่ควรมีข้อขัดแย้งในการผสาน หากคุณทำเช่นนั้นอาจหมายถึงว่าfilter-branchคำสั่งทำงานไม่ถูกต้องหรือมีplugins/my-pluginไดเรกทอรีอยู่main-projectแล้ว
ตรวจสอบให้แน่ใจว่าได้ป้อนข้อความการมอบหมายที่อธิบายได้สำหรับผู้มีส่วนร่วมในอนาคตสงสัยว่าแฮกเกอร์กำลังทำอะไรเพื่อสร้างที่เก็บที่มีสองราก
คุณสามารถเห็นกราฟการคอมมิตใหม่ซึ่งควรมีคอมมิททรูทสองอันโดยใช้git logคำสั่งด้านบน โปรดทราบว่าเฉพาะmasterสาขาที่จะถูกรวม ซึ่งหมายความว่าหากคุณมีงานสำคัญในmy-pluginสาขาอื่นที่คุณต้องการรวมเข้ากับmain-projectต้นไม้คุณควรละเว้นการลบmy-pluginรีโมตจนกว่าคุณจะทำการผสานเหล่านี้ หากคุณไม่ทำเช่นนั้นการผูกมัดจากสาขาเหล่านั้นจะยังคงอยู่ในที่main-projectเก็บ แต่บางอันจะไม่สามารถเข้าถึงได้และไวต่อการรวบรวมขยะในที่สุด (นอกจากนี้คุณจะต้องอ้างถึงพวกเขาโดย SHA เนื่องจากการลบรีโมตจะลบสาขาการติดตามระยะไกล)
เป็นทางเลือกหลังจากคุณรวมทุกอย่างที่คุณต้องการเก็บไว้my-pluginแล้วคุณสามารถลบmy-pluginรีโมตโดยใช้:
$ git remote remove my-plugin
ตอนนี้คุณสามารถลบสำเนาของที่my-pluginเก็บที่คุณเปลี่ยนประวัติได้อย่างปลอดภัย ในกรณีของฉันฉันยังเพิ่มการบอกเลิกการลงในที่my-pluginเก็บจริงหลังจากการผสานเสร็จสมบูรณ์และถูกผลัก
ผ่านการทดสอบบน Mac OS X El Capitan ด้วยและgit --version 2.9.0 zsh --version 5.2ไมล์สะสมของคุณอาจแตกต่างกันไป
อ้างอิง: