ลบโฟลเดอร์และเนื้อหาออกจากประวัติของ git / GitHub


318

ฉันทำงานที่เก็บข้อมูลในบัญชี GitHub ของฉันและนี่เป็นปัญหาที่ฉันสะดุด

  • โปรเจ็กต์ Node.js พร้อมโฟลเดอร์ที่ติดตั้งแพ็กเกจ npm สองสามตัว
  • แพ็คเกจอยู่ในnode_modulesโฟลเดอร์
  • เพิ่มโฟลเดอร์นั้นไปยังที่เก็บ git และผลักรหัสไปที่ github (ไม่ได้คิดเกี่ยวกับส่วนของ npm ในเวลานั้น)
  • รับรู้ว่าคุณไม่จำเป็นต้องใช้โฟลเดอร์นั้นเพื่อเป็นส่วนหนึ่งของรหัส
  • ลบโฟลเดอร์นั้นแล้วผลักออก

ในกรณีดังกล่าวขนาดของ repo git รวมอยู่ที่ประมาณ6MBซึ่งโค้ดจริง (ทั้งหมดยกเว้นโฟลเดอร์นั้น) มีขนาดประมาณ300 KBเท่านั้น

ตอนนี้สิ่งที่ฉันกำลังมองหาในที่สุดคือวิธีการกำจัดรายละเอียดของโฟลเดอร์แพ็คเกจจากประวัติของ git ดังนั้นหากใครบางคนลอกแบบพวกเขาไม่ต้องดาวน์โหลดประวัติมูลค่า 6mb ที่ไฟล์จริงเท่านั้นที่พวกเขาจะได้รับ ณ ค่าคอมมิชชันล่าสุดจะเป็น 300KB

ฉันค้นหาวิธีแก้ปัญหาที่เป็นไปได้สำหรับสิ่งนี้และลองใช้ทั้งสองวิธี

สรุปสาระสำคัญดูเหมือนว่ามันทำงานที่ไหนหลังจากที่เรียกใช้สคริปต์ก็แสดงให้เห็นว่ามันได้รับการกำจัดของโฟลเดอร์นั้นและหลังจากนั้นมันก็แสดงให้เห็นว่า 50 commits ที่แตกต่างกันได้รับการแก้ไข แต่มันก็ไม่ให้ฉันกดรหัสนั้น เมื่อฉันพยายามที่จะผลักดันมันก็กล่าวว่าBranch up to dateแต่แสดงให้เห็นว่า 50 git statusกระทำการแก้ไขเมื่อ อีกสองวิธีไม่ได้ช่วยเช่นกัน

ตอนนี้แม้ว่ามันจะแสดงให้เห็นว่ามันกำจัดประวัติของโฟลเดอร์นั้นเมื่อฉันตรวจสอบขนาดของ repo บน localhost ของฉันมันก็ยังคงอยู่ประมาณ 6MB (ฉันลบrefs/originalโฟลเดอร์ด้วย แต่ไม่เห็นการเปลี่ยนแปลงขนาดของ repo)

สิ่งที่ฉันต้องการชี้แจงคือถ้ามีวิธีการกำจัดไม่เพียง แต่ประวัติการกระทำ (ซึ่งเป็นสิ่งเดียวที่ฉันคิดว่าเกิดขึ้น) แต่ยังไฟล์คอมไพล์เหล่านั้นคือการรักษาสมมติว่าต้องการย้อนกลับ

ช่วยบอกว่ามีการนำเสนอวิธีแก้ปัญหาสำหรับสิ่งนี้และนำไปใช้กับ localhost ของฉัน แต่ไม่สามารถทำซ้ำกับ GitHub repo ได้มันเป็นไปได้ที่จะลอกเลียน repo นั้นย้อนกลับไปที่คอมมิชชันแรกแล้วใช้มัน (หรือนั่นหมายความว่า ยังมีประวัติของการกระทำเหล่านั้นทั้งหมดหรือไม่ - aka. 6MB)

เป้าหมายสุดท้ายของฉันคือการหาวิธีที่ดีที่สุดในการกำจัดเนื้อหาของโฟลเดอร์ออกจากคอมไพล์เพื่อให้ผู้ใช้ไม่ต้องดาวน์โหลดสิ่งของ 6MB และยังมีคอมมิทอื่น ๆ ที่ไม่เคยสัมผัสโฟลเดอร์โมดูล มาก) ในประวัติศาสตร์ของคอมไพล์

ฉันจะทำสิ่งนี้ได้อย่างไร


3
หากคำตอบข้อใดข้อหนึ่งด้านล่างนี้แก้ปัญหาของคุณได้บางทีคุณควรลองตอบคำถามให้เป็นคำตอบ meta.stackexchange.com/questions/5234/…
starbeamrainbowlabs

คำตอบที่ดีที่สุดคือ: stackoverflow.com/a/32886427/5973334
Kuzeko

คำตอบ:


556

หากคุณอยู่ที่นี่เพื่อคัดลอกรหัสวาง:

นี่คือตัวอย่างที่ลบออกnode_modulesจากประวัติ

