Git Filter-Repo เป็นเครื่องมือที่หลากหลายสำหรับการเขียนประวัติใหม่ซึ่งรวมถึงความสามารถที่ฉันไม่พบที่อื่น มันตกอยู่ในพื้นที่เดียวกันของเครื่องมือเช่นเดียวกับ Git Filter-Branch แต่ไม่มีประสิทธิภาพที่ไม่ดีทำให้เกิดความสามารถมากขึ้นและมีการออกแบบที่ปรับขนาดการใช้งานได้ดีกว่ากรณีการเขียนใหม่เล็กน้อย Git Filter-Repo ได้รับการแนะนำโดยโครงการ GIT แทน Git Filter-Branch
ในขณะที่ผู้ใช้ส่วนใหญ่อาจจะใช้ตัวกรอง-เรปโปเป็นเครื่องมือบรรทัดคำสั่งง่ายๆ (และน่าจะใช้เพียงไม่กี่แฟล็ก) ที่ตัวกรองหลักของมันมีไลบรารีสำหรับการสร้างเครื่องมือการเขียนประวัติ ดังนั้นผู้ใช้ที่มีความต้องการพิเศษสามารถใช้ประโยชน์จากมันเพื่อสร้างเครื่องมือการเขียนประวัติศาสตร์ใหม่ทั้งหมดอย่างรวดเร็ว
ต้องใช้ตัวกรอง-รีเปะ:
git-filter-repo
เป็นสคริปต์ Python ไฟล์เดียวซึ่งทำเพื่อทำการติดตั้งสำหรับการใช้งานพื้นฐานในหลาย ๆ ระบบเล็กน้อย: เพียงแค่วางไฟล์นั้นลงในเส้นทาง $ ของคุณ
ดู Install.md สำหรับสิ่งที่นอกเหนือจากการใช้งานขั้นพื้นฐานหรือกรณีพิเศษ จำเป็นต้องมีคำแนะนำที่เกี่ยวข้องมากขึ้นหากมีการสมัครอย่างใดอย่างหนึ่งต่อไปนี้:
สำหรับเอกสารที่ครอบคลุม:
หากคุณต้องการเรียนรู้จากตัวอย่าง:
สิ่งนี้ครอบคลุมรายละเอียดเพิ่มเติมในบทความ Git Rev News เกี่ยวกับตัวกรอง Repo แต่มีไฮไลท์บางอย่างสำหรับคู่แข่งหลัก:
สาขากรองนั้นช้าอย่างมากอย่างไม่น่าเชื่อ (คำสั่งซื้อหลายระดับช้ากว่าที่ควรจะเป็น) สำหรับที่เก็บที่ไม่สำคัญ
สาขากรองเต็มไปด้วย gotchas ที่สามารถทำลายการเขียนใหม่ของคุณอย่างเงียบ ๆ หรืออย่างน้อยก็ขัดขวางความพยายาม "ทำความสะอาด" ของคุณโดยให้คุณมีปัญหาและยุ่งมากกว่าที่คุณเริ่มต้น
สาขากรองเป็นเรื่องยากมากที่จะใช้สำหรับการเขียนใหม่ซึ่งเป็นเรื่องที่ไม่น่าสนใจเล็กน้อย
โครงการ GIT ได้ระบุว่าปัญหาข้างต้นเกี่ยวกับการกรอง-สาขาไม่สามารถแก้ไขได้ย้อนหลัง พวกเขาแนะนำให้คุณหยุดใช้สาขาตัวกรอง
แฟน ๆ ที่ตายยากของสาขาไส้กรองอาจสนใจในตัวกรอง-(aka filter-branch-ish) การปรับแต่งของตัวกรอง-สาขาตามตัวกรอง-รีเป่ repo)
แผ่นโกงมีให้แสดงวิธีการแปลงคำสั่งตัวอย่างจากคู่มือของตัวกรอง-สาขาเป็นคำสั่งตัวกรอง-รีเปโป
เครื่องมือที่ยอดเยี่ยมสำหรับเวลา แต่ในขณะที่มันทำให้บางสิ่งง่าย ๆ มันถูก จำกัด ให้เขียนซ้ำไม่กี่ชนิด
สถาปัตยกรรมของมันไม่สามารถตอบสนองการเขียนซ้ำได้มากขึ้น
สถาปัตยกรรมของมันนำเสนอข้อบกพร่องและข้อบกพร่องบางอย่างแม้กระทั่งสำหรับ usecase ที่ตั้งใจไว้
แฟน ๆ ของ BFG อาจสนใจ BFG-ISH ซึ่งเป็นการปรับแต่ง BFG ขึ้นอยู่กับตัวกรอง Repo ซึ่งรวมถึงคุณสมบัติใหม่และข้อบกพร่องหลายประการที่สัมพันธ์กับ BFG
แผ่นโกงมีให้แสดงวิธีการแปลงคำสั่งตัวอย่างจากคู่มือของ BFG repo cleaner เป็นคำสั่งตัวกรอง repo
สมมติว่าเราต้องการสกัดชิ้นส่วนของที่เก็บด้วยความตั้งใจที่จะรวมชิ้นส่วนนั้นเข้ากับ repo ที่ใหญ่กว่าอื่น ๆ สำหรับการสกัดเราต้องการ:
การทำสิ่งนี้ด้วยตัวกรองรีเปโปนั้นง่ายเหมือนคำสั่งต่อไปนี้:
git filter-repo --path src/ --to-subdirectory-filter my-module --tag-rename ' ' : ' my-module- '
(คำพูดเดียวไม่จำเป็น แต่ทำให้ชัดเจนยิ่งขึ้นกับมนุษย์ว่าเรากำลังแทนที่สตริงว่างเป็นคำนำหน้าด้วย my-module-
)
BFG Repo Cleaner ไม่สามารถเขียนใหม่ได้ ในความเป็นจริงการเปลี่ยนแปลงที่ต้องการทั้งสามประเภทนั้นอยู่นอกความสามารถ
ตัวกรอง-สาขามาพร้อมกับกองคำเตือน (เพิ่มเติมในด้านล่าง) แม้เมื่อคุณคิดว่าการเรียกร้องที่จำเป็น:
git filter-branch
--tree-filter ' mkdir -p my-module &&
git ls-files
| grep -v ^src/
| xargs git rm -f -q &&
ls -d *
| grep -v my-module
| xargs -I files mv files my-module/ '
--tag-name-filter ' echo "my-module-$(cat)" '
--prune-empty -- --all
git clone file:// $( pwd ) newcopy
cd newcopy
git for-each-ref --format= " delete %(refname) " refs/tags/
| grep -v refs/tags/my-module-
| git update-ref --stdin
git gc --prune=now
บางคนอาจสังเกตเห็นว่าการเรียกใช้ตัวกรอง-สาขาข้างต้นจะช้ามากเนื่องจากการใช้-กรองทรี; คุณสามารถใช้ตัวเลือก-ตัวกรอง-ตัวกรองของตัวกรอง-สาขาเปลี่ยนคำสั่งด้านบนเป็น:
git filter-branch
--index-filter ' git ls-files
| grep -v ^src/
| xargs git rm -q --cached;
git ls-files -s
| sed "s%$(printf \t)%&my-module/%"
| git update-index --index-info;
git ls-files
| grep -v ^my-module/
| xargs git rm -q --cached '
--tag-name-filter ' echo "my-module-$(cat)" '
--prune-empty -- --all
git clone file:// $( pwd ) newcopy
cd newcopy
git for-each-ref --format= " delete %(refname) " refs/tags/
| grep -v refs/tags/my-module-
| git update-ref --stdin
git gc --prune=now
อย่างไรก็ตามสำหรับคำสั่งตัวกรอง-สาขามีกองคำเตือน ก่อนอื่นบางคนอาจสงสัยว่าทำไมฉันถึงรายการคำสั่งห้าคำสั่งที่นี่สำหรับ Filter-Branch แม้จะมีการใช้งาน-ทั้งหมดและ-แท็กชื่อ-กรองและ manpage ของกรอง-สาขาอ้างว่าโคลนเพียงพอที่จะกำจัดวัตถุเก่าขั้นตอนพิเศษในการลบแท็กอื่น ๆ และทำ GC อื่น ทำความสะอาดวัตถุเก่าและหลีกเลี่ยงการผสมประวัติศาสตร์ใหม่และเก่าก่อนที่จะผลักดันที่ไหนสักแห่ง ข้อแม้อื่น ๆ :
หนึ่งสามารถแฮ็คนี้พร้อมกับสิ่งที่ชอบ:
git fast-export --no-data --reencode=yes --mark-tags --fake-missing-tagger
--signed-tags=strip --tag-of-filtered-object=rewrite --all
| grep -vP ' ^M [0-9]+ [0-9a-f]+ (?!src/) '
| grep -vP ' ^D (?!src/) '
| perl -pe ' s%^(M [0-9]+ [0-9a-f]+ )(.*)$%1my-module/2% '
| perl -pe ' s%^(D )(.*)$%1my-module/2% '
| perl -pe s%refs/tags/%refs/tags/my-module-%
| git -c core.ignorecase=false fast-import --date-format=raw-permissive
--force --quiet
git for-each-ref --format= " delete %(refname) " refs/tags/
| grep -v refs/tags/my-module-
| git update-ref --stdin
git reset --hard
git reflog expire --expire=now --all
git gc --prune=now
แต่สิ่งนี้มาพร้อมกับคำเตือนและข้อ จำกัด ที่น่ารังเกียจ:
ไม่มีเครื่องมือการกรองที่เก็บที่มีอยู่ในสิ่งที่ฉันต้องการ พวกเขาทั้งหมดขึ้นมาสั้น ๆ สำหรับความต้องการของฉัน ไม่มีเครื่องมือใดที่ให้คุณลักษณะใด ๆ แปดประการด้านล่างที่ฉันต้องการและไม่มีเครื่องมือใดที่มีมากกว่าสองในสี่ลักษณะล่าสุด:
[รายงานเริ่มต้น] ให้การวิเคราะห์ผู้ใช้ repo ของพวกเขาเพื่อช่วยให้พวกเขาเริ่มต้นในสิ่งที่จะตัดหรือเปลี่ยนชื่อแทนที่จะคาดหวังให้พวกเขาคาดเดาหรือหาเครื่องมืออื่น ๆ เพื่อหามัน (ทริกเกอร์เช่นการใช้งานครั้งแรกด้วยธงพิเศษเช่น -วิเคราะห์)
[Keep vs. Remove] แทนที่จะเพียงแค่ให้วิธีการสำหรับผู้ใช้ในการลบเส้นทางที่เลือกได้อย่างง่ายดายนอกจากนี้ยังให้ธงสำหรับผู้ใช้เพื่อ รักษา เส้นทางบางเส้นทางเท่านั้น แน่นอนว่าผู้ใช้สามารถแก้ปัญหานี้ได้โดยระบุเพื่อลบเส้นทางทั้งหมดนอกเหนือจากเส้นทางที่พวกเขาต้องการ แต่ความจำเป็นในการระบุเส้นทางทั้งหมดที่ เคย มีอยู่ในพื้นที่เก็บข้อมูล ทุก ครั้งอาจเจ็บปวดมาก สำหรับตัวกรอง-สาขาการใช้ท่อเช่น git ls-files | grep -v ... | xargs -r git rm
อาจเป็นวิธีแก้ปัญหาที่สมเหตุสมผล แต่อาจไม่เป็นไปได้และไม่ตรงไปตรงมาสำหรับผู้ใช้ นอกจากนี้คำสั่งเหล่านั้นมักจะเป็นระบบปฏิบัติการเฉพาะ (คุณสามารถมองเห็น gnuism ในตัวอย่างที่ฉันให้ไว้ได้หรือไม่)
[การเปลี่ยนชื่อ] ควรเปลี่ยนชื่อเส้นทางได้ง่าย ตัวอย่างเช่นนอกเหนือจากการอนุญาตให้คนหนึ่งปฏิบัติต่อไดเรกทอรีย่อยบางส่วนเป็นรูทของที่เก็บแล้วยังมีตัวเลือกสำหรับผู้ใช้ในการสร้างรูทของที่เก็บจะกลายเป็นไดเรกทอรีย่อย และโดยทั่วไปจะอนุญาตให้เปลี่ยนไฟล์และไดเรกทอรีได้อย่างง่ายดาย ให้การตรวจสอบสติว่าการเปลี่ยนชื่อทำให้หลายไฟล์มีอยู่ที่เส้นทางเดียวกัน (และเพิ่มการจัดการพิเศษเพื่อที่ว่าหากมีการคัดลอกชื่อ OldName-> newName โดยไม่ต้องแก้ไขจากนั้นกรอง OldName-> NewName จะไม่กระตุ้นการตรวจสอบสติและตายในการกระทำนั้น)
[ความปลอดภัยที่ชาญฉลาดมากขึ้น] การเขียนสำเนาของผู้อ้างอิงต้นฉบับไปยังเนมสเปซพิเศษภายใน repo ไม่ได้ให้กลไกการกู้คืนที่ใช้งานง่าย หลายคนต้องดิ้นรนเพื่อกู้คืนโดยใช้สิ่งนั้น เกือบทุกคนที่ฉันเคยเห็นการดำเนินการกรองพื้นที่เก็บข้อมูลได้ทำเช่นนั้นด้วยโคลนสดเพราะการเช็ดโคลนในกรณีที่เกิดข้อผิดพลาดเป็นกลไกการกู้คืนที่ง่ายขึ้นอย่างมาก สนับสนุนอย่างยิ่งว่าเวิร์กโฟลว์โดยการตรวจจับและประกันตัวหากเราไม่ได้อยู่ในโคลนสดเว้นแต่ผู้ใช้จะแทนที่ -กำลัง
[Auto Shrink] ลบ cruft เก่าโดยอัตโนมัติและส่งที่เก็บข้อมูลสำหรับผู้ใช้หลังจากการกรอง (เว้นแต่จะถูกแทนที่); สิ่งนี้ทำให้สิ่งต่าง ๆ ง่ายขึ้นสำหรับผู้ใช้ช่วยหลีกเลี่ยงการผสมประวัติศาสตร์เก่าและใหม่เข้าด้วยกันและหลีกเลี่ยงปัญหาที่กระบวนการหลายขั้นตอนสำหรับการหดตัว repo ที่บันทึกไว้ใน manpage ไม่ทำงานจริงในบางกรณี (ฉันกำลังมองคุณตัวกรอง-สาขา)
[การแยกที่สะอาด] หลีกเลี่ยงผู้ใช้ที่สับสน (และป้องกันไม่ให้เกิดสิ่งเก่า ๆ อีกครั้ง) เนื่องจากการผสม repo เก่าและ repo ที่เขียนใหม่เข้าด้วยกัน (นี่เป็นปัญหาโดยเฉพาะอย่างยิ่งกับตัวกรอง-สาขาเมื่อใช้ตัวเลือก-แท็กชื่อตัวกรองและบางครั้งก็เป็นปัญหาเมื่อกรองชุดย่อยของสาขาเท่านั้น)
[ความเก่งกาจ] ให้ความสามารถแก่ผู้ใช้ในการขยายเครื่องมือหรือแม้แต่เขียนเครื่องมือใหม่ที่ใช้ประโยชน์จากความสามารถที่มีอยู่และให้การขยายความสามารถนี้ในลักษณะที่ (a) หลีกเลี่ยงความจำเป็นในการแยกกระบวนการแยกต่างหาก (ซึ่งจะทำลายประสิทธิภาพ), (b) หลีกเลี่ยงการทำให้ผู้ใช้ระบุคำสั่งเชลล์ที่ขึ้นอยู่กับระบบปฏิบัติการ (ซึ่งจะป้องกันไม่ให้ผู้ใช้ไม่ให้แชร์คำสั่งซึ่งกันและกัน), (c) ใช้ประโยชน์จากโครงสร้างข้อมูลที่หลากหลาย (เนื่องจากแฮช, dicts, รายการและอาร์เรย์เป็นเรื่องยากในเชลล์) และ ( d) ให้ความสามารถในการจัดการสตริงที่สมเหตุสมผล (ซึ่งขาดอย่างมากในเปลือกหอย)
[การอ้างอิงที่กระทำเก่า] เป็นวิธีสำหรับผู้ใช้ในการใช้รหัสคอมมิชชันเก่ากับที่เก็บใหม่ (โดยเฉพาะอย่างยิ่งผ่านการแมปจากเก่าไปยังแฮชใหม่ที่มี Refs/ แทนที่/ การอ้างอิง)
[commitme message ความสอดคล้อง] หากข้อความที่ส่งข้อความอ้างถึงการกระทำอื่น ๆ โดย ID (เช่น "สิ่งนี้กลับมาใช้เวลา 01234567890ABCDEF", "ในการกระทำ 0013DeadBeeF9A ... ") ข้อความที่ควรเขียนใหม่เพื่ออ้างถึงรหัส
[การตัดแต่งกิ่งที่ว่างเปล่า] การกระทำที่ว่างเปล่าเนื่องจากการกรองควรถูกตัดแต่ง หากผู้ปกครองของการกระทำถูกตัดแต่งบรรพบุรุษที่ไม่ได้รับการตัดแต่งครั้งแรกจะต้องเป็นพ่อแม่คนใหม่ หากไม่มีบรรพบุรุษที่ไม่ได้รับการคัดเลือกและการกระทำนั้นไม่ใช่การรวมกันมันจะกลายเป็นรากฐานใหม่ หากไม่มีบรรพบุรุษที่ไม่ได้รับการแต่งตั้งและการกระทำนั้นเป็นการผสานการผสานจะมีผู้ปกครองน้อยกว่าหนึ่งคน (และทำให้มีแนวโน้มที่จะกลายเป็นความกระทำที่ไม่ได้ใช้งานซึ่งจะถูกตัดออกหากไม่มีการเปลี่ยนแปลงไฟล์ของตัวเอง) . สิ่งหนึ่งที่ควรทราบเป็นพิเศษที่นี่คือเราตัดทอนซึ่งกลายเป็นว่างเปล่าไม่ได้ดำเนินการซึ่งเริ่มว่างเปล่า บางโครงการโดยเจตนาสร้างการกระทำที่ว่างเปล่าด้วยเหตุผลการกำหนดเวอร์ชันหรือการเผยแพร่และไม่ควรลบสิ่งเหล่านี้ (เป็นกรณีพิเศษการกระทำที่เริ่มว่างเปล่า แต่ผู้ปกครองถูกตัดออกไปก็จะได้รับการพิจารณาว่ามี "ว่างเปล่า")
[การตัดแต่งกิ่งที่ดีขึ้น] การตัดแต่งกิ่งซึ่งกลายเป็นที่ว่างเปล่าอาจทำให้เกิดการเปลี่ยนแปลงโทโพโลยีและมีกรณีพิเศษมากมาย โดยปกติแล้วการรวมการรวมจะไม่ถูกลบออกเนื่องจากจำเป็นต้องมีการรักษาทอพอโลยีกราฟ แต่การตัดแต่งกิ่งของผู้ปกครองและบรรพบุรุษอื่น ๆ อาจส่งผลให้ผู้ปกครองสูญเสียหนึ่งคนขึ้นไป กรณีง่ายๆได้ถูกบันทึกไว้ข้างต้นแล้ว: หากการผสานเกิดขึ้นสูญเสียผู้ปกครองมากพอที่จะกลายเป็นผู้กระทำที่ไม่ได้เข้ามาและไม่มีการเปลี่ยนแปลงไฟล์ก็สามารถตัดแต่งได้เช่นกัน Merge Commits ยังสามารถมีทอพอโลยีที่เสื่อมสภาพได้: มันอาจจบลงด้วยการทำหน้าที่ Merge_Base เป็นทั้งพ่อแม่ (ถ้าการแทรกแซงทั้งหมดจากการซื้อคืนเดิมถูกตัดแต่ง) หรืออาจจบลงด้วยผู้ปกครองคนหนึ่ง พ่อแม่. ในกรณีเช่นนี้หากการผสานไม่มีการเปลี่ยนแปลงไฟล์ของตัวเองการรวมการรวมสามารถตัดแต่งได้ อย่างไรก็ตามเท่าที่เราทำกับการตัดแต่งกิ่งที่ว่างเปล่าเราไม่ตัดการรวมการรวมที่เริ่มเสื่อมสภาพ (ซึ่งบ่งชี้ว่ามันอาจเป็นความตั้งใจเช่น-ไม่มีการผสาน-FF) ของพวกเขาเอง
[ความเร็ว] การกรองควรเร็วพอสมควร
ดูแนวทางที่มีส่วนร่วม
ผู้เข้าร่วมในชุมชนตัวกรอง-รีปโปคาดว่าจะปฏิบัติตามมาตรฐานเดียวกันกับโครงการ GIT ดังนั้นจรรยาบรรณ GIT จึงใช้
การทำงานเกี่ยวกับตัวกรอง-รีเป้และรุ่นก่อนได้ผลักดันการปรับปรุงจำนวนมากไปสู่การส่งออกอย่างรวดเร็วและรวดเร็ว (และคำสั่งอื่น ๆ เป็นครั้งคราว) ใน Core Git ตามสิ่งที่ตัวกรอง REPO ต้องทำงาน: