Intro: คุณมี 5 โซลูชั่นให้เลือก
โปสเตอร์ต้นฉบับระบุ:
ฉันตั้งใจส่งไฟล์ที่ไม่ต้องการ ... ไปยังที่เก็บข้อมูลของฉันหลายคอมมิชชันที่ผ่านมา ... ฉันต้องการลบไฟล์ออกจากประวัติที่เก็บอย่างสมบูรณ์
เป็นไปได้ไหมที่จะเขียนประวัติการเปลี่ยนแปลงใหม่ที่filename.orig
ไม่เคยถูกเพิ่มไปยังที่เก็บในตอนแรก?
มีหลายวิธีในการลบประวัติไฟล์โดยสมบูรณ์จาก git:
- การแก้ไขความมุ่งมั่น
- ฮาร์ดรีเซ็ต (อาจรวมถึงการรีบูต)
- การปฏิเสธแบบไม่โต้ตอบ
- rebases แบบโต้ตอบ
- กรองกิ่งไม้
ในกรณีของโปสเตอร์ต้นฉบับการแก้ไขความมุ่งมั่นไม่ใช่ทางเลือกด้วยตัวเองเนื่องจากเขาได้ทำสัญญาเพิ่มเติมหลายรายการในเวลาต่อมา แต่เพื่อความสมบูรณ์ฉันจะอธิบายถึงวิธีการทำเช่นนั้นสำหรับทุกคนที่เพียงแค่ต้องการ เพื่อแก้ไขการกระทำก่อนหน้านี้
โปรดทราบว่าโซลูชันเหล่านี้ทั้งหมดเกี่ยวข้องกับการแก้ไข / เขียนประวัติ / คอมมิทใหม่ด้วยวิธีอื่นดังนั้นทุกคนที่มีสำเนาเก่าของคอมมิทจะต้องทำงานพิเศษเพื่อซิงค์ประวัติของพวกเขากับประวัติใหม่อีกครั้ง
โซลูชันที่ 1: การแก้ไขข้อผูกพัน
หากคุณทำการเปลี่ยนแปลงโดยไม่ตั้งใจ (เช่นการเพิ่มไฟล์) ในการคอมมิชชันก่อนหน้าของคุณและคุณไม่ต้องการให้ประวัติการเปลี่ยนแปลงนั้นมีอยู่อีกต่อไปคุณสามารถแก้ไขคอมมิชชันก่อนหน้าเพื่อลบไฟล์ออกได้:
git rm <file>
git commit --amend --no-edit
โซลูชันที่ 2: ฮาร์ดรีเซ็ต (อาจบวก Rebase)
เช่นเดียวกับโซลูชัน # 1 หากคุณต้องการกำจัดการกระทำก่อนหน้านี้คุณก็มีตัวเลือกที่จะทำการฮาร์ดรีเซ็ตไปยังพาเรนต์ของมัน:
git reset --hard HEAD^
คำสั่งที่จะยากรีเซ็ตสาขาของคุณไปก่อนหน้านี้ 1 เซนต์ปกครองกระทำ
อย่างไรก็ตามหากเช่นเดียวกับผู้โพสต์ดั้งเดิมคุณได้กระทำหลายอย่างหลังจากที่คุณยอมรับว่าต้องการยกเลิกการเปลี่ยนแปลงคุณยังสามารถใช้ฮาร์ดรีเซ็ตเพื่อปรับเปลี่ยนได้ แต่การทำเช่นนั้นเกี่ยวข้องกับการใช้การรีบูต นี่คือขั้นตอนที่คุณสามารถใช้เพื่อแก้ไขการคอมมิชชันย้อนหลังในประวัติศาสตร์:
# Create a new branch at the commit you want to amend
git checkout -b temp <commit>
# Amend the commit
git rm <file>
git commit --amend --no-edit
# Rebase your previous branch onto this new commit, starting from the old-commit
git rebase --preserve-merges --onto temp <old-commit> master
# Verify your changes
git diff master@{1}
โซลูชันที่ 3: Rebase แบบไม่โต้ตอบ
วิธีนี้จะได้ผลหากคุณเพียงแค่ต้องการลบการกระทำออกจากประวัติทั้งหมด:
# Create a new branch at the parent-commit of the commit that you want to remove
git branch temp <parent-commit>
# Rebase onto the parent-commit, starting from the commit-to-remove
git rebase --preserve-merges --onto temp <commit-to-remove> master
# Or use `-p` insteda of the longer `--preserve-merges`
git rebase -p --onto temp <commit-to-remove> master
# Verify your changes
git diff master@{1}
โซลูชันที่ 4: การคืนเงินแบบโต้ตอบ
โซลูชันนี้จะช่วยให้คุณสามารถทำสิ่งเดียวกันกับโซลูชัน # 2 และ # 3 ได้เช่นแก้ไขหรือลบคอมมิชชันที่ย้อนกลับไปในประวัติศาสตร์มากกว่าการกระทำก่อนหน้าของคุณทันทีดังนั้นโซลูชันที่คุณเลือกใช้นั้นขึ้นอยู่กับคุณ การรีแอคทีฟแบบโต้ตอบนั้นไม่เหมาะสำหรับการรีบูตคอมมิทนับร้อยด้วยเหตุผลด้านประสิทธิภาพดังนั้นฉันจะใช้การรีบาวด์ที่ไม่ต้องมีการโต้ตอบหรือโซลูชันสาขาตัวกรอง (ดูด้านล่าง) ในสถานการณ์แบบนั้น
ในการเริ่มต้น rebase เชิงโต้ตอบให้ใช้สิ่งต่อไปนี้:
git rebase --interactive <commit-to-amend-or-remove>~
# Or `-i` instead of the longer `--interactive`
git rebase -i <commit-to-amend-or-remove>~
สิ่งนี้จะทำให้ git ย้อนกลับประวัติการส่งกลับไปยังพาเรนต์ของการส่งที่คุณต้องการแก้ไขหรือลบ จากนั้นจะแสดงรายการของการย้อนกลับที่กระทำในลำดับย้อนกลับในสิ่งที่ git โปรแกรมแก้ไขถูกตั้งค่าให้ใช้ (นี่คือ Vim โดยค่าเริ่มต้น):
pick 00ddaac Add symlinks for executables
pick 03fa071 Set `push.default` to `simple`
pick 7668f34 Modify Bash config to use Homebrew recommended PATH
pick 475593a Add global .gitignore file for OS X
pick 1b7f496 Add alias for Dr Java to Bash config (OS X)
ความมุ่งมั่นที่คุณต้องการแก้ไขหรือลบจะอยู่ที่ด้านบนสุดของรายการนี้ หากต้องการลบออกเพียงลบบรรทัดในรายการ มิฉะนั้นแทนที่ "รับ" กับ "แก้ไข" ใน 1 เซนต์หลัง, ต้องการเพื่อ:
edit 00ddaac Add symlinks for executables
pick 03fa071 Set `push.default` to `simple`
git rebase --continue
ถัดไปป้อน หากคุณเลือกที่จะลบการกระทำทั้งหมดแสดงว่าคุณต้องทำทั้งหมด (นอกเหนือจากการยืนยันให้ดูขั้นตอนสุดท้ายสำหรับการแก้ปัญหานี้) หากในอีกทางหนึ่งคุณต้องการแก้ไขการคอมมิชชันคอมไพล์จะทำการคอมมิตใหม่อีกครั้งและหยุดการรีบูตชั่วคราว
Stopped at 00ddaacab0a85d9989217dd9fe9e1b317ed069ac... Add symlinks
You can amend the commit now, with
git commit --amend
Once you are satisfied with your changes, run
git rebase --continue
ณ จุดนี้คุณสามารถลบไฟล์และแก้ไขการกระทำจากนั้นดำเนินการรีบูตต่อ:
git rm <file>
git commit --amend --no-edit
git rebase --continue
แค่นั้นแหละ. เป็นขั้นตอนสุดท้ายไม่ว่าคุณจะแก้ไขหรือลบการกระทำมันเป็นความคิดที่ดีเสมอที่จะตรวจสอบว่าไม่มีการเปลี่ยนแปลงอื่น ๆ ที่ไม่คาดคิดเกิดขึ้นกับสาขาของคุณ
git diff master@{1}
โซลูชันที่ 5: การกรองสาขา
ในที่สุดโซลูชันนี้จะดีที่สุดหากคุณต้องการลบร่องรอยการมีอยู่ของไฟล์ทั้งหมดจากประวัติและไม่มีวิธีแก้ไขอื่นใดที่เหมาะกับงาน
git filter-branch --index-filter \
'git rm --cached --ignore-unmatch <file>'
ที่จะลบออก<file>
จากการกระทำทั้งหมดเริ่มต้นจากการกระทำของรูต หากคุณเพียงต้องการเขียนช่วงการคอมมิทHEAD~5..HEAD
ใหม่คุณสามารถส่งค่านั้นเป็นอาร์กิวเมนต์เพิ่มเติมfilter-branch
ได้ตามที่ระบุไว้ใน
คำตอบนี้ :
git filter-branch --index-filter \
'git rm --cached --ignore-unmatch <file>' HEAD~5..HEAD
อีกครั้งหลังจากที่filter-branch
เสร็จสมบูรณ์โดยปกติแล้วคุณควรตรวจสอบว่าไม่มีการเปลี่ยนแปลงที่ไม่คาดคิดอื่น ๆ โดยทำให้สาขาของคุณแตกต่างจากสถานะก่อนหน้าก่อนการดำเนินการกรอง:
git diff master@{1}
ทางเลือก Filter-Branch: BFG Repo Cleaner
ฉันได้ยินมาว่าเครื่องมือBFG Repo Cleanerทำงานเร็วกว่าgit filter-branch
ดังนั้นคุณอาจต้องการตรวจสอบว่าเป็นตัวเลือกด้วย มันถูกกล่าวถึงอย่างเป็นทางการแม้ในเอกสารประกอบสาขาการกรองเป็นทางเลือกที่ทำงานได้:
git-filter-branch ช่วยให้คุณสามารถเขียนเชลล์สคริปต์ที่ซับซ้อนของประวัติ Git ของคุณได้ แต่คุณอาจไม่ต้องการความยืดหยุ่นนี้หากคุณเพียงแค่ลบข้อมูลที่ไม่ต้องการเช่นไฟล์หรือรหัสผ่านขนาดใหญ่ สำหรับการดำเนินการเหล่านั้นคุณอาจต้องการพิจารณาBFG Repo-Cleanerทางเลือกที่ใช้ JVM กับ git-filter-branch โดยทั่วไปจะเร็วกว่าอย่างน้อย 10-50x สำหรับกรณีการใช้งานและมีลักษณะที่แตกต่างกันมาก:
รุ่นใด ๆ โดยเฉพาะอย่างยิ่งของไฟล์สามารถทำความสะอาดได้ว่าครั้งหนึ่ง BFG ซึ่งแตกต่างจาก git-filter-branch ไม่ได้ให้โอกาสคุณในการจัดการไฟล์แตกต่างกันไปตามที่หรือเมื่อมันถูกคอมมิทภายในประวัติของคุณ ข้อ จำกัด นี้จะช่วยให้ผลประโยชน์หลักของ BFG และเป็นอย่างดีเหมาะกับงานของการทำความสะอาดข้อมูลที่ไม่ดี - คุณไม่สนใจที่ข้อมูลที่ไม่ดีคือคุณเพียงแค่ต้องการให้มันหายไป
โดยค่าเริ่มต้น BFG ใช้ประโยชน์จากเครื่องมัลติคอร์อย่างเต็มที่การล้างการสร้างไฟล์ทรีแบบขนาน ทำความสะอาด git-filter-branch กระทำตามลำดับ (เช่นในเธรดเดียว) แม้ว่ามันจะเป็น
ไปได้ที่จะเขียนตัวกรองที่มีความคล้ายคลึงกันของตัวเองในสคริปต์ที่ดำเนินการกับแต่ละกระทำ
ตัวเลือกคำสั่งมีมากเข้มงวดกว่าสาขาคอมไพล์กรองและทุ่มเทเพียงเพื่อให้งานของการลบที่ไม่พึงประสงค์ DATA- --strip-blobs-bigger-than 1M
เช่น:
แหล่งข้อมูลเพิ่มเติม
- Pro Git § 6.4 เครื่องมือ Git - ประวัติการเขียนซ้ำ
- คอมไพล์กรองสาขา (1) หน้าคู่มือการใช้งาน
- คอมไพล์กระทำ (1) หน้าคู่มือการใช้งาน
- คอมไพล์รีเซ็ต (1) หน้าคู่มือการใช้งาน
- -git rebase (1) หน้าคู่มือการใช้งาน
- น้ำยาทำความสะอาด BFG (ดูคำตอบนี้จากผู้สร้างเอง )