Git

Git #

1. Git. Для чего нужен Cherry Pick #

Что такое git cherry-pick #

git cherry-pick переносит один или несколько конкретных коммитов из другой ветки в текущую.

При этом Git не объединяет ветки целиком, как при merge, а берёт изменения выбранного коммита и создаёт на их основе новый коммит с новым hash.

Пример #

Есть две ветки:

main:     A---B---C
                  \
feature:           D---E---F

Допустим, коммит E исправляет критическую ошибку, и его нужно перенести в main, но остальные изменения из feature пока не готовы.

git switch main
git cherry-pick <hash-коммита-E>

Результат:

main:     A---B---C---E'
                  \
feature:           D---E---F

E' содержит те же изменения, что и E, но это другой коммит с другим hash.

Для чего используется #

Основные сценарии:

  1. Перенос исправления ошибки

    Ошибка исправлена в одной ветке, но исправление нужно также добавить в main, develop или релизную ветку.

  2. Перенос случайно созданного коммита

    Коммит сделали не в той ветке:

git switch correct-branch
git cherry-pick <commit-hash>

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

  1. Выборочное добавление функциональности

    В ветке находится много коммитов, но нужен только один конкретный.

  2. Backport исправлений

    Например, исправление из новой версии проекта переносится в старую поддерживаемую версию:

main
release/2.0
release/1.0

Как использовать #

Сначала переходят в ветку, куда нужно перенести коммит:

git switch main

Затем выполняют:

git cherry-pick a1b2c3d

Где a1b2c3d — hash нужного коммита.

Посмотреть историю коммитов:

git log --oneline

Пример:

a1b2c3d Fix payment validation
e4f5a6b Add payment endpoint

Перенос нескольких коммитов #

Последовательно указать hash:

git cherry-pick a1b2c3d e4f5a6b 123abcd

Git применит их в указанном порядке.

Можно перенести диапазон коммитов:

git cherry-pick A..D

Это перенесёт коммиты после A и до D включительно. Сам коммит A не войдёт.

Чтобы включить также A:

git cherry-pick A^..D

Что делать при конфликте #

Если изменения конфликтуют, Cherry Pick остановится:

CONFLICT (content): Merge conflict in file.py

Нужно:

  1. Исправить конфликтующие файлы.

  2. Добавить исправленные файлы:

git add .
  1. Продолжить:
git cherry-pick --continue

Отменить весь процесс:

git cherry-pick --abort

Пропустить проблемный коммит:

git cherry-pick --skip

Полезные варианты #

Применить изменения без автоматического создания коммита:

git cherry-pick --no-commit <hash>

Или сокращённо:

git cherry-pick -n <hash>

Это полезно, когда нужно применить несколько коммитов, немного изменить результат и создать один общий коммит:

git cherry-pick -n commit1 commit2
git commit -m "Apply selected fixes"

Отличие от merge #

merge объединяет историю двух веток:

git merge feature

cherry-pick переносит только выбранные коммиты:

git cherry-pick <hash>

Пример:

В feature находится 10 коммитов.

merge:
перенесёт всю ветку.

cherry-pick:
может перенести только 1 нужный коммит.

Отличие от rebase #

rebase обычно переносит последовательность коммитов одной ветки на новую базу и переписывает историю ветки.

cherry-pick берёт конкретно выбранные коммиты и применяет их к текущей ветке.

Главный недостаток #

Cherry Pick создаёт копию коммита с новым hash. Если слишком часто переносить одни и те же изменения между ветками, история может стать запутанной:

feature: E
main:    E'
release: E''

Все три коммита могут содержать одинаковые изменения, но Git считает их разными коммитами.

Поэтому cherry-pick лучше использовать для точечного переноса изменений, а для регулярной синхронизации веток обычно применять merge или rebase.


2. Для чего нужен Rebase #

Что такое git rebase #

git rebase переносит последовательность коммитов одной ветки на другую базу.

Проще говоря: Git берёт ваши коммиты, временно убирает их, перемещает ветку на новую точку и заново применяет эти коммиты поверх неё.

Пример #

Есть ветки:

