Урок курса

Переключиться с незавершёнными правками

Git и GitHub с нуля: бесплатный курс с практикой

Ветка указывает на коммит. Незакоммиченная правка находится в рабочем файле или индексе и ещё не входит в историю ветки. Перед git switch важно определить, сможет ли Git сменить ветку без потери этой правки.

Незакоммиченная правка не принадлежит ветке

Ветка — это указатель на коммит. Когда вы говорите «мои правки на ветке feature», это точное утверждение только если правки зафиксированы коммитом, на который указывает feature. До коммита изменённый файл существует только в рабочей папке или в индексе — ни там, ни там нет понятия «принадлежность ветке».

Рабочая папка и индекс — общие для всего репозитория. Их содержимое не привязано ни к одной ветке. Ветка main не «хранит» рабочую папку, и ветка feature тоже её не хранит. Хранить умеет только коммит.

Представьте такую ситуацию: вы на main, добавили строку в README.md, но не сделали git add и git commit. В этот момент ваша правка не является частью main. Она не станет частью feature, если вы переключитесь. Она просто лежит в рабочей папке — нигде, ни в какой ветке.

Это разграничение важно, потому что интуиция часто подсказывает обратное: «я работал на main, значит правка принадлежит main». Но Git не знает, на какой ветке вы редактировали файл. Он видит только разницу между рабочим файлом и последним зафиксированным снимком. Принадлежность ветке появляется в момент коммита — не раньше.

Два исхода git switch с незакоммиченными изменениями

При git switch <ветка> Git учитывает версии файлов в текущем коммите, целевой ветке, индексе и рабочей папке. Защитный отказ возникает, если переключение потребовало бы перезаписать локальные изменения. Одного сравнения коммитов двух веток для предсказания недостаточно.

В подготовленном опыте prepared/switch_refused индекс совпадает с HEAD, а рабочая правка README.md отличается и от индекса, и от целевой версии feature. Переключение потребовало бы заменить рабочий файл и уничтожить эту правку. Именно в этом состоянии Git отказывается:

error: Your local changes to the following files would be overwritten by checkout:
        README.md
Please commit your changes or stash them before you switch branches.

Переключения не происходит. Рабочая папка и HEAD остаются нетронутыми — правки целы.

В подготовленном опыте prepared/switch_portable обе ветки указывают на один коммит, индекс совпадает с HEAD, а README.md изменён только в рабочей папке. Перезаписывать этот файл не требуется: Git переключает HEAD на feature, сохраняя рабочую правку и индекс.

Эти два результата относятся к указанным подготовленным состояниям. Разные версии файла в коммитах веток сами по себе не гарантируют отказ: например, если рабочий файл и индекс уже совпадают с целевой версией, переключение может пройти. Перед действием смотрите git status, git diff и git diff --staged, чтобы различать рабочую правку и подготовленное изменение; отказ связан с угрозой их перезаписи.

Проверка обоих исходов на prepared-репозиториях

Для каждого независимого опыта распакуйте полный git_practice.zip в новую отдельную пустую папку. Ниже используются два самостоятельных репозитория из архива; результат первого опыта не переносится во второй. В обоих уже есть .git и подготовленная история: повторный git init не нужен.

prepared/switch_portable — правка переносится

Откройте терминал в папке git_practice первой свежей распаковки. Затем выполните команды отдельно:

cd prepared/switch_portable
git config user.name "Student"
git config user.email "student@example.com"

Терминал теперь находится именно в git_practice/prepared/switch_portable. Можно подставить свои значения автора. Эти локальные настройки нужны для последующего коммита в выбранном репозитории; настройки другой папки сюда не переносятся.

Начальное состояние: HEAD на main, обе ветки указывают на один коммит, README.md изменён в рабочей папке.

$ git status
On branch main
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
        modified:   README.md

Индекс чист относительно HEAD, staged-изменений нет. Изменённый файл — только в рабочей папке. Ветки на одном коммите, значит README.md в feature совпадает с README.md в main. Git не будет перезаписывать файл.

Предсказание: git switch feature выполнится без ошибки, правка останется в рабочей папке.

$ git switch feature
Switched to branch 'feature'

$ git status
On branch feature
Changes not staged for commit:
        modified:   README.md

Правка видна на feature точно так же, как была на main. Git перенёс её — не потому что она «переехала с main», а потому что она просто осталась в рабочей папке и ничто её не стёрло.


prepared/switch_refused — Git отказывает

Для этого опыта распакуйте полный архив ещё раз в другую новую пустую папку и откройте терминал в её папке git_practice. Не используйте изменённую копию первого опыта. Затем выполните команды отдельно:

cd prepared/switch_refused
git config user.name "Student"
git config user.email "student@example.com"

