คุณจะแก้ไขการผสานที่ไม่ดีได้อย่างไรและเล่นซ้ำสิ่งที่ดีของคุณในการรวมถาวร?


407

ฉันตั้งใจส่งไฟล์ที่ไม่ต้องการ ( filename.origขณะแก้ไขการผสาน) ไปยังที่เก็บข้อมูลของฉันหลายครั้งก่อนหน้านี้โดยที่ฉันไม่ได้สังเกตจนกระทั่งตอนนี้ ฉันต้องการลบไฟล์ออกจากประวัติของที่เก็บอย่างสมบูรณ์

เป็นไปได้ไหมที่จะเขียนประวัติการเปลี่ยนแปลงใหม่ที่filename.origไม่เคยถูกเพิ่มไปยังที่เก็บในตอนแรก?



คำตอบ:


297

โปรดอย่าใช้สูตรนี้หากสถานการณ์ของคุณไม่ใช่สิ่งที่อธิบายไว้ในคำถาม สูตรนี้ใช้สำหรับการแก้ไขการผสานที่ไม่ดีและการเล่นซ้ำของคุณมุ่งมั่นในการผสานคงที่

แม้ว่าจะทำสิ่งที่คุณต้องการมันค่อนข้างคำสั่งที่ซับซ้อนและผมก็อาจจะเลือกที่จะทำเช่นนี้กับfilter-branch git rebaseอาจเป็นความชอบส่วนตัว filter-branchสามารถทำได้ในคำสั่งเดียวที่ซับซ้อนกว่าเล็กน้อยในขณะที่rebaseโซลูชันกำลังดำเนินการทางตรรกะที่เทียบเท่ากันทีละขั้นตอน

ลองสูตรต่อไปนี้:

# create and check out a temporary branch at the location of the bad merge
git checkout -b tmpfix <sha1-of-merge>

# remove the incorrectly added file
git rm somefile.orig

# commit the amended merge
git commit --amend

# go back to the master branch
git checkout master

# replant the master branch onto the corrected merge
git rebase tmpfix

# delete the temporary branch
git branch -d tmpfix

(โปรดทราบว่าคุณไม่จำเป็นต้องมีสาขาชั่วคราวจริง ๆ คุณสามารถทำได้ด้วย 'HEAD เดี่ยว' แต่คุณต้องจดบันทึกรหัสยืนยันที่สร้างโดยgit commit --amendขั้นตอนในการจัดหาให้กับgit rebaseคำสั่งแทนที่จะใช้สาขาชั่วคราว ชื่อ.)


6
จะไม่git rebase -iเร็วกว่าและยังคงเป็นเรื่องง่ายหรือไม่ $ git rebase -i <sh1-of-merge> ทำเครื่องหมายถูกต้องว่าเป็น "แก้ไข" $ git rm somefile.orig $ git กระทำ - แก้ไขการคืนเงิน $ git - ต่อไปอย่างไรก็ตามด้วยเหตุผลบางอย่างฉันยังคงมีไฟล์นั้นอยู่ที่ไหนสักแห่งสุดท้าย เวลาที่ฉันทำอย่างนั้น อาจหายไปบางสิ่งบางอย่าง
Wernight

12
git rebase -iมีประโยชน์มากโดยเฉพาะอย่างยิ่งเมื่อคุณมีการดำเนินการ rebase-y หลายอย่างเพื่อดำเนินการ แต่มันเป็นความเจ็บปวดที่ถูกต้องที่จะอธิบายอย่างแม่นยำเมื่อคุณไม่ได้ชี้ไปที่ไหล่ของใครบางคนและสามารถเห็นสิ่งที่พวกเขากำลังทำ ฉันใช้เสียงเรียกเข้า แต่ไม่ใช่ทุกคนที่จะพอใจกับ: "ggjcesquash <Esc> jddjp: wq" และคำแนะนำเช่น "ย้ายบรรทัดบนสุดไปเป็นหลังบรรทัดที่สองปัจจุบันและเปลี่ยนคำแรกในบรรทัดที่สี่เป็น 'แก้ไข' ตอนนี้บันทึกและ ออกจาก "ดูเหมือนว่าจะซับซ้อนกว่าขั้นตอนจริงอย่างรวดเร็ว ปกติคุณจะจบลงด้วยบางส่วน--amendและ--continueการกระทำเช่นเดียว
CB Bailey

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

