Возвращение серии нажатых сливается и фиксируется в Git (без перезаписи истории)
Контекст
Один из моих товарищей по команде ошибочно подтолкнул некоторые фиксации к нашей основной ветке развития. Мы маленькая, дружная команда. Наш удаленный репозиторий размещен на внутреннем сервере.
Вот вершина нашего журнала фиксации (все эти фиксации уже были нажаты):
$ git log develop -6 --pretty=oneline --abbrev-commit
faada93 Merge branch 'develop' of <our_repo_path>.git
244d174 Support classes again
a97a877 Pruned all unused references (again).
8c29252 Merge branch 'develop' of <our_repo_path>.git
a78b993 Support models & methods - product types & categories
da8b496 Resolved JIRA issue PPF-182
da8b496
является последним фиксатором, который мы хотели сохранить в нашей ветке develop
, поэтому нам нужно было вернуть 5 последних коммитов. Мы создали новую ветку из 8c29252
, чтобы продолжить работу в "ветке функций".
Я пробовал много вещей, руководствуясь этим ответом и этот пост из Linus, и закончил тем, что вы можете увидеть в моей истории терминалов ниже. Но я не уверен, что то, что я закончил, - это "правильный путь". Информация, которую я нашел, была сложной; Я не смог определить "лучшее решение" для этой конкретной проблемы.
Вопрос
Был ли подход, который я выбрал (см. подробности ниже), хороший способ вернуть эти 5 коммитов, не навредив нашей истории? Есть ли более простой или "более правильный" способ выполнить одно и то же?
Кроме всего прочего, я решил создать новую ветку из da8b496
(git checkout -b new-develop da8b496
) и отказаться от нашей текущей ветки develop
, но это просто не понравилось.
Что я закончил делать (подробности)
Во-первых, я создал новую ветвь для commits a78b993
и 8c29252
, потому что эти коммиты содержат работу, которую мы хотим сохранить, и, в конечном счете, сможем вернуться к нашей основной ветке развития.
$ git checkout -b new-feature-brach 8c29252
Затем я начал возвращать оскорбительные коммиты в нашей ветке разработки.
Я попробовал это сначала, но это не сработало (вероятно, потому что некоторые из коммитов являются слияниями):
$ git revert a78b993..HEAD
error: a cherry-pick or revert is already in progress
hint: try "git cherry-pick (--continue | --quit | --abort)"
fatal: revert failed
Итак... Я вручную отменил каждую фиксацию; один за другим:
$ git revert -m 1 faada93
[develop 40965a5] Revert "Merge branch 'develop' of <our_repo_path>.git"
8 files changed, 167 insertions(+), 3 deletions(-)
$ git revert 244d174
[develop 3cebd68] Revert "Support classes again"
45 files changed, 557 insertions(+), 1572 deletions(-)
(list of affected files)
$ git revert a97a877
error: could not revert a97a877... Pruned all unused references (again).
hint: after resolving the conflicts, mark the corrected paths
hint: with 'git add <paths>' or 'git rm <paths>'
hint: and commit the result with 'git commit'
$ git mergetool
Merging:
exampleFile1.cs
exampleFile2.cs
Deleted merge conflict for 'exampleFile1.cs':
{local}: deleted
{remote}: modified file
Use (m)odified or (d)eleted file, or (a)bort? m
Deleted merge conflict for 'exampleFile2.cs':
{local}: deleted
{remote}: modified file
Use (m)odified or (d)eleted file, or (a)bort? m
$ git commit -m "Adding files to be reverted along with the next commit."
[develop 15bc02b] Adding files to be able to revert the next commit in line.
2 files changed, 239 insertions(+)
(list of affected files here)
$ git revert -m 1 8c29252
# On branch develop
# Your branch is ahead of 'origin/develop' by 3 commits.
# (use "git push" to publish your local commits)
#
# Untracked files:
# (use "git add <file>..." to include in what will be committed)
#
# exampleFile1.cs.orig
# exampleFile2.cs.orig
nothing added to commit but untracked files present (use "git add" to track)
$ git revert a78b993
[develop 841e77c] Revert "Support models & methods - product types & categories"
2 files changed, 239 deletions(-)
(list of affected files here)
Зафиксировать журнал после завершения всех ревертов:
$ git log develop -10 --pretty=oneline --abbrev-commit
841e77c Revert "Support models & methods - product types & categories"
15bc02b Adding files to be able to revert the next commit in line.
3cebd68 Revert "Support classes again"
40965a5 Revert "Merge branch 'develop' of <our_repo_path>.git"
faada93 Merge branch 'develop' of <our_repo_path>.git
244d174 Support classes again
a97a877 Pruned all unused references (again).
8c29252 Merge branch 'develop' of <our_repo_path>.git
a78b993 Support models & methods - product types & categories
da8b496 Resolved JIRA issue PPF-182
График после возврата:
$ git log --graph --oneline -8 develop
* 841e77c Revert "Support models & methods - product types & categories"
* 15bc02b Adding files to be able to revert the next commit in line.
* 3cebd68 Revert "Support classes again"
* 40965a5 Revert "Merge branch 'develop' of <our_repo_path>.git"
* faada93 Merge branch 'develop' of <our_repo_path>.git
|\
| * a97a877 Pruned all unused references (again).
| * 8c29252 Merge branch 'develop' of <our_repo_path>.git
| |\
| | * da8b496 Resolved JIRA issue PPF-182
Кажется правильным для меня. Наконец, я удаляю некоторые файлы резервных копий, которые я не хочу сохранять:
$ git clean -fd
(list of affected files here)
Текущее состояние чиста:
$ git status
# On branch develop
# Your branch is ahead of 'origin/develop' by 4 commits.
# (use "git push" to publish your local commits)
#
nothing to commit, working directory clean
И затем я все время отдаю на пульт:
git push origin develop
Ответы
Ответ 1
У вас небольшая совместная команда, поэтому общение не является проблемой. Сделайте историю фиксации так, как она должна была выглядеть:
git branch -f develop dab4896
git branch newfeature 8c29252
git push -f origin develop newfeature
и у вас есть все обновления. Все готово.
Такая ошибка является одной из причин переписывания.
Ответ 2
Несмотря на то, что ваша история изменилась, вы можете создавать ветки, которые позволяют вам вернуться и экспериментировать. Git означает, что вам не нужно говорить "вы должны иметь" . Если вы сходитесь в реальности, вам нравится лучше, тогда идите с ней. В противном случае выбросьте его.
В приведенных ниже примерах создаются новые ветки, которые оставляют все остальное в вашем репозитории.
Альтернатива 1: git revert
Сначала создайте ветвь нуля в точке, где вы начали свое приключение.
$ git checkout -b tmp-revert faada93
Задав диапазон фиксации, git revert
отменит несколько коммитов.
$ git revert da8b496..faada93
Альтернатива 2: git commit-tree
Рассмотрим приведенную ниже диаграмму Git Внутренние - git Объекты, раздел 10.2 во втором издании Pro Git Скот Чакон и Бен Страуб. Верхний фиксатор ( "третий фиксатор" ) имеет хэш SHA1, который начинается с 1a410e
. В контексте этой истории 1a410e^{tree}
разрешится к 3c4e9c
, т.е. Объект дерева сразу же будет зафиксирован третьим.
Рисунок 151 из Pro Git, 2-е изд.
Изучите эту модель, чтобы понять, как git отслеживает контент. Создание нового четвертого коммита, дерево которого идентично второму коммитту (т.е. 0155eb
), добавит новый объект фиксации, который будет делиться или "указывать" на существующее дерево и капли, а не добавлять новые повторяющиеся объекты.
Читайте дальше, чтобы узнать, как выполнить эту низкоуровневую строчку с помощью git commit-tree
.
Начните с создания другой временной ветки для работы.
$ git checkout -b tmp-ctree faada93
В этот момент вы хотите создать новую фиксацию, где ее дерево (т.е. совершенный код) идентично таковому da8b496
, последнему фиксации, который вы хотели сохранить. Это дерево непосредственно адресуется в git: da8b496^{tree}
.
git commit-tree
- это "сантехника", команда низкого уровня в git - против "фарфора". Это может показаться неудобным или незнакомым для использования, но в этом случае он обеспечивает точное управление желаемым результатом.
Создайте новый непривязанный коммит, дерево которого совпадает с деревом da8b496
s, а родительский (-p
) является концом текущей ветки, faada93
в вашем случае. Обратите внимание, что git commit-tree
считывает сообщение фиксации нового фиксации на стандартном входе, которое команда ниже содержит команду echo
.
$ echo Revert back to da8b496 | \
git commit-tree da8b496^{tree} -p $(git rev-parse tmp-ctree)
new-commit-sha1
Выделенная курсивом часть не является частью команды. Он указывает, что git commit-tree
выводит хэш SHA1 только что созданного коммита. Зная новые коммиты SHA1, вы можете переместить ветвь в эту точку, например,
$ git merge new-commit-sha1
В приведенной выше команде замените new-commit-sha1 на выход из git commit-tree
. (Вы можете сделать то же самое git reset --hard new-commit-sha1
, но жесткий reset - это острый инструмент, где лучше избегать случайного использования.)
Вы можете перевернуть все вышеперечисленное в одну составную команду.
$ git merge --ff-only $(echo Revert back to da8b496 | \
git commit-tree da8b496^{tree} -p $(git rev-parse tmp-ctree))
Переключатель --ff-only
на git merge
предназначен для предотвращения сюрпризов. Ваше намерение состоит в том, чтобы новая фиксация была быстрой перемоткой или потомком текущей ветки ветки - на самом деле ее непосредственный ребенок!
Cleanup
Чтобы удалить временные ветки выше, переключитесь на другой и уберите его, мистер Макманус. Ваши другие ветки будут такими же, как вы их оставили.
$ git checkout develop
$ git branch -D tmp-revert tmp-ctree
Оба должны быть одинаковыми, как вы можете проверить с помощью
$ git diff tmp-revert tmp-ctree
Чтобы сохранить его, объедините его в ветвь develop
.
$ git checkout develop
$ git merge --ff-only tmp-ctree
$ git push origin develop
Ответ 3
Могу ли я предположить, что это можно было бы считать дубликатом этого ответа: Сделать текущую ветвь git главной ветвью
Отличное решение Jefromi:
[git branch better_branch <last good commit>]
git checkout better_branch
git merge --strategy=ours master # keep the content of this branch, but record a merge
git checkout master
git merge better_branch # fast-forward master up to the merge
Ответ 4
То, что вы пытаетесь сделать, очень рискованно.
действительно вы можете вернуть и удалить те коммиты, которые вы уже нажали на репо, но если кто-то уже потянул ваши изменения, и у него есть commitId, который вы собираетесь удалить, репо может стать "неустойчивым" и git не сможет обрабатывать pull и push, поскольку вы удалили фиксацию, которая теперь удалена из истории.
Сделайте это (верните и удалите фиксацию) только, и только если никто еще не потянет это сообщение.