main:    A---B---C
              \
feature:       D---E

В main появился новый коммит C, а ветка feature была создана от B.

Выполняем:

git switch feature
git rebase main

Результат:

main:    A---B---C
                  \
feature:           D'---E'

Коммиты D и E были созданы заново поверх C, поэтому получили новые hash:

D → D'
E → E'

Для чего нужен Rebase #

1. Обновить свою ветку изменениями из main #

Пока разработчик работает в feature, другие разработчики добавляют коммиты в main.

Чтобы перенести свою работу поверх актуального main:

git switch feature
git fetch origin
git rebase origin/main

После этого ветка будет выглядеть так, будто она была создана от последней версии main.

2. Сделать историю линейной #

Без Rebase при объединении веток может появиться merge-коммит:

A---B---C-------M
     \         /
      D---E---F

После Rebase история может выглядеть линейно:

A---B---C---D'---E'---F'

Такую историю проще читать через:

git log --oneline

3. Подготовить ветку перед Merge Request #

Обычный сценарий:

git switch feature
git fetch origin
git rebase origin/main

После успешного Rebase:

git push --force-with-lease

Это обновляет удалённую ветку с переписанной историей.

Лучше использовать именно:

git push --force-with-lease

А не:

git push --force

--force-with-lease проверяет, не появились ли в удалённой ветке чужие изменения.

Отличие Rebase от Merge #

Merge #

git switch feature
git merge main

Результат:

main:    A---B---C
              \   \
feature:       D---E---M

merge сохраняет реальную структуру истории и обычно создаёт merge-коммит.

Rebase #

git switch feature
git rebase main

Результат:

main:    A---B---C
                  \
feature:           D'---E'

rebase переписывает историю и делает её линейной.

Главное различие #

merge  — объединяет две истории;
rebase — переносит одну историю поверх другой.

Конфликты при Rebase #

Если Git обнаружит конфликт, процесс остановится:

CONFLICT (content): Merge conflict in file.py

Нужно исправить конфликт, затем:

git add file.py
git rebase --continue

Если конфликтующих файлов несколько:

git add .
git rebase --continue

Отменить весь Rebase:

git rebase --abort

Пропустить текущий коммит:

git rebase --skip

Со --skip нужно быть осторожным: изменения пропущенного коммита не попадут в итоговую ветку.

Интерактивный Rebase #

Интерактивный Rebase позволяет редактировать историю коммитов:

git rebase -i HEAD~4

Откроется список последних четырёх коммитов:

pick a1b2c3d Add user model
pick e4f5a6b Fix typo
pick 123abcd Add validation
pick 456defg Fix validation

Доступные действия:

pick    оставить коммит
reword  изменить сообщение коммита
edit    остановиться и изменить коммит
squash  объединить с предыдущим коммитом
fixup   объединить без сохранения сообщения
drop    удалить коммит

Например:

pick a1b2c3d Add user model
fixup e4f5a6b Fix typo
pick 123abcd Add validation
fixup 456defg Fix validation

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

Когда Rebase использовать нельзя #

Не следует делать Rebase общей ветки, которой уже пользуются другие разработчики.

Опасный пример:

git switch main
git rebase another-branch
git push --force

Если другие разработчики уже скачали старую историю main, после такого Rebase их история разойдётся с удалённой.

Основное правило:

Не переписывайте публичную историю.

Обычно безопасно делать Rebase:

  • локальной ветки;

  • личной feature-ветки;

  • ветки, с которой работает только один разработчик;

  • коммитов, которые ещё не были опубликованы.

Rebase текущей ветки на main #

Типичный рабочий сценарий:

git switch feature/payment-validation
git fetch origin
git rebase origin/main

Если появились конфликты:

git add .
git rebase --continue

После завершения:

git push --force-with-lease

Отличие от Cherry Pick #

cherry-pick переносит отдельные выбранные коммиты:

git cherry-pick a1b2c3d

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

git rebase main
cherry-pick — выбрать конкретный коммит;
rebase      — перенести историю ветки на новую основу.

Итог #

