Удалить папку и ее содержимое из git/истории GitHub

Я работал над репозиторием на моей учетной записи GitHub, и это проблема, на которую я наткнулся.

  • Node.js проект с папкой с несколькими установленными пакетами npm
  • Пакеты находились в папке node_modules
  • Добавил эту папку в репозиторий git и нажал код на github (в то время не думал о части npm)
  • Понял, что вам действительно не нужна эта папка, чтобы быть частью кода
  • Удалил эту папку, нажал ее

В этом случае размер полного репозитория git был около 6 МБ, где фактический код (все, кроме этой папки) был всего около 300 КБ.

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

Я искал возможные решения для этого и пробовал эти 2 метода

Кажется, что Gist работал, где после запуска script он показал, что он избавился от этой папки, и после этого он показал, что было изменено 50 разных коммитов. Но это не позволило мне нажать этот код. Когда я попытался нажать, он сказал Branch up to date, но показал, что 50 коммитов были изменены на git status. Другие 2 метода тоже не помогли.

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

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

Давайте скажем, что решение представлено для этого и применяется на моем локальном хосте, но не может быть воспроизведено в этом репо GitHub, можно ли клонировать это репо, откат к первому фиксатору выполнить трюк и нажать его (или это означает что git будет по-прежнему иметь историю всех этих коммитов? - aka. 6MB).

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

Как я могу это сделать?

Ответ 1

Если вы хотите скопировать и вставить код:

Это пример, который удаляет 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 -f ilter), что и HEAD (ваша текущая ветвь), выполняя команду 'rm -rf node_modules'. Эта команда удаляет папку node_modules (-r, без -r, rm не будет удалять папки), без запроса пользователю (-f). Добавленный --prune-empty удаляет бесполезные (ничего не меняющие) фиксации рекурсивно.

Вторая строка удаляет ссылку на эту старую ветку.

Остальные команды относительно просты.

Ответ 2

Я нахожу, что параметр --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

В дополнение к популярному ответу выше я хотел бы добавить несколько заметок для Windows -систем. Команда

git filter-branch --tree-filter 'rm -rf node_modules' --prune-empty HEAD
  • работает отлично без модификации! Поэтому вы не должны использовать Remove-Item, del или что-то еще вместо rm -rf.

  • Если вам нужно указать путь к файлу или каталогу, используйте слэши, например ./path/to/node_modules

Ответ 4

Лучший и самый точный метод, который я нашел, это загрузить файл 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

Ответ 5

Завершить копию и вставить рецепт, просто добавив команды в комментарии (для решения для копирования-вставки), после тестирования:

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

Ответ 6

Для пользователя 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

Ответ 7

Я удалил папки bin и obj из старых проектов С#, используя git на windows. Будь осторожен с

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

Это разрушает целостность установки git, удаляя папку usr/bin в папке git install.

Ответ 8

На 2 верхних ответа вопрос: все строки можно легко понять, но ~~~ bash git for-each-ref --format = "% (refname)" refs/original/| xargs -n 1 git update-ref -d ~~~ не будет делать ничего для каждой моей версии git, поскольку git for-each-ref ничего не перечисляет. Я протестировал и обнаружил: ~~~ bash git for-each-ref --format = "% (refname)" refs/remotes/origin/~~~ может перечислить все мои удаленные ссылки. @Mohsen или @lee -n etherton, пожалуйста, помогите по этому вопросу?