Видаліть папку та її вміст з історії git / GitHub


318

Я працював над сховищем для свого облікового запису GitHub, і це проблема, на яку я натрапив.

  • Проект Node.js із папкою з кількома встановленими пакетами npm
  • Пакети були в node_modulesпапці
  • Додав цю папку до сховища git і перемістив код до github (тоді не думав про npm-частину)
  • Зрозуміло, що ця папка насправді не потрібна, щоб ця папка була частиною коду
  • Видалили цю папку, натиснули її

У цьому випадку розмір загальної кількості git repo становив близько 6 Мб, де фактичний код (усе, крім цієї папки), становив лише близько 300 КБ .

Тепер те, що я шукаю, врешті-решт, це спосіб позбутися деталей цієї папки пакунків з історії git, тому якщо хтось її клонує, їм не доведеться завантажувати історію вартістю 6 Мб, де будуть отримані єдині фактичні файли. станом на останнє зобов’язання було б 300 КБ.

Я шукав можливі рішення для цього і спробував ці 2 методи

Gist здавалося, що він працює там, де після запуску сценарію він показав, що він позбувся цієї папки, а потім показав, що 50 різних комітетів були змінені. Але це не дозволило мені натиснути цей код. Коли я намагався натиснути на це, він сказав, Branch up to dateале показав, що 50 команд було змінено на git status. Інші 2 методи також не допомогли.

Тепер, хоча це показало, що він позбувся історії цієї папки, коли я перевірив розмір цього репо на своєму localhost, він все ще був близько 6 Мб. (Я також видалив refs/originalпапку, але зміни розміру репо не побачив).

Я хочу роз’яснити, якщо є спосіб позбутися не лише історії фіксації (що, на мій погляд, єдине, що, на мою думку, сталося), але і цих файлів git, припускаючи, що хочеться відкатати.

Скажімо, рішення для цього представлено і застосовується до мого localhost, але не можу бути відтворено до цього репортажу GitHub, чи можна клонувати це репо, відкат до першого фіксу виконувати трюк і натискати його (чи це означає, що git буде все ще є історія всіх цих комітетів? - ака. 6 МБ).

Моя кінцева мета тут - в основному знайти найкращий спосіб позбутися вмісту папки з git, щоб користувачеві не довелося завантажувати речі, що коштують 6 Мб, і все-таки можливо мати інші коміти, які ніколи не торкалися папки модулів (це досить багато з них) в історії git.

Як я можу це зробити?


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

Що насправді робить git:

Перший рядок повторює всі посилання на тому ж дереві ( --tree-filter), що і HEAD (ваша поточна гілка), запускаючи команду rm -rf node_modules. Ця команда видаляє папку node_modules ( -rбез -r, rmпапки не видаляється), без запиту, наданого користувачеві ( -f). Додані --prune-emptyвидалення марно (нічого не змінюючи) здійснює рекурсивно.

Другий рядок видаляє посилання на цю стару гілку.

Решта команд відносно прості.


3
Лише бічна примітка: я git count-objects -vперевіряв, чи файли фактично видалено, але розмір сховища залишається тим самим, поки я знову не клонував сховище. Git зберігає копію всіх оригінальних файлів, які я думаю.
Девід Ікарді

4
З недавнім git це, мабуть, слід читати --force-with-lease, ні --force.
Griwes

4
Жодна з цих команд не працює на вікнах. Або принаймні не Windows 10, будь ласка, опублікуйте ОС, на якій працює «вирізати та вставити»
Девід

3
Для користувачів Windows 10 це чудово працює під Bash for Windows (я використовував Ubuntu)
Andrej Kyselica

3
Я спробував це з оболонкою Windows і з git bash, і не вийшло. Перша команда проходить, друга команда провалюється!
Мохі Елдін

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: з docs ( git-scm.com/docs/git-filter-branch ). "--index-filter: ... схожий на деревний фільтр, але не перевіряє дерево, що робить його набагато швидшим"
Lee Netherton

23
Чому це не прийнята відповідь? Це так ретельно.
Божевільний фізик

2
Якщо ви робите це в Windows, вам потрібні подвійні лапки замість одинарних лапок.
Kris Morness

12
Перехід --quietдо git rmвищезгаданого перевищив мою переписку принаймні на коефіцієнт 4.
ctusch

46

На додаток до популярної відповіді вище, я хотів би додати кілька приміток для Windows- систем. Команда

git filter-branch --tree-filter 'rm -rf node_modules' --prune-empty HEAD
  • прекрасно працює без будь-яких модифікацій! Таким чином, ви не повинні використовувати Remove-Item, delабо що - небудь інше замість rm -rf.

  • Якщо вам необхідно вказати шлях до файлу або використовуйте каталог слеш як./path/to/node_modules


Це не буде працювати в Windows, якщо каталог містить. (крапка) у назві.
Корнеліу Середюк

4
І я знайшов рішення. Використовуйте подвійні перевернуті коми для команди rm, як це: "rm -rf node.modules".
Корнеліу Середюк

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-файлів:

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

1
дуже просто :) якщо ви хочете зробити так, щоб видалити лише певну папку, це допоможе: stackoverflow.com/questions/21142986/…
emjay

9

Здається, що остання відповідь на це полягає в тому, щоб не використовувати filter-branchбезпосередньо (принаймні, сам git більше не рекомендує це), а перенести цю роботу на зовнішній інструмент. Зокрема, наразі рекомендується git-filter-repo . Автор цього інструменту надає аргументи щодо того, чому використання filter-branchбезпосередньо може призвести до проблем.

Більшість багаторядкових сценаріїв, викладених вище, можна видалити dirз історії:

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

Інструмент є більш потужним, ніж просто це, мабуть. Ви можете застосовувати фільтри за автором, електронною поштою, перейменовуванням тощо ( повну сторінку тут ). Крім того, це швидко . Установка проста - вона розповсюджується в різноманітних форматах .


Гарний інструмент! Працює добре на Ubuntu 20.04, ви можете просто pip3 install git-filter-repoтак як це лише stdlib і не встановлює ніяких залежностей. У Ubuntu 18 це несумісно з версією git Error: need a version of git whose diff-tree command has the --combined-all-paths optiondocker run -ti ubuntu:20.04
distro

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.
завершення

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.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.