Подтвердить что ты не робот

Программная замена последних двух коммитов

Я знаю, как поменять местами последние две транзакции, используя git rebase в интерактивном режиме (git rebase -i HEAD~2, ddjp:x в Vim), но я хотел бы сделать это программно с помощью обертки script, поскольку это то, делая относительно часто.

Чтобы быть более конкретным, я хочу переписать историю из

A---B---C---D HEAD

к

A---B---D---C HEAD

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

4b9b3361

Ответ 1

Это должно сделать это:

git tag old
git reset --hard HEAD~2
git cherry-pick old
git cherry-pick old~1
git tag -d old

Сначала вы помечаете место, где вы находитесь как old, затем верните две коммиты, git cherry-pick совершает в другом порядке и удаляет тег old.

Ответ 2

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

ВАЖНО

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

Отказ публичной службы в стороне...

Шаг 1

A---B---C---D (master, HEAD, ORIG_HEAD)

git rebase --quiet --onto HEAD~2 HEAD~1 HEAD

Это занимает то, что находится в HEAD, которое не находится в HEAD ~ 1, и применяет его к HEAD ~ 2. После выполнения этой rebase у вас будет эта история. Имейте в виду, что git-rebase --onto превратит вас в безголовое состояние. (У меня там --quiet, так что последняя команда не плюет на экран текст на стене).

A---B---C---D (master, ORIG_HEAD)
     \
      D'  (HEAD)

Теперь нам нужно получить C, примененный после D', для этого мы можем использовать git-cherry-pick. Когда выполняется git-rebase --onto, как было указано выше, исходная история фиксации перед сохранением сохраняется в ORIG_HEAD, потому что git не изменит ее, пока вы не выполните какое-либо другое действие. Это полезно, если вы испортите rebase, но мы будем использовать его здесь для вишневого выбора.

Шаг 2

git cherry-pick ORIG_HEAD~1

A---B---C---D (master, ORIG_HEAD)
     \
      D'---C'  (HEAD)

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

Простейшим способом сделать вручную будет следующее:

git log -1    ###copy the the SHA1
git checkout master
git reset --hard <SHA1>

Однако все дело в автоматизации, и есть способы сделать это менее очевидным, но требуют меньше команд.

Шаг 3

git update-ref refs/heads/master $(git rev-parse HEAD)

Используя git-rev-parse, я получаю только фиксацию HEAD и ничего больше. Применяя это к git -update-ref, я могу "reset" мастер-ветку, не проверяя ее сначала. Теперь, когда мастер установлен правильно, я могу идти вперед и мастер проверки (точка обновления-ref заключается в уменьшении количества шагов для возможного псевдонима /bash script).

Шаг 4

git checkout --quiet master

Снова, я передаю --quiet, чтобы уменьшить количество текста, сбрасываемого на экране после каждой команды.

Bash Script

Если вы хотите сделать это как bash script, вы можете еще больше автоматизировать весь процесс и заставить его работать динамически на любой ветки, которую вы хотите, а не только на мастере.

#!/bin/bash
branch=$(git name-rev --name-only HEAD)
git rebase --onto HEAD~2 HEAD~1 HEAD
git cherry-pick ORIG_HEAD~1
git update-ref refs/heads/$branch $(git rev-parse HEAD)
git checkout --quiet $branch

Git Псевдоним

Тем не менее, это так же легко сбрасывается в псевдоним git, не создавая фактически файл для bash script. Создание файла и настройка псевдонима не являются какими-либо средствами, но это еще одна вещь, которую нужно понять, и многие люди этого не делают. Здесь простой псевдоним git, выполните эту команду один раз..

git config --global alias.flip-last "!branch=$(git name-rev --name-only HEAD); git rebase --quiet --onto HEAD~2 HEAD~1 HEAD; git cherry-pick ORIG_HEAD~1; git update-ref refs/heads/$branch $(git rev-parse HEAD); git checkout --quiet $branch"

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

git flip-last

GitHub Gist

Это был интересный маленький script, чтобы написать, я бросил его на github gist. Не стесняйтесь разветвлять его, вносить изменения, ставить его, что угодно.

https://gist.github.com/eddiemoya/5456992

Ответ 3

Для справки, это script, в результате я использовал:

. "$(git --exec-path)/git-sh-setup"
require_clean_work_tree swap2

if [ -e $GIT_DIR/sequencer ]; then
    die "Cherry-pick already in progress; swap aborted"
fi

HEAD=`git rev-list --max-count=1 HEAD`
git reset --hard HEAD~2
if git cherry-pick $HEAD $HEAD^; then
    echo "Successfully swapped top two commits."
else
    git cherry-pick --abort
    git reset --hard $HEAD
    die "Failed to swap top two commits."
fi