Как удалить старую историю из репозитория git?
Боюсь, я не смог найти ничего похожего на этот конкретный сценарий.
У меня есть git-репозиторий с большой историей: 500+ веток, 500+ тегов, начиная с середины 2007 года. Он содержит ~19 500 коммитов. Мы хотели бы удалить всю историю до 1 января 2010 года, чтобы сделать ее меньше и легче иметь дело (мы сохраним полную копию истории в архивном хранилище).
Я знаю, что коммит, который я хочу, стал корнем нового репозитория. Однако я не могу найти правильный git mojo для усечения репо, чтобы начать с этого коммита. Я угадываю какой-то вариант
git filter-branch
привлечение трансплантатов будет необходимо; может также понадобиться обработать каждую из 200+ веток, которые мы хотим сохранить отдельно, а затем соединить репо (что я знаю, как сделать).
Кто-нибудь когда-нибудь делал что-то подобное? У меня есть git 1.7.2.3, если это имеет значение.
13 ответов
Просто создайте прививку родителя вашего нового корневого коммита без родителя (или с пустым коммитом, например, с настоящим корневым коммитом вашего репозитория). Например echo "<NEW-ROOT-SHA1>" > .git/info/grafts
После создания трансплантата он вступает в силу сразу же; Вы должны быть в состоянии смотреть на git log
и увидим, что ненужные старые коммиты исчезли:
$ echo 4a46bc886318679d8b15e05aea40b83ff6c3bd47 > .git/info/grafts
$ git log --decorate | tail --lines=11
commit cb3da2d4d8c3378919844b29e815bfd5fdc0210c
Author: Your Name <your.email@example.com>
Date: Fri May 24 14:04:10 2013 +0200
Another message
commit 4a46bc886318679d8b15e05aea40b83ff6c3bd47 (grafted)
Author: Your Name <your.email@example.com>
Date: Thu May 23 22:27:48 2013 +0200
Some message
Если все выглядит так, как задумано, вы можете просто сделать простой git filter-branch -- --all
сделать его постоянным.
ВНИМАНИЕ: после выполнения шага ответвления фильтра все идентификаторы изменений будут изменены, поэтому любой, кто использует старое репо, никогда не должен сливаться с кем-либо, использующим новое репо.
Возможно, уже слишком поздно отправлять ответ, но поскольку эта страница является первым результатом Google, она все равно может быть полезна.
Если вы хотите освободить место в своем репозитории git, но не хотите перестраивать все свои коммиты (перебазировать или прививать) и при этом иметь возможность выталкивать / извлекать / объединять людей, имеющих полное репо, вы можете использовать git клон мелкий клон (параметр--depth).
; Clone the original repo into limitedRepo
git clone file:///path_to/originalRepo limitedRepo --depth=10
; Remove the original repo, to free up some space
rm -rf originalRepo
cd limitedRepo
git remote rm origin
Вы можете обуздать существующее репо, выполнив следующие действия:
; Shallow to last 5 commits
git rev-parse HEAD~5 > .git/shallow
; Manually remove all other branches, tags and remotes that refers to old commits
; Prune unreachable objects
git fsck --unreachable ; Will show you the list of what will be deleted
git gc --prune=now ; Will actually delete your data
Ps: Старые версии git не поддерживали клонирование /push/pull из / для мелких репозиториев.
Этот метод прост для понимания и отлично работает. Аргумент к сценарию ($1
) - это ссылка (тег, хэш, ...) на коммит, начиная с которого вы хотите сохранить свою историю.
#!/bin/bash
git checkout --orphan temp $1 # create a new branch without parent history
git commit -m "Truncated history" # create a first commit on this branch
git rebase --onto temp $1 master # now rebase the part of master branch that we want to keep onto this branch
git branch -D temp # delete the temp branch
# The following 2 commands are optional - they keep your git repo in good shape.
git prune --progress # delete all the objects w/o references
git gc --aggressive # aggressively collect garbage; may take a lot of time on large repos
Обратите внимание, что старые теги все еще будут присутствовать; поэтому вам может потребоваться удалить их вручную
примечание: я знаю, что это почти так же, как @yoyodin, но здесь есть несколько важных дополнительных команд и информации. Я пытался отредактировать ответ, но так как это существенное изменение в ответе @ yoyodin, мое редактирование было отклонено, поэтому вот информация!
Попробуйте этот метод Как усечь историю Git:
#!/bin/bash
git checkout --orphan temp $1
git commit -m "Truncated history"
git rebase --onto temp $1 master
git branch -D temp
Вот $1
это SHA-1 коммита, который вы хотите сохранить, и скрипт создаст новую ветку, которая содержит все коммиты между $1
а также master
и вся старая история отбрасывается. Обратите внимание, что этот простой сценарий предполагает, что у вас нет существующей ветви с именем temp
, Также обратите внимание, что этот скрипт не очищает данные git для старой истории. Бежать git gc --prune=all && git repack -a -f -F -d
после того, как вы убедились, что вы действительно хотите потерять всю историю. Вам также может понадобиться rebase --preserve-merges
но имейте в виду, что реализация этой функции в git не идеальна. Проверьте результаты вручную, если вы используете это.
В качестве альтернативы переписыванию истории, рассмотрите возможность использования git replace
как в этой статье из книги Pro Git. Обсуждаемый пример включает замену родительского коммита для имитации начала дерева, сохраняя при этом всю историю как отдельную ветвь для безопасного хранения.
Если вы хотите сохранить репозиторий в восходящем потоке с полной историей, но локальными меньшими извлечениями, сделайте поверхностный клон с git clone --depth=1 [repo]
,
Нажав коммит, вы можете сделать
git fetch --depth=1
обрезать старые коммиты. Это делает старые коммиты и их объекты недоступными.git reflog expire --expire-unreachable=now --all
, Срок действия всех старых коммитов и их объектовgit gc --aggressive --prune=all
убрать старые предметы
Смотрите также Как удалить локальную историю git после коммита?,
Обратите внимание, что вы не можете перенести этот "мелкий" репозиторий куда-либо еще: "мелкое обновление не разрешено". См. Удалено отклонено (мелкое обновление не разрешено) после изменения удаленного URL Git. Если вы хотите к этому, вы должны придерживаться прививки.
Мне нужно было прочитать несколько ответов и другую информацию, чтобы понять, что я делаю.
1. Игнорировать все, что старше определенного коммита
Файл .git/info/grafts
может определить фальшивых родителей для коммита. Строка с просто идентификатором коммита говорит, что у коммита нет родителя. Если мы хотим сказать, что мы заботимся только о последних 2000 коммитах, мы можем набрать:
git rev-parse HEAD~2000 > .git/info/grafts
git rev-parse дает нам идентификатор коммита 2000-го родителя текущего коммита. Приведенная выше команда перезапишет файл трансплантатов, если он присутствует. Проверьте, если это там в первую очередь.
2. Переписать историю Git (необязательно)
Если вы хотите, чтобы этот привитый поддельный родитель был реальным, запустите:
git filter-branch -- --all
Это изменит все идентификаторы коммитов. Каждая копия этого хранилища должна быть принудительно обновлена.
3. Очистить место на диске
Я не делал шаг 3. Я хотел, чтобы моя копия оставалась совместимой с апстримом. Я просто хотел сэкономить место на диске. Чтобы забыть все старые коммиты:
git prune
git gc
Альтернатива: мелкие копии
Если у вас есть мелкая копия другого хранилища и вы просто хотите сэкономить место на диске, вы можете обновить .git/shallow
, Но будьте осторожны, чтобы ничто не указывало на коммит из ранее. Таким образом, вы можете запустить что-то вроде этого:
git fetch --prune
git rev-parse HEAD~2000 > .git/shallow
git prune
git gc
Запись на мелководье работает как прививка. Но будьте осторожны, чтобы не использовать трансплантаты и мелкие одновременно. По крайней мере, там нет одинаковых записей, это не удастся.
Если у вас все еще есть старые ссылки (теги, ветви, удаленные заголовки), которые указывают на более старые коммиты, они не будут очищены, и вы не сэкономите больше дискового пространства.
Здесь слишком много ответов, которые не являются актуальными, а некоторые не полностью объясняют последствия. Вот что сработало для меня при сокращении истории с использованием последней версии git 2.26:
Сначала создайте фиктивный коммит. Эта фиксация появится как первая фиксация в вашем усеченном репо. Вам это нужно, потому что этот коммит будет содержать все базовые файлы для истории, которую вы ведете. SHA - это идентификатор предыдущей фиксации фиксации, которую вы хотите сохранить (в этом примере,8365366
). Строка Initial будет отображаться как сообщение о фиксации первой фиксации. Если вы используете Windows, введите следующую команду из командной строки Git Bash.
# 8365366 is id of parent commit after which you want to preserve history
echo 'Initial' | git commit-tree 8365366^{tree}
Вышеупомянутая команда напечатает SHA, например, d10f7503bc1ec9d367da15b540887730db862023
.
Теперь просто введите:
# d10f750 is commit ID from previous command
git rebase --onto d10f750 8365366
Это сначала поместит все файлы после фиксации 8365366
в фиктивную фиксацию d10f750
. Затем он будет воспроизводить все коммиты после 8365366 поверхd10f750
. в заключениеmaster
Указатель ветки будет обновлен до последней воспроизведенной фиксации.
Теперь, если вы хотите отправить это усеченное репо, просто выполните git push -f
.
Следует помнить о нескольких вещах (это относится и к другим методам, и к этому): Теги не передаются. Хотя идентификаторы и временные метки фиксации сохраняются, вы увидите, что GitHub показывает эти фиксации в виде единовременного заголовка, напримерCommits on XY date
.
К счастью, можно сохранить усеченную историю как "архив", а позже вы можете присоединиться обратно к усеченному репо с архивным репо. Для этого см. Это руководство.
При перебазировании или толкании к голове / мастеру эта ошибка может возникнуть
remote: GitLab: You are not allowed to access some of the refs!
To git@giturl:main/xyz.git
! [remote rejected] master -> master (pre-receive hook declined)
error: failed to push some refs to 'git@giturl:main/xyz.git'
Для решения этой проблемы в git dashboard следует удалить главную ветку из "Защищенных веток"
тогда вы можете запустить эту команду
git push -f origin master
или же
git rebase --onto temp $1 master
Для существующего репозитория, клонированного ранее с помощью
--depth
git clone --depth=1 ...
Просто делать
git pull --depth=1 --update-shallow
В моем случае я хочу разделить репо на две части, сохранить историю, но очистить историю журнала от файлов, отфильтрованных из нового репо.
Это было решение:
PATHS=path_a path_b
git filter-branch -f --prune-empty --index-filter "git read-tree --empty
git reset \$GIT_COMMIT -- $PATHS " -- --all -- $PATHS
Таким образом, я получил новое репо с полной историей журнала коммитов, но только для пути, который я хотел сохранить;
Вы можете удалить каталог, файлы, а также всю историю, связанную с dir или файлом, используя нижеприведенный jar [загрузить его] и команды
Файл bfg.jar: https://rtyley.github.io/bfg-repo-cleaner/
git clone --bare repo-url cd repo_dir java -jar bfg.jar --delete-folder имя_папки git reflog expire --expire= сейчас --all && git gc --prune= сейчас - агрессивный git push --mirror repo_url
- удалить данные git, rm .git
- мерзавец
- добавить git remote
- принудительный толчок