วิธีการลบ / ลบไฟล์ขนาดใหญ่จากการคอมมิทประวัติในที่เก็บ Git?


708

บางครั้งฉันทำ DVD-rip ลงในโปรเจ็กต์ของเว็บไซต์จากนั้นสะเพร่าgit commit -a -m ...และในกรณีที่ซื้อคืน repo นั้นมีจำนวน 2.2 กิ๊ก ครั้งต่อไปที่ฉันทำการแก้ไขลบไฟล์วิดีโอและยืนยันทุกอย่าง แต่ไฟล์บีบอัดยังคงอยู่ในที่เก็บในประวัติ

ฉันรู้ว่าฉันสามารถเริ่มต้นสาขาจากการกระทำเหล่านั้นและรีบูตสาขาหนึ่งไปยังสาขาอื่น แต่ฉันควรทำอย่างไรเพื่อรวม 2 คอมมิชชันเข้าด้วยกันเพื่อไม่ให้ไฟล์ขนาดใหญ่ปรากฏในประวัติและถูกล้างข้อมูลในขั้นตอนการรวบรวมขยะ


9
บทความนี้จะช่วยให้คุณhelp.github.com/removing-sensitive-data
MBO

2
ที่เกี่ยวข้อง: สมบูรณ์ลบไฟล์จากทั่วทุกพื้นที่เก็บข้อมูล Git กระทำประวัติศาสตร์

1
โปรดทราบว่าหากไฟล์ขนาดใหญ่ของคุณอยู่ในเขตย่อยคุณจะต้องระบุเส้นทางแบบเต็ม
Johan


คำตอบมากมายด้านล่างพูดถึง BFG ง่ายกว่าgit filter-branchแต่ฉันพบว่าสิ่งที่ตรงกันข้ามนั้นเป็นจริง
2540625

คำตอบ:


605

ใช้BFG Repo-Cleanerทางเลือกที่ง่ายกว่าและเร็วกว่าgit-filter-branchออกแบบมาโดยเฉพาะสำหรับการลบไฟล์ที่ไม่ต้องการออกจากประวัติ Git

ปฏิบัติตามคำแนะนำการใช้งานอย่างระมัดระวังส่วนแกนหลักเป็นเพียงแค่นี้:

$ java -jar bfg.jar --strip-blobs-bigger-than 100M my-repo.git

ไฟล์ใด ๆ ที่มีขนาดเกิน 100MB (ซึ่งไม่ได้อยู่ในการส่งล่าสุดของคุณ) จะถูกลบออกจากประวัติของที่เก็บ Git ของคุณ จากนั้นคุณสามารถใช้git gcเพื่อล้างข้อมูลที่ตายแล้ว:

$ git gc --prune=now --aggressive

โดยทั่วไปแล้ว BFG นั้นเร็วกว่าการรันอย่างน้อย10-50xgit-filter-branchและใช้งานได้ง่ายกว่าโดยทั่วไป

การเปิดเผยอย่างสมบูรณ์: ฉันเป็นผู้แต่ง BFG Repo-Cleaner


4
@ มากมันคุ้มค่าที่จะทำซ้ำขั้นตอนการโคลนและการล้างทั้งหมดเพื่อดูว่าข้อความที่ขอให้คุณดึงเกิดขึ้นอีกครั้งหรือไม่ แต่มันเกือบจะแน่นอนเพราะเซิร์ฟเวอร์ระยะไกลของคุณได้รับการกำหนดค่าให้ปฏิเสธการอัปเดตที่ไม่ใช่กรอไปข้างหน้า จากการสูญเสียประวัติศาสตร์ - ซึ่งเป็นสิ่งที่คุณต้องการจะทำ) คุณต้องเปลี่ยนการตั้งค่าบนรีโมตหรือล้มเหลวผลักดันประวัติ repo ที่อัปเดตไปเป็น repo เปล่าใหม่
Roberto Tyley

1
@RobertoTyley ขอบคุณ ฉันลองมา 3 ครั้งและผลลัพธ์ทั้งหมดก็มีข้อความเหมือนกัน ดังนั้นฉันจึงคิดว่าคุณถูกต้องเกี่ยวกับเซิร์ฟเวอร์ระยะไกลที่ได้รับการกำหนดค่าให้ปฏิเสธการอัปเดตที่ไม่ส่งต่ออย่างรวดเร็ว ฉันจะลองผลักดัน repo ที่อัปเดตไปเป็น repo ใหม่ ขอบคุณ!
โทนี่

7
@ RobertoTyley สมบูรณ์แบบคุณประหยัดเวลาของฉันขอบคุณมาก โดยวิธีการที่อาจจะทำgit push --forceตามขั้นตอนของคุณมิฉะนั้น repo ระยะไกลยังคงไม่เปลี่ยนแปลง
li2

3
+1 git push --forceเพื่อเพิ่ม ควรค่าแก่การสังเกตด้วย: การกดการบังคับอาจไม่ได้รับอนุญาตจากระยะไกล (gitlab.com ไม่ได้ตามค่าเริ่มต้นต้อง "ป้องกัน" สาขา)
MatrixManAtYrService

