Как я могу сквош мой последний X коммитирует вместе в одну фиксацию с помощью Git?
Используйте git rebase -i <after-this-commit>
и замените "pick" на второй и последующие коммиты "squash" или "fixup", как описано в руководстве.
В этом примере <after-this-commit>
представляет собой либо хэш SHA1, либо относительное местоположение из HEAD текущей ветки, из которой анализируются коммиты для команды rebase. Например, если пользователь хочет просмотреть 5 коммитов из текущего HEAD в прошлом, команда git rebase -i HEAD~5
.
<after-this-commit>
?
Вы можете сделать это довольно легко без git rebase
или git merge --squash
. В этом примере мы будем выдавливать последние 3 коммита.
Если вы хотите написать новое сообщение о фиксации с нуля, этого достаточно:
git reset --soft HEAD~3 &&
git commit
Если вы хотите начать редактирование нового сообщения о фиксации с помощью конкатенации существующих сообщений фиксации (например, похоже на то, что начнется с списка инструкций pick/squash/squash/.../squash git rebase -i
), тогда вам понадобится для извлечения этих сообщений и передачи их на git commit
:
git reset --soft HEAD~3 &&
git commit --edit -m"$(git log --format=%B --reverse HEAD..HEAD@{1})"
Оба этих метода скворуют последние три коммита в один новый коммит таким же образом. Мягкий reset просто переводит HEAD в последнюю фиксацию, которую вы не хотите раздавить. Ни индекс, ни рабочее дерево не касаются мягкого reset, оставляя индекс в нужном состоянии для вашего нового коммита (т.е. Он уже имеет все изменения от коммитов, которые вы собираетесь "выбросить" ).
git rebase --squash-recent
, или даже git commit --amend-many
.
3
, например.
Вы можете использовать git merge --squash
для этого, что немного более элегантно, чем git rebase -i
. Предположим, что вы на хозяине, и вы хотите выкачать последние 12 коммитов в один.
ПРЕДУПРЕЖДЕНИЕ. Сначала убедитесь, что вы выполняете проверку git reset --hard
что git status
чиста (поскольку git reset --hard
будет отбрасывать ступенчатые и неустановленные изменения)
Затем:
# Reset the current branch to the commit just before the last 12:
git reset --hard HEAD~12
# HEAD@{1} is where the branch was just before the previous command.
# This command sets the state of the index to be as it would just
# after a merge from that commit:
git merge --squash HEAD@{1}
# Commit those squashed changes. The commit message will be helpfully
# prepopulated with the commit messages of all the squashed commits:
git commit
Документация для git merge
описывает параметр --squash
более подробно.
Обновление: единственное реальное преимущество этого метода в сравнении с более простым git reset --soft HEAD~12 && git commit
предложенным Крисом Джонсеном в его ответе, заключается в том, что вы получаете сообщение о фиксации, предварительно заполненное каждым сообщением о коммитстве, которое вы раздавите.
git rebase -i
, но не объясняете причину. Ориентировочно, потому что мне кажется, что на самом деле все наоборот, и это взлом; Разве вы не выполняете больше команд, чем необходимо, только для того, чтобы заставить git merge
выполнить одно из действий, специально предназначенных для git rebase
?
git merge --squash
также проще в использовании в скрипте. По сути, причина была в том, что для этого вам вообще не нужна «интерактивность» git rebase -i
.
Я рекомендую избегать git reset
, когда это возможно, особенно для Git -новей. Если вам действительно не нужно автоматизировать процесс, основанный на ряде коммитов, существует менее экзотический способ...
git merge --squash (working branch name)
git commit
Сообщение о фиксации будет предварительно заполнено на основе сквоша.
На основе ответ Криса Джонсена,
Добавьте глобальный псевдоним squash из bash: (или Git Bash в Windows)
git config --global alias.squash '!f(){ git reset --soft HEAD~${1} && git commit --edit -m"$(git log --format=%B --reverse HEAD..HEAD@{1})"; };f'
... или с помощью командной строки Windows:
git config --global alias.squash "!f(){ git reset --soft HEAD~${1} && git commit --edit -m\"$(git log --format=%B --reverse HEAD..HEAD@{1})\"; };f"
Теперь ваш ~/.gitconfig
должен содержать этот псевдоним:
[alias]
squash = "!f(){ git reset --soft HEAD~${1} && git commit --edit -m\"$(git log --format=%B --reverse HEAD..HEAD@{1})\"; };f"
Использование:
git squash N
... который автоматически сквозит вместе, последний N
совершает, включительно.
Примечание. Полученное сообщение фиксации представляет собой комбинацию всех раздавленных коммитов по порядку. Если вы недовольны этим, вы всегда можете git commit --amend
изменить его вручную. (Или отредактируйте псевдоним, чтобы он соответствовал вашим вкусам.)
git squash -m "New summary."
и N
автоматически определяется как число невыдвинутых коммитов.
Благодаря этому удобному сообщению в блоге я обнаружил, что вы можете использовать эту команду для раздачи последних трех коммитов:
git rebase -i HEAD~3
Это удобно, так как оно работает, даже если вы находитесь в местном филиале без информации отслеживания/удаленного репо.
Команда откроет интерактивный редактор переадресации, который затем позволит вам изменить порядок, сквош, слово и т.д. В соответствии с нормальным.
Использование интерактивного редактора переадресации:
В интерактивном редакторе сводки отображаются последние три фиксации. Это ограничение было определено HEAD~3
при запуске команды git rebase -i HEAD~3
.
Последняя фиксация, HEAD
, отображается сначала в строке 1. Строки, начинающиеся с #
- это комментарии/документация.
Отображаемая документация довольно ясна. На любой заданной строке вы можете изменить команду из команды pick
на команду по вашему выбору.
Я предпочитаю использовать команду fixup
как это "сжимает" фиксацию в фиксации в строке выше и отбрасывает сообщение коммита.
Поскольку фиксация в строке 1 - HEAD
, в большинстве случаев вы оставите это как pick
. Вы не можете использовать squash
или fixup
как нет другой фиксации, чтобы раздавить фиксацию.
fixup
pick
в строке 1. Если вы выбрали squash
или fixup
для коммита в строке 1, git покажет сообщение «error: невозможно« исправить »без предыдущего коммита». Затем он даст вам возможность исправить это: «Вы можете исправить это с помощью« git rebase --edit-todo »и затем запустить« git rebase --continue »». или вы можете просто прервать и начать заново: «Или вы можете прервать ребаз с помощью« git rebase --abort ».».
Если вы используете TortoiseGit, вы можете использовать функцию Combine to one commit
:
Show Log
Combine to one commit
из контекстного меню
Эта функция автоматически выполняет все необходимые одиночные шаги git. К несчастью, доступно только для Windows.
Основываясь на в этой статье, я нашел этот метод проще для моего использования.
Моя ветвь "dev" опередила "origin/dev" на 96 коммитов (поэтому эти коммиты еще не были нажаты на удаленный компьютер).
Я хотел пробить эти коммиты в один, прежде чем нажимать изменения. Я предпочитаю reset ветки до состояния "origin/dev" (это оставит все изменения из 96 коммитов неустановленными), а затем зафиксирует изменения сразу:
git reset origin/dev
git add --all
git commit -m 'my commit message'
1) Определить короткий хэш-код фиксации
# git log --pretty=oneline --abbrev-commit
abcd1234 Update to Fix for issue B
cdababcd Fix issue B
deab3412 Fix issue A
....
Здесь даже git log --oneline
также можно использовать для получения короткого хэша.
2) Если вы хотите скворовать (слить) последние два фиксации
# git rebase -i deab3412
3) Это открывает редактор nano
для слияния. И это выглядит как ниже
....
pick cdababcd Fix issue B
pick abcd1234 Update to Fix for issue B
....
4) Переименуйте слово pick
в squash
который присутствует до abcd1234
. После переименования он должен выглядеть следующим образом.
....
pick cdababcd Fix issue B
squash abcd1234 Update to Fix for issue B
....
5) Теперь сохраните и закройте редактор nano
. Нажмите ctrl + o
и нажмите Enter
для сохранения. Затем нажмите ctrl + x
для выхода из редактора.
6) Затем редактор nano
снова открывается для обновления комментариев, при необходимости обновляет его.
7) Теперь его сжатие успешно, вы можете проверить его, проверив журналы.
# git log --pretty=oneline --abbrev-commit
1122abcd Fix issue B
deab3412 Fix issue A
....
8) Теперь нажмите на репо. Примечание, чтобы добавить знак +
перед именем ветки. Это означает принудительный толчок.
# git push origin +master
Примечание. Это основано на использовании git на оболочке ubuntu
. Если вы используете разные Windows
(Windows
или Mac
), то выше команды такие же, кроме редактора. Вы можете получить другой редактор.
Для этого вы можете использовать следующую команду git.
git rebase -i HEAD~n
n (= 4 здесь) - это номер последнего фиксации. Затем у вас есть следующие параметры,
pick 01d1124 Message....
pick 6340aaa Message....
pick ebfd367 Message....
pick 30e0ccb Message....
Обновить, как показано ниже,
p 01d1124 Message....
s 6340aaa Message....
s ebfd367 Message....
s 30e0ccb Message....
Подробнее см. Ссылка
Удачи!
Ответ на аномии хорош, но я чувствовал себя неуверенно в этом, поэтому решил добавить пару скриншотов.
Посмотрите, где вы находитесь, с git log
. Самое главное, найти хеш фиксации первого коммита, который вы не хотите сквош. Таким образом, только:
Выполнить git rebase -i [your hash]
, в моем случае:
$ git rebase -i 2d23ea524936e612fae1ac63c95b705db44d937d
В моем случае я хочу раздавить все на коммит, который был первым во времени. Заказ выполняется с первого до последнего, так что точно так же, как в git log
. В моем случае я хочу:
Если вы выбрали только одну фиксацию и раздавили остальное, вы можете настроить одно сообщение коммита:
Это. Как только вы сохраните этот (:wq
), все готово. Посмотрите на это с git log
.
git log
Если вы находитесь на удаленной ветке (называемой feature-branch
), клонированной из Золотого репозитория (golden_repo_name
), тогда здесь техника для сквоша ваших коммиттов в одну:
Оформить золотое репо
git checkout golden_repo_name
Создайте из него новую ветку (золотое репо) следующим образом
git checkout -b dev-branch
Скомбинировать сквош с вашей локальной ветвью, у которой уже есть
git merge --squash feature-branch
Зафиксируйте свои изменения (это будет единственная фиксация, которая идет в dev-ветке)
git commit -m "My feature complete"
Нажмите ветку в локальный репозиторий
git push origin dev-branch
Это супер-пупер kludgy, но каким-то классным способом, поэтому я просто брошу его в кольцо:
GIT_EDITOR='f() { if [ "$(basename $1)" = "git-rebase-todo" ]; then sed -i "2,\$s/pick/squash/" $1; else vim $1; fi }; f' git rebase -i foo~5 foo
Перевод: предоставить новый "редактор" для git, который, если редактируемое имя файла git-rebase-todo
(приглашение для интерактивной переадресации), изменяет все, кроме первого "выбрать", чтобы "сквош", и в противном случае порождает vim - так что, когда вам будет предложено изменить сжатое сообщение фиксации, вы получите vim. (И, очевидно, я раздавил последние пять коммитов на ветке foo, но вы можете изменить это, как вам нравится.)
Я бы, вероятно, сделал то, что Марк Лонгэр предложил.
Если вы хотите хлюпить каждую фиксацию в одном коммите (например, при первом выпуске проекта в первый раз), попробуйте:
git checkout --orphan <new-branch>
git commit
Что может быть действительно удобно:
Найдите хеш-фиксацию, которую вы хотите сквозировать сверху; скажем, d43e15
Теперь используйте
git reset d43e15
git commit -am 'new commit name'
Я думаю, что самый простой способ сделать это - сделать новую ветку от мастера и выполнить слияние --squash ветки функций.
git checkout master
git checkout -b feature_branch_squashed
git merge --squash feature_branch
Затем у вас есть все изменения, готовые к фиксации.
В филиале вы хотите комбинировать коммиты, запускать:
git rebase -i HEAD~(n number of commits back to review)
Это откроет текстовый редактор, и вы должны переключить "pick" перед каждой фиксацией с помощью "squash", если вы хотите, чтобы эти коммиты были объединены вместе. Например, если вы хотите объединить все коммиты в единицу, "выбрать" - это первая сделанная вами фиксация, а все будущие (помещенные ниже первой) должны быть установлены на "squash". Если вы используете vim, используйте : x в режиме вставки, чтобы сохранить и выйти из редактора.
Затем, чтобы завершить rebase:
git rebase --continue
Подробнее об этом и других способах переписать историю фиксации см. В этом полезном сообщении
--continue
и vim :x
.
git add
правильную конфигурацию в свои файлы, вы используете git rebase --continue
чтобы перейти к следующему git rebase --continue
и начать слияние. :x
есть одна команда , которая сохранит изменения файла при использовании ВИМ см это
git rebase -i HEAD^^
где число ^ равно X
(в этом случае сквош две последние фиксации)
В вопросе может быть неоднозначным, что подразумевается под "последним".
например git log --graph
выводит следующее (упрощенное):
* commit H0
|
* merge
|\
| * commit B0
| |
| * commit B1
| |
* | commit H1
| |
* | commit H2
|/
|
Тогда последними фиксациями по времени являются H0, merge, B0. Чтобы раздавить их, вам придется переустановить свою объединенную ветку на фиксацию H1.
Проблема состоит в том, что H0 содержит H1 и H2 (и, как правило, больше коммит до слияния и после ветвления), а B0 - нет. Таким образом, вы должны управлять изменениями от H0, слияния, H1, H2, B0 по крайней мере.
Можно использовать rebase, но по-разному, а затем в других упомянутых ответах:
rebase -i HEAD~2
Это покажет вам варианты выбора (как упоминалось в других ответах):
pick B1
pick B0
pick H0
Поставьте сквош вместо выбора в H0:
pick B1
pick B0
s H0
После сохранения и возврата rebase будет применяться фиксация по очереди после H1. Это означает, что он попросит вас снова разрешить конфликты (где HEAD сначала будет H1, а затем накапливает фиксации по мере их применения).
После того, как rebase закончит, вы можете выбрать сообщение для раздавленных H0 и B0:
* commit squashed H0 and B0
|
* commit B1
|
* commit H1
|
* commit H2
|
P.S. Если вы просто сделаете несколько reset для BO:
(например, используя reset --mixed
, который более подробно объясняется здесь https://stackoverflow.com/questions/2563632/how-can-i-merge-two-commits-into-one-if-i-already-started-rebase):
git reset --mixed hash_of_commit_B0
git add .
git commit -m 'some commit message'
то вы раздавите в B0 изменения H0, H1, H2 (потеря полностью фиксирует изменения после разветвления и перед слиянием.
Я нахожу, что более общим решением является не указание "N" коммитов, а скорее идентификатор ветки/фиксации, который вы хотите сквозировать поверх. Это менее подвержено ошибкам, чем подсчет коммиттов до определенной фиксации - просто укажите тег напрямую или если вы действительно хотите подсчитать, вы можете указать HEAD ~ N.
В моем рабочем процессе я начинаю ветвь, и моя первая фиксация на этой ветке суммирует цель (то есть, как правило, то, что я буду нажимать как "окончательное" сообщение для этой функции в публичный репозиторий.) Поэтому, когда я все, что я хочу сделать, это git squash master
вернуться к первому сообщению, а затем я готов нажать.
Я использую псевдоним:
squash = !EDITOR="\"_() { sed -n 's/^pick //p' \"\\$1\"; sed -i .tmp '2,\\$s/^pick/f/' \"\\$1\"; }; _\"" git rebase -i
Это приведет к тому, что история будет раздавлена до того, как она это сделает - это дает вам возможность восстановить, захватив старый идентификатор фиксации с консоли, если вы хотите вернуться. (Пользователи Solaris отмечают, что используют опцию GNU sed -i
, пользователи Mac и Linux должны быть в порядке с этим.)
Чтобы сквош последних 10 коммитов в 1 одиночный commit:
git reset --soft HEAD~10 && git commit -m "squashed commit"
Если вы также хотите обновить удаленную ветвь с помощью сжатой фиксации:
git push -f
В дополнение к другим отличным ответам я хотел бы добавить, что git rebase -i
всегда меня путает с порядком фиксации - от более старого до более нового или наоборот? Итак, это мой рабочий процесс:
git rebase -i HEAD~[N]
, где N - количество коммитов, к которым я хочу присоединиться, начиная с самого последнего. Так что git rebase -i HEAD~5
означало бы "squash последние 5 коммитов в новую";Посмотрите на эту суть:
Вам нужно будет ввести, например. git-squash 3
и что он. Последние три коммита объединяются в один с конкатенированными сообщениями.
Сначала я узнаю количество коммитов между моей ветвью функций и текущей ветвью мастера
git checkout master
git rev-list master.. --count
Затем я создаю еще одну ветвь, основанную на ветке my-feature, не затрагивая my-feature
ветвь.
Наконец, я бегу
git checkout my-feature
git checkout -b my-rebased-feature
git checkout master
git checkout my-rebased-feature
git rebase master
git rebase head^x -i
// fixup/pick/rewrite
git push origin my-rebased-feature -f // force, if my-rebased-feature was ever pushed, otherwise no need for -f flag
// make a PR with clean history, delete both my-feature and my-rebased-feature after merge
Надеюсь, это поможет, спасибо.
А как насчет ответа на вопрос, связанный с таким рабочим процессом?
merge --squash
после PR, но команда подумала, что это замедлит процесс.) Я не видел такой рабочий процесс на этой странице. (Это могут быть мои глаза.) Если я правильно понимаю правильную rebase
, для нескольких объединений потребуется несколько разрешений конфликтов. Я даже не хочу об этом думать!
Таким образом, это, похоже, работает для нас.
git pull master
git checkout -b new-branch
git checkout -b new-branch-temp
git checkout new-branch
git merge --squash new-branch-temp
//ставит все изменения в стадииgit commit 'one message to rule them all'
git push
Другой способ сделать это, если у вас есть тонна коммитов, состоит в том, чтобы делать сквош ПОСЛЕ фиксации, такой как git rebase -i <hashbeforeyouwanttosquash>
Это откроет ваш редактор, чтобы выбрать/сквош, как обычно.
Просто добавьте эту функцию bash к вашему bash файла .zshrc.
# Squash last X commits with a Commit message.
# Usage: squash X 'COMMIT_MSG'
# where X= Number of last commits.
# where COMMIT_MSG= New commit msg.
function squash() {
if [ -z "${1}" -o -z "${2}" ]; then
echo "Usage: \`squash X COMMIT_MSG\`"
echo "X= Number of last commits."
echo "COMMIT_MSG= New commit msg."
return 1
fi
git reset --soft HEAD~"$1"
git add . && git ci -m "$2" # With 100 emoji
git push --force
}
Затем просто запустите
squash X 'New Commit Message'
И все готово.
Если вы используете GitUp, выберите фиксацию, которую хотите объединить со своим родителем, и нажмите S. Вы должны сделать это один раз для каждой фиксации, но это намного проще, чем придумать правильное заклинание командной строки. Особенно, если это то, что вы делаете только время от времени.
Эта команда работала для меня -
git rebase -i HEAD~<no. of commits you want to squash>
также вы хотите проверить git log
перед раздачей, чтобы получить фактическое значение no. из коммитов быть раздавленным!
Перейдите на главную ветку и убедитесь, что вы обновлены:
sh
git checkout master && git fetch && git pull
Объедините ветвь вашей функции в основную ветвь локально:
sh
git merge feature_branch
Reset локальная главная ветвь в исходное состояние:
sh
git reset origin/master
Теперь все ваши изменения считаются неустановленными. Вы можете выполнить и передать их в один или несколько коммитов.
sh
git add . --all
git commit