git filter-branch --tree-filter "rm -rf node_modules" --prune-empty HEAD
git for-each-ref --format="%(refname)" refs/original/ | xargs -n 1 git update-ref -d
echo node_modules/ >> .gitignore
git add .gitignore
git commit -m 'Removing node_modules from git history'
git gc
git push origin master --force

สิ่งที่คอมไพล์ทำจริง:

บรรทัดแรก iterates ผ่านการอ้างอิงทั้งหมดบนต้นเดียวกัน ( --tree-filter) ในฐานะหัวหน้า (สาขาปัจจุบันของคุณ) rm -rf node_modulesใช้คำสั่ง คำสั่งนี้จะลบ node_modules โฟลเดอร์ ( -rโดยไม่-r, rmจะไม่ลบโฟลเดอร์) โดยไม่ได้รับพรอมต์ให้กับผู้ใช้ ( -f) การ--prune-emptyลบที่เพิ่มเข้ามานั้นไร้ประโยชน์ (ไม่เปลี่ยนแปลงอะไร) ที่กระทำซ้ำ ๆ

บรรทัดที่สองจะลบการอ้างอิงไปยังสาขาเก่านั้น

ส่วนที่เหลือของคำสั่งนั้นค่อนข้างตรงไปตรงมา


3
แค่ข้อสังเกตด้านข้าง: ฉันเคยgit count-objects -vตรวจสอบว่าไฟล์ถูกลบจริง ๆ หรือไม่ แต่ขนาดของที่เก็บยังคงเหมือนเดิมจนกว่าฉันจะโคลนที่เก็บอีกครั้ง Git ต้องคัดลอกไฟล์ต้นฉบับทั้งหมดที่ฉันคิด
Davide Icardi

4
ด้วยคอมไพล์ที่ไม่โบราณนี้อาจจะอ่านไม่ได้--force-with-lease --force
Griwes

4
คำสั่งเหล่านี้ไม่ทำงานบน windows หรืออย่างน้อยก็ไม่ใช่ Windows 10 โปรดโพสต์ระบบปฏิบัติการที่ "cut and paste" ทำงานได้
David

3
สำหรับผู้ใช้ Windows 10 มันใช้งานได้ดีกับ Bash สำหรับ Windows (ฉันใช้ Ubuntu)
Andrej Kyselica

3
ฉันลองกับ windows shell และ git bash และใช้งานไม่ได้ ผ่านคำสั่งแรกคำสั่งที่สองล้มเหลว!
Mohy Eldeen

240

ฉันพบว่า--tree-filterตัวเลือกที่ใช้ในคำตอบอื่น ๆ อาจช้ามากโดยเฉพาะในที่เก็บข้อมูลขนาดใหญ่ที่มีข้อผูกมัดมากมาย

นี่คือวิธีที่ฉันใช้เพื่อลบไดเรกทอรีออกอย่างสมบูรณ์จากประวัติ git โดยใช้--index-filterตัวเลือกซึ่งทำงานเร็วกว่ามาก:

# Make a fresh clone of YOUR_REPO
git clone YOUR_REPO
cd YOUR_REPO

# Create tracking branches of all branches
for remote in `git branch -r | grep -v /HEAD`; do git checkout --track $remote ; done

# Remove DIRECTORY_NAME from all commits, then remove the refs to the old commits
# (repeat these two commands for as many directories that you want to remove)
git filter-branch --index-filter 'git rm -rf --cached --ignore-unmatch DIRECTORY_NAME/' --prune-empty --tag-name-filter cat -- --all
git for-each-ref --format="%(refname)" refs/original/ | xargs -n 1 git update-ref -d

# Ensure all old refs are fully removed
rm -Rf .git/logs .git/refs/original

# Perform a garbage collection to remove commits with no refs
git gc --prune=all --aggressive

# Force push all branches to overwrite their history
# (use with caution!)
git push origin --all --force
git push origin --tags --force

คุณสามารถตรวจสอบขนาดของพื้นที่เก็บข้อมูลก่อนและหลังgcด้วย:

git count-objects -vH

3
คุณช่วยอธิบายได้ไหมว่าทำไมเรื่องนี้ถึงเร็วกว่ากัน?
knocte

7
@knocte: จากเอกสาร ( git-scm.com/docs/git-filter-branch ) "- ดัชนีตัวกรอง: ... คล้ายกับตัวกรองทรี แต่ไม่ได้ตรวจสอบต้นไม้ซึ่งทำให้เร็วขึ้นมาก"
Lee Netherton

23
ทำไมนี่ไม่ใช่คำตอบที่ยอมรับ? มันละเอียดมาก
นักฟิสิกส์บ้า

2
หากทำสิ่งนี้ใน Windows คุณต้องใช้เครื่องหมายคำพูดคู่แทนการใส่เครื่องหมายคำพูดเดี่ยว
Kris Morness

12
ผ่าน--quietไปgit rmด้านบนเร่งเขียนใหม่ของฉันอย่างน้อยโดยปัจจัยที่ 4
ctusch

46

นอกเหนือจากคำตอบที่ได้รับความนิยมข้างต้นฉันต้องการเพิ่มหมายเหตุเล็กน้อยสำหรับWindows -systems คำสั่ง