25
ฉันคิดว่าศัพท์แสงของทรัมป์ผลลัพธ์ของเครื่องมือค่อนข้างมาก
Chris

564

สิ่งที่คุณต้องการทำคือก่อกวนอย่างมากหากคุณเผยแพร่ประวัติแก่ผู้พัฒนารายอื่น โปรดดู“ การกู้คืนจากการอัปสตรีม Rebase” ในgit rebaseเอกสารประกอบสำหรับขั้นตอนที่จำเป็นหลังจากซ่อมแซมประวัติของคุณ

คุณมีอย่างน้อยสองตัวเลือก: git filter-branchและการรีบูตแบบโต้ตอบทั้งคู่อธิบายไว้ด้านล่าง

การใช้ git filter-branch

ผมมีปัญหาที่คล้ายกันที่มีขนาดใหญ่การทดสอบข้อมูลไบนารีจากการนำเข้าการโค่นล้มและเขียนเกี่ยวกับการลบข้อมูลจากเก็บคอมไพล์

บอกว่าประวัติคอมไพล์ของคุณคือ:

$ git lola --name-status
* f772d66 (HEAD, master) Login page
| A     login.html
* cb14efd Remove DVD-rip
| D     oops.iso
* ce36c98 Careless
| A     oops.iso
| A     other.html
* 5af4522 Admin page
| A     admin.html
* e738b63 Index
  A     index.html

โปรดทราบว่าgit lolaเป็นนามแฝงที่ไม่ได้มาตรฐาน แต่มีประโยชน์อย่างมาก ด้วย--name-statusสวิทช์เราสามารถเห็นการแก้ไขทรีที่เกี่ยวข้องกับแต่ละการกระทำ

ในการกระทำ“ ประมาท” (ชื่อวัตถุของ SHA1 คือ ce36c98) ไฟล์oops.isoคือ DVD-rip ที่ถูกเพิ่มเข้าไปโดยไม่ตั้งใจและถูกลบออกในการคอมมิชชันถัดไป cb14efd การใช้เทคนิคที่อธิบายไว้ในบล็อกโพสต์ดังกล่าวคำสั่งในการดำเนินการคือ:

git filter-branch --prune-empty -d /dev/shm/scratch \
  --index-filter "git rm --cached -f --ignore-unmatch oops.iso" \
  --tag-name-filter cat -- --all

ตัวเลือก:

  • --prune-emptyลบการกระทำที่กลายเป็นที่ว่างเปล่า ( เช่นอย่าเปลี่ยนต้นไม้) อันเป็นผลมาจากการดำเนินการตัวกรอง ในกรณีทั่วไปตัวเลือกนี้จะสร้างประวัติที่สะอาดกว่า
  • -dตั้งชื่อไดเรกทอรีชั่วคราวที่ยังไม่มีอยู่สำหรับสร้างประวัติที่ถูกกรอง หากคุณกำลังทำงานบนลินุกซ์กระจายทันสมัยระบุต้นไม้/dev/shmจะส่งผลในการดำเนินการได้เร็วขึ้น
  • --index-filterเป็นเหตุการณ์หลักและทำงานกับดัชนีในแต่ละขั้นตอนในประวัติศาสตร์ คุณต้องการลบoops.isoที่ใดก็ตามที่พบ แต่จะไม่ปรากฏในทุกการกระทำ คำสั่งgit rm --cached -f --ignore-unmatch oops.isoลบ DVD-rip เมื่อมีอยู่และไม่ล้มเหลวเป็นอย่างอื่น
  • --tag-name-filterอธิบายวิธีเขียนชื่อแท็กซ้ำ ตัวกรองcatคือการดำเนินการระบุตัวตน ที่เก็บของคุณเช่นตัวอย่างด้านบนอาจไม่มีแท็กใด ๆ แต่ฉันได้รวมตัวเลือกนี้ไว้สำหรับการใช้งานทั่วไป
  • -- ระบุจุดสิ้นสุดของตัวเลือกเพื่อ git filter-branch
  • --allต่อไปนี้--เป็นชวเลขสำหรับการอ้างอิงทั้งหมด ที่เก็บของคุณเช่นเดียวกับตัวอย่างด้านบนอาจมีผู้อ้างอิง (master) เพียงคนเดียว แต่ฉันได้รวมตัวเลือกนี้ไว้สำหรับเรื่องทั่วไป

หลังจากปั่นป่วนประวัติตอนนี้:

$ git lola --name-status
* 8e0a11c (HEAD, master) Login page
| A     login.html
* e45ac59 Careless
| A     other.html
|
| * f772d66 (refs/original/refs/heads/master) Login page
| | A   login.html
| * cb14efd Remove DVD-rip
| | D   oops.iso
| * ce36c98 Careless
|/  A   oops.iso
|   A   other.html
|
* 5af4522 Admin page
| A     admin.html
* e738b63 Index
  A     index.html