6
@UncleCJ: ไฟล์ของคุณถูกเพิ่มในการคอมมิชชันหรือไม่? นี้เป็นสิ่งสำคัญ. สูตรนี้ออกแบบมาเพื่อรับมือกับความผิดพลาดที่ผสาน มันจะไม่ทำงานหากไฟล์ที่คุณไม่ต้องการถูกเพิ่มลงในการคอมมิทตามปกติ
CB Bailey

1
ฉันประหลาดใจที่ฉันสามารถทำได้ทั้งหมดโดยใช้ smartgit และไม่มีเทอร์มินัลเลย ขอบคุณสำหรับสูตร!
cregox

209

Intro: คุณมี 5 โซลูชั่นให้เลือก

โปสเตอร์ต้นฉบับระบุ:

ฉันตั้งใจส่งไฟล์ที่ไม่ต้องการ ... ไปยังที่เก็บข้อมูลของฉันหลายคอมมิชชันที่ผ่านมา ... ฉันต้องการลบไฟล์ออกจากประวัติที่เก็บอย่างสมบูรณ์

เป็นไปได้ไหมที่จะเขียนประวัติการเปลี่ยนแปลงใหม่ที่filename.origไม่เคยถูกเพิ่มไปยังที่เก็บในตอนแรก?

มีหลายวิธีในการลบประวัติไฟล์โดยสมบูรณ์จาก git:

  1. การแก้ไขความมุ่งมั่น
  2. ฮาร์ดรีเซ็ต (อาจรวมถึงการรีบูต)
  3. การปฏิเสธแบบไม่โต้ตอบ
  4. rebases แบบโต้ตอบ
  5. กรองกิ่งไม้

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

โปรดทราบว่าโซลูชันเหล่านี้ทั้งหมดเกี่ยวข้องกับการแก้ไข / เขียนประวัติ / คอมมิทใหม่ด้วยวิธีอื่นดังนั้นทุกคนที่มีสำเนาเก่าของคอมมิทจะต้องทำงานพิเศษเพื่อซิงค์ประวัติของพวกเขากับประวัติใหม่อีกครั้ง


โซลูชันที่ 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เช่น:

แหล่งข้อมูลเพิ่มเติม

  1. Pro Git § 6.4 เครื่องมือ Git - ประวัติการเขียนซ้ำ
  2. คอมไพล์กรองสาขา (1) หน้าคู่มือการใช้งาน
  3. คอมไพล์กระทำ (1) หน้าคู่มือการใช้งาน
  4. คอมไพล์รีเซ็ต (1) หน้าคู่มือการใช้งาน
  5. -git rebase (1) หน้าคู่มือการใช้งาน
  6. น้ำยาทำความสะอาด BFG (ดูคำตอบนี้จากผู้สร้างเอง )

ไม่filter-branchก่อให้เกิดการคำนวณของแฮช? หากทีมทำงานกับ repo ที่ควรกรองไฟล์ขนาดใหญ่พวกเขาจะทำเช่นนี้ได้อย่างไรเพื่อให้ทุกคนจบลงด้วยสถานะเดียวกันของ repo
YakovL

@YakovL คำนวณใหม่ทุกอย่าง การกระทำที่แท้จริงนั้นไม่อาจเปลี่ยนแปลงได้ มันสร้างประวัติศาสตร์ใหม่ทั้งหมดและย้ายตัวชี้สาขาของคุณไป วิธีเดียวที่จะทำให้แน่ใจว่าทุกคนมีประวัติเหมือนกันคือการฮาร์ดรีเซ็ต
นักฟิสิกส์บ้า

