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.
Для чего используется #
Основные сценарии:
Перенос исправления ошибки
Ошибка исправлена в одной ветке, но исправление нужно также добавить в
main,developили релизную ветку.Перенос случайно созданного коммита
Коммит сделали не в той ветке:
git switch correct-branch
git cherry-pick <commit-hash>
После этого исходный коммит при необходимости удаляют из неправильной ветки.
Выборочное добавление функциональности
В ветке находится много коммитов, но нужен только один конкретный.
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
Нужно:
Исправить конфликтующие файлы.
Добавить исправленные файлы:
git add .
- Продолжить:
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'
Сравнение #
| Критерий | merge | rebase |
|---|---|---|
| История | Сохраняет реальную структуру веток | Делает историю линейной |
| 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:
Загружает изменения.
Сразу объединяет их с текущей веткой.
Было:
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 fetch | git 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>