git rebase нужен для того, чтобы:

  • обновить feature-ветку относительно актуального main;

  • получить линейную и чистую историю;

  • убрать лишние merge-коммиты;

  • объединить, переименовать или удалить локальные коммиты;

  • подготовить аккуратную историю перед Merge Request.

Главный риск Rebase — он изменяет hash коммитов и переписывает историю. Поэтому его нужно осторожно применять к уже опубликованным веткам.


3. В чем разница между Git rebase и Git merge? #

Главное отличие #

git merge объединяет две ветки, сохраняя их историю.

git rebase переносит коммиты одной ветки поверх другой и переписывает историю.

merge  — объединить истории;
rebase — перенести коммиты на новую основу.

Исходная ситуация #

Допустим, ветка feature была создана от main, после чего обе ветки изменились:

          C---D  feature
         /
A---B---E---F    main

Коммиты C и D находятся в feature, а E и F появились в main.

Что делает merge #

Находясь в feature:

git switch feature
git merge main

Git объединит обе истории:

          C---D------M  feature
         /           /
A---B---E---F--------   main

M — merge-коммит, который имеет двух родителей:

родитель 1: D
родитель 2: F

История показывает, что ветки развивались параллельно, а затем были объединены.

Что делает rebase #

Находясь в feature:

git switch feature
git rebase main

Git временно уберёт коммиты C и D, передвинет feature на F, а затем заново применит изменения:

A---B---E---F---C'---D'  feature

Коммиты C' и D' содержат примерно те же изменения, что C и D, но это уже новые коммиты с новыми hash.

C != C'
D != D'

Сравнение #

Критерийmergerebase
ИсторияСохраняет реальную структуру ветокДелает историю линейной
Hash коммитовОбычно не меняетсяМеняется у перенесённых коммитов
Merge-коммитМожет появитьсяНе создаётся
Переписывание историиНетДа
Безопасность для общих ветокОбычно безопасенМожет быть опасен
Читаемость git logМожет быть сложнееОбычно проще
КонфликтыОбычно решаются один раз при объединенииМогут возникать отдельно на каждом коммите

Merge не всегда создаёт merge-коммит #

Если текущую ветку можно просто передвинуть вперёд, Git выполнит fast-forward.

Было:

A---B---C  main
         \
          D---E  feature

При выполнении:

git switch main
git merge feature

Результат:

A---B---C---D---E  main

Отдельного merge-коммита не будет.

Чтобы принудительно создать его:

git merge --no-ff feature

Разница при конфликтах #

При merge #

git merge main

Git пытается объединить итоговые состояния веток. Обычно конфликт исправляется один раз.

После исправления:

git add .
git commit

Отменить merge:

git merge --abort

При rebase #

git rebase main

Git применяет коммиты по одному. Поэтому конфликт может возникнуть несколько раз:

Применение C' → конфликт
Применение D' → ещё один конфликт

После каждого исправления:

git add .
git rebase --continue

Отменить rebase:

git rebase --abort

Когда использовать merge #

merge предпочтителен, когда:

  • ветка общая и с ней работают несколько разработчиков;

  • важно сохранить реальную историю разработки;

  • нельзя переписывать опубликованные коммиты;

  • нужно безопасно объединить долгоживущие ветки;

  • команда использует merge-коммиты для отображения завершённых задач.

Пример:

git switch main
git merge feature/payment

Когда использовать rebase #

rebase удобен, когда:

  • нужно обновить личную feature-ветку относительно main;

  • хочется получить линейную историю;

  • нужно убрать промежуточные merge-коммиты;

  • коммиты ещё не используются другими разработчиками;

  • нужно подготовить аккуратную ветку перед Merge Request.

Пример:

git switch feature/payment
git fetch origin
git rebase origin/main

После Rebase опубликованную ветку обычно приходится отправлять так:

git push --force-with-lease

Обычный git push может быть отклонён, потому что история ветки изменилась.

Опасный сценарий #

Не следует делать Rebase общей ветки, если другие разработчики уже забрали её коммиты:

git switch shared-feature
git rebase main
git push --force

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

Основное правило:

Не делайте rebase публичной истории,
если не согласовали это с командой.