Терминал теперь находится именно в git_practice/prepared/switch_refused. Можно подставить свои значения автора. Эти локальные настройки нужны для последующего коммита в выбранном репозитории; настройки другой папки сюда не переносятся.

Начальное состояние: HEAD на main, ветки main и feature имеют разные версии README.md, файл изменён в рабочей папке.

$ git status
On branch main
Changes not staged for commit:
        modified:   README.md

В feature другая версия README.md. Переключение потребовало бы перезаписи файла.

Предсказание: git switch feature выдаст ошибку.

$ git switch feature
error: Your local changes to the following files would be overwritten by checkout:
        README.md
Please commit your changes or stash them before you switch branches.
Aborted

$ git status
On branch main
Changes not staged for commit:
        modified:   README.md

Git не переключился. HEAD по-прежнему на main, правка цела. Именно здесь нужно принять решение, что с ней делать.

Коммит или restore: два пути к чистому состоянию

Когда Git отказал в переключении, у вас два варианта: сохранить правку или отказаться от неё.

Путь A — зафиксировать правку коммитом

Если правка нужна, её нужно сохранить в истории текущей ветки:

$ git add README.md
$ git diff --staged          # убедиться, что в индексе именно то, что нужно
$ git commit -m "Описание правки"
$ git status                 # должно быть: nothing to commit, working tree clean
$ git switch feature

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

Путь Б — отказаться от правки

Если правка не нужна, её можно сбросить:

$ git restore README.md
$ git status                 # nothing to commit, working tree clean
$ git switch feature

git restore <файл> восстанавливает рабочий файл из индекса. Поскольку в prepared-репозиториях индекс совпадает с HEAD (staged-изменений нет), команда вернёт файл к последнему зафиксированному состоянию. Правка уничтожается безвозвратно — Git не сможет её восстановить.

Если бы в индексе была staged-правка (строка «Changes to be committed» в git status), простой git restore README.md сбросил бы рабочий файл до состояния индекса, а не HEAD — то есть staged-правка осталась бы. Чтобы убрать её полностью, сначала нужен git restore --staged README.md, и только потом git restore README.md. В prepared-репозиториях этого урока этот случай не возникает, но помнить о нём стоит.

Ловушка с перенесённой правкой

В сценарии switch_portable Git молча переносит правку на feature. Легко решить, что правка «была на main и теперь потерялась там». На самом деле она никогда не принадлежала main — и теперь не принадлежит feature. Чтобы привязать её к конкретной ветке, нужно сделать коммит именно там:

# уже на feature после переноса правки
$ git add README.md
$ git commit -m "Зафиксировать правку на feature"
$ git log --oneline --all
  a1b2c3d (HEAD -> feature) Зафиксировать правку на feature
  9f8e7d6 (main) Исходный коммит

Теперь feature ушла вперёд относительно main, а правка зафиксирована именно там, где нужно.

Задача 1

Два независимых опыта на main, только README имеет неподготовленную рабочую правку. В опыте П обе ветки на одном коммите. В опыте О переход к feature перезаписал бы эту правку другой версией файла; рабочий текст отличается от обеих закоммиченных версий. Каковы результаты обычного git switch feature?

Выберите один вариант
Без регистрации · результат не сохраняется

Задача 2

Практика: отдельно распакуйте ZIP и войдите в git_practice/prepared/switch_portable. В этой папке main и feature на одном коммите, а рабочая правка README уже подготовлена на диске, не в индексе. Проверьте status и diff, переключитесь на feature и повторите проверки. Запишите активную ветку и diff до и после успешного переключения, не создавая коммит. Письменно объясните коллеге, почему незакоммиченная правка не стала частью ветки только из-за переключения.

Платформа проверяет только ответ ниже; действия в своём репозитории и письменные ответы вы проверяете самостоятельно.

В контрольной записи switch завершился успешно, но commit не выполняли. Diff на feature показывает перенесённую рабочую строку. Можно ли считать эту строку уже записанной в истории feature?

Выберите один вариант
Без регистрации · результат не сохраняется

Задача 3

Практика: начните со свежей отдельной копии git_practice/prepared/switch_refused. Прочитайте status и рабочий README, попробуйте переключиться на feature и проверьте результат. Запишите отказ switch, исходную ветку и сохранённую правку; затем явно выберите сохранение коммитом либо отказ от правки и подтвердите чистое состояние перед повторным switch.

Платформа проверяет только ответ ниже; действия в своём репозитории и письменные ответы вы проверяете самостоятельно.

В другом контрольном случае индекс README содержит Б, HEAD содержит А, рабочий файл совпадает с Б. Выполнили только обычный restore README.md. Достаточно ли этого, чтобы считать индекс очищенным от подготовленной правки?

Выберите один вариант
Без регистрации · результат не сохраняется

Продолжить с сохранением прогресса

Перейдите к уроку в учебном кабинете.

Перейти к интерактивному уроку