Есть ли способ раздавить количество коммитов не в интерактивном режиме?

Я пытаюсь раздавить ряд коммитов — от HEAD до HEAD~3. Есть ли быстрый способ сделать это или мне нужно использовать rebase --interactive?


person Phillip    schedule 01.09.2011    source источник
comment
Аналогичный вопрос: stackoverflow.com/questions/5189560/   -  person koppor    schedule 14.07.2012


Ответы (8)


Убедитесь, что ваше рабочее дерево чистое, затем

git reset --soft HEAD~3
git commit -m 'new commit message'
person wilhelmtell    schedule 01.09.2011
comment
@wilhelmtell: Отлично! Теперь я смогу создать псевдоним git, например. mysquash 3 'какое-то сообщение', чтобы сократить это до одной строки? - person Phillip; 01.09.2011
comment
Если это просто количество строк: git reset --soft HEAD~3 && git commit -m "my message" - person KingCrunch; 02.09.2011
comment
@Phillip: Вы можете встроить функцию оболочки в псевдоним git. git config alias.mysquash '!f(){ git reset --soft HEAD~$1 && git commit ${2:+-m "$2"}; };f'. git mysquash 3 'some message' будет работать, но я также настроил его так, чтобы git musquash 3 полностью опускал флаг -m, поэтому в этом случае вы получите интерактивный git commit пользовательский интерфейс. - person Lily Ballard; 02.09.2011
comment
@KevinBallard, просто чтобы уточнить, я думаю, что под mu вы имели в виду my. - person Acumenus; 22.04.2014
comment
Как автоматизировать HEAD~3, чтобы отразить последний отправленный коммит? Я не хочу вводить 3 и т. д. - person Acumenus; 22.04.2014
comment
Я нашел более понятным использование хэша SHA коммита, который я хотел сжать, вместо HEAD~3 - person JuJoDi; 14.05.2014
comment
этот ответ приводит к ! [отклонено] master -> master (без быстрой перемотки вперед) ошибка... - person sebnukem; 19.03.2015
comment
Просто чтобы было ясно: это не то же самое, что сквош. Сквош также объединит сообщения фиксации. Если вы сделаете программный сброс, вы потеряете все сообщения коммитов. Если вы хотите раздавить, попробуйте stackoverflow.com/a/27697274/974186 - person René Link; 03.08.2015
comment
@sebnukem - это когда мы пытаемся отправить ветку, а пульт настроен так, чтобы отклонять принудительные нажатия. - person avmohan; 17.02.2016
comment
если это коммиты слияния, то потеряется всякое представление о родителях, не так ли? - person George Mauer; 02.08.2019

Мне лично нравится решение wilhelmtell:

git reset --soft HEAD~3
git commit -m 'new commit message'

Однако я сделал псевдоним с некоторой проверкой ошибок, чтобы вы могли сделать это:

git squash 3 'my commit message'

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

Скрипт для сжатия (squash.sh)

#!/bin/bash
#

#get number of commits to squash
squashCount=$1

#get the commit message
shift
commitMsg=$@

#regular expression to verify that squash number is an integer
regex='^[0-9]+$'

echo "---------------------------------"
echo "Will squash $squashCount commits"
echo "Commit message will be '$commitMsg'"

echo "...validating input"
if ! [[ $squashCount =~ $regex ]]
then
    echo "Squash count must be an integer."
elif [ -z "$commitMsg" ]
then
    echo "Invalid commit message.  Make sure string is not empty"
else
    echo "...input looks good"
    echo "...proceeding to squash"
    git reset --soft HEAD~$squashCount
    git commit -m "$commitMsg"
    echo "...done"
fi

echo
exit 0

Затем, чтобы подключить этот скрипт squash.sh к псевдониму git, создайте еще один скрипт для настройки ваших псевдонимов git следующим образом (create_aliases.command или create_aliases.sh ):

