Сквош первых двух коммитов в Git?

С помощью git rebase --interactive <commit> вы можете сквоить любое количество коммитов вместе в один.

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

Есть ли способы его достижения?


Умеренно связанные:

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

Если вам интересно: git: как вставить фиксацию в качестве первой, сдвигая все остальные?

Ответ 1

Обновление Июль 2012 (git 1.7.12+)

Теперь вы можете переустановить все коммиты до корня и выбрать вторую фиксацию Y для раздачи с первым X.

git rebase -i --root master

pick sha1 X
squash sha1 Y
pick sha1 Z
git rebase [-i] --root $tip

Теперь эту команду можно использовать для перезаписи всей истории, ведущей от "$tip" до корневой фиксации.

См. зафиксировать df5df20c1308f936ea542c86df1e9c6974168472 на GitHub из Chris Webb (arachsys).


Оригинальный ответ (февраль 2009 г.)

Я верю, что вы найдете разные рецепты для этого в вопросе SO " Как объединить первые две записи репозитория git?"

Чарльз Бейли предоставил там подробный ответ, напоминая нам, что фиксация является полным деревом ( не просто отличается от предыдущих состояний).
И здесь старая фиксация ( "первоначальная фиксация" ) и новая фиксация (результат раздавливания) не будут иметь общего предка.
Это означает, что вы не можете "commit --amend" первоначального коммита в новый, а затем переустановите на новый начальный фиксатор историю предыдущего первоначального коммита (много конфликтов)

(последнее предложение больше не верно с git rebase -i --root <aBranch>)

Скорее (с A исходным "первоначальным фиксацией" и B последующее коммит необходимо передать в начальный):

  • Вернитесь к последнему фиксации, который мы хотим сформировать для первоначальной фиксации (отсоединить HEAD):

    git checkout <sha1_for_B>
    
  • Reset указатель ветвления на начальную фиксацию, но оставив индекс и рабочее дерево целыми:

    git reset --soft <sha1_for_A>
    
  • Изменить исходное дерево, используя дерево из 'B':

    git commit --amend
    
  • Временно пометьте эту новую начальную фиксацию (или вы можете помнить новую команду commit sha1 вручную):

    git tag tmp
    
  • Вернитесь к исходной ветке (возьмите мастер для этого примера):

    git checkout master
    
  • Повторить все коммиты после B на новый начальный коммит:

    git rebase --onto tmp <sha1_for_B>
    
  • Удалить временный тег:

    git tag -d tmp
    

Таким образом, "rebase --onto" не вводит конфликты во время слияния, так как он переустанавливает историю, сделанную после последнего фиксации (B), чтобы быть сжатой в начальную (которая была A) до tmp (представляющий сжатый новый начальный коммит): тривиальная перемотка вперед просто сливается.

Это работает для "A-B", но также "A-...-...-...-B" (любое количество коммитов может быть сжато в исходное таким образом)

Ответ 2

Я переработал VonC script, чтобы делать все автоматически и не спрашивать меня ни о чем. Вы даете ему два commit SHA1, и он будет раздавить все между ними в одно сообщение с именем "squashed history":

#!/bin/sh
# Go back to the last commit that we want
# to form the initial commit (detach HEAD)
git checkout $2

# reset the branch pointer to the initial commit (= $1),
# but leaving the index and working tree intact.
git reset --soft $1

# amend the initial tree using the tree from $2
git commit --amend -m "squashed history"

# remember the new commit sha1
TARGET=`git rev-list HEAD --max-count=1`

# go back to the original branch (assume master for this example)
git checkout master

# Replay all the commits after $2 onto the new initial commit
git rebase --onto $TARGET $2

Ответ 3

Для чего это стоит, я избегаю этой проблемы, всегда создавая первую команду "no-op", в которой единственное, что в репозитории - это пустой .gitignore:

https://github.com/DarwinAwardWinner/git-custom-commands/blob/master/bin/git-myinit

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

Ответ 4

Это приведет к второму фиксации в первом:

A-B-C-... -> AB-C-...

git filter-branch --commit-filter '
    if [ "$GIT_COMMIT" = <sha1ofA> ];
    then
        skip_commit "[email protected]";
    else
        git commit-tree "[email protected]";
    fi
' HEAD

Сообщение об ошибке для AB будет взято из B (хотя я бы предпочел бы от A).

Имеет тот же эффект, что и Uwe Kleine-König, но работает и для не начального A.

Ответ 5

Если вы просто хотите выкопать все коммиты в единую начальную фиксацию, просто reset репозиторий и изменить первый фиксатор:

git reset hash-of-first-commit
git add -A
git commit --amend

Git reset оставит рабочее дерево неповрежденным, поэтому все еще есть. Поэтому просто добавьте файлы, используя команды git add, и внесите изменения в первый коммит с этими изменениями. По сравнению с rebase -i вы потеряете возможность объединить комментарии git.

Ответ 6

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

Рассмотрим следующий пример:

a---b---HEAD
 \
  \
   '---d

Скручивание a и b в новую фиксацию "ab" приведет к двум различным деревьям, которые в большинстве случаев нежелательны, поскольку git -merge и git -rebase больше не будет работать через две ветки.

ab---HEAD

a---d

Если вы действительно этого хотите, это можно сделать. Посмотрите git -фильтр-ветвь для мощного (и опасного) инструмента для перезаписи истории.

Ответ 7

Для этого можно использовать ветвь фильтра git. например.

git filter-branch --parent-filter \
'if test $GIT_COMMIT != <sha1ofB>; then cat; fi'

Это приводит к тому, что AB-C отбрасывает журнал фиксации A.

Ответ 8

Вы можете использовать rebase interactive для изменения последних двух коммитов, прежде чем они будут перенесены на удаленный

git rebase HEAD^^ -i

Ответ 9

Существует более простой способ сделать это. Предположим, что вы находитесь на ветке master

Создайте новую сироту, которая удалит всю историю фиксации:

$ git checkout --orphan new_branch

Добавьте свое начальное сообщение об ошибке:

$ git commit -a

Избавьтесь от старой несвязанной ветки мастера:

$ git branch -D master

Переименуйте текущую ветку new_branch в master:

$ git branch -m master