Сквош первые два коммита в Git? [дубликат]


этот вопрос уже есть ответ здесь:

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

Это все здорово, если вы не хотите раздавить коммиты в начальный коммит. Это кажется невозможным делать.

есть ли способы этого добиться?


умеренно, связанных с:

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

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

9 443

9 ответов:

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

теперь вы можете перебазировать все коммиты до root и выбрать второй коммит Y быть раздавленным первым X.

git rebase -i --root master

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

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

посмотреть commit df5df20c1308f936ea542c86df1e9c6974168472 на GitHub С Крис Уэбб (arachsys).


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

я считаю, что вы найдете различные рецепты для этого в так вопрос"как я могу объединить первые два коммита в Git-репозиторий?"

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

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

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

  1. вернитесь к последнему коммиту, который мы хотим сформировать начальный коммит (отсоединить голову):

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

    git reset --soft <sha1_for_A>
    
  3. изменить начальное дерево, используя дерево из 'B':

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

    git tag tmp
    
  5. вернитесь к исходной ветви (предположим, мастер для этого примера):

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

    git rebase --onto tmp <sha1_for_B>
    
  7. удалите временный тег:

    git tag -d tmp
    

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

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

я переработал скрипт VonC, чтобы делать все автоматически и не просить меня ни о чем. Вы даете ему два коммита SHA1s, и он раздавит все между ними в один коммит под названием "раздавленная история":

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

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

# amend the initial tree using the tree from 
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  onto the new initial commit
git rebase --onto $TARGET 

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

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

таким образом, никогда нет причин связываться с первым коммитом.

Если вы просто хотите раздавить все коммиты в один, начальный коммит, просто сбросьте репозиторий и измените первый коммит:

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

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

это раздавит второй коммит в первый:

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

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

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

имеет тот же эффект, как ответить Уве Кляйн-Кениг, но работает не начальный, а также.

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

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

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

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

ab---HEAD

a---d

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

вы можете использовать git filter-branch для этого. например,

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

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

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

git rebase HEAD^^ -i

есть более простой способ сделать это. Давайте предположим, что вы находитесь на master филиала

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

$ git checkout --orphan new_branch

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

$ git commit -a

избавьтесь от старой несвязанной главной ветви:

$ git branch -D master

переименовать текущую ветку new_branch до master:

$ git branch -m master