Как вы исправляете плохое слияние и воспроизводите свои хорошие коммиты на фиксированное слияние?

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

Можно ли переписать историю изменений таким образом, что filename.orig никогда не добавлялось в repository в первую очередь?

    12 Solutions collect form web for “Как вы исправляете плохое слияние и воспроизводите свои хорошие коммиты на фиксированное слияние?”

    Пожалуйста, не используйте этот рецепт, если ваша ситуация не та, что описана в вопросе. Этот рецепт предназначен для исправления плохого слияния и повторения ваших хороших коммитов на фиксированное слияние.

    Хотя filter-branch будет делать то, что вы хотите, это довольно сложная команда, и я бы, вероятно, решил сделать это с помощью git rebase . Это, вероятно, личное предпочтение. filter-branch может сделать это в одной, немного более сложной команде, тогда как решение для rebase выполняет эквивалентные логические операции по одному шагу за раз.

    Попробуйте следующий рецепт:

     # create and check out a temporary branch at the location of the bad merge git checkout -b tmpfix  # remove the incorrectly added file git rm somefile.orig # commit the amended merge git commit --amend # go back to the master branch git checkout master # replant the master branch onto the corrected merge git rebase tmpfix # delete the temporary branch git branch -d tmpfix 

    (Обратите внимание, что на самом деле вам не нужна временная ветка, вы можете сделать это с помощью «отдельно стоящей HEAD», но вам нужно принять к сведению идентификатор фиксации, сгенерированный с помощью git commit --amend step, чтобы предоставить git rebase вместо использования имени временной ветви.)

    Введение: у вас есть 5 доступных решений

    Оригинальный плакат гласит:

    Я случайно совершил нежелательный файл … в моем репозитории несколько коммитов назад … Я хочу полностью удалить файл из истории репозитория.

    Можно ли переписать историю изменений таким образом, что filename.orig никогда не добавлялось в repository в первую очередь?

    Существует множество способов удалить историю файла полностью из git:

    1. Внесение поправок.
    2. Жесткие сбрасывания (возможно, плюс перебаза).
    3. Неинтерактивная перезагрузка.
    4. Интерактивные перестановки.
    5. Фильтрация ветвей.

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

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


    Решение 1. Внесение изменений в Commits

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

     git rm  git commit --amend --no-edit 

    Решение 2. Жесткий сброс (возможно, плюс ребаза)

    Как и решение №1, если вы просто хотите избавиться от своего предыдущего коммита, у вас также есть возможность просто выполнить жесткий сброс родителя:

     git reset --hard HEAD^ 

    Эта команда будет жестко сбросить вашу ветвь до предыдущего 1- го родительского коммита.

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

     # Create a new branch at the commit you want to amend git checkout -b temp  # Amend the commit git rm  git commit --amend --no-edit # Rebase your previous branch onto this new commit, starting from the old-commit git rebase --preserve-merges --onto temp  master # Verify your changes git diff master@{1} 

    Решение 3: Неинтерактивная ребаза

    Это будет работать, если вы просто хотите полностью удалить фиксацию из истории:

     # Create a new branch at the parent-commit of the commit that you want to remove git branch temp  # Rebase onto the parent-commit, starting from the commit-to-remove git rebase --preserve-merges --onto temp  master # Or use `-p` insteda of the longer `--preserve-merges` git rebase -p --onto temp  master # Verify your changes git diff master@{1} 

    Решение 4: Интерактивные ребазы

    Это решение позволит вам выполнить те же действия, что и решения № 2 и № 3, т. Е. Изменение или удаление завершается еще дальше в истории, чем предыдущая фиксация, поэтому решение, которое вы решите использовать, зависит от вас. Интерактивные перестановки не подходят для восстановления сотен коммитов по соображениям производительности, поэтому я бы использовал неинтерактивные переустановки или решение ветви фильтра (см. Ниже) в таких ситуациях.

    Чтобы начать интерактивную перезагрузку, используйте следующее:

     git rebase --interactive ~ # Or `-i` instead of the longer `--interactive` git rebase -i ~ 

    Это приведет к тому, что git перемотает историю фиксации обратно в родительский элемент коммита, который вы хотите изменить или удалить. Затем он представит вам список команд перемотки в обратном порядке в любом редакторе git, который будет использоваться (по умолчанию это Vim):

     pick 00ddaac Add symlinks for executables pick 03fa071 Set `push.default` to `simple` pick 7668f34 Modify Bash config to use Homebrew recommended PATH pick 475593a Add global .gitignore file for OS X pick 1b7f496 Add alias for Dr Java to Bash config (OS X) 

    Конец, который вы хотите изменить или удалить, будет в верхней части этого списка. Чтобы удалить его, просто удалите его строку в списке. В противном случае замените «pick» на «edit» на 1- й строке, например:

     edit 00ddaac Add symlinks for executables pick 03fa071 Set `push.default` to `simple` 

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

     Stopped at 00ddaacab0a85d9989217dd9fe9e1b317ed069ac... Add symlinks You can amend the commit now, with git commit --amend Once you are satisfied with your changes, run git rebase --continue 

    На этом этапе вы можете удалить файл и изменить фиксацию, а затем продолжить rebase:

     git rm  git commit --amend --no-edit git rebase --continue 

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

     git diff master@{1} 

    Решение 5: Фильтрация ветвей

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

     git filter-branch --index-filter \ 'git rm --cached --ignore-unmatch ' 

    Это удалит из всех коммитов, начиная с корневой фиксации. Если вместо этого вы просто хотите переписать диапазон фиксации HEAD~5..HEAD , то вы можете передать это как дополнительный аргумент для filter-branch , как указано в этом ответе :

     git filter-branch --index-filter \ 'git rm --cached --ignore-unmatch ' HEAD~5..HEAD 

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

     git diff master@{1} 

    Фильтрующая ветка Альтернатива: BFG Repo Cleaner

    Я слышал, что средство BFG Repo Cleaner работает быстрее, чем git filter-branch , поэтому вы можете также проверить это как вариант. Это официально упоминается в документации по фильтру-ветви в качестве жизнеспособной альтернативы:

    git-filter-branch позволяет создавать сложные перезаписываемые сценарии вашей истории Git, но вам, вероятно, не нужна эта гибкость, если вы просто удаляете ненужные данные, такие как большие файлы или пароли. Для этих операций вы можете захотеть рассмотреть BFG Repo-Cleaner , альтернативу JVM-основе для ветви git-фильтра, как правило, по меньшей мере на 10-50 раз быстрее для этих прецедентов и с совершенно разными характеристиками:

    • Любая конкретная версия файла очищается ровно один раз . BFG, в отличие от ветки git-filter, не дает вам возможности обрабатывать файл по-разному в зависимости от того, где и когда оно было совершено в вашей истории. Это ограничение дает основную выгоду производительности BFG и хорошо подходит для задачи очистки плохих данных – вам все равно, где плохие данные, вы просто хотите, чтобы это исчезло .

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

    • Параметры команды гораздо более ограничительны, чем ветка git-filter, и предназначены только для задач удаления нежелательных данных, например: --strip-blobs-bigger-than 1M .

    Дополнительные ресурсы

    1. Pro Git § 6.4 Инструменты Git – история перезаписи .
    2. git-filter-branch (1) Ручная страница .
    3. git-commit (1) Ручная страница .
    4. git-reset (1) Ручная страница .
    5. git-rebase (1) Ручная страница .
    6. BFG Repo Cleaner (см. Также этот ответ от создателя ).

    Если вы ничего не git commit --amend с тех пор, просто git rm файл и git commit --amend .

    Если у вас есть

     git filter-branch \ --index-filter 'git rm --cached --ignore-unmatch path/to/file/filename.orig' merge-point..HEAD 

    пройдут каждое изменение от merge-point до HEAD , удалите filename.orig и перепишите это изменение. Использование --ignore-unmatch означает, что команда не сбой, если по какой-либо причине имя_файла отсутствует. Это рекомендуемый путь из раздела «Примеры» на странице man-ветви git-filter .

    Примечание для пользователей Windows: путь к файлу должен использовать косые черты

    Это лучший способ:
    http://github.com/guides/completely-remove-a-file-from-all-revisions

    Просто не забудьте сначала копировать копии файлов.

    РЕДАКТИРОВАТЬ

    Редакция Neon, к сожалению, была отвергнута во время обзора.
    См. Сообщение Neons ниже, это может содержать полезную информацию!


    Например, чтобы удалить все *.gz файлы, случайно *.gz в repository git:

     $ du -sh .git ==> eg 100M $ git filter-branch --index-filter 'git rm --cached --ignore-unmatch *.gz' HEAD $ git push origin master --force $ rm -rf .git/refs/original/ $ git reflog expire --expire=now --all $ git gc --prune=now $ git gc --aggressive --prune=now 

    Это все еще не работало для меня? (Я сейчас в git версии 1.7.6.1)

     $ du -sh .git ==> eg 100M 

    Не знаю, почему, так как у меня только одна главная ветвь. В любом случае, я, наконец, получил мой git repo, действительно очищенный, нажав на новый пустой и голый repository git, например

     $ git init --bare /path/to/newcleanrepo.git $ git push /path/to/newcleanrepo.git master $ du -sh /path/to/newcleanrepo.git ==> eg 5M 

    (да!)

    Затем я клонирую это в новый каталог и переместил его в папку .git. например

     $ mv .git ../large_dot_git $ git clone /path/to/newcleanrepo.git ../tmpdir $ mv ../tmpdir/.git . $ du -sh .git ==> eg 5M 

    (да, наконец, очистился!)

    После проверки того, что все в порядке, вы можете удалить ../large_dot_git и ../tmpdir (возможно, через пару недель или месяц с этого ../tmpdir , на всякий случай …)

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

    Чтобы сделать это как можно проще, я бы рекомендовал использовать BFG Repo-Cleaner , более быструю и быструю альтернативу git-filter-branch специально разработанной для удаления файлов из истории Git. Одним из способов облегчения вашей жизни является то, что она фактически обрабатывает все ссылки по умолчанию (все tags, ветки и т. Д.), Но это также на 10-50 раз быстрее.

    Вы должны внимательно следить за шагами здесь: http://rtyley.github.com/bfg-repo-cleaner/#usage – но основной бит – это просто: загрузите баннер BFG (требуется Java 6 или выше) и запустите эту команду :

     $ java -jar bfg.jar --delete-files filename.orig my-repo.git 

    Вся ваша история хранилища будет проверена, и любой файл с именем filename.orig (который не находится в вашей последней фиксации ) будет удален. Это значительно проще, чем использовать git-filter-branch чтобы сделать то же самое!

    Полное раскрытие: я являюсь автором BFG Repo-Cleaner.

     You should probably clone your repository first. Remove your file from all branches history: git filter-branch --tree-filter 'rm -f filename.orig' -- --all Remove your file just from the current branch: git filter-branch --tree-filter 'rm -f filename.orig' -- --HEAD Lastly you should run to remove empty commits: git filter-branch -f --prune-empty -- --all 

    Чтобы добавить это к решению Чарльза Бейли, я просто использовал git rebase -i, чтобы удалить ненужные файлы из более ранней фиксации, и это сработало как шарм. Шаги:

     # Pick your commit with 'e' $ git rebase -i # Perform as many removes as necessary $ git rm project/code/file.txt # amend the commit $ git commit --amend # continue with rebase $ git rebase --continue 

    Самый простой способ, который я нашел, был предложен leontalbot (как комментарий), который является публикацией, опубликованной Anoopjohn . Я думаю, что его стоит собственное пространство в качестве ответа:

    (Я преобразовал его в скрипт bash)

     #!/bin/bash if [[ $1 == "" ]]; then echo "Usage: $0 FILE_OR_DIR [remote]"; echo "FILE_OR_DIR: the file or directory you want to remove from history" echo "if 'remote' argument is set, it will also push to remote repository." exit; fi FOLDERNAME_OR_FILENAME=$1; #The important part starts here: ------------------------ git filter-branch -f --index-filter "git rm -rf --cached --ignore-unmatch $FOLDERNAME_OR_FILENAME" -- --all rm -rf .git/refs/original/ git reflog expire --expire=now --all git gc --prune=now git gc --aggressive --prune=now if [[ $2 == "remote" ]]; then git push --all --force fi echo "Done." 

    Все кредиты Annopjohn , а также leontalbot для указания.

    ЗАМЕТКА

    Имейте в виду, что сценарий не включает проверки, поэтому убедитесь, что вы не ошибаетесь и у вас есть резервная копия, если что-то пойдет не так. Это сработало для меня, но это может не сработать в вашей ситуации. ИСПОЛЬЗУЙТЕ ЭТО С ПРЕДОСТЕРЕЖЕНИЕМ (перейдите по ссылке, если хотите узнать, что происходит).

    Определенно, git filter-branch – это путь.

    К сожалению, этого недостаточно, чтобы полностью удалить filename.orig из вашего репо, поскольку на него по-прежнему можно ссылаться tags, записи reflog, пульты и т. Д.

    Я рекомендую удалить все эти ссылки, а затем вызвать сборщик мусора. Вы можете использовать скрипт git forget-blob с этого сайта, чтобы сделать все это за один шаг.

    git forget-blob filename.orig

    Если это последняя фиксация, которую вы хотите очистить, я попытался с git версии 2.14.3 (Apple Git-98):

     touch empty git init git add empty git commit -m init # 92K .git du -hs .git dd if=/dev/random of=./random bs=1m count=5 git add random git commit -m mistake # 5.1M .git du -hs .git git reset --hard HEAD^ git reflog expire --expire=now --all git gc --prune=now # 92K .git du -hs .git 

    Это то, для чего была разработана git filter-branch .

    Вы также можете использовать:

    git reset HEAD file/path

    Interesting Posts

    Возможно ли установить конкретный каталог на сервере Linux на локальную машину Windows?

    Два компьютера с объединенными ресурсами процессора и памяти?

    Способы улучшения полноты файлов для восстановления и сканирования данных?

    Проверка жесткого диска с использованием инструментов dd и md5sum

    Как сохранить групповые ресурсы на Samba с помощью клиентов OSX?

    Как подделать сетевой путь?

    Существуют ли какие-либо драйверы файловой системы Reiser для Windows XP?

    Как изменить иконки панели инструментов Firefox?

    Является ли Windows 10 «активировать режим разработчика» опасным?

    Lost Mac DVD, как я могу получить новую копию диска?

    Запутывание разделов Fedora на новой установке

    Как обмениваться Интернетом в Windows 7 с помощью двух сетевых карт?

    Songbird не запускается из-за GStreamer в Ubuntu 9.10

    Как превратить папку в USB-накопитель / массовое хранилище?

    Как настроить Linux-машину для игнорирования Wi-Fi при подключении через локальную сеть?

    Давайте будем гением компьютера.