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

Ответы:

148

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

git reset --soft HEAD~3
git commit -m 'new commit message'
wilhelmtell
источник
@wilhelmtell: Отлично! Смогу ли я создать псевдоним git, например «mysquash 3 'some message'», чтобы сократить его до одной строки?
Филлип
5
Если это просто количество строк:git reset --soft HEAD~3 && git commit -m "my message"
KingCrunch 01
7
@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в этом случае вы получили интерактивный интерфейс.
Лили Баллард
3
Чтобы прояснить: это не то же самое, что сквош. Сквош также объединит сообщения фиксации. Если вы сделаете мягкий сброс, вы потеряете все сообщения о коммитах. Если вы хотите раздавить, попробуйте stackoverflow.com/a/27697274/974186
Рене Линк
1
@sebnukem - это когда мы пытаемся нажать ветку, а пульт дистанционного управления настроен на отклонение принудительных нажатий.
avmohan
30

Мне лично нравится решение Вильгельмтелла :

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 '----------------'
n8tr
источник
4
Вы также можете поместить его в $PATHnamed, git-squash.shи он автоматически получит псевдоним git squash. Я не изменил ваш ответ на случай, если есть причина использовать create-aiases.shсценарий, о котором я не знаю.
Я в основном использую скрипт create_aliases.command, чтобы даже наши менеджеры и дизайнеры могли легко настроить. Просто дважды щелкните сценарий, и все они настроены (тем более, что у меня есть сценарий установки в нашем репо, и относительный путь известен). Тогда им даже не нужно перезапускать терминал.
n8tr
1
Я пробовал это, и мое сообщение о фиксации читается как счетчик сквоша и не работает, потому что это не целое число.
Минф
1
Решение заключалось в том, чтобы добавить - после /squash.sh \ $ 1 \ $ 2 '
Minthos
1
Идея решения мне нравится, но вышеприведенный комментарий еще не учтен в решении. Между одинарными и двойными кавычками должен быть знак минус.
Physicalattraction
10

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

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

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

гармоничный
источник
2
Это правильный ответ, потому что он подавляет серию коммитов, ведущих к заголовку, и использует сообщение 1-го коммита. Это не интерактивно.
Лэндон Кун
9

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

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

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

Жюльен Вайсберг
источник
К сожалению, у меня это не работает в Windows. Еще получите интерактивный редактор.
abergmeier
3

Используйте следующую команду, чтобы сжать последние 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

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

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

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

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

Джасир
источник
1

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

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

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

git rebase --onto HEAD ~ 4 HEAD ~ master

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

Грег Бэкон
источник
Спасибо, Грег; Под словом «discards» вы имеете в виду, что эти промежуточные коммиты подлежат очистке с помощью git gc?
Филлип
@Phillip Да, промежуточные коммиты становятся мусором, как и старый HEAD, потому что он переписан, чтобы иметь в HEAD~4качестве своего родителя.
Грег Бэкон