ขอให้สังเกตว่าการกระทำที่ "ประมาท" ใหม่จะเพิ่มขึ้นเท่านั้นother.htmlและการกระทำที่ "ลบ DVD-rip" จะไม่อยู่ในสาขาหลักอีกต่อไป สาขาที่มีข้อความระบุว่าrefs/original/refs/heads/masterคุณมีข้อผูกพันดั้งเดิมในกรณีที่คุณทำผิดพลาด หากต้องการลบให้ทำตามขั้นตอนใน“ รายการตรวจสอบสำหรับลดขนาดพื้นที่เก็บข้อมูล”

$ git update-ref -d refs/original/refs/heads/master
$ git reflog expire --expire=now --all
$ git gc --prune=now

สำหรับทางเลือกที่ง่ายกว่าให้โคลนที่เก็บเพื่อทิ้งบิตที่ไม่ต้องการ

$ cd ~/src
$ mv repo repo.old
$ git clone file:///home/user/src/repo.old repo

การใช้file:///...โคลน URL จะคัดลอกวัตถุแทนที่จะสร้างฮาร์ดลิงก์เท่านั้น

ตอนนี้ประวัติของคุณคือ:

$ git lola --name-status
* 8e0a11c (HEAD, master) Login page
| A     login.html
* e45ac59 Careless
| A     other.html
* 5af4522 Admin page
| A     admin.html
* e738b63 Index
  A     index.html

ชื่อวัตถุ SHA1 สำหรับการกระทำสองรายการแรก (“ ดัชนี” และ“ หน้าผู้ดูแลระบบ”) ยังคงเหมือนเดิมเพราะการดำเนินการตัวกรองไม่ได้แก้ไขการกระทำเหล่านั้น “ ประมาท” หายไปoops.isoและ“ หน้าเข้าสู่ระบบ” ได้รับผู้ปกครองใหม่ดังนั้น SHA1 ของพวกเขาจึงเปลี่ยนไป

rebase แบบโต้ตอบ

ด้วยประวัติของ:

$ git lola --name-status
* f772d66 (HEAD, master) Login page
| A     login.html
* cb14efd Remove DVD-rip
| D     oops.iso
* ce36c98 Careless
| A     oops.iso
| A     other.html
* 5af4522 Admin page
| A     admin.html
* e738b63 Index
  A     index.html

คุณต้องการลบออกoops.isoจาก "ประมาท" ราวกับว่าคุณไม่ได้เพิ่มมันแล้ว "ลบ DVD-rip" นั้นไร้ประโยชน์สำหรับคุณ ดังนั้นแผนของเราที่จะเข้าสู่การปฏิเสธแบบโต้ตอบคือการรักษา“ หน้าผู้ดูแลระบบ” แก้ไข“ ประมาท” และยกเลิก“ ลบ DVD-rip”

การ$ git rebase -i 5af4522เริ่มต้นใช้งานตัวแก้ไขพร้อมเนื้อหาดังต่อไปนี้

pick ce36c98 Careless
pick cb14efd Remove DVD-rip
pick f772d66 Login page

# Rebase 5af4522..f772d66 onto 5af4522
#
# Commands:
#  p, pick = use commit
#  r, reword = use commit, but edit the commit message
#  e, edit = use commit, but stop for amending
#  s, squash = use commit, but meld into previous commit
#  f, fixup = like "squash", but discard this commit's log message
#  x, exec = run command (the rest of the line) using shell
#
# If you remove a line here THAT COMMIT WILL BE LOST.
# However, if you remove everything, the rebase will be aborted.
#

ดำเนินการตามแผนของเราเราปรับเปลี่ยนเป็น

edit ce36c98 Careless
pick f772d66 Login page

# Rebase 5af4522..f772d66 onto 5af4522
# ...

นั่นก็คือเราลบบรรทัดที่มี“ลบ DVD-ฉีก” และเปลี่ยนการดำเนินการที่“ประมาท” ที่จะเป็นมากกว่าeditpick

การเลิกเขียนตัวแก้ไขทำให้เราที่พร้อมท์คำสั่งพร้อมข้อความต่อไปนี้

Stopped at ce36c98... Careless
You can amend the commit now, with

        git commit --amend

Once you are satisfied with your changes, run

        git rebase --continue

ตามที่ข้อความบอกเราเราอยู่ที่“ ประมาท” ที่เราต้องการแก้ไขดังนั้นเราจึงเรียกใช้สองคำสั่ง

$ git rm --cached oops.iso
$ git commit --amend -C HEAD
$ git rebase --continue

ไฟล์แรกจะลบไฟล์ที่ละเมิดออกจากดัชนี ขั้นที่สองแก้ไขหรือแก้ไข“ ประมาท” ให้เป็นดัชนีที่ได้รับการปรับปรุงและ-C HEADสั่งให้คอมไพล์กลับมาใช้ข้อความยืนยันเดิมอีกครั้ง ในที่สุดgit rebase --continueไปข้างหน้ากับส่วนที่เหลือของการดำเนินการ rebase

สิ่งนี้ให้ประวัติของ:

$ git lola --name-status
* 93174be (HEAD, master) Login page
| A     login.html
* a570198 Careless
| A     other.html
* 5af4522 Admin page
| A     admin.html
* e738b63 Index
  A     index.html