Типичный рабочий процесс #

Разработчик работает в своей ветке:

git switch feature/payment-validation

Обновляет информацию с сервера:

git fetch origin

Переносит свои коммиты поверх актуального main:

git rebase origin/main

После успешного Rebase:

git push --force-with-lease

Затем Merge Request можно объединить через fast-forward или squash.

Итог #

git merge:
- сохраняет историю ветвления;
- не переписывает существующие коммиты;
- безопаснее для общих веток;
- может создать merge-коммит.

git rebase:
- переносит коммиты на новую основу;
- создаёт новые hash;
- делает историю линейной;
- лучше подходит для личных feature-веток.

Практическое правило:

Своя ветка      → rebase допустим и часто удобен.
Общая ветка     → обычно merge.
Публичная история → rebase только по договорённости.


4. В чем разница между Git fetch и Git pull? #

Главное отличие #

git fetch только загружает новые данные из удалённого репозитория.

git pull загружает данные и сразу пытается применить их к текущей локальной ветке.

git fetch = получить изменения

git pull = получить изменения + объединить их

Что делает git fetch #

git fetch origin

Git загружает:

  • новые коммиты;

  • новые ветки;

  • новые теги;

  • информацию об удалённых ветках.

Но текущая локальная ветка и файлы проекта не изменяются.

Например, было:

origin/main: A---B---C
main:        A---B

После:

git fetch origin

Получится:

origin/main: A---B---C
main:        A---B

Git обновил указатель origin/main, но локальная ветка main осталась на коммите B.

Посмотреть полученные изменения можно так:

git log main..origin/main

Или:

git diff main origin/main

После проверки изменения можно объединить вручную:

git merge origin/main

Либо перенести свои коммиты поверх удалённой ветки:

git rebase origin/main

Что делает git pull #

git pull origin main

По умолчанию это примерно соответствует двум командам:

git fetch origin
git merge origin/main

То есть Git:

  1. Загружает изменения.

  2. Сразу объединяет их с текущей веткой.

Было:

origin/main: A---B---C
main:        A---B

После:

git pull

Результат:

origin/main: A---B---C
main:        A---B---C

Если локальная и удалённая ветки развивались параллельно:

             D  main
            /
A---B---C
        \
         E  origin/main

При обычном git pull Git может создать merge-коммит:

             D---M  main
            /   /
A---B---C---E

git pull --rebase #

Вместо merge можно использовать rebase:

git pull --rebase

Это примерно соответствует:

git fetch
git rebase origin/main

Результат будет линейным:

A---B---C---E---D'

Локальный коммит D будет создан заново поверх актуального origin/main.

Сравнение #

Критерийgit fetchgit pull
Загружает удалённые измененияДаДа
Изменяет текущую локальную веткуНетДа
Изменяет рабочие файлыНетМожет
Может вызвать конфликтыОбычно нетДа
Позволяет сначала проверить измененияДаНет, сразу объединяет
БезопасностьБолее безопасенТребует большей осторожности

Что такое origin/main #

origin/main — локальное представление состояния удалённой ветки main.

Это не сама удалённая ветка, а указатель, который Git обновляет после:

git fetch

или:

git pull

Пример:

main         — ваша локальная ветка
origin/main  — последнее известное состояние удалённой ветки

Когда использовать git fetch #

git fetch предпочтителен, когда нужно:

  • проверить чужие изменения перед объединением;

  • посмотреть новые коммиты;

  • сравнить локальную и удалённую ветки;

  • избежать неожиданного изменения файлов;

  • самостоятельно выбрать между merge и rebase.

Типичный вариант:

git fetch origin
git log main..origin/main
git diff main origin/main
git merge origin/main

Когда использовать git pull #

git pull удобен, когда:

  • нужно быстро обновить локальную ветку;

  • локальных изменений нет;

  • вы понимаете, каким способом Git объединит историю;

  • вероятность конфликтов небольшая.

Например:

git switch main
git pull

Важный момент с незакоммиченными изменениями #

Если в рабочей директории есть незакоммиченные изменения, git fetch обычно безопасен:

git fetch

Он не меняет файлы проекта.