118

หากคุณไม่ได้มุ่งมั่นอะไรตั้งแต่เพียงไฟล์และgit rmgit commit --amend

ถ้าคุณมี

git filter-branch \
--index-filter 'git rm --cached --ignore-unmatch path/to/file/filename.orig' merge-point..HEAD

จะผ่านการเปลี่ยนแปลงแต่ละครั้งจากmerge-pointถึงHEADลบชื่อไฟล์. orig และเขียนการเปลี่ยนแปลงอีกครั้ง การใช้--ignore-unmatchหมายถึงคำสั่งจะไม่ล้มเหลวหากชื่อไฟล์ด้วยเหตุผลบางอย่างขาดหายไปจากการเปลี่ยนแปลง นั่นเป็นวิธีที่แนะนำจากส่วนตัวอย่างในหน้าคนคอมไพล์กรองสาขา

หมายเหตุสำหรับผู้ใช้ Windows: เส้นทางของไฟล์จะต้องใช้เครื่องหมายทับซ้าย


3
ขอบคุณ! สาขาตัวกรอง git ทำงานสำหรับฉันที่ตัวอย่างการปฏิเสธที่ให้ไว้เป็นคำตอบไม่ได้: ขั้นตอนดูเหมือนจะใช้งานได้ แต่จากนั้นการกดล้มเหลว ดึงแล้วดันสำเร็จ แต่ไฟล์ยังอยู่ใกล้ พยายามที่จะทำซ้ำขั้นตอนการ rebase แล้วมันก็ยุ่งทั้งหมดที่มีความขัดแย้งผสาน ฉันใช้คำสั่งตัวกรองสาขาที่แตกต่างกันเล็กน้อย แต่ "วิธีการที่ได้รับการปรับปรุง" ได้รับที่นี่: github.com/guides/completely-remove-a-file-from-all-revisions git filter-branch -f --index- ตัวกรอง 'git update-index - ลบชื่อไฟล์' <introduction-revision-sha1> ..
เฮด

1
ฉันไม่แน่ใจว่าวิธีไหนดีกว่า Git เอกสารทางการของgit-filter-branchดูเหมือนจะให้คนแรก
Wernight

5
ลองดูzyxware.com/articles/4027/…ฉันพบว่าเป็นโซลูชั่นที่สมบูรณ์และตรงไปตรงมาที่สุดที่เกี่ยวข้องfilter-branch
leontalbot

2
@atomicules ถ้าคุณพยายามที่จะผลัก repo โลคัลไปที่รีโมต git จะยืนยันที่จะดึงจากรีโมตก่อนเพราะมันมีการเปลี่ยนแปลงที่คุณไม่มีอยู่ในเครื่อง คุณสามารถใช้ - บังคับให้ตั้งค่าสถานะเพื่อผลักดันไปยังรีโมท - มันจะลบไฟล์ออกจากที่นั่นโดยสิ้นเชิง แต่ระวังให้แน่ใจว่าคุณจะไม่เขียนทับสิ่งอื่นนอกจากไฟล์เท่านั้น
sol0mka

1
จำไว้ว่าให้ใช้"และไม่'ใช้ Windows หรือคุณจะได้รับข้อผิดพลาด "การแก้ไขที่ไม่ดี" ที่เป็นวลี
cz

49

นี่เป็นวิธีที่ดีที่สุด:
http://github.com/guides/completely-remove-a-file-from-all-revisions

เพียงให้แน่ใจว่าได้สำรองสำเนาของไฟล์ก่อน

แก้ไข

การแก้ไขโดยNeonถูกปฏิเสธอย่างน่าเสียดายในระหว่างการตรวจสอบ
ดูโพสต์ Neons ด้านล่างมันอาจมีข้อมูลที่เป็นประโยชน์!