สิ่งที่คุณต้องการ


4
เหตุใดฉันจึงไม่สามารถผลักดันเมื่อใช้ตัวกรอง git สาขาไม่สามารถผลักดันผู้อ้างอิงบางคนไปที่ 'git@bitbucket.org: product / myproject.git' เพื่อป้องกันไม่ให้คุณสูญเสียประวัติ การเปลี่ยนแปลงก่อนที่จะผลักดันอีกครั้ง
Agung Prasetyo

11
เพิ่มตัวเลือก-f(หรือ--force) ลงในgit pushคำสั่งของคุณ:“ โดยปกติแล้วคำสั่งปฏิเสธที่จะอัพเดทการอ้างอิงระยะไกลที่ไม่ใช่บรรพบุรุษของผู้อ้างอิงในท้องถิ่นที่ใช้เพื่อเขียนทับมัน การตั้งค่าสถานะนี้ปิดใช้งานการตรวจสอบ สิ่งนี้สามารถทำให้ที่เก็บข้อมูลรีโมตสูญเสียการคอมมิท ใช้ด้วยความระมัดระวัง”
Greg Bacon

5
นี่เป็นคำตอบที่น่าพิศวงอย่างละเอียดเกี่ยวกับการใช้ git-filter-branch เพื่อลบไฟล์ขนาดใหญ่ที่ไม่ต้องการออกจากประวัติ แต่มันก็คุ้มค่าที่สังเกตได้ว่าตั้งแต่ Greg เขียนคำตอบของเขา BFG Repo-Cleaner ได้รับการปล่อยตัวออกมา ใช้ - ดูคำตอบของฉันสำหรับรายละเอียด
Roberto Tyley

1
หลังจากที่ฉันทำตามขั้นตอนด้านบนแล้วที่เก็บระยะไกล (บน GitHub) จะไม่ลบไฟล์ขนาดใหญ่ มี แต่คนท้องถิ่นเท่านั้น ฉันบังคับผลักและนาด้า ฉันพลาดอะไรไป
azatar

1
สิ่งนี้ยังทำงานได้บน dirs ... "git rm --cached -rf --ignore-unmatch path/to/dir"...
rynop

198

ทำไมไม่ใช้คำสั่งที่เรียบง่าย แต่มีประสิทธิภาพนี้

git filter-branch --tree-filter 'rm -f DVD-rip' HEAD

--tree-filterตัวเลือกรันคำสั่งที่ระบุหลังจากเช็คเอาต์ของโครงการแต่ละคนและแล้ว recommits ผล ในกรณีนี้คุณลบไฟล์ที่เรียกว่า DVD-rip จากทุก snapshot ไม่ว่าจะมีอยู่หรือไม่ก็ตาม

ถ้าคุณรู้ว่าคอมมิชชันใดที่เปิดตัวไฟล์ขนาดใหญ่ (พูด 35dsa2) คุณสามารถแทนที่ HEAD ด้วย 35dsa2 .. HEAD เพื่อหลีกเลี่ยงการเขียนประวัติศาสตร์มากเกินไปดังนั้นหลีกเลี่ยงการแยกส่วนหากคุณยังไม่ได้ผลักดัน ความคิดเห็นนี้ได้รับความอนุเคราะห์จาก @ alpha_989 สำคัญเกินกว่าที่จะออกจากที่นี่

ดูลิงค์นี้


3
นี่เป็นทางออกที่ดี! ฉันได้สร้างส่วนสำคัญที่มีสคริปต์ไพ ธ อนเพื่อแสดงรายการไฟล์ & git cmd ที่จะลบไฟล์ที่คุณต้องการให้ทำความสะอาดgist.github.com/ariv3ra/16fd94e46345e62cfcbf
punkdata

5
ดีกว่า bfg มาก ฉันไม่สามารถล้างไฟล์จากคอมไพล์ด้วย bfg ได้ แต่คำสั่งนี้ช่วยได้
podarok

4
มันเยี่ยมมาก เพียงบันทึกย่อสำหรับคนอื่น ๆ ที่คุณจะต้องทำเช่นนี้ต่อสาขาหากไฟล์ขนาดใหญ่อยู่ในหลายสาขา
James

2
บน Windows ฉันได้fatal: bad revision 'rm'ซึ่งผมแก้ไขโดยใช้แทน" 'คำสั่งโดยรวม:git filter-branch --force --index-filter "git rm --cached -r --ignore-unmatch oops.iso" --prune-empty --tag-name-filter cat -- --all
marcotama

2
ถ้าคุณรู้ว่าcommitที่คุณใส่ในไฟล์ (พูด35dsa2), คุณสามารถแทนที่ด้วยHEAD ช้ากว่าวิธีนั้นอย่างมากที่จะไม่ลองชำระค่าคอมมิททั้งหมดและเขียนใหม่ ถ้าคุณใช้ HEAD มันจะพยายามทำเช่นนั้น 35dsa2..HEADtree-filterindex-filter
alpha_989

86

