Как удалить старую историю после запуска git filter-branch?

Предположим, что у меня есть такое дерево:

... -- a -- b -- c -- d -- ...
             \
              e -- a -- k

и я хочу, чтобы он стал просто

... -- a -- b -- c -- d -- ...

Я знаю, как присоединить название ветки к "e". Я знаю, что то, что я собираюсь сделать, изменит историю, и это плохо. Также, я думаю, мне нужно использовать что-то вроде rebase или filter-branch. Но как именно - я потерян.

Ok. Ситуация следующая: у меня сейчас довольно большое дерево (вот так)

                 s -- p -- r   
                /
a -- b -- c -- d -- e --- g -- w
           \               \
            t -- p -- l     y -- k

но в моей одной из первых коммитов (например, для "b" для ex.) Я добавил двоичные файлы, что делает весь репо очень тяжелым. Поэтому я решил забрать их. Я сделал это с фильтрующей ветвью. И теперь у меня есть две длинные ветки коммитов, идентичные друг другу, начиная со второй фиксации.

                 s -- p -- r   
                /
a -- b -- c -- d -- e --- g -- w
      \    \               \
       \    t -- p -- l     y -- k
        \
         \             s'-- p'-- r'  
          \           /
           b'-- c'-- d'-- e'--- g'-- w'
                 \               \
                  t'-- p'-- l'    y'-- k'

где b '- это фиксация без двоичного файла. Поэтому я не могу слиться. Я не хочу, чтобы все это дерево было в истории дублировано так.

Ответ 1

После импорта репозитория Subversion с многолетней историей я столкнулся с аналогичной проблемой с раздуванием из множества двоичных активов. В git: сокращение импорта Subversion, я описываю обрезку моего репозитория git с 4.5 GiB до 100 MiB.

Предполагая, что вы хотите удалить из всех файлов, удаленных в "Удалить медиафайлы" (6fe87d), вы можете адаптировать подход из моего сообщения в блоге к вашему репо

$ git filter-branch -d /dev/shm/git --index-filter \
  "git rm --cached -f --ignore-unmatch media/Optika.1.3.?.*; \
   git rm --cached -f --ignore-unmatch media/lens.svg; \
   git rm --cached -f --ignore-unmatch media/lens_simulation.swf; \
   git rm --cached -f --ignore-unmatch media/v.html" \
  --tag-name-filter cat --prune-empty -- --all

В вашем реестре github нет тегов, но я включаю фильтр тегов-имен, если у вас есть личные теги.

Документация git filter-branch охватывает параметр --prune-empty.

--prune-empty
Некоторые виды фильтров генерируют пустые коммиты, которые оставляют дерево нетронутым. Этот переключатель позволяет git-filter-branch игнорировать такие коммиты...

Использование этой опции означает, что ваша перезаписанная история не будет содержать команду "Удалить медиафайлы", поскольку она больше не влияет на дерево. Медиа файлы никогда не создаются в новой истории.

В этот момент вы увидите дублирование в своем репозитории из-за другого документированного поведения.

Оригинальные ссылки, если они отличаются от перезаписанных, будут сохранены в пространстве имен refs/original/.

Если вы довольны недавно переписанной историей, удалите резервные копии.

$ git for-each-ref --format="%(refname)" refs/original/ | \
  xargs -n 1 git update-ref -d

Git проявляет бдительность в отношении защиты вашей работы, поэтому даже после того, как все это намеренное переписывание и удаление reflog сохранят старые фиксации. Очистите их последовательностью из двух команд:

$ git reflog expire --verbose --expire=0 --all
$ git gc --prune=0

Теперь ваш локальный репозиторий готов, но вам нужно нажать обновления GitHub. Вы могли бы делать их по одному. Для локальной ветки, скажем, мастера, вы запустите

$ git push -f origin master

Скажите, что у вас больше нет локальной ветки issue5. У вашего клона все еще есть ссылка ref origin/issue5, которая отслеживает, где она находится в вашем репозитории GitHub. Запуск git filter-branch также изменяет все исходные ссылки, поэтому вы можете обновить GitHub без ветки.

$ git push -f origin origin/issue5:issue5

Если все ваши локальные ветки соответствуют их соответствующим коммитам на стороне GitHub (т.е. не разгруженные коммиты), вы можете выполнить массовое обновление.

$ git for-each-ref --format="%(refname)" refs/remotes/origin/ | \
  grep -v 'HEAD$' | perl -pe 's,^refs/remotes/origin/,,' | \
  xargs -n 1 -I '{}' git push -f origin 'refs/remotes/origin/{}:{}'

Вывод первого этапа - это список имен:

$ git for-each-ref --format="%(refname)" refs/remotes/origin/
refs/remotes/origin/HEAD
refs/remotes/origin/issue2
refs/remotes/origin/issue3
refs/remotes/origin/issue5
refs/remotes/origin/master
refs/remotes/origin/section_merge
refs/remotes/origin/side-media-icons
refs/remotes/origin/side-pane-splitter
refs/remotes/origin/side-popup
refs/remotes/origin/v2

Мы не хотим псевдо-ref HEAD и удаляем его с помощью grep -v. В остальном мы используем Perl для удаления префикса refs/remotes/origin/ и для каждого запускаем команду формы

$ git push -f origin refs/remotes/origin/BRANCH:BRANCH

Ответ 2

Попробуйте:

git имя ветки -d

Возможно, вам придется использовать это вместо:

git имя ветки -D

Ответ 3

Вы можете использовать git filter-branch снова, но на этот раз с параметром -parent-filter. При этом вы можете отменить фиксацию, установив ссылки своих родителей на ничего. Я думаю, вы можете использовать опцию -commit-filter для той же цели. В вашем репозитории будет оставлено много разных объектов, поэтому вам нужно сделать git gc --prune = now.

Вот пример того, как -parent-фильтр можно использовать для удаления родителей http://git.661346.n2.nabble.com/purging-unwanted-history-td1507638.html

Ответ 4

Вы можете удалить ветки с помощью git branch -D branch_name и удалить удаленные ветки с помощью git push remote_name :branch_name.

В течение некоторого времени коммиты останутся без ссылок в вашем репозитории (см. git gc doc), но будет использовать только дисковое пространство в случае вы понимаете, что позже вы допустили ошибку.

И поскольку вы удалили удаленные ветки, новый git clone не должен извлекать неподтвержденные коммиты.

Ответ 5

В вашем примере вы можете попробовать git rebase b b'?