git filter-branch --tree-filter 'rm -rf node_modules' --prune-empty HEAD
  • ทำงานได้อย่างสมบูรณ์แบบโดยไม่ต้องดัดแปลงใด ๆ ! ดังนั้นคุณไม่ต้องใช้Remove-Item, หรือสิ่งอื่นแทนdelrm -rf

  • หากคุณต้องการระบุพา ธ ไปยังไฟล์หรือไดเรกทอรีให้ใช้เครื่องหมายทับเช่น./path/to/node_modules


สิ่งนี้จะไม่ทำงานบน Windows หากไดเรกทอรีมี (dot) ในชื่อ
Corneliu Serediuc

4
และฉันก็พบทางออก ใช้ double inverted-commas สำหรับคำสั่ง rm ดังนี้: "rm -rf node.modules"
Corneliu Serediuc

23

วิธีที่ดีที่สุดและถูกต้องที่สุดที่ฉันพบคือดาวน์โหลดไฟล์ bfg.jar: https://rtyley.github.io/bfg-repo-cleaner/

จากนั้นรันคำสั่ง:

git clone --bare https://project/repository project-repository
cd project-repository
java -jar bfg.jar --delete-folders DIRECTORY_NAME  # i.e. 'node_modules' in other examples
git reflog expire --expire=now --all && git gc --prune=now --aggressive
git push --mirror https://project/new-repository

หากคุณต้องการลบไฟล์ให้ใช้ตัวเลือก delete-files แทน:

java -jar bfg.jar --delete-files *.pyc

1
ง่ายมาก :) ถ้าคุณต้องการทำให้มั่นใจว่ามีเพียงโฟลเดอร์ที่ถูกลบออกไปเท่านั้นสิ่งนี้จะช่วยได้: stackoverflow.com/questions/21142986/…
emjay

9

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

สคริปต์หลายบรรทัดส่วนใหญ่ด้านบนเพื่อลบออกdirจากประวัติสามารถเขียนใหม่เป็น:

git filter-repo --path dir --invert-paths

เครื่องมือมีพลังมากกว่าที่เห็นได้ชัด คุณสามารถใช้ตัวกรองตามผู้แต่งอีเมลชื่อและอื่น ๆ ( manpage เต็มรูปแบบที่นี่ ) นอกจากนี้ยังเป็นไปอย่างรวดเร็ว ติดตั้งง่าย - มันกระจายอยู่ในหลากหลายรูปแบบ


เครื่องมือที่ดี! ทำงานได้ดีบน Ubuntu 20.04 คุณสามารถทำได้pip3 install git-filter-repoตั้งแต่ stdlib-only และไม่ได้ติดตั้งการพึ่งพาใด ๆ ใน Ubuntu 18 มันเข้ากันไม่ได้กับเวอร์ชั่นคอมไพล์ของ distro Error: need a version of git whose diff-tree command has the --combined-all-paths optionแต่มันก็ง่ายพอที่จะรันบน adocker run -ti ubuntu:20.04
kubanczyk

7

ทำสูตรคัดลอกและวางอย่างสมบูรณ์เพียงแค่เพิ่มคำสั่งในความคิดเห็น (สำหรับวิธีคัดลอกวาง) หลังจากทดสอบแล้ว:

git filter-branch --tree-filter 'rm -rf node_modules' --prune-empty HEAD
echo node_modules/ >> .gitignore
git add .gitignore
git commit -m 'Removing node_modules from git history'
git gc
git push origin master --force

หลังจากนี้คุณสามารถลบบรรทัด "node_modules /" ออกจาก. gitignore


ทำไมคุณจะลบออกnode_modulesจาก.gitignore? เพื่อที่พวกเขาจะได้ตั้งใจทำอีกครั้ง?
Adamski

1
มันไม่ถูกลบออกจาก gitignore แต่ถูกเพิ่มไปยัง gitignore ข้อความคอมมิชชันบอกว่า "git history" ไม่ใช่ "gitignore" :)
Danny Tuppeny

แต่ความคิดเห็นกล่าวว่าแล้วคุณสามารถลบจากnode_modules .gitignore
zavr

7

สำหรับผู้ใช้ Windows โปรดทราบว่าจะใช้"แทนการ' เพิ่ม-fเพื่อบังคับให้คำสั่งหากมีข้อมูลสำรองอื่นอยู่แล้ว

git filter-branch -f --tree-filter "rm -rf FOLDERNAME" --prune-empty HEAD
git for-each-ref --format="%(refname)" refs/original/ | xargs -n 1 git update-ref -d
echo FOLDERNAME/ >> .gitignore
git add .gitignore
git commit -m "Removing FOLDERNAME from git history"
git gc
git push origin master --force

3

ฉันลบโฟลเดอร์ bin และ obj ออกจากโครงการเก่า C # โดยใช้ git บน windows ระวังด้วย

git filter-branch --tree-filter "rm -rf bin" --prune-empty HEAD

มันจะทำลายความสมบูรณ์ของการติดตั้ง git โดยการลบโฟลเดอร์ usr / bin ในโฟลเดอร์การติดตั้ง git

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