(คำตอบที่ดีที่สุดที่ฉันเคยเห็นสำหรับปัญหานี้คือ: https://stackoverflow.com/a/42544963/714112คัดลอกที่นี่เนื่องจากหัวข้อนี้ปรากฏสูงในการจัดอันดับการค้นหาของ Google แต่คนอื่นไม่ได้)

shell เปลือกอย่างรวดเร็วที่โดดเด่นหนึ่งซับ🚀

เชลล์สคริปต์นี้แสดงวัตถุ Blob ทั้งหมดในที่เก็บซึ่งเรียงลำดับจากที่เล็กที่สุดไปหามากที่สุด

สำหรับ repo ตัวอย่างของฉันมันวิ่งเร็วกว่าที่พบที่นี่ประมาณ100 เท่า
ในระบบ Athlon II X4 ที่ไว้ใจได้ของฉันจัดการกับที่เก็บ Linux Kernelด้วยวัตถุ 5,622,155 ในเวลาเพียงไม่กี่นาทีเพียงนาที

สคริปต์ฐาน

git rev-list --objects --all \
| git cat-file --batch-check='%(objecttype) %(objectname) %(objectsize) %(rest)' \
| awk '/^blob/ {print substr($0,6)}' \
| sort --numeric-sort --key=2 \
| cut --complement --characters=13-40 \
| numfmt --field=2 --to=iec-i --suffix=B --padding=7 --round=nearest

เมื่อคุณเรียกใช้โค้ดข้างต้นคุณจะได้ผลลัพธ์ที่มนุษย์อ่านง่ายเช่นนี้

...
0d99bb931299  530KiB path/to/some-image.jpg
2ba44098e28f   12MiB path/to/hires-image.png
bd1741ddce0d   63MiB path/to/some-video-1080p.mp4

Removal กำจัดไฟล์อย่างรวดเร็ว🚀

สมมติว่าคุณต้องการลบไฟล์aและbทุกการกระทำที่เข้าถึงได้จากHEADคุณสามารถใช้คำสั่งนี้:

git filter-branch --index-filter 'git rm --cached --ignore-unmatch a b' HEAD

3
หาก repo ของคุณมีแท็กใด ๆ คุณอาจต้องการเพิ่มการตั้งค่าสถานะ--tag-name-filter catเพื่อติดแท็กคอมมิทใหม่ที่เกี่ยวข้องในขณะที่ถูกเขียนใหม่ (เช่นgit filter-branch --index-filter 'git rm --cached --ignore-unmatch a b' --tag-name-filter cat HEADดูคำตอบที่เกี่ยวข้องนี้ )
naitsirhc

3
คำแนะนำสำหรับ Mac และข้อมูลอื่น ๆ จะปรากฏในโพสต์ต้นฉบับที่เชื่อมโยง
nruth

3
git filter-branch --index-filter 'git rm --cached --ignore-unmatch <filename>' HEADworkorder ด้านขวาของไม้ตี
eleijonmarck

คำตอบที่ฉันชอบ ปรับแต่งเล็กน้อยที่จะใช้กับ mac os (โดยใช้คำสั่ง gnu)git rev-list --objects --all \ | git cat-file --batch-check='%(objecttype) %(objectname) %(objectsize) %(rest)' \ | awk '/^blob/ {print substr($0,6)}' \ | sort --numeric-sort --key=2 \ | gnumfmt --field=2 --to=iec-i --suffix=B --padding=7 --round=nearest
Florian Oswald

สคริปต์สุดเจ๋งกับ rev-list แต่มันก็ไม่ได้ผลสำหรับฉันในนามแฝงความคิดใด ๆ ที่จะทำเช่นนั้น
Robin Manoli

47

หลังจากลองทำตามคำตอบทุกข้อในที่สุดฉันก็พบว่าอัญมณีนี้ซึ่งลบออกอย่างรวดเร็วและลบไฟล์ขนาดใหญ่ในที่เก็บของฉันและอนุญาตให้ฉันซิงค์อีกครั้ง: http://www.zyxware.com/articles/4027/how-to-delete -Files ถาวรจากของคุณท้องถิ่นและระยะไกล Git-เก็บ

ซีดีไปยังโฟลเดอร์ทำงานในพื้นที่ของคุณและเรียกใช้คำสั่งต่อไปนี้:

git filter-branch -f --index-filter "git rm -rf --cached --ignore-unmatch FOLDERNAME" -- --all

แทนที่ FOLDERNAME ด้วยไฟล์หรือโฟลเดอร์ที่คุณต้องการลบออกจากที่เก็บ git ที่กำหนด

เมื่อดำเนินการเสร็จแล้วให้เรียกใช้คำสั่งต่อไปนี้เพื่อล้างที่เก็บในเครื่อง:

rm -rf .git/refs/original/
git reflog expire --expire=now --all
git gc --prune=now
git gc --aggressive --prune=now

ตอนนี้ผลักดันการเปลี่ยนแปลงทั้งหมดไปยังที่เก็บระยะไกล:

git push --all --force

สิ่งนี้จะทำความสะอาดที่เก็บระยะไกล


ทำงานเหมือนเสน่ห์สำหรับฉัน
Ramon Vasconcelos

3
สิ่งนี้ใช้ได้สำหรับฉันเช่นกัน รับการกำจัดของโฟลเดอร์ที่เฉพาะเจาะจง (ในกรณีของฉันหนึ่งที่มีไฟล์ที่มีขนาดใหญ่เกินไปหรือ Github repo) ในพื้นที่เก็บข้อมูล แต่เก็บไว้ในระบบไฟล์ในกรณีที่มันมีอยู่
skizzo

ทำงานให้ฉัน! ไม่มีประวัติที่เหลือซึ่งอาจทำให้สับสน (ถ้ามีคนที่จะทำการโคลนนิ่งในตอนนี้) ตรวจสอบให้แน่ใจว่าคุณมีแผนที่จะอัปเดตลิงก์ที่ขาดการอ้างอิงและอื่น ๆ
ruoho ruotsi

38

คำสั่งเหล่านี้ทำงานในกรณีของฉัน:

git filter-branch --force --index-filter 'git rm --cached -r --ignore-unmatch oops.iso' --prune-empty --tag-name-filter cat -- --all
rm -rf .git/refs/original/
git reflog expire --expire=now --all
git gc --prune=now
git gc --aggressive --prune=now

มันแตกต่างจากเวอร์ชั่นด้านบนเล็กน้อย

สำหรับผู้ที่ต้องการผลักดันสิ่งนี้ไปยัง GitHub / Bitbucket (ฉันทดสอบเพียงแค่นี้ด้วย Bitbucket):

# WARNING!!!
# this will rewrite completely your bitbucket refs
# will delete all branches that you didn't have in your local

git push --all --prune --force

# Once you pushed, all your teammates need to clone repository again
# git pull will not work

4
แตกต่างจากด้านบนอย่างไรดีกว่า?
Andy Hayden

1
ด้วยเหตุผลบางรุ่น mkljun git rm --cached filesจะไม่ลดลงพื้นที่คอมไพล์ในกรณีของฉันฉันแล้วได้ออกจากดัชนีไฟล์โดยใช้ ข้อเสนอของ Greg Bacon นั้นสมบูรณ์กว่าและค่อนข้างเหมือนกับของฉันคนนี้ แต่เขาพลาดดัชนี --force สำหรับกรณีเมื่อคุณใช้ตัวกรองสาขาหลายครั้งและเขาเขียนข้อมูลมากว่าเวอร์ชั่นของฉันเหมือนประวัติย่อ ของมัน
Kostanos

1
สิ่งนี้ช่วยได้จริงๆ แต่ฉันต้องการใช้-fตัวเลือกไม่เพียงแค่-rfที่นี่git rm --cached -rf --ignore-unmatch oops.isoแทนที่จะgit rm --cached -r --ignore-unmatch oops.isoเป็นตาม @ lfender6445 ด้านล่าง
drstevok

10

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

$ git filter-branch --index-filter "git rm -rf --cached --ignore-unmatch YOURFILENAME" HEAD
$ rm -rf .git/refs/original/ 
$ git reflog expire --all 
$ git gc --aggressive --prune
$ git push origin master --force

11
อย่ารันคำสั่งเหล่านี้ยกเว้นว่าคุณต้องการสร้างความเจ็บปวดอันยิ่งใหญ่ให้กับตัวเอง มันลบไฟล์ซอร์สโค้ดดั้งเดิมของฉันจำนวนมาก ฉันคิดว่ามันจะล้างไฟล์ขนาดใหญ่บางส่วนจากประวัติการกระทำของฉันใน GIT (ตามคำถามเดิม) อย่างไรก็ตามฉันคิดว่าคำสั่งนี้ออกแบบมาเพื่อกำจัดไฟล์อย่างถาวรจากซอร์สโค้ดดั้งเดิมของคุณ (แตกต่างกันมาก!) ระบบของฉัน: Windows, VS2012, Git Source Provider Provider
Contango

2
ฉันใช้คำสั่งนี้: git filter-branch --force --index-filter 'git rm --cached -r --ignore-unmatch oops.iso' --prune-empty --tag-name-filter cat -- --allแทนคำสั่งแรกจากรหัสของคุณ
Kostanos

9

git filter-branch --tree-filter 'rm -f path/to/file' HEAD ทำงานได้ค่อนข้างดีสำหรับฉันแม้ว่าฉันจะพบปัญหาเดียวกันกับที่อธิบายไว้ที่นี่ซึ่งฉันแก้ไขได้โดยทำตามคำแนะนำนี้นี้

หนังสือ pro-git มีทั้งบทเกี่ยวกับประวัติการเขียนใหม่ - ดูที่ส่วนfilter-branch/ การลบไฟล์ออกจากทุกส่วนที่กระทำ


8

หากคุณรู้ว่าการกระทำของคุณเป็นเรื่องล่าสุดแทนที่จะต้องผ่านต้นไม้ทั้งหมดให้ทำดังนี้: git filter-branch --tree-filter 'rm LARGE_FILE.zip' HEAD~10..HEAD


7

ฉันพบสิ่งนี้ด้วยบัญชี bitbucket ซึ่งฉันได้เก็บข้อมูลสำรอง ginormous * .jpa ไว้ในเว็บไซต์ของฉันโดยไม่ได้ตั้งใจ

git filter-branch --prune-empty --index-filter 'git rm -rf --cached --ignore-unmatch MY-BIG-DIRECTORY-OR-FILE' --tag-name-filter cat -- --all

Relpace MY-BIG-DIRECTORYกับโฟลเดอร์ในคำถามที่จะสมบูรณ์เขียนประวัติของคุณ ( รวมถึงแท็ก )

แหล่งที่มา: https://web.archive.org/web/20170727144429/http://naleid.com:80/blog/2012/01/17/finding-and-purging-big-files-from-git-history/


1
คำตอบนี้ช่วยฉันได้ยกเว้นสคริปต์ในคำตอบมีปัญหาเล็กน้อยและไม่สามารถค้นหาได้ในทุกสาขา แต่คำสั่งในลิงค์ทำได้ดีมาก
Ali B

5

สิ่งนี้จะลบออกจากประวัติของคุณ

git filter-branch --force --index-filter 'git rm -r --cached --ignore-unmatch bigfile.txt' --prune-empty --tag-name-filter cat -- --all

มันใช้งานได้สำหรับฉันขอบคุณ !!
Sonja Brits

มันใช้งานได้ในกรณีของฉัน ฉันเรียกใช้สิ่งนี้ในสาขาหลักของคุณ
S. Domeng

4

โดยทั่วไปแล้วฉันทำในสิ่งที่เป็นคำตอบนี้: https://stackoverflow.com/a/11032521/1286423

(สำหรับประวัติฉันจะคัดลอกวางไว้ที่นี่)

$ git filter-branch --index-filter "git rm -rf --cached --ignore-unmatch YOURFILENAME" HEAD
$ rm -rf .git/refs/original/ 
$ git reflog expire --all 
$ git gc --aggressive --prune
$ git push origin master --force

มันใช้งานไม่ได้เพราะฉันชอบที่จะเปลี่ยนชื่อและย้ายสิ่งต่าง ๆ มากมาย ดังนั้นไฟล์ขนาดใหญ่บางไฟล์อยู่ในโฟลเดอร์ที่ถูกเปลี่ยนชื่อและฉันคิดว่า gc ไม่สามารถลบการอ้างอิงไปยังไฟล์เหล่านั้นได้เนื่องจากการอ้างอิงในtreeวัตถุที่ชี้ไปยังไฟล์เหล่านั้น ทางออกสุดท้ายของฉันในการฆ่ามันคือ:

# First, apply what's in the answer linked in the front
# and before doing the gc --prune --aggressive, do:

# Go back at the origin of the repository
git checkout -b newinit <sha1 of first commit>
# Create a parallel initial commit
git commit --amend
# go back on the master branch that has big file
# still referenced in history, even though 
# we thought we removed them.
git checkout master
# rebase on the newinit created earlier. By reapply patches,
# it will really forget about the references to hidden big files.
git rebase newinit

# Do the previous part (checkout + rebase) for each branch
# still connected to the original initial commit, 
# so we remove all the references.

# Remove the .git/logs folder, also containing references
# to commits that could make git gc not remove them.
rm -rf .git/logs/

# Then you can do a garbage collection,
# and the hidden files really will get gc'ed
git gc --prune --aggressive

repo ของฉัน (the .git) เปลี่ยนจาก 32MB เป็น 388KB ซึ่งแม้แต่ตัวกรองสาขาก็ไม่สามารถทำความสะอาดได้


4

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

# Do it in a new testing branch
$ git checkout -b test

# Remove file-name from every commit on the new branch
# --index-filter, rewrite index without checking out
# --cached, remove it from index but not include working tree
# --ignore-unmatch, ignore if files to be removed are absent in a commit
# HEAD, execute the specified command for each commit reached from HEAD by parent link
$ git filter-branch --index-filter 'git rm --cached --ignore-unmatch file-name' HEAD

# The output is OK, reset it to the prior branch master
$ git checkout master
$ git reset --soft test

# Remove test branch
$ git branch -d test

# Push it with force
$ git push --force origin master

2

ใช้ส่วนขยาย Gitเป็นเครื่องมือ UI มีปลั๊กอินชื่อ "ค้นหาไฟล์ขนาดใหญ่" ซึ่งค้นหาไฟล์ lage ในที่เก็บและอนุญาตให้ลบไฟล์เหล่านั้นได้อย่างถาวร

อย่าใช้ 'git filter-branch' ก่อนที่จะใช้เครื่องมือนี้เนื่องจากจะไม่สามารถค้นหาไฟล์ที่ถูกลบโดย 'filter-branch' (Altough 'filter-branch' จะไม่ลบไฟล์ออกจากไฟล์ชุดที่เก็บ) .


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

ใช่มันช้า แต่ใช้งานได้ ... คุณรู้อะไรเร็วกว่านี้ไหม
Nir

1
ยังไม่ได้ใช้ แต่เป็น BFG Repo-Cleaner ตามคำตอบอื่นในหน้านี้
kristianp


2

มีคำตอบที่ดีมากในชุดข้อความนี้ แต่ในขณะที่หลายคำตอบล้าสมัย การใช้git-filter-branchจะไม่แนะนำเพราะมันเป็นเรื่องยากที่จะใช้และชะมัดช้าเก็บขนาดใหญ่

git-filter-repo เร็วกว่าและใช้ง่ายกว่ามาก

git-filter-repoเป็นสคริปต์ Python ได้ที่ GitHub: https://github.com/newren/git-filter-repo

คุณต้องการเพียงหนึ่งไฟล์: สคริปต์ Python3 git-filter-repo คัดลอกไปยังเส้นทางที่รวมอยู่ในตัวแปร PATH ใน Windows คุณอาจต้องเปลี่ยนบรรทัดแรกของสคริปต์ (อ้างอิง INSTALL.md) คุณต้องติดตั้ง Python3 ติดตั้งในระบบของคุณ แต่นี่ไม่ใช่เรื่องใหญ่

ก่อนอื่นคุณสามารถเรียกใช้

git filter-repo --analyze

สิ่งนี้จะช่วยให้คุณกำหนดสิ่งที่ต้องทำต่อไป

คุณสามารถลบไฟล์ DVD-rip ของคุณได้ทุกที่:

 git filter-repo --invert-paths --path-match DVD-rip

ตัวกรอง repo นั้นเร็วมาก งานที่ใช้เวลาประมาณ 9 ชั่วโมงในคอมพิวเตอร์ของฉันโดยตัวกรองสาขาเสร็จสมบูรณ์ใน 4 นาทีโดยตัวกรองซ้ำ คุณสามารถทำสิ่งดีๆได้มากมายด้วยตัวกรองซ้ำ อ้างถึงเอกสารประกอบสำหรับสิ่งนั้น

คำเตือน:ทำสิ่งนี้บนสำเนาของที่เก็บของคุณ การกระทำของ repo ตัวกรองจำนวนมากไม่สามารถเลิกทำได้ ตัวกรอง repo จะเปลี่ยนแฮชการคอมมิชต์ของคอมมิทที่แก้ไขทั้งหมด (แน่นอน) และการสืบทอดทั้งหมดของพวกเขาลงไปเป็นคอมมิทสุดท้าย!


1

เมื่อคุณพบปัญหานี้git rmจะไม่พอเพราะคอมจำได้ว่าไฟล์นั้นมีอยู่ครั้งเดียวในประวัติศาสตร์ของเราและจะเก็บไว้อ้างอิง

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

ฉันใส่กัน git forget-blobสคริปต์เล็ก ๆ ที่พยายามลบการอ้างอิงเหล่านี้ทั้งหมดแล้วใช้ git filter-branch เพื่อเขียนทุกการกระทำในสาขา

เมื่อหยดของคุณไม่ได้รับการยืนยันอย่างสมบูรณ์ git gcก็จะกำจัดมัน

git forget-blob file-to-forgetการใช้งานสวยเรียบง่าย คุณสามารถรับข้อมูลเพิ่มเติมได้ที่นี่

https://ownyourbits.com/2017/01/18/completely-remove-a-file-from-a-git-repository-with-git-forget-blob/

ฉันรวบรวมมันเข้าด้วยกันขอบคุณคำตอบจาก Stack Overflow และบางรายการในบล็อก ให้เครดิตกับพวกเขา!


คุณควรจะได้รับสิ่งนี้ใน homebrew
Cameron E

0

นอกเหนือจากgit filter-branch(ช้า แต่บริสุทธิ์วิธีแก้ปัญหาคอมไพล์) และBFG (ง่ายและมีประสิทธิภาพมาก), นอกจากนี้ยังมีอีกเครื่องมือหนึ่งในการกรองด้วยประสิทธิภาพที่ดี:

https://github.com/xoofx/git-rocket-filter

จากคำอธิบาย:

วัตถุประสงค์ของ git-rocket-filter นั้นคล้ายกับคำสั่งgit-filter-branchในขณะที่ให้คุณสมบัติที่เป็นเอกลักษณ์ต่อไปนี้:

  • เขียนใหม่อย่างรวดเร็วของการกระทำและต้นไม้ (ตามลำดับ x10 ถึง x100)
  • การสนับสนุนในตัวสำหรับรายการสีขาวด้วย --keep (เก็บไฟล์หรือไดเรกทอรี) และบัญชีดำด้วยตัวเลือก - ลบ
  • ใช้. gignignore เช่นรูปแบบสำหรับการกรองต้นไม้
  • การเขียนสคริปต์ C # ที่ง่ายและรวดเร็วสำหรับทั้งการกรองการกรองและการกรองต้นไม้
  • รองรับการเขียนสคริปต์ในการกรองต้นไม้ต่อรูปแบบไฟล์ / ไดเรกทอรี
  • ตัดการกระทำที่ว่างเปล่า / ไม่เปลี่ยนแปลงโดยอัตโนมัติรวมถึงการรวมการกระทำ
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.