เช่นการลบ*.gzไฟล์ทั้งหมดโดยไม่ตั้งใจไปยังที่เก็บ git:

$ du -sh .git ==> e.g. 100M
$ git filter-branch --index-filter 'git rm --cached --ignore-unmatch *.gz' HEAD
$ git push origin master --force
$ rm -rf .git/refs/original/
$ git reflog expire --expire=now --all
$ git gc --prune=now
$ git gc --aggressive --prune=now

ยังไม่ได้ผลสำหรับฉัน (ขณะนี้ฉันอยู่ในคอมไพล์รุ่น 1.7.6.1)

$ du -sh .git ==> e.g. 100M

ไม่แน่ใจว่าทำไมเพราะฉันมีสาขาหลักเพียงสาขาเดียว อย่างไรก็ตามในที่สุดฉันก็ได้รับ repo git ของฉันจริง ๆ แล้วทำความสะอาดโดยการเข้าไปใน repository git ที่ว่างเปล่าและใหม่เช่น

$ git init --bare /path/to/newcleanrepo.git
$ git push /path/to/newcleanrepo.git master
$ du -sh /path/to/newcleanrepo.git ==> e.g. 5M 

(ใช่!)

จากนั้นฉันโคลนไปยังไดเรกทอรีใหม่และย้ายไปที่โฟลเดอร์. git ของมันลงในไดเรกทอรีนี้ เช่น

$ mv .git ../large_dot_git
$ git clone /path/to/newcleanrepo.git ../tmpdir
$ mv ../tmpdir/.git .
$ du -sh .git ==> e.g. 5M 

(ใช่! ในที่สุดก็ทำความสะอาด!)

หลังจากตรวจสอบว่าทั้งหมดเรียบร้อยแล้วคุณสามารถลบ../large_dot_gitและ../tmpdirไดเรกทอรี (อาจจะในอีกไม่กี่สัปดาห์หรือเดือนจากนี้ในกรณี ... )


1
สิ่งนี้ใช้ได้กับฉันก่อนที่ "ยังไม่ได้ผลสำหรับฉัน" ความคิดเห็น
shadi

คำตอบที่ดี แต่แนะนำให้เพิ่ม--prune-emptyคำสั่งกรองสาขา
ideasman42

27

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

เพื่อให้ง่ายที่สุดฉันขอแนะนำให้ใช้BFG Repo-Cleanerทางเลือกที่ง่ายกว่าและเร็วกว่าgit-filter-branchสำหรับการลบไฟล์ออกจากประวัติ Git โดยเฉพาะ วิธีหนึ่งที่ทำให้ชีวิตของคุณง่ายขึ้นที่นี่คือที่จริงแล้วมันจัดการการอ้างอิงทั้งหมดโดยค่าเริ่มต้น (แท็กทั้งหมด, สาขา, ฯลฯ ) แต่ก็เร็วกว่า10 - 50 เท่า

คุณควรทำตามขั้นตอนอย่างระมัดระวังที่นี่: http://rtyley.github.com/bfg-repo-cleaner/#usage - แต่แกนบิตเป็นเพียงแค่นี้: ดาวน์โหลดขวด BFG (ต้องใช้ Java 6 ขึ้นไป) และเรียกใช้คำสั่งนี้ :

$ java -jar bfg.jar --delete-files filename.orig my-repo.git

ประวัติพื้นที่เก็บข้อมูลทั้งหมดของคุณจะถูกสแกนและไฟล์ใด ๆ ที่มีชื่อfilename.orig(ที่ไม่ได้อยู่ในของคุณล่าสุดกระทำ ) จะถูกลบออก มันง่ายกว่าการใช้git-filter-branchในการทำสิ่งเดียวกัน!

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


4
นี้เป็นเครื่องมือที่ดี: คำสั่งเดียวก็ผลิตออกชัดเจนมากและให้ล็อกไฟล์ที่ตรงกับทุกเก่ากระทำไปใหม่ ฉันไม่ชอบการติดตั้ง Java แต่มันก็คุ้มค่า
mikemaccana

