Сквош мой последний X совершает вместе с помощью Git


Как я могу раздавить мои последние X коммиты вместе в один коммит с помощью Git?

29 2453

29 ответов:

использовать git rebase -i <after-this-commit> и замените "pick "на втором и последующих коммитах на" squash "или" fixup", как описано в руководство.

В этом примере <after-this-commit> является либо хэшем SHA1, либо относительным местоположением от головы текущей ветви, из которой коммиты анализируются для команды rebase. Например, если пользователь хочет просмотреть 5 коммитов из текущей головы в прошлом, команда git rebase -i HEAD~5.

вы можете сделать это довольно легко, без 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})"

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

можно использовать git merge --squash для этого, что немного более элегантно, чем git rebase -i. Предположим, вы находитесь на master, и вы хотите раздавить последние 12 коммитов в один.

предупреждение: сначала убедитесь, что вы совершаете свою работу-проверьте это 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

The документация git merge описание в более деталь.


обновление: единственное реальное преимущество этого метода перед более простым git reset --soft HEAD~12 && git commit предложил Крис Джонсен в ответ это то, что вы получаете сообщение фиксации, предварительно заполненное каждым сообщением фиксации, которое вы сжимаете.

Я рекомендую избегать git reset когда это возможно-особенно для Git-для новичков. Если вам действительно не нужно автоматизировать процесс на основе конечно, есть и менее экзотический способ...

  1. поставить должно быть сжато фиксации на рабочей ветви (если они еще не являются таковыми) - использование гифок для этого
  2. Проверьте целевую ветвь (например, 'master')
  3. git merge --squash (working branch name)
  4. git commit

сообщение фиксации будет предварительно заселенный на основе сквоша.

на основе ответ Криса Джонсена,

добавить глобальный псевдоним "сквош" из bash: (или Git Bash на Windows)

git config --global alias.squash '!f(){ git reset --soft HEAD~ && git commit --edit -m"$(git log --format=%B --reverse HEAD..HEAD@{1})"; };f'

... или с помощью командной строки Windows:

git config --global alias.squash "!f(){ git reset --soft HEAD~ && git commit --edit -m\"$(git log --format=%B --reverse HEAD..HEAD@{1})\"; };f"


Ваш ~/.gitconfig теперь должен содержать этот псевдоним:

[alias]
    squash = "!f(){ git reset --soft HEAD~ && git commit --edit -m\"$(git log --format=%B --reverse HEAD..HEAD@{1})\"; };f"


Использование:

git squash N

... Который автоматически сжимает вместе последний N совершает включительно.

Примечание: результирующее сообщение фиксации комбинация всех раздавленных коммитов, по порядку. Если вы недовольны этим, вы всегда можете git commit --amend изменить его вручную. (Или измените псевдоним в соответствии со своими вкусами.)

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

git rebase -i HEAD~3

это удобно, как это работает, даже если вы находитесь на локальном филиале без отслеживания информации/удаленного РЕПО.

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


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

интерактивный редактор rebase показывает последние три коммита. Это ограничение было определено с помощью HEAD~3 при выполнении команды git rebase -i HEAD~3.

самый последний коммит, HEAD, отображается сначала в строке 1. Строки, начинающиеся с # комментарии / документация.

документация отображается довольно ясно. В любой заданной строке вы можете изменить команду с pick команды по вашему выбору.

I предпочитаю использовать команду fixup поскольку это "сжимает" изменения фиксации в фиксации в строке выше и отбрасывает сообщение фиксации.

как фиксация в строке 1 HEAD в большинстве случаев вы бы оставить это как pick. Вы не можете использовать squash или fixup поскольку нет другого коммита, чтобы раздавить коммит.

interactive rebase editor

Если вы используете TortoiseGit, вы можете использовать функцию Combine to one commit:

  1. откройте контекстное меню TortoiseGit
  2. выберите Show Log
  3. отметьте соответствующие изменения в журнале
  4. выберите Combine to one commit в контекстном меню

Combine commits

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

на основе в этой статье Я нашел этот метод проще для моего usecase.

моя ветвь " dev "опережала" origin/dev " на 96 коммитов (так что эти коммиты еще не были перенесены на пульт).

Я хотел раздавить эти коммиты в один, прежде чем нажимать на изменение. Я предпочитаю сбросить ветку в состояние "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 или 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....

для получения дополнительной информации нажмите на ссылке

удачи!!