#!/bin/sh
echo '-----------------------'
echo 'adding git aliases....'
echo '-----------------------'
echo
git config --global alias.squash "!bash -c 'bash <path to scripts directory>/squash.sh \$1 \$2' -"
#add your other git aliases setup here
#and here
#etc.
echo '------------------------------------'
echo 'here is your global gitconfig file:'
echo '------------------------------------'
more ~/.gitconfig
echo 
echo
echo '----------------'
echo 'end of script...'
echo '----------------'
person n8tr    schedule 03.06.2014
comment
Вы также можете поместить его в $PATH с именем git-squash.sh, и он автоматически получит псевдоним git squash. Я не изменил ваш ответ на тот случай, если есть причина использовать скрипт create-aiases.sh, о которой я не знаю. - person ; 12.06.2014
comment
В основном я использую скрипт create_aliases.command, чтобы даже наши продакт-менеджеры и дизайнеры могли легко его настроить. Просто дважды щелкните скрипт, и все они настроены (тем более, что у меня есть скрипт установки в нашем репозитории, и относительный путь известен). Тогда им даже не нужно перезапускать терминал. - person n8tr; 13.06.2014
comment
Я попробовал это, и он читает мое сообщение о коммите как счетчик сквоша и терпит неудачу, потому что это не целое число. - person Minthos; 12.08.2014
comment
Решение состояло в том, чтобы добавить - после /squash.sh \$1 \$2' - person Minthos; 12.08.2014
comment
Minthos Да, и это уже есть в приведенном выше решении — см. эту строку: git config --global alias.squash !sh -c 'sh ‹путь к каталогу скриптов›/squash.sh \$1 \$2' - person n8tr; 12.08.2014
comment
Идея решения мне нравится, но комментарий выше пока не учтен в решении. Между одинарной и двойной кавычками должен быть знак минус. - person physicalattraction; 27.10.2015
comment
Если предыдущие коммиты уже были отправлены, должен ли я принудительно отправить сюда созданный коммит? - person physicalattraction; 27.10.2015
comment
Благодаря physicalattraction обновлен файл -. Кроме того, да, вам придется принудительно нажать, если вы уже отправили ранее не раздавленные коммиты. Я рекомендую принудительно нажимать только на ваши собственные ветки, а не на основную ветку. - person n8tr; 27.10.2015

Чтобы добавить к ответу wilhelmtell, я считаю удобным выполнить программный сброс до HEAD~2, а затем изменить фиксацию HEAD~3:

git reset --soft HEAD~2
git commit --all --amend --no-edit    

Это объединит все коммиты с коммитом HEAD~3 и будет использовать его сообщение коммита. Обязательно начните с чистого рабочего дерева.

person harmonious    schedule 20.08.2017
comment
Это правильный ответ, потому что он сжимает серию коммитов, ведущих к голове, и использует сообщение 1-го коммита. Он неинтерактивный. - person Landon Kuhn; 29.09.2017
comment
В этом сценарии я думаю, что параметр --all в git commit не нужен, потому что измененные файлы уже помещены после мягкого сброса. Документ для git commit --all - person li ki; 17.06.2021

Я использовал:

EDITOR="sed -i '2,/^$/s/^pick\b/s/'" git rebase -i <ref>

Работал вполне нормально. Только не пытайтесь вести журнал коммитов со строкой, начинающейся с «выбрать» :)

person Julien Wajsberg    schedule 20.09.2013
comment
К сожалению, это не работает для меня в Windows. Еще получите интерактивный редактор. - person abergmeier; 12.11.2014

Используйте следующую команду, чтобы раздавить последние 4 коммита в последнем коммите:

git squash 4

С псевдонимом:

squash = !"f() { NL=$1; GIT_EDITOR=\"sed -i '2,$NL s/pick/squash/;/# This is the 2nd commit message:/,$ {d}'\"; git rebase -i HEAD~$NL; }; f"
sq = !git squash $1
sqpsf = !git squash $1 && git psf 

Из https://github.com/brauliobo/gitconfig/blob/master/configs/.gitconfig

person brauliobo    schedule 01.03.2015
comment
Вы не указали, какая ОС/оболочка используется здесь. Это решение, вероятно, не будет работать для всех других рабочих столов, которые используют люди. - person Rick-777; 11.03.2016
comment
да, вы должны использовать linux/bash|zsh для этого - person brauliobo; 12.03.2016

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

git commit -am "$(git log -1 --skip=1 --pretty=%B | xargs && git reset --soft HEAD~2)"

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

person Jasir    schedule 28.06.2017

Чтобы раздавить все, так как ветвь была разветвлена ​​от мастера:

git reset --soft $(git merge-base --fork-point master) \
  && git commit --verbose --reedit-message=HEAD --reset-author
person Andy    schedule 12.02.2018
comment
--reedit-message=HEAD будет использовать сообщение последней фиксации, которая не является частью сжатия. Скорее всего, это не тот, который вам нужен. Чтобы получить сообщение о первом включенном коммите, либо (1) замените HEAD на хеш фиксации, сообщение о котором вы хотите получить, либо ( 2) перейти к первому включенному коммиту и git commit --amend --reedit-message=HEAD. Вот что делает гармоничный ответ. - person Maëlan; 09.11.2018

Вы можете быть довольно близко с

git rebase --onto HEAD~4 HEAD~ master

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

person Greg Bacon    schedule 01.09.2011
comment
Спасибо, Грег; под «отбрасыванием» вы подразумеваете, что эти промежуточные коммиты подлежат очистке с помощью git gc? - person Phillip; 02.09.2011
comment
@Phillip Да, промежуточные коммиты становятся мусором, как и старый HEAD, потому что он переписан так, чтобы HEAD~4 был его родителем. - person Greg Bacon; 06.09.2011