นี่เป็นสิ่งเดียวที่ทำงานได้สำหรับฉัน แต่เป็นเพราะฉันไม่ได้ทำงานอย่างถูกต้องสาขาตัวกรอง :-)
Kevin LaBranche

14
You should probably clone your repository first.

Remove your file from all branches history:
git filter-branch --tree-filter 'rm -f filename.orig' -- --all

Remove your file just from the current branch:
git filter-branch --tree-filter 'rm -f filename.orig' -- --HEAD    

Lastly you should run to remove empty commits:
git filter-branch -f --prune-empty -- --all

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

4

เพียงเพื่อเพิ่มเข้าไปในโซลูชันของ Charles Bailey ฉันเพิ่งใช้ git rebase -i เพื่อลบไฟล์ที่ไม่ต้องการออกจากการคอมมิชชันก่อนหน้านี้และมันทำงานได้อย่างมีเสน่ห์ ขั้นตอน:

# Pick your commit with 'e'
$ git rebase -i

# Perform as many removes as necessary
$ git rm project/code/file.txt

# amend the commit
$ git commit --amend

# continue with rebase
$ git rebase --continue

4

วิธีที่ง่ายที่สุดที่ผมพบว่าได้รับการแนะนำโดยleontalbot(เป็นความคิดเห็น) ซึ่งเป็นโพสต์ตีพิมพ์โดย Anoopjohn ฉันคิดว่ามันคุ้มค่ากับพื้นที่ของตัวเองเป็นคำตอบ:

(ฉันแปลงเป็นสคริปต์ทุบตี)

#!/bin/bash
if [[ $1 == "" ]]; then
    echo "Usage: $0 FILE_OR_DIR [remote]";
    echo "FILE_OR_DIR: the file or directory you want to remove from history"
    echo "if 'remote' argument is set, it will also push to remote repository."
    exit;
fi
FOLDERNAME_OR_FILENAME=$1;

#The important part starts here: ------------------------

git filter-branch -f --index-filter "git rm -rf --cached --ignore-unmatch $FOLDERNAME_OR_FILENAME" -- --all
rm -rf .git/refs/original/
git reflog expire --expire=now --all
git gc --prune=now
git gc --aggressive --prune=now

if [[ $2 == "remote" ]]; then
    git push --all --force
fi
echo "Done."

เครดิตทั้งหมดไปที่Annopjohnและเพื่อleontalbotชี้ให้เห็น

บันทึก

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


3

แน่นอนgit filter-branchเป็นวิธีที่จะไป

น่าเศร้าที่นี่จะไม่เพียงพอที่จะลบออกfilename.origจาก repo ของคุณอย่างสมบูรณ์เนื่องจากยังสามารถอ้างอิงได้ด้วยแท็กรายการ reflog รีโมตและอื่น ๆ

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

git forget-blob filename.orig


1

หากเป็นการยอมรับล่าสุดที่คุณต้องการทำความสะอาดฉันลองกับ git เวอร์ชั่น 2.14.3 (Apple Git-98):

touch empty
git init
git add empty
git commit -m init

# 92K   .git
du -hs .git

dd if=/dev/random of=./random bs=1m count=5
git add random
git commit -m mistake

# 5.1M  .git
du -hs .git

git reset --hard HEAD^
git reflog expire --expire=now --all
git gc --prune=now

# 92K   .git
du -hs .git

git reflog expire --expire=now --all; git gc --prune=nowเป็นสิ่งที่เลวร้ายมากที่ต้องทำ ถ้าคุณกำลังวิ่งออกมาจากพื้นที่ดิสก์ให้ขยะคอมไพล์เก็บกระทำเหล่านี้หลังจากไม่กี่สัปดาห์
avmohan

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


-1

คุณยังสามารถใช้:

git reset HEAD file/path


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