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

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

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

связанные help.github.com/articles/…

Trevor Boyd Smith 21.06.2018 21:59
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
415
2
311 665
11
Перейти к ответу Данный вопрос помечен как решенный

Ответы 11

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

Ответ принят как подходящий

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

Хотя 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 <sha1-of-merge>

# 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, чтобы передать его команде git rebase вместо временное имя ветки.)

Разве git rebase -i не был бы быстрее и таким же простым? $ git rebase -i <sh1-of-merge> Отметьте правильный как "edit" $ git rm somefile.orig $ git commit --amend $ git rebase --continue Однако по какой-то причине у меня все еще есть этот файл где-то в последнюю очередь раз я это сделал. Наверное, чего-то не хватает.

Wernight 18.01.2010 07:04
git rebase -i очень полезен, особенно когда вам нужно выполнить несколько операций rebase-y, но сложно описать точно, когда вы на самом деле не указываете кому-то через плечо и не видите, что они делают с помощью своего редактора. Я использую vim, но не все будут довольны: «ggjcesquash <Esc> jddjp: wq» и такими инструкциями, как «Переместите верхнюю строку после текущей второй строки и измените первое слово в четвертой строке на« редактировать », теперь сохраните и quit "быстро кажутся более сложными, чем действия на самом деле. Обычно вы также выполняете некоторые действия --amend и --continue.
CB Bailey 18.01.2010 21:54

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

user58777 22.03.2010 22:08

Я попытался сделать это, когда понял, что OS X.DS_Store был добавлен, но 1) коммит, в котором он был добавлен, был дублирован с помощью rebase 2) Даже с rebase --onto (уже усложняя рецепт), конечно, более свежие версии был изменен .DS_Store, поэтому filter-branch помогло мне

HiQ CJ 20.04.2010 13:20

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

CB Bailey 20.04.2010 14:42

Я поражен, как я мог делать все это с помощью smartgit и вообще без терминала! Спасибо за рецепт!

cregox 15.09.2011 01:22

@CharlesBailey Я использовал ваши инструкции, чтобы изменить историю локальной ветки (т.е. мои файлы не были получены в результате слияния), и это отлично сработало. К вашему сведению.

duma 25.07.2013 20:49

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

aliteralmind 24.12.2014 15:58

Это было намного сложнее, чем я ожидал. В итоге мне пришлось несколько раз удалять файл (в каждой фиксации), затем git add --all :/, а затем git rebase --continue (поскольку он пролез через каждую фиксацию). Если это не сработало, то git rebase --skip. Затем, наконец, вручную исправьте конфликты <<<<<, которые я обнаружил в файлах. Вроде забрали, но папка .git все равно огромна. Честно говоря, я не совсем уверен, что я сделал, но, похоже, это шаг в правильном направлении. (Я только что понял, что удалил файлы через проводник Windows, а НЕ с git rm. Не уверен в последствиях ...)

aliteralmind 24.12.2014 19:01

Однажды я случайно добавил ОГРОМНЫЙ файл. Я закончил тем, что просто уничтожил свой каталог .git, повторно добавил файлы и запустил репозиторий git заново.

sudo 26.08.2016 11:10

Это довольно круто. Я никогда полностью не понимал, что делает git rebase. Теперь я знаю! Спасибо.

Rolf 09.04.2018 12:42

Если с тех пор вы ничего не зафиксировали, просто 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 означает, что команда не завершится ошибкой, если по какой-то причине filename.orig отсутствует в изменении. Это рекомендуемый способ из раздела «Примеры» в страница руководства git-filter-branch.

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

Спасибо! git filter-branch работал у меня там, где пример перебазирования, приведенный в качестве ответа, не работал: шаги, казалось, сработали, но затем нажатие не удалось. Вытащил, затем успешно нажал, но файл все еще был рядом. Пытался повторить шаги перебазирования, а затем все пошло наперекосяк с конфликтами слияния. Я использовал немного другую команду filter-branch, «Улучшенный метод», приведенный здесь: github.com/guides/completely-remove-a-file-from-all-revision‌ s git filter-branch -f --index-filter 'git update-index --remove filename' <introduction-revision-sha1> ..ГОЛОВА

atomicules 07.01.2010 18:26

Я не уверен, какой из них является методом улучшен. Официальная документация Git для git-filter-branch, похоже, дает первое.

Wernight 18.01.2010 07:20

Ознакомьтесь с zyxware.com/articles/4027/…, я считаю его наиболее полным и прямым решением, которое включает filter-branch.

leontalbot 28.08.2014 06:32

Спасибо! Я бы предложил только одно улучшение: --prune-empty; эта опция для git filter-branch удалит любые коммиты, которые касались только файла, который вы хотите удалить (поскольку после удаления они будут просто пустыми коммитами)

BingsF 18.11.2015 19:56

@atomicules, если вы попытаетесь переместить локальное репо на удаленное, git будет сначала настаивать на извлечении с удаленного, потому что в нем есть изменения, которых у вас нет локально. Вы можете использовать флаг --force для отправки на удаленный компьютер - он полностью удалит файлы оттуда. Но будьте осторожны, убедитесь, что вы не будете принудительно перезаписывать что-либо, кроме файлов.

sol0mka 15.08.2016 20:39

Не забудьте использовать ", а не ' при использовании Windows, иначе вы получите неверно сформулированную ошибку «плохая редакция».

c z 14.03.2017 11:58

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

git reset HEAD file/path

Если файл был добавлен в фиксацию, это даже не удаляет файл из индекса, а просто сбрасывает индекс до версии файла HEAD.

CB Bailey 03.09.2009 08:52

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

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

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

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


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

$ du -sh .git ==> e.g. 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 ==> e.g. 100M

Не знаю почему, поскольку у меня была только ОДНА главная ветка. В любом случае, я наконец-то полностью очистил репозиторий git, вставив его в новый пустой и пустой репозиторий git, например

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

(да!)

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

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

(да! наконец-то прибрался!)

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

Это работало для меня до того, как "Это все еще не сработало для меня?" комментарий

Shadi 12.07.2017 12:41

Отличный ответ, но предложите добавить --prune-empty в команду filter-branch.

ideasman42 22.10.2017 06:41

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

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

Вам следует внимательно выполнить следующие шаги: 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.

Это отличный инструмент: одна команда, она дает очень четкий вывод и предоставляет файл журнала, который соответствует каждой старой фиксации новой. Я не люблю устанавливать Java, но это того стоит.

mikemaccana 07.12.2014 19:27

Это единственное, что у меня сработало, но это похоже на то, что я неправильно работал с git filter-branch. :-)

Kevin LaBranche 16.03.2017 21:33

Просто чтобы добавить это к решению Чарльза Бейли, я просто использовал 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 за указание на это.

ПРИМЕЧАНИЕ

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

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

Хотя все ответы, кажется, относятся к треку ветвей фильтра, этот подчеркивает, как очистить ВСЕ ветки в вашей истории.

Cameron Lowell Palmer 13.01.2019 18:11

Определенно, git filter-branch - это то, что вам нужно.

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

Я также рекомендую удалить все эти ссылки, а затем вызвать сборщик мусора. Вы можете использовать сценарий 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 reflog expire --expire=now --all; git gc --prune=now - очень плохая вещь. Если у вас не заканчивается место на диске, позвольте git garbage собрать эти коммиты через несколько недель.
avmohan 11.12.2019 21:21

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

clarkttfu 12.12.2019 06:10

Другие вопросы по теме