ХХ полезных советов для пользователей Git среднего уровня. Часть 2
Про reset, незапланированно снова про альясы, про замечательный filter-branch, про мерджи и разрешение конфликтов с помощью rerere, про rebase (интерактивный и не очень) и, в завершение, про обслуживание своей гитницы.
1. Где у Git`a reset
Если брать самые частые случаи, когда нужно так или иначе отменить последний коммит или изменения после оного, то это будет команды git reset (она же git reset —mixed HEAD) и git reset —hard. Первая удаляет из индекса последний коммит, но не трогает изменения в файлах, вторая же беспощадно приводит и индекс, и файлы к первозданному виду^W^Wсостоянию на момент указанного коммита.
Иными словами, отменить правки, пусть даже и за`stage`нные через git add, можно сделав $ git reset HEAD (помните альяс git unstage?).
А полностью дропнуть, скажем, последний коммит (т.е. отмотать время на момент предпоследнего коммита) — $ git reset —hard HEAD^.
2. Еще альясы
По итогам изучения history|grep » git «| sort -d|uniq, я добавил еще один альяс — $ git config —global alias.amend ‘commit —amend -C HEAD’ — по команде git amend перезаписывается последний коммит с тем же сообщением.
Возможно, добавлю туда ключ -a (чтобы не делать git add каждый раз).
3. filter-branch — спасение для растяпы!
git filter-branch позволяет более чем широкие возможности для манипуляций с историей.
Я познакомился с этой командой, когда заметил, что храню боевые пароли не во внутренней ветке, а в мастере.
Слава Богу, что заметил я это перед пушем в общедоступный репозитарий!
$ git filter-branch —tree-filter «sed -e ‘s#my_secret_pass#dbpassword#’ -e ‘s#my_db_hostdbhost.tld#’ -i settings.py» HEAD
Сим нехитрым действием я переписал пароли в файле настроек в каждом коммите.
Можно сужать область применения, задавая уточняющие параметры —index-filter, —msg-filter, —commit-filter—tag-name-filter и другие.
—all означает все бранчи.
4. И снова про мерджи.
Спасибо ghisguth за наводку на потенциально полезный инструмент обращения с конфликтами про слиянии веток.
Сам я им еще не пользуюсь, но закоменченная (чтобы не забыть) строчка в конфиге уже есть.
Итак, встречайте — git rerere — позволяет записывать решение конфликтов и при повторном мерже использовать их.
Включается так — $ git config —global rerere.enabled 1
Полезно, если есть долгоживущий бранч разработки, который точно конфликтует с другим бранчем (скажем, мастером), то «обучение» гита телодвижениям при мердже происходит в такой последовательности: сначала делаем git pull origin master, устраняем конфликты, коммитим и откатываем тестовое слияние обратно — $ git reset —hard HEAD^. При настоящем слиянии будут автоматически проделаны все те же телодвижения, что мы сделали руками.
Вообще-то git rerere запускается без вмешательства пользователя и без аргументов, но можно подкорректировать ход работы:
Команды: git rerere [clear|diff|status|gc]
clear — Ресет метаданных, используемых rerere, если автоматическое решение конфликта отменяется.
Например, git am [—skip|—abort] или git rebase [—skip|—abort] автоматически использует этот параметр.
diff — Отображение диффа для текущего состояния разрешения конфликта. Полезно для отслеживания что изменилось за время устранения конфликта. дополнительные параметры передаются прямо команде diff.
В отличие от diff, выводит только имена файлов, которые отслеживаются для разрешения конфликта.
gc — Удаляет записи конфликтных слияний, которые имели место быть много времени назад. По умолчанию, неразрешенные конфликты старше 15ти дней и разрешенные старше 60ти дней вычищаются. Эти значения описаны в gc.rerereunresolved и gc.rerereresolved.
5. Про rebase
Про ребэйз уже было сказано, что эта команда позволяет «переместить» сделанные изменения наверх, поместив в историю коммиты из мастера, которые «ушли вперед» за время внесения локальных правок.
Есть еще одно интересное применение — rebase —onto.
Таким образом можно перенести свои коммиты, основываясь на совершенно третьей сторонней ветке.
Например, есть такая картина:
.-x————master
. |
. \ ——server-experiments
. |
. \—http-interface
Чтобы смерджить ветку с новым интерфейсом в мастер, оставив игрища с серверной частью, надо сделать
$ git rebase —onto master server-experiments http-interface
После этого можно переключаться в мастер и мерджить ветки (git checkout master; git merge http-interface) — по итогу у нас есть основная ветка, влитый в неё новёхонький интерфейс и совершенно отдельно — оставшаяся ветка с экспериментами над серверной частью.
PROFIT!
Можно ребейзить не всё подряд, а интерактивно и выборочно — для этого нужна опция -i (—interactive).
$ git rebase -i master откроет редактор со списком указанных коммитов вида «command SHA commit_message».
Команды бывают:
p|pick — использовать коммит;
e|edit — использовать коммит, но сделать паузу ради —amend (процесс остановится перед следующим пунктом, чтобы можно было внести какие-то свои особые правки — например, разбить изменение на более мелкие коммиты)
s|squash — использовать коммит, но объединить с предыдущим коммитом. (в процессе будет открыто окно редактора, чтобы можно было соорудить общий commit message. Этакий глобальный commit —amend)
Удаление линии приведёт к удалению коммита.
Продолжить естественный ход вещей можно с помощью $ git rebase —continue
6. Обслуживание
Если идёт активная разработка и вы не пушите код на удалённые сервер, то стоит периодически выполнять сборку мусора:
$ git gc
Это подчистит мусор — удалит ни к чему не привязанные объекты и эффективно (пере-|у-)пакует оставшиеся.
Я сказал про удалённые сервера, потому что перед пушем на другой сервер объекты обрабатываются автоматически.
Вот, кажется, и всё, что я хотел бы сказать по поводу гита)
Как удалить файлы из уже сделанного комита?
У меня есть git репозиторий, с программой, в который я случайно добавил куча видео очень давно. Сами видео уже были давно удалены, я удалял их из гита ( git rm ExampleResult ) (Все они лежат в папке ExampleResult). Но проблема возникла когда я попытался это все залить на гитхаб, так как теперь репозиторий весит около 90 ГБ и любые манипуляции проводить с ним очень долго. (С пушем я сдался на строке Compressing Objects). Что можно сделать что бы это исправить. git ls-files выдаёт
.gitignore README.md config.py main.py requirements.txt video_divider.py
Попытка фильтрации:
(venv) git filter-branch --tree-filter 'rm -rf *.mp4' HEAD WARNING: git-filter-branch has a glut of gotchas generating mangled history rewrites. Hit Ctrl-C before proceeding to abort, then use an alternative filtering tool such as 'git filter-repo' (https://github.com/newren/git-filter-repo/) instead. See the filter-branch manual page for more details; to squelch this warning, set FILTER_BRANCH_SQUELCH_WARNING=1. Proceeding with filter-branch. usage: git filter-branch [--setup ] [--subdirectory-filter ] [--env-filter ] [--tree-filter ] [--index-filter ] [--parent-filter ] [--msg-filter ] [--commit-filter ] [--tag-name-filter ] [--original ] [-d ] [-f | --force] [--state-branch ] [--] [. ]
Как я понял это значит что команда не выполнилась. Папка .git до сих пор весит под 100 Гб. Так же пытался удалить всю директорию ExampleResult в которой все файлы и находятся, но это не увенчалось успехом.
>git filter-branch --index-filter 'git rm -r --cached --ignore-unmatch ExampleResult' HEAD fatal: ambiguous argument 'rm': unknown revision or path not in the working tree. Use '--' to separate paths from revisions, like this: 'git [. ] -- [. ]'
Вопросы с меткой [git-filter-branch]
Руководство по использованию метки git-filter-branch отсутствует.
7 вопросов
Конкурсные
Неотвеченные
- Конкурсные 0
- Неотвеченные
- Цитируемые
- Рейтинг
- Неотвеченные (мои метки)
587 показов
Как объединить последовательные коммиты с одинаковыми именами?
Представьте себе такой git-репозиторий: Как видно, что в таком git-репозитории есть две группы коммитов, которые имеют одинаковые имена и идут последовательно: сделал фичу A сделал фичу A сделал .
задан 7 июн 2021 в 6:48
106 показов
Удалить большой файл из git
Добавил большой файл в коммит, github отказался его принимать: remote: error: File system/storage/logs/error.log is 182.48 MB; this exceeds GitHub’s file size limit of 100.00 MB Применил: git .
задан 5 мар 2019 в 14:07
127 показов
Как удалить папки _notes из всех подпапок во всех ветках за всю историю?
Как удалить папки _notes из всех подпапок во всех ветках за всю историю?
задан 14 окт 2017 в 17:50
172 показа
Не получается запушить на github после filter-branch
Нужно удалить некоторые файлы из всех коммитов, использовал для этого git filter-branch —tree-filter ‘rm -f passwords.txt’ HEAD После этого сделал новый коммит, но при попытке запушить на github .
задан 21 сен 2015 в 19:43
13k показов
Как удалить файл из истории Git?
Не могу полностью удалить файл из истории Git. Файл — бинарник с русским названием, и так получилось что пришлось чуть исправить его название — изменить регистр пары букв. Сделал коммит, залил на .
задан 27 мая 2015 в 0:02
5k показов
Удалить файл из коммита, который был отправлен (git pull) на сервер
Волей случая, и моей неосторожности, попал в репозиторий тестовый файл довольно большого размера, который не должен был туда попасть. Заметил я это, когда начал делать push на сервер. В общем, в .
задан 15 мар 2015 в 8:19
1k показов
Как изменить сообщение любого конкретного коммита?
Несмотря на большое количество этого вопроса в интернете, нормального решения я так и не нашел. В основном предлагают git rebase -i, но он как-то жестко перелопачивает все последующие коммиты, меняя .
задан 30 янв 2015 в 22:37
-
Важное на Мете
Связанные метки
Подписаться на ленту
Лента новых вопросов с меткой [git-filter-branch]
Для подписки на ленту скопируйте и вставьте эту ссылку в вашу программу для чтения RSS.
Дизайн сайта / логотип © 2023 Stack Exchange Inc; пользовательские материалы лицензированы в соответствии с CC BY-SA . rev 2023.10.27.43697
Нажимая «Принять все файлы cookie» вы соглашаетесь, что Stack Exchange может хранить файлы cookie на вашем устройстве и раскрывать информацию в соответствии с нашей Политикой в отношении файлов cookie.
7.6 Инструменты Git — Перезапись истории
Неоднократно при работе с Git, вам может потребоваться по какой-то причине внести исправления в историю коммитов. Одно из преимуществ Git заключается в том, что он позволяет вам отложить принятие решений на самый последний момент. Область индексирования позволяет вам решить, какие файлы попадут в коммит непосредственно перед его выполнением; благодаря команде git stash вы можете решить, что не хотите продолжать работу над какими-то изменениями; также можете внести изменения в сделанные коммиты так, чтобы они выглядели как будто они произошли по-другому. В частности, можно изменить порядок коммитов, сообщения или изменённые в коммитах файлы, объединить вместе или разбить на части, полностью удалить коммит — но только до того, как вы поделитесь своими наработками с другими.
В этом разделе вы познакомитесь со способами решения всех этих задач и научитесь перед публикацией данных приводить историю коммитов в нужный вам вид.
Примечание
Не отправляйте свои наработки, пока вы ими не довольны
Одно из основных правил Git заключается в том, что, так как большую часть работы вы делаете в своём локальном репозитории, то вы вольны переписывать свою историю локально. Однако, как только вы отправите свои наработки, то это уже совсем другая история и вам следует рассматривать отправленные изменения как финальные до тех пор, пока у вас не появится весомая причина что-то изменить. Если кратко, то вы должны воздержаться от отправки своих изменений, пока не будете полностью довольны и готовы поделиться ими со всем миром.
Изменение последнего коммита
Изменение вашего последнего коммита, наверное, наиболее частое исправление истории, которое вы будете выполнять. Наиболее часто с вашим последним коммитом вам будет нужно сделать две основные операции: изменить сообщение коммита или изменить только что сделанный снимок, добавив, изменив или удалив файлы.
Если вы хотите изменить только сообщение вашего последнего коммита, это очень просто:
$ git commit --amend
Эта команда откроет в вашем текстовом редакторе сообщение вашего последнего коммита, для того, чтобы вы могли его исправить. Когда вы сохраните его и закроете редактор, будет создан новый коммит, содержащий это сообщение, который теперь и будет вашим последним коммитом.
Если вы создали коммит и затем хотите изменить зафиксированный снимок, добавив или изменив файлы (возможно, вы забыли добавить вновь созданный файл, когда совершали изначальный коммит), то процесс выглядит в основном так же. Вы добавляете в индекс необходимые изменения, редактируя файл и выполняя для него git add или git rm для отслеживаемого файла, а последующая команда git commit —amend берет вашу текущую область подготовленных изменений и делает её снимок для нового коммита.
Вы должны быть осторожными, используя этот приём, так как при этом изменяется SHA-1 коммита. Поэтому, как и с операцией rebase — не изменяйте ваш последний коммит, если вы уже отправили его в общий репозиторий.
Изменённый коммит может потребовать изменения сообщения коммита
При изменении коммита существует возможность изменить как его содержимое, так и сообщение коммита. Если в коммит внесены существенные изменения, то почти наверняка следует обновить и его сообщение, чтобы оно более точно отражало содержимое коммита.
С другой стороны, если изменения незначительны (исправление опечаток, добавление в коммит забытого файла), то текущее сообщение вполне можно оставить; чтобы лишний раз не вызывать редактор, просто добавьте изменённые файлы в индекс и выполните команду:
$ git commit --amend --no-edit
Изменение сообщений нескольких коммитов
Для изменения коммита, расположенного раньше в вашей истории, вам нужно обратиться к более сложным инструментам. В Git отсутствуют инструменты для переписывания истории, но вы можете использовать команду rebase , чтобы перебазировать группу коммитов туда же на HEAD, где они были изначально, вместо перемещения их в другое место. С помощью интерактивного режима команды rebase , вы можете останавливаться после каждого нужного вам коммита и изменять сообщения, добавлять файлы или делать что-то другое, что вам нужно. Вы можете запустить rebase в интерактивном режиме, добавив опцию -i к git rebase . Вы должны указать, какие коммиты вы хотите изменить, передав команде коммит, на который нужно выполнить перебазирование.
Например, если вы хотите изменить сообщения последних трёх коммитов, или сообщение какого-то одного коммита этой группы, то передайте как аргумент команде git rebase -i родителя последнего коммита, который вы хотите изменить — HEAD~2^ или HEAD~3 . Может быть, проще будет запомнить ~3 , так как вы хотите изменить последние три коммита; но не забывайте, что вы, в действительности, указываете четвертый коммит с конца — родителя последнего коммита, который вы хотите изменить:
$ git rebase -i HEAD~3
Напомним, что это команда перебазирования — каждый коммит, входящий в диапазон HEAD~3..HEAD , будет изменён вне зависимости от того, изменили вы сообщение или нет. Не включайте в такой диапазон коммит, который уже был отправлен на центральный сервер: сделав это, вы можете запутать других разработчиков, предоставив вторую версию одних и тех же изменений.
Выполнение этой команды отобразит в вашем текстовом редакторе список коммитов, в нашем случае, например, следующее:
pick f7f3f6d Change my name a bit pick 310154e Update README formatting and add blame pick a5f4a0d Add cat-file # Rebase 710f0f8..a5f4a0d onto 710f0f8 # # Commands: # p, pick = use commit # r, reword = use commit, but edit the commit message # e, edit = use commit, but stop for amending # s, squash = use commit, but meld into previous commit # f, fixup = like "squash", but discard this commit's log message # x, exec = run command (the rest of the line) using shell # b, break = stop here (continue rebase later with 'git rebase --continue') # d, drop = remove commit # l, label = label current HEAD with a name # t, reset = reset HEAD to a label # m, merge [-C | -c ] [# ] # . create a merge commit using the original merge commit's # . message (or the oneline, if no original merge commit was # . specified). Use -c to reword the commit message. # # These lines can be re-ordered; they are executed from top to bottom. # # If you remove a line here THAT COMMIT WILL BE LOST. # # However, if you remove everything, the rebase will be aborted. # # Note that empty commits are commented out
Важно отметить, что коммиты перечислены в порядке, противоположном порядку, который вы обычно видите при использовании команды log . Если вы выполните log , то увидите следующее:
$ git log --pretty=format:"%h %s" HEAD~3..HEAD a5f4a0d Add cat-file 310154e Update README formatting and add blame f7f3f6d Change my name a bit
Обратите внимание на обратный порядок. Команда rebase в интерактивном режиме предоставит вам скрипт, который она будет выполнять. Она начнет с коммита, который вы указали в командной строке ( HEAD~3 ) и повторит изменения, внесённые каждым из коммитов, сверху вниз. Наверху отображается самый старый коммит, а не самый новый, потому что он будет повторен первым.
Вам необходимо изменить скрипт так, чтобы он остановился на коммите, который вы хотите изменить. Для этого измените слово pick на слово edit напротив каждого из коммитов, после которых скрипт должен остановиться. Например, для изменения сообщения только третьего коммита, измените файл следующим образом:
edit f7f3f6d Change my name a bit pick 310154e Update README formatting and add blame pick a5f4a0d Add cat-file
Когда вы сохраните сообщение и выйдете из редактора, Git переместит вас к самому раннему коммиту из списка и вернёт вас в командную строку со следующим сообщением:
$ git rebase -i HEAD~3 Stopped at f7f3f6d. Change my name a bit You can amend the commit now, with git commit --amend Once you're satisfied with your changes, run git rebase --continue
Эти инструкции говорят вам в точности то, что нужно сделать. Выполните:
$ git commit --amend
Измените сообщение коммита и выйдите из редактора. Затем выполните:
$ git rebase --continue
Эта команда автоматически применит два оставшиеся коммита и завершится. Если вы измените pick на edit в других строках, то можете повторить эти шаги для соответствующих коммитов. Каждый раз Git будет останавливаться, позволяя вам исправить коммит, и продолжит, когда вы закончите.
Упорядочивание коммитов
Вы также можете использовать интерактивное перебазирование для изменения порядка или полного удаления коммитов. Если вы хотите удалить коммит «Add cat-file» и изменить порядок, в котором были внесены два оставшихся, то вы можете изменить скрипт перебазирования с такого:
pick f7f3f6d Change my name a bit pick 310154e Update README formatting and add blame pick a5f4a0d Add cat-file
pick 310154e Update README formatting and add blame pick f7f3f6d Change my name a bit
Когда вы сохраните скрипт и выйдете из редактора, Git переместит вашу ветку на родителя этих коммитов, применит 310154e , затем f7f3f6d и после этого остановится. Вы, фактически, изменили порядок этих коммитов и полностью удалили коммит «Add cat-file».
Объединение коммитов
С помощью интерактивного режима команды rebase также можно объединить несколько коммитов в один. Git добавляет полезные инструкции в сообщение скрипта перебазирования:
# # Commands: # p, pick = use commit # r, reword = use commit, but edit the commit message # e, edit = use commit, but stop for amending # s, squash = use commit, but meld into previous commit # f, fixup = like "squash", but discard this commit's log message # x, exec = run command (the rest of the line) using shell # b, break = stop here (continue rebase later with 'git rebase --continue') # d, drop = remove commit # l, label = label current HEAD with a name # t, reset = reset HEAD to a label # m, merge [-C | -c ] [# ] # . create a merge commit using the original merge commit's # . message (or the oneline, if no original merge commit was # . specified). Use -c to reword the commit message. # # These lines can be re-ordered; they are executed from top to bottom. # # If you remove a line here THAT COMMIT WILL BE LOST. # # However, if you remove everything, the rebase will be aborted. # # Note that empty commits are commented out
Если вместо «pick» или «edit» вы укажете «squash», Git применит изменения из текущего и предыдущего коммитов и предложит вам объединить их сообщения. Таким образом, если вы хотите из этих трёх коммитов сделать один, вы должны изменить скрипт следующим образом:
pick f7f3f6d Change my name a bit squash 310154e Update README formatting and add blame squash a5f4a0d Add cat-file
Когда вы сохраните скрипт и выйдете из редактора, Git применит изменения всех трёх коммитов и затем вернёт вас обратно в редактор, чтобы вы могли объединить сообщения коммитов:
# This is a combination of 3 commits. # The first commit's message is: Change my name a bit # This is the 2nd commit message: Update README formatting and add blame # This is the 3rd commit message: Add cat-file
После сохранения сообщения, вы получите один коммит, содержащий изменения всех трёх коммитов, существовавших ранее.
Разбиение коммита
Разбиение коммита отменяет его и позволяет затем по частям индексировать и фиксировать изменения, создавая таким образом столько коммитов, сколько вам нужно. Например, предположим, что вы хотите разбить средний коммит на два. Вместо одного коммита «Update README formatting and add blame» вы хотите получить два разных: первый — «Update README formatting», и второй — «Add blame». Вы можете добиться этого, изменив в скрипте rebase -i инструкцию для разбиваемого коммита на «edit»:
pick f7f3f6d Change my name a bit edit 310154e Update README formatting and add blame pick a5f4a0d Add cat-file
Затем, когда скрипт вернёт вас в командную строку, вам нужно будет отменить индексацию изменений этого коммита, и создать несколько коммитов на основе этих изменений. Когда вы сохраните скрипт и выйдете из редактора, Git переместится на родителя первого коммита в вашем списке, применит первый коммит ( f7f3f6d ), применит второй ( 310154e ), и вернёт вас в консоль. Здесь вы можете отменить коммит с помощью команды git reset HEAD^ , которая, фактически, отменит этот коммит и удалит из индекса изменённые файлы. Теперь вы можете добавлять в индекс и фиксировать файлы, пока не создадите требуемые коммиты, а после этого выполнить команду git rebase —continue :
$ git reset HEAD^ $ git add README $ git commit -m 'Update README formatting' $ git add lib/simplegit.rb $ git commit -m 'Add blame' $ git rebase --continue
Git применит последний коммит ( a5f4a0d ) из скрипта, и ваша история примет следующий вид:
$ git log -4 --pretty=format:"%h %s" 1c002dd Add cat-file 9b29157 Add blame 35cfb2b Update README formatting f7f3f6d Change my name a bit
И снова, при этом изменились SHA-1 хеши всех коммитов в вашем списке, поэтому убедитесь, что ни один коммит из этого списка ранее не был отправлен в общий репозиторий. Обратите внимание, что последний коммит в списке ( f7f3f6d ) не изменился. Несмотря на то, что коммит был в списке перебазирования, он был отмечен как «pick» и применён до применения перебазирования, поэтому Git оставил его нетронутым.
Удаление коммита
Если вы хотите избавиться от какого-либо коммита, то удалить его можно во время интерактивного перебазирования rebase -i . Напишите слово «drop» перед коммитом, который хотите удалить, или просто удалите его из списка:
pick 461cb2a This commit is OK drop 5aecc10 This commit is broken
Из-за того, как Git создаёт объекты коммитов, удаление или изменение коммита влечёт за собой перезапись всех последующих коммитов. Чем дальше вы вернётесь в историю ваших коммитов, тем больше коммитов потребуется переделать. Это может вызвать множество конфликтов слияния, особенно если у вас много последующих коммитов, которые зависят от удалённого.
Если во время подобного перебазирования вы поняли, что это была не очень хорошая идея, то всегда можно остановиться. Просто выполните команду git rebase —abort и ваш репозиторий вернётся в то состояние, в котором он был до начала перебазирования.
Если вы завершили перебазирование, а затем решили, что полученный результат это не то, что вам нужно — воспользуйтесь командой git reflog , чтобы восстановить предыдущую версию вашей ветки. Дополнительную информацию по команде reflog можно найти в разделе Восстановление данных главы 10.
Примечание
Дрю Дево создал практическое руководство с упражнениями по использованию git rebase . Найти его можно здесь: https://git-rebase.io/
Продвинутый инструмент: filter-branch
Существует ещё один способ переписывания истории, который вы можете использовать при необходимости изменить большое количество коммитов каким-то программируемым способом — например, изменить глобально ваш адрес электронной почты или удалить файл из всех коммитов. Для этого существует команда filter-branch , и она может изменять большие периоды вашей истории, поэтому вы, возможно, не должны её использовать кроме тех случаев, когда ваш проект ещё не стал публичным и другие люди ещё не имеют наработок, основанных на коммитах, которые вы собираетесь изменить. Однако, эта команда может быть очень полезной. Далее вы ознакомитесь с несколькими обычными вариантами использованиями этой команды, таким образом, вы сможете получить представление о том, на что она способна.
Команда git filter-branch таит в себе много подводных камней и более не является рекомендуемым способом изменения истории. Вместо этого, рассмотрите возможность использования Python скрипта git-filter-repo , который лучше подходит для большинства ситуаций, в которых вы обычно используете filter-branch . С документаций и исходным кодом скрипта можно ознакомиться здесь: https://github.com/newren/git-filter-repo.
Удаление файла из каждого коммита
Такое случается довольно часто. Кто-нибудь случайно зафиксировал огромный бинарный файл, неосмотрительно выполнив git add . , и вы хотите отовсюду его удалить. Возможно, вы случайно зафиксировали файл, содержащий пароль, а теперь хотите сделать ваш проект общедоступным. В общем, утилиту filter-branch вы, вероятно, захотите использовать, чтобы привести к нужному виду всю вашу историю. Для удаления файла passwords.txt из всей вашей истории вы можете использовать опцию —tree-filter команды filter-branch :
$ git filter-branch --tree-filter 'rm -f passwords.txt' HEAD Rewrite 6b9b3cf04e7c5686a9cb838c3f36a8cb6a0fc2bd (21/21) Ref 'refs/heads/master' was rewritten
Опция —tree-filter выполняет указанную команду после переключения на каждый коммит и затем повторно фиксирует результаты. В данном примере, вы удаляете файл passwords.txt из каждого снимка вне зависимости от того, существует он или нет. Если вы хотите удалить все случайно зафиксированные резервные копии файлов, созданные текстовым редактором, то вы можете выполнить нечто подобное git filter-branch —tree-filter ‘rm -f *~’ HEAD .
Вы можете посмотреть, как Git изменит деревья и коммиты, а затем уже переместить указатель ветки. Как правило, хорошим подходом будет выполнение всех этих действий в тестовой ветке и, после проверки полученных результатов, установка на неё указателя основной ветки. Для выполнения filter-branch на всех ваших ветках, вы можете передать команде опцию —all .
Установка подкаталога как корневого каталога проекта
Предположим, вы выполнили импорт из другой системы контроля версий и получили в результате подкаталоги, которые не имеют никакого смысла ( trunk , tags и так далее). Если вы хотите сделать подкаталог trunk корневым для каждого коммита, команда filter-branch может помочь вам в этом:
$ git filter-branch --subdirectory-filter trunk HEAD Rewrite 856f0bf61e41a27326cdae8f09fe708d679f596f (12/12) Ref 'refs/heads/master' was rewritten
Теперь вашим новым корневым каталогом проекта будет являться подкаталог trunk . Git также автоматически удалит коммиты, которые не затрагивали этот подкаталог.
Глобальное изменение адреса электронной почты
Ещё один типичный случай возникает, когда вы забыли выполнить git config для настройки своего имени и адреса электронной почты перед началом работы, или, возможно, хотите открыть исходные коды вашего рабочего проекта и изменить везде адрес вашей рабочей электронной почты на персональный. В любом случае вы можете изменить адрес электронный почты сразу в нескольких коммитах с помощью команды filter-branch . Вы должны быть осторожны, чтобы изменить только свои адреса электронной почты, для этого используйте опцию —commit-filter :
$ git filter-branch --commit-filter ' if [ "$GIT_AUTHOR_EMAIL" = "schacon@localhost" ]; then GIT_AUTHOR_NAME="Scott Chacon"; GIT_AUTHOR_EMAIL="schacon@example.com"; git commit-tree "$@"; else git commit-tree "$@"; fi' HEAD
Эта команда пройдёт по всем коммитам и установит в них ваш новый адрес. Так как коммиты содержат значения SHA-1-хешей их родителей, эта команда изменяет хеш SHA-1 каждого коммита в вашей истории, а не только тех, которые соответствовали адресам электронной почты.