А git pull может завершиться ошибкой или привести к конфликтам, поскольку пытается изменить текущую ветку и рабочие файлы.

Перед pull желательно проверить состояние:

git status

Затем либо создать коммит:

git add .
git commit -m "Save current work"

Либо временно убрать изменения:

git stash
git pull
git stash pop

Практическое правило #

Нужно только узнать, что изменилось:
git fetch

Нужно сразу обновить текущую ветку:
git pull

Нужно обновить ветку с линейной историей:
git pull --rebase

Для более контролируемой работы обычно используют:

git fetch
git rebase origin/main

или:

git fetch
git merge origin/main

Так разработчик сначала получает изменения, а затем самостоятельно решает, как их применить.


5. Как отменить коммит в Git? #

Способ зависит от ситуации #

В Git под «отменить коммит» могут подразумеваться разные действия:

  • удалить последний коммит, сохранив изменения;
  • удалить коммит вместе с изменениями;
  • безопасно отменить уже опубликованный коммит;
  • исправить последний коммит.

1. Коммит ещё не отправлен: сохранить изменения #

Удалить последний коммит, но оставить изменения подготовленными в индексе:

git reset --soft HEAD~1

Было:

A---B---C  HEAD

Станет:

A---B  HEAD

При этом изменения из C останутся в состоянии staged, как после git add.

Подходит, когда нужно заново сформировать коммит:

git reset --soft HEAD~1
git commit -m "Новое сообщение"

2. Удалить коммит, оставить изменения без git add #

git reset HEAD~1

Это аналог:

git reset --mixed HEAD~1

Последний коммит удалится, а изменения останутся в рабочих файлах, но будут убраны из индекса.

После этого git status покажет их как незакоммиченные:

Changes not staged for commit

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

3. Удалить коммит вместе с изменениями #

git reset --hard HEAD~1

Последний коммит удалится вместе со всеми его изменениями.

A---B---C  HEAD
          ↓ reset --hard HEAD~1
A---B      HEAD

Команда опасная:

git reset --hard HEAD~1

Она удаляет незакоммиченные изменения из рабочих файлов. Перед выполнением нужно проверить:

git status

4. Коммит уже отправлен в удалённый репозиторий #

Если коммит уже находится в общей ветке, лучше использовать:

git revert <hash-коммита>

Например:

git revert a1b2c3d

git revert не удаляет старый коммит. Он создаёт новый коммит, который выполняет противоположные изменения.

Было:

A---B---C

После отмены C:

A---B---C---R

Где R отменяет изменения коммита C.

Затем:

git push

Это безопасный способ для main, develop и других общих веток, поскольку существующая история не переписывается.

reset и revert #

КомандаЧто делаетПереписывает историюДля опубликованных коммитов
git reset --softУдаляет коммит, сохраняет staged-измененияДаОбычно нет
git reset --mixedУдаляет коммит, сохраняет изменения в файлахДаОбычно нет
git reset --hardУдаляет коммит и измененияДаОбычно нет
git revertСоздаёт новый отменяющий коммитНетДа

Главное правило:

Коммит только локальный → git reset
Коммит уже опубликован  → git revert

5. Исправить последний коммит #

Когда удалять коммит не требуется, а нужно изменить его сообщение:

git commit --amend -m "Новое сообщение"

Добавить забытый файл в последний коммит:

git add forgotten_file.py
git commit --amend --no-edit

--no-edit оставляет прежнее сообщение коммита.

После --amend hash коммита изменяется, поскольку Git создаёт новый коммит вместо старого.

6. Отменить несколько последних коммитов #

Сохранить изменения из трёх последних коммитов:

git reset --soft HEAD~3

Оставить изменения в рабочих файлах:

git reset HEAD~3

Удалить три коммита вместе с изменениями:

git reset --hard HEAD~3

7. Отменить не последний коммит #

Для опубликованной истории:

git revert <hash>

Найти hash:

git log --oneline

Пример:

e83c121 Add payment endpoint
a1b2c3d Add broken validation
7c8d9e0 Add user model

Отменить средний коммит:

git revert a1b2c3d

