Разница между merge и rebase в git

Ни rebase, ни merge при pull сами по себе не “стирают” наработки — в обоих случаях ваши коммиты остаются в репозитории (в истории ветки или в виде старых SHAs, пока живёт reflog).

  • pull.rebase false (merge): git pull сделает merge commit, ваши и чужие коммиты не переписываются, история ветки просто срастается веткой. Визуально “безопаснее” для головы: вы явно видите сходящиеся линии.
  • pull.rebase true (rebase): ваши локальные коммиты копируются поверх удалённых (новые хеши). Содержимое коммитов обычно то же, но “старые” коммиты после успешного rebase могут остаться только в git reflog — это нормально, оттуда при необходимости восстанавливают (git reset --hard к нужному ref из reflog), если вдруг что-то пошло не так.

Если главное — “не потерять наработки”:

  1. Перед рискованными шагами — коммит (или git stash) и по желанию отдельная ветка (git branch backup-tgisn-46) — копия указателя, дешёвый бэкэп.
  2. Не путать с потерями команды вроде git reset --hard, force push без нужды, git clean -fd.
  3. После rebase/merge пару дней не трогать git gc агрессивноreflog хранит запасной выход.

Практично для “не терять, не усложнять историю сильно”: git config pull.rebase false — при расхождении вы всегда получите merge, без перезаписи локальных коммитов. Если вам уже зашёл вариант rebase (как в прошлом pull --rebase) — можно оставить pull.rebase true, просто не push --force на общие ветки без договорённости.

Итог: потеря наработок от выбора стратегии pull не идёт; риск — в других командах и в force push; для спокойствия — коммит + ветка-бэкэп перед непонятными операциями.

Вот та же ситуация, что у вас была: один общий коммит C, дальше на origin пять коммитов r1…r5, на локали — один L (мой наработок).

1. До pull (разошлись)

gitGraph
  commit id: "C (общий)"
  branch local
  checkout local
  commit id: "L (мой коммит)"
  checkout main
  commit id: "r1"
  commit id: "r2"
  commit id: "r3"
  commit id: "r4"
  commit id: "r5"

Смысл: кончик local = L, кончик main = r5, общий предок — C. Это не потеряно в обоих вариантах — меняется только, как сшивается история.


2. После git pull без rebase (pull.rebase falsemerge)

gitGraph
  commit id: "C"
  branch local
  checkout local
  commit id: "L"
  checkout main
  commit id: "r1"
  commit id: "r2"
  commit id: "r3"
  commit id: "r4"
  commit id: "r5"
  checkout local
  merge main
  commit id: "M (merge commit)"

Появляется merge commit M с двумя родителями (L и r5). Коммиты L и r1…r5 как объекты остаются; история ветвистая, переписывания нет.


3. После git pull с rebase (pull.rebase truerebase)

gitGraph
  commit id: "C"
  commit id: "r1"
  commit id: "r2"
  commit id: "r3"
  commit id: "r4"
  commit id: "r5"
  commit id: "L′ (тот же патч, новый hash)"

История линейная: ваш наработок лежит сверху удалённых. У L в истории новый коммит L′ (тот же дифф относительно r5, другой hash); старый L как раз ещё какое-то время виден в git reflog, пока не уберёте reflog expire.


Кратко: merge — развилка + merge-узел; rebase — одна линия, ваш коммит “переигран” поверх. Наработок в смысле изменений в файлах в обоих нормальных сценариях не исчезает; отличается форма истории и хеши локального коммита при rebase.