если вы находитесь в удаленной ветке(называется feature-branch) клонированный из золотого хранилища (golden_repo_name), то вот метод, чтобы раздавить ваши коммиты в один:

  1. оформить золотой РЕПО

    git checkout golden_repo_name
    
  2. создайте из него новую ветку (золотое РЕПО) следующим образом

    git checkout -b dev-branch
    
  3. сквош слияние с вашей локальной ветви, что вы уже

    git merge --squash feature-branch
    
  4. зафиксируйте свои изменения (это будет единственный коммит, который идет в dev-ветвь)

    git commit -m "My feature complete"
    
  5. переместите ветку в локальный репозиторий

    git push origin dev-branch
    

аномии отвечают - это хорошо, но я чувствовала себя неуверенно об этом, поэтому я решил добавить пару скриншотов.

Шаг 0: git log

смотрите, где вы находитесь с git log. Самое главное, найти хэш коммита первого коммита вы не хочу раздавить. Так что только то:

enter image description here

Шаг 1: git rebase

выполнить git rebase -i [your hash] в моей дело:

$ git rebase -i 2d23ea524936e612fae1ac63c95b705db44d937d

Шаг 2: Выберите / сквош, что вы хотите

в моем случае, я хочу раздавить все на фиксации, которая была первой во времени. На заказ от первого до последнего, так как в git log. В моем случае, я хочу:

enter image description here

Шаг 3: отрегулируйте сообщения

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

enter image description here

вот и все. После того как вы сохраните (:wq), ты молодец. Взгляните на него с git log.

это супер-пупер kludgy, но в какой-то крутой способ, поэтому я просто брошу его в кольцо:

GIT_EDITOR='f() { if [ "$(basename )" = "git-rebase-todo" ]; then sed -i "2,$s/pick/squash/" ; else vim ; fi }; f' git rebase -i foo~5 foo

перевод: предоставьте новый "редактор" для git, который, если имя файла для редактирования git-rebase-todo (интерактивное приглашение rebase) изменяет все, кроме первого "выбора", на "сквош", а в противном случае порождает vim - так что, когда вам будет предложено отредактировать раздавленное сообщение фиксации, вы получите vim. (И, очевидно, я раздавил последние пять коммитов на ветке foo, но вы можете это изменить как вам будет угодно.)

Я бы, наверное, сделал что предложил Марк Лонгэр, хотя.

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

git checkout --orphan <new-branch>
git commit

что может быть очень удобно: Найдите хэш фиксации, который вы хотите раздавить сверху; скажите, что это d43e15 Теперь используйте

git reset d43e15

git commit -am 'new commit name'

Я думаю, что самый простой способ сделать это-сделать новую ветвь от master и сделать слияние --сквош ветви функции.

git checkout master
git checkout -b feature_branch_squashed
git merge --squash feature_branch

тогда у вас есть все изменения готовы совершить.

Я нахожу более общее решение не указывать' N ' коммитов, а скорее ветвь/commit-id, который вы хотите раздавить поверх. Это менее подвержено ошибкам, чем подсчет коммитов до определенного коммита-просто укажите тег напрямую, или если вы действительно хотите подсчитать, вы можете указать HEAD~N.

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

Я использую псевдоним:

squash = !EDITOR="\"_() { sed -n 's/^pick //p' \"\\"; sed -i .tmp '2,\$s/^pick/f/' \"\\"; }; _\"" git rebase -i

это сбросит историю, которая будет раздавлена, прежде чем она это сделает-это дает вам возможность восстановить, захватив старый идентификатор фиксации с консоли, если вы хотите вернуться. (Пользователи Solaris отмечают, что он использует GNU sed -i опция, пользователи Mac и Linux должны быть в порядке с этим.)

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

git log --graph выводит следующие (упрощенный):
* commit H0
|
* merge
|\
| * commit B0
| |
| * commit B1
| | 
* | commit H1
| |
* | commit H2
|/
|

тогда последние коммиты по времени являются H0, merge, B0. Чтобы раздавить их, вам придется перебазировать объединенную ветку на фиксацию H1.

проблема в том, что содержится н0 Н1 и Н2 (и, как правило, более совершает до слияния и после разветвления), а В0-нет. Так что вы должны управлять изменениями от х0, слияния, Н1, Н2, В0 в наименьший.

можно использовать rebase, но по-разному, а затем в других упомянутых ответах:

rebase -i HEAD~2

это покажет вам варианты выбора (как уже упоминалось в других ответов):

pick B1
pick B0
pick H0

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

pick B1
pick B0
s H0

после сохранения и выхода rebase будет применять коммиты по очереди после H1. Это означает, что он попросит вас снова разрешить конфликты (где HEAD сначала будет H1, а затем накапливает коммиты как они применяются).

после завершения перебазирования вы можете выбрать сообщение для squashed H0 и B0:

* commit squashed H0 and B0
|
* commit B1
| 
* commit H1
|
* commit H2
|

P. S. Если вы просто сделать некоторые сброса БО: (например, с помощью reset --mixed это объясняется более подробно здесь https://stackoverflow.com/a/18690845/2405850):

git reset --mixed hash_of_commit_B0
git add .
git commit -m 'some commit message'

затем вы сквош в B0 изменения H0, H1, H2 (потеря полностью фиксирует изменения после ветвления и перед слиянием.

git rebase -i HEAD^^

где число ^ ' s равно X

(в этом случае сквош двух последних коммитов)

взгляните на эту суть:

Gist-Easy git-squash

вам нужно будет ввести, например,git-squash 3 и это все. Последние три коммита слились в один, а их сообщения были объединены.

В дополнение к другим отличным ответам, я хотел бы добавить, как git rebase -i всегда путает меня с порядком фиксации-от старого к новому или наоборот? Так что это мой рабочий процесс:

  1. git rebase -i HEAD~[N], где N-количество коммитов, к которым я хочу присоединиться,начиная с последней. Так что git rebase -i HEAD~5 будет означать "сквош последние 5 коммитов в новый";
  2. редактор всплывает, показывая список коммитов, которые я хочу объединить. Теперь они отображаются в обратный порядок: более старая фиксация находится сверху. Отметьте как "сквош" или " s " все коммиты там кроме первого/старшего: он будет использоваться в качестве отправной точки. Сохраните и закройте редактор;
  3. редактор снова появляется с сообщением по умолчанию для новой фиксации: измените его на свои потребности, сохраните и закройте. Сквош завершен!

источники и дополнительные гласит: #1,#2.

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

git reset --soft HEAD~10 && git commit -m "squashed commit"

Если вы также хотите обновить удаленную ветку с помощью squashed commit:

git push -f

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

git rebase -i HEAD~(n number of commits back to review)

это откроет текстовый редактор, и вы должны переключить "pick" перед каждой фиксацией с помощью "squash", если вы хотите, чтобы эти фиксации были объединены вместе. Например, если вы хотите объединить все коммиты в один, "pick" - это первый коммит, который вы сделали, и все будущие (помещенные ниже первого) должны быть установлены в "squash". При использовании vim используйте : x в режиме вставки для сохранения и выйдите из редактора.

затем, чтобы закончить перебазирование:

git rebase --continue

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

как насчет ответа на вопрос, связанный с такой документооборот?

  1. многие локальные коммиты, смешивается с несколькими слияниями из master,
  2. наконец толчок к удаленному,
  3. PR и слияние в master Юра. (Да, было бы проще для разработчика merge --squash после пиара, но команда думала, что это замедлит процесс.)

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

так, это, кажется, работает для нас.

  1. git pull master
  2. git checkout -b new-branch
  3. git checkout -b new-branch-temp
  4. редактировать и совершать много локально, слияние мастер регулярно
  5. git checkout new-branch
  6. git merge --squash new-branch-temp // добавляет все изменения в этап
  7. git commit 'one message to rule them all'
  8. git push
  9. рецензент делает PR и сливается с мастером.

Если вы используете GitUp, выберите фиксацию, которую вы хотите объединить с ее родителем, и нажмите S. Вы должны сделать это один раз для каждого коммита, но это гораздо проще, чем придумать правильное заклинание командной строки. Особенно если это то, что вы делаете только один раз в то время.

просто добавьте эту функцию 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 "" -o -z "" ]; 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~""
    git add . && git ci -m "" # With 100 emoji
    git push --force
}

тогда просто беги

squash X 'New Commit Message'

и вы сделали.

другой способ сделать это, если у вас есть тонна коммитов, - это сделать сквош после фиксации, например git rebase -i <hashbeforeyouwanttosquash>

это откроет ваш редактор, чтобы выбрать / сквош, как обычно.

см.https://git-scm.com/docs/git-rebase#_interactive_mode

сначала я узнаю количество коммитов между моей ветвью функции и текущей главной ветвью по

git checkout master
git rev-list master.. --count

затем я создаю другую ветвь на основе ветви my-feature, keep 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

надеюсь, что это помогает, спасибо.

переключитесь на главную ветку и убедитесь, что вы в курсе:

sh git checkout master && git fetch && git pull

объединить вашу ветку в ветку master на месте:

sh git merge feature_branch

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

sh git reset origin/master

теперь все ваши изменения считаются проиндексированы изменилось. Вы можете совершить их в один или несколько коммитов.

sh git add . --all git commit