Git попытается отменить только изменения этого коммита. Если последующие коммиты зависят от него, могут возникнуть конфликты.

8. Отменить merge-коммит #

Для merge-коммита нужно указать основного родителя:

git revert -m 1 <hash-merge-коммита>

Например:

git revert -m 1 abc1234

-m 1 означает, что первый родитель merge-коммита считается основной линией истории.

Если после reset нужно обновить удалённую ветку #

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

git reset --hard HEAD~1
git push --force-with-lease

Использовать следует именно:

git push --force-with-lease

а не обычный --force, поскольку --force-with-lease не даст случайно перезаписать новые чужие коммиты.

Для общей ветки такой подход обычно не применяется — используется git revert.

Как восстановить случайно удалённый коммит #

После git reset коммит часто можно найти через:

git reflog

Пример:

a1b2c3d HEAD@{0}: reset: moving to HEAD~1
e4f5a6b HEAD@{1}: commit: Add payment validation

Восстановить состояние:

git reset --hard e4f5a6b

Либо создать отдельную ветку на потерянном коммите:

git branch recovery e4f5a6b

Практическая памятка #

# Убрать последний коммит, оставить изменения staged
git reset --soft HEAD~1

# Убрать последний коммит, оставить изменения в файлах
git reset HEAD~1

# Удалить последний коммит и его изменения
git reset --hard HEAD~1

# Безопасно отменить опубликованный коммит
git revert <commit-hash>

# Исправить последний коммит
git commit --amend


6. Как посмотреть историю коммитов в Git? #

Основная команда #

История коммитов текущей ветки выводится командой:

git log

Для каждого коммита Git показывает hash, автора, дату и сообщение коммита. Выход из режима просмотра — клавиша q.

Краткая история #

Чаще всего удобнее использовать сокращённый формат:

git log --oneline

Пример:

a1b2c3d Fix payment validation
e4f5a6b Add payment endpoint
7c8d9e0 Create user model

Каждая строка содержит сокращённый hash и сообщение коммита.

История в виде графа #

Для просмотра веток и merge-коммитов:

git log --oneline --graph --decorate --all

Параметры:

--oneline    компактный формат
--graph      графическое отображение ветвления
--decorate   названия веток и тегов
--all        история всех локально известных веток

Пример:

*   83ad931 (HEAD -> main) Merge branch 'feature'
|\
| * c542adb Add validation
| * 12fa901 Add endpoint
|/
* 711c420 Create project

Последние несколько коммитов #

Показать только последние пять коммитов:

git log -5

Или в кратком формате:

git log -5 --oneline

Опция -<n> ограничивает вывод последними n коммитами.

История конкретной ветки #

git log main

Для удалённой ветки после git fetch:

git log origin/main

Сравнить две ветки:

git log main..feature

Эта команда покажет коммиты, которые есть в feature, но отсутствуют в main.

Обратное сравнение:

git log feature..main

История конкретного файла #

git log -- path/to/file.py

Например:

git log -- app/services/payment.py

Git покажет коммиты, затрагивавшие указанный файл. Разделитель -- отделяет параметры команды от пути.

Чтобы одновременно увидеть изменения файла:

git log -p -- app/services/payment.py

Посмотреть изменения в коммитах #

git log -p

Параметр -p показывает diff каждого коммита.

Для краткой статистики:

git log --stat

Пример:

app/main.py       | 12 +++++++++---
app/settings.py   |  4 ++++
2 files changed, 13 insertions(+), 3 deletions(-)

Найти коммиты конкретного автора #

git log --author="Иван"

Можно использовать имя или часть адреса электронной почты:

git log --author="ivan@example.com"

Фильтрация по автору поддерживается параметром --author.

Найти коммит по сообщению #

git log --grep="payment"

Команда покажет коммиты, в сообщениях которых встречается payment.

Поиск без учёта регистра:

git log --grep="payment" -i

История за определённый период #

Коммиты за последнюю неделю:

git log --since="1 week ago"

До определённой даты:

git log --until="2026-07-01"

Диапазон дат:

git log --since="2026-07-01" --until="2026-07-27"

Для ограничения истории используются параметры --since, --after, --until и --before.

Найти изменение конкретного текста #

Найти коммиты, которые добавили или удалили строку:

git log -S "function_name"

Например:

git log -S "validate_payment"

Опция -S ищет коммиты, изменившие количество вхождений указанной строки.

Чтобы сразу увидеть diff:

git log -S "validate_payment" -p

Посмотреть один конкретный коммит #

git show <commit-hash>

Например:

git show a1b2c3d

Будут показаны данные коммита и внесённые им изменения.

Только информация без diff:

git show --stat a1b2c3d

История перемещений HEAD #

git log показывает историю коммитов, а git reflog — локальную историю перемещений HEAD: reset, rebase, переключения веток и другие операции. Это полезно для поиска случайно потерянного коммита. ( Git)

git reflog

Пример:

a1b2c3d HEAD@{0}: reset: moving to HEAD~1
e4f5a6b HEAD@{1}: commit: Add validation

Практический вариант #

Для повседневной работы наиболее информативна команда:

git log --oneline --graph --decorate --all

Для истории одного файла:

git log --oneline -- path/to/file.py

Для подробного просмотра последнего коммита:

git show HEAD


7. Как переключиться на конкретный коммит в Git? #

Переключение на конкретный коммит #

Сначала найдите hash нужного коммита:

git log --oneline

Пример:

a1b2c3d Fix payment validation
e4f5a6b Add payment endpoint
7c8d9e0 Initial commit

Затем переключитесь на нужный коммит:

git switch --detach a1b2c3d

После этого рабочие файлы будут приведены к состоянию проекта на момент коммита a1b2c3d. Git перейдёт в состояние detached HEAD, то есть HEAD будет указывать непосредственно на коммит, а не на ветку.

Старый вариант через checkout #

Та же операция через более старую команду:

git checkout a1b2c3d

Для явного указания detached-режима:

git checkout --detach a1b2c3d

git checkout по-прежнему поддерживается, но для переключения между ветками и коммитами обычно понятнее использовать git switch.

Что означает detached HEAD #

Обычно HEAD указывает на текущую ветку:

HEAD → main → C

При переключении на конкретный коммит:

main → C
        \
         D---E

HEAD → D

Вы находитесь на коммите D, но не в ветке.

В таком состоянии можно:

  • просматривать старую версию проекта;

  • запускать и тестировать код;

  • изменять файлы;

  • создавать коммиты.

Однако новые коммиты не будут принадлежать именованной ветке. При последующем переключении их можно потерять из обычного отображения истории, если не создать ветку.

Создать ветку от выбранного коммита #

Когда нужно не просто посмотреть коммит, а начать от него работу:

git switch -c fix-old-version a1b2c3d

Результат:

a1b2c3d ← fix-old-version ← HEAD

Теперь все новые коммиты будут добавляться в ветку fix-old-version.

Аналог через checkout:

git checkout -b fix-old-version a1b2c3d

Если уже находитесь в detached HEAD #

Допустим, вы переключились на старый коммит:

git switch --detach a1b2c3d

Затем внесли изменения и создали новый коммит:

git add .
git commit -m "Fix old version"

Чтобы сохранить этот коммит в ветке:

git switch -c fix-old-version

Новая ветка будет создана от текущего положения HEAD.

Как вернуться обратно #

Вернуться на main:

git switch main

Или на предыдущую ветку:

git switch -

Через checkout:

git checkout main

Переключение относительно текущего коммита #

На предыдущий коммит:

git switch --detach HEAD~1

На три коммита назад:

git switch --detach HEAD~3

Официальная документация Git также использует git switch --detach HEAD~3 как пример временного перехода на старый коммит.

Важное отличие от git reset #

git switch --detach a1b2c3d

Просто показывает состояние выбранного коммита и не передвигает существующие ветки.

git reset --hard a1b2c3d

Передвигает текущую ветку на выбранный коммит и может удалить изменения из рабочей директории.

Поэтому для просмотра старой версии используйте:

git switch --detach <commit>

А для продолжения работы от старого коммита:

git switch -c <new-branch> <commit>