Nikita Mandrykin

Практикуюсь в программировании. C • Python • Assembly • Linux

Назад

Вспоминаем Git

📚 Содержание

👋 1. Введение

Git нужен прежде всего для подстраховки: без него проект легко потерять или случайно сделать неработоспособным. Это утилита, которая фиксирует историю изменений в «чёрный ящик» для быстрого отката и позволяет работать над кодом всей команде сразу.

С Git можно внедрять любые идеи, не рискуя стабильностью готового продукта. Даже если вы потеряете доступ к локальным файлам или всё сломаете, проект можно будет восстановить. Хотя многие новички ограничиваются командами push и pull, в реальной работе они используются реже всего. Намного важнее уметь исправлять ошибки и гибко управлять историей.


🚀 2. Старт и конфигурация

Настройка

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

Что нужно обязательно настроить?

Необходимо обозначить себя, а именно прописать в настройках Git своё имя и почту.

git config --global user.name "Your Name"
git config --global user.email "your_email@example.com"
Настройка текстового редактора

Когда Git нужно, чтобы вы ввели сообщение (например, при коммите без флага -m или при rebase), он открывает текстовый редактор по умолчанию. Если его не настроить, может открыться Vim или Vi, из которых новичку бывает трудно выйти.

Чтобы установить понравившийся редактор:

VS Code:

git config --global core.editor "code --wait"

Nano:

git config --global core.editor "nano"

Vim (если вы его любите):

git config --global core.editor "vim"

Флаг --wait для VS Code важен: он говорит терминалу подождать, пока вы сохраните файл и закроете вкладку в редакторе, прежде чем продолжить работу с Git.

Настройка названия основной ветки

В 2020 году, в рамках “повышения инклюзивности и устранения исторически неуместных терминов, связанных с рабством” платформы вроде GitHub, GitLab, утилита git и прочие инклюзивные питомники переименовали стандартную ветку master в main. Казалось бы, и что? Но если у вас каким-то невообразимым образом git изначально называет ветку master, то он будет лаять, как собака на чужаков, об этом:

hint: Using 'master' as the name for the initial branch. This default branch name
hint: is subject to change. To configure the initial branch name to use in all
hint: of your new repositories, which will suppress this warning, call:
hint: 
hint: 	git config --global init.defaultBranch <name>
hint: 
hint: Names commonly chosen instead of 'master' are 'main', 'trunk' and
hint: 'development'. The just-created branch can be renamed via this command:
hint: 
hint: 	git branch -m <name>

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

git config --global init.defaultBranch main

Как вы могли заметить, в примерах применения настроек у команды git config есть необязательный флаг --global. О его значении речь пойдёт дальше.

Уровни конфигурации

Вообще, есть три уровня конфигурации, но я упомяну только о двух: глобальном и локальном.

Глобальная настройка

Применение

Ко всем репозиториям пользователя на машине.

Файл

~/.gitconfig (в Unix/macOS) или C:\Users\<Имя>\.gitconfig (в Windows).

Команда

git config --global <key> <value>

Локальная настройка

Применение

Только для текущего репозитория (проекта).

Файл

.git/config внутри каталога вашего репозитория.

Команда

git config --local <key> <value> (или просто git config <key> <value>, если не указан уровень)

Приоритет: Если настройка задана на разных уровнях, локальная конфигурация всегда имеет приоритет над глобальной, а глобальная — над системной.

Как посмотреть, что уже настроено?

Чтобы увидеть весь список ваших текущих настроек, введите:

git config --list

Или, чтобы узнать, откуда взялась конкретная настройка:

git config --list --show-origin

🏗️ 3. Создание репозитория и принципы работы

Создание репозитория

Перейдите в каталог, в котором вы желаете проинициализировать репозиторий, и введите:

git init

После чего в консоль будет выведено сообщение о успешной инициализации репозитория:

Initialized empty Git repository in <directory_path>

В папке появится скрытая директория .git, где и будет храниться вся магия (история изменений и настройки).

Принципы работы с Git

  1. Один коммит — одна задача. Не смешивайте исправление бага в футере и вёрстку новой формы в одном сохранении. Это упрощает откат, если одна из задач «выстрелит в ногу».
  2. Смысловые сообщения. Оставляйте такие описания, чтобы через месяц вы (или ваш коллега) поняли, что было сделано, не вчитываясь в диффы.
  3. Не переписывайте публичную историю. Если коммиты уже ушли на сервер (в общий доступ) — забудьте про amend и rebase.

🙈 4. Игнорирование файлов (.gitignore)

.gitignore - специальный текстовый файл, который говорит Git игнорировать определенные файлы и папки (обычно это логи, временные файлы, зависимости и т.д.), чтобы они не попадали в коммиты, поддерживая репозиторий чистым и отслеживая только важные файлы.

Как это работает

  1. Создание файла: В корневой директории проекта необходимо создать файл с именем .gitignore.
  2. Указание правил: В созданном файле вы записываете имена файлов, папок или шаблоны, которые Git должен игнорировать.
    # 1. ФАЙЛ: Игнорировать конкретный файл с паролями в корне
    secrets.txt
    
    # 2. ДИРЕКТОРИЯ: Игнорировать папку с временными файлами (везде в проекте)
    temp/
    
    # 3. КОРНЕВАЯ ДИРЕКТОРИЯ: Игнорировать папку "build" только в корне проекта
    /build/
    
    # 4. ШАБЛОН: Игнорировать все файлы журналов (логи)
    *.log
    
    # 5. ИСКЛЮЧЕНИЕ: Игнорировать все фото, КРОМЕ логотипа
    # Важно: исключение ! не сработает, если родительская папка файла уже полностью проигнорирована
    *.jpg
    !logo.jpg
    
  3. Игнорирование: При выполнении команд типа git status или git add ., Git проверяет этот файл и пропускает все, что соответствует указанным правилам, не добавляя их в индекс и коммиты.

Что делать, если файл уже попал в индекс, а потом был добавлен в .gitignore?

Что такое индекс?

Индекс (или Staging Area) — это промежуточный слой между вашими файлами на диске и итоговой историей (коммитами).

Проще всего это понять через метафору «Фотографии»:

  1. Рабочая директория (Working Directory): Это ваша комната, где вы делаете ремонт. Вы можете передвигать мебель, мусорить, красить стены.
  2. Индекс (Index / Staging Area): Это видоискатель фотоаппарата. Вы выбираете, какие именно изменения попадут в кадр. Вы можете покрасить три стены, но в кадр («индекс») добавить только одну.
  3. Репозиторий (Commit): Это нажатие кнопки затвора. Всё, что было в видоискателе, превратилось в снимок и сохранилось в альбом истории.

Для исправления этой оплошности (когда файл уже «в кадре», а вы хотите его игнорировать) воспользуемся командой git rm с флагом --cached.

Для одного файла:

git rm --cached filename

Для целой директории: Если вы по ошибке закинули в репозиторий целую папку (например, node_modules или dist), используйте флаг -r (recursive) для рекурсивного удаления:

git rm -r --cached folder_name

Что именно делают эти команды?

  • Удаляют из индекса: Git перестает отслеживать эти объекты. Они больше не часть репозитория.
  • Оставляют на диске: Файлы и папки физически остаются в вашей рабочей директории. Git их не тронет, он просто их «забудет».
  • Меняют статус на “untracked”: Если эти пути прописаны в .gitignore, они просто исчезнут из вывода git status.

Продолжаем в том же стиле. Раздел про работу с изменениями — это «база», которую нужно знать чуть глубже, чем просто add ..


🔄 5. Работа с изменениями

Это основной цикл жизни разработчика: внес правки — подготовил их — зафиксировал.

Индексация (git add)

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

  • Добавить всё сразу:
    git add *
    
  • Добавить файлы по маске (во всех вложенных папках): Если нужно собрать, например, только все .js файлы в проекте:
    git add **/filename.*
    
    Здесь ** — это мощный инструмент, который говорит Git искать совпадения во всех поддиректориях на любую глубину.
  • Выборочное добавление нескольких папок: Чтобы не прописывать команду для каждой папки отдельно, можно использовать перечисление в фигурных скобках:
    git add {directory1, directory2}/*
    
  • Комбинирование решений: Все вышеперечисленные подходы можно компоновать в одной команде. Git прекрасно понимает сложные запросы, если вам нужно собрать специфический набор файлов за один раз:
    git add {src,tests}/**/*.js docs/*.md README.md
    
    В этом примере мы добавили все JS-файлы из папок src и tests (включая вложенные), все markdown-файлы из папки docs и конкретный файл README.md в корне.

Фиксация (git commit)

Если индекс — это видоискатель, то коммит — это нажатие кнопки затвора. В этот момент Git делает снимок состояния ваших файлов и навсегда сохраняет его в историю.

Базовые команды

  • Классический коммит:
    git commit -m "feat: add authentication logic"
    
    Флаг -m (message) обязателен. Если его не указать, Git откроет текстовый редактор по умолчанию (обычно Vi), чтобы вы ввели описание там.
  • Коммит «для ленивых» (Skip Staging): Если вы правите уже существующие файлы (которые Git уже отслеживает) и вам лень каждый раз писать git add, можно объединить два шага в один:
    git commit -am "fix: correct calculations"
    
    Важно: Флаг -a не подцепит новые файлы (untracked). Если вы создали новый файл, его всё равно придется сначала добавить через git add вручную.
Соглашение о коммитах (Conventional Commits)

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

Структура: <type>: <description>

Основные типы:

  • feat: (feature) новая функциональность.
  • fix: исправление бага.
  • refactor: правка кода, не меняющая логику (чистка, оптимизация).
  • docs: изменения в документации.
  • style: правки стиля (пробелы, форматирование), не влияющие на код.
  • chore: рутинные задачи (обновление зависимостей, конфиги сборки).
  • test: добавление или исправление тестов.

На каком языке писать и в каком стиле?

  1. Английский — стандарт. По возможности пишите на нём. В английских коммитах используется повелительное наклонение (Imperative mood). Представьте, что вы отдаёте команду коду:

    • feat: add email validation (Добавь валидацию), а не added или adds.
    • Лайфхак: сообщение должно логично продолжать фразу: «If applied, this commit will… [your message]».
  2. Как писать на русском? Если проект русскоязычный, то «добавить» (инфинитив) звучит как задача из списка дел, а «добавь» (повелительное) — слишком агрессивно.

    В русском языке естественнее всего использовать прошедшее время, совершенный вид. Это звучит как отчёт о проделанной работе:

    • feat: реализовал валидацию email
    • fix: исправил падение при пустом вводе

Пример: feat: реализовал валидацию email в форме регистрации

Исправление последнего коммита (–amend)

Инструмент для тех, кто уже нажал Enter, но через секунду вспомнил о важной детали. Вместо того чтобы плодить «мусорные» коммиты вроде «fix: typo» или «add forgotten file», можно просто «вскрыть» последний коммит и заменить его исправленным.

Здесь есть два основных сценария:

Сценарий 1: Забыл добавить файл или изменить код (сообщение верное)

Добавьте забытые правки в индекс (git add .) и выполните:

git commit --amend --no-edit
  • --amend — берет всё, что сейчас лежит в индексе, и «вклеивает» в последний коммит.
  • --no-edit — говорит Git оставить старое сообщение коммита без изменений.

Сценарий 2: Опечатался в сообщении (код верный)

Если код в порядке, но вы хотите переписать текст коммита:

git commit --amend -m "updated commit message"

(Если не использовать флаг -m, Git откроет текстовый редактор с вашим старым сообщением для ручной правки).

Ловушка дат: Author Date vs Commit Date

Мало кто знает, что у каждого коммита в Git две даты:

  1. Author Date: Когда код был изначально написан (создан первый коммит).
  2. Commit Date: Когда коммит был последний раз изменен (в нашем случае — время выполнения amend).

По умолчанию --amend сохраняет оригинальную дату автора, но обновляет дату коммита на текущую. В истории это может выглядеть странно: в логе стоит вчерашнее число, хотя фактически это «сегодняшний» измененный объект.

Как управлять датами?

  • Обновить всё (сделать коммит «сегодняшним»): Если вы хотите, чтобы обе даты стали текущими (будто вы только что создали коммит с нуля), используйте флаг --reset-author:
    git commit --amend --no-edit --reset-author
    
  • «Заморозить» всё (сохранить старые даты): Бывают случаи, когда нужно ювелирно исправить код, но при этом сохранить иллюзию, что коммит вообще не менялся (сохранить и Author, и Commit Date). Для этого нужно принудительно передать Git дату старого коммита через переменную окружения:
    GIT_COMMITTER_DATE="$(git log -1 --format=%cd)" git commit --amend --no-edit
    
    Здесь мы вытаскиваем дату последнего коммита и подставляем её в процесс создания обновленного коммита.

Цена ошибки: Команда amend не просто правит старый коммит — она удаляет его и создает совершенно новый объект с новым хешем.

Золотое правило: Никогда не делайте amend для коммитов, которые уже улетели на сервер (push). Это перепишет историю и создаст конфликт версий у всей команды. Коллеги за такое спасибо не скажут.


🔍 6. Инспекция: что происходит в проекте?

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

Сравнение изменений (git diff)

Команда git diff показывает разницу между состояниями файлов в формате «было — стало». По умолчанию (без флагов) она сравнивает вашу рабочую директорию с индексом — то есть показывает изменения, которые вы ещё не добавили через git add.

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

  • Просмотр того, что уже готово к коммиту: Если вы уже выполнили git add, обычный diff ничего не покажет. Чтобы увидеть изменения, которые находятся в индексе (в видоискателе), используйте:
    git diff --staged
    # Или полный синоним (старая версия):
    git diff --cached
    
  • Сравнение конкретного файла: Если правок много, а вас интересует одна точка:
    git diff path/to/file.txt
    
  • Сравнение веток или хешей: Вы можете узнать разницу между вашей текущей веткой и любой другой, или сравнить два конкретных состояния:
    git diff main dev                 # Между ветками
    git diff commithash1 commithash2  # Между конкретными хешами
    
  • Краткая статистика (–stat): Если изменений слишком много и вы не хотите читать каждую строчку кода, флаг --stat покажет только список измененных файлов и количество добавленных/удаленных строк:
    git diff --stat
    

Чтение истории (git log)

Стандартный git log выдает слишком громоздкую «простыню» текста. Чтобы история стала читаемой и полезной, нужно уметь её фильтровать и оформлять.

  • «Ультимативное дерево» (God Mode): Эта комбинация флагов превращает лог в компактную, цветную схему, где наглядно видны все ветки и их слияния:
    git log --graph --oneline --all --decorate
    
    • --graph — рисует текстовое дерево связей.
    • --oneline — сжимает каждый коммит в одну строку (хеш + заголовок).
    • --all — показывает историю всех веток сразу.
    • --decorate — подсвечивает названия веток и теги (HEAD, main, dev).
  • Поиск самого первого коммита: Если в репозитории тысячи записей, а вам нужно найти «точку отсчета», поможет фильтр по количеству родителей. У самого первого коммита в проекте их ноль:
    git log --max-parents=0
    
  • Кастомное форматирование: Флаг --format позволяет вывести только нужные данные в удобном вам виде. Например, только дату, автора и суть правки:
    git log --format="%ad | %an: %s" --date=short
    
    Здесь: %ad — дата, %an — имя автора, %s — сообщение коммита.

🌿 7. Ветки и навигация

Ветка в Git — это не копия файлов, а всего лишь подвижный указатель на конкретный коммит. Это делает работу с ними мгновенной и «дешевой».

Управление ветками (git branch)

  • Посмотреть список всех веток:
    git branch
    
    Текущая ветка будет отмечена звездочкой и выделена цветом.
  • Создать новую ветку:
    git branch feature-login
    
    Эта команда создаст ветку, но не переключит вас на неё. Вы останетесь там, где были.

Навигация (git switch)

Раньше для переключения между ветками использовалась команда git checkout. Но она была слишком «комбайном»: умела и ветки менять, и файлы восстанавливать. В современных версиях Git для навигации есть специализированная и безопасная команда switch.

  • Перейти на существующую ветку:
    git switch main
    
  • Создать новую ветку и сразу перейти на неё:
    git switch -c feature-ui
    
    (Флаг -c сокращение от create).

Слияние веток (git merge)

Слияние — это не просто «свалить всё в одну кучу». В Git есть три разных сценария того, как ваши правки попадут в основную ветку. Эта схема идеально их иллюстрирует:

Типы слияний в Git: Fast-forward, Three-way и Empty merge

1. Fast-forward merge (Линейная перемотка)

Это происходит, когда история линейна. Вы создали ветку, сделали в ней пару коммитов, а в основной ветке master за это время ничего не изменилось.

  • Механика: На левой части схемы видно, что Git просто берет указатель master и «перетягивает» его на верхушку вашей ветки.
  • Результат: Нового коммита не создается. История выглядит как прямая линия, будто вы всё время писали код в master.
  • Минус: Вы теряете информацию о том, что работа велась в отдельной ветке. В логах это выглядит как «каша» из коммитов.

2. Three-way merge (Трёхстороннее слияние)

Классический случай для командной работы, когда ветки разошлись (центральная часть схемы). Пока вы работали над задачей, кто-то другой успел закинуть коммиты в master.

  • Механика: У веток разные пути. Чтобы их объединить, Git находит их общего предка (точку расхождения) и сравнивает его с верхушками обеих веток.
  • Результат: Git создает новый Merge Commit (фиолетовый кружок), который объединяет две истории.
  • Конфликты: На схеме видна «молния» (Conflict). Если вы и коллега изменили одну и ту же строку, Git не сможет сам решить, чей код важнее, и попросит вас разрулить это вручную.

3. Empty merge (Слияние через –no-ff)

Это «искусственное» слияние (правая часть схемы). Вы используете его, когда Git мог бы сделать простую перемотку (как в первом случае), но вы запрещаете ему это делать флагом --no-ff.

  • Зачем это нужно: Вы принудительно заставляете Git создать Merge Commit.
  • Почему «Empty»: Технически этот коммит не несет в себе новых изменений кода (ведь в master и так ничего не менялось), он лишь фиксирует сам факт слияния.
  • Результат: В истории появляется характерная «петля».
  • Главный плюс: Структурность. Даже через год вы увидите в логах границы задачи: где она началась и где закончилась. Если фича окажется багованной, вы сможете откатить её всю целиком, отменив один этот «пустой» коммит.

Команда для «чистой» истории с петлями:

git merge --no-ff feature-branch

⚡ 8. Перебазирование (git rebase)

Вместо того чтобы создавать коммит слияния, rebase берет ваши коммиты и по очереди «переклеивает» их на верхушку другой ветки. История получается идеально линейной, будто вы и не работали параллельно, а писали код строго друг за другом.

Зачем это нужно?

  1. Актуализация ветки: Вы работали неделю в своей ветке, а main за это время ушёл далеко вперёд. Вместо того чтобы делать merge main (и плодить лишний коммит), вы делаете rebase main — и ваши правки оказываются поверх последних изменений.
  2. Чистка истории: Перед тем как вливать код в основной проект, принято «причесывать» свои коммиты, чтобы коллеги не видели ваши fix, fix2, typo.

Интерактивный режим (git rebase -i)

Самый мощный инструмент в арсенале. Он позволяет открыть список последних коммитов в текстовом редакторе и буквально изменить прошлое.

Команда:

git rebase -i HEAD~3  # Работаем с тремя последними коммитами

Что можно сделать в открывшемся списке:

  • pick — оставить коммит как есть.
  • reword — изменить только сообщение коммита (аналог amend, но для старых записей).
  • squash — склеить коммит с предыдущим (сообщения тоже объединятся).
  • fixup — склеить коммит с предыдущим, но выкинуть его сообщение (идеально для мелких правок).
  • drop — полностью удалить коммит из истории.

Rebase от самого начала (–root)

Если вам нужно перелопатить вообще всю историю проекта (например, изменить самый первый коммит или склеить всё в один гигантский «Initial commit»), используйте флаг --root:

git rebase -i --root

Внимание: это радикальное действие, которое полностью перепишет все хеши в репозитории.

Решение конфликтов

При merge конфликт решается один раз. При rebase конфликт может возникать в каждом переносимом коммите.

Процесс:

  1. Git останавливается на коммите, где возник конфликт.
  2. Вы правите файлы вручную.
  3. Добавляете их в индекс: git add ..
  4. Продолжаете процесс:
    git rebase --continue
    
    (Никаких git commit делать не нужно! Git сам создаст новый коммит на основе старого).

Золотое правило Rebase: Никогда не перебазируйте ветку, которую вы уже отправили в общий доступ (push). Вы перепишете историю, и у всех остальных разработчиков репозиторий «сойдёт с ума» при попытке синхронизации. Rebase — только для локальных правок.


Мы подошли к самому важному разделу. Git называют «машиной времени» именно из-за этих команд. Здесь мы разберём, как возвращать файлы к жизни и как грамотно откатываться назад, если всё пошло не по плану.


🚑 9. Исправление ошибок и откат

В Git почти любую ошибку можно исправить, если вы ещё не удалили папку .git. Главное — выбрать правильный инструмент в зависимости от того, как далеко зашла проблема.

Возврат файлов к жизни (git restore)

Эта команда — ваш «ластик» для рабочей директории. Она нужна, когда вы что-то изменили в файле, но поняли, что наворотили лишнего и хотите вернуть всё, как было в последнем коммите.

  • Отменить незакоммиченные правки в файле:
    git restore filename
    
  • Вытащить файл из индекса (unstage): Если вы случайно сделали git add, но передумали включать файл в будущий коммит:
    git restore --staged filename
    

Тяжелая артиллерия (git reset)

reset — это команда для перемещения «во времени» (отката по истории коммитов). У неё есть три режима, которые критически важно различать:

  1. –soft (Мягкий откат): Откатывает только коммит. Все ваши правки остаются в индексе (готовы к новому коммиту). Идеально, если вы хотите переделать последний коммит или склеить несколько последних в один.

    git reset --soft HEAD~1
    
  2. –mixed (Средний откат — по умолчанию): Откатывает коммит и выкидывает правки из индекса, но оставляет их в файлах. Используется, если вы хотите пересмотреть, что именно вы добавляли в индекс.

    git reset --mixed HEAD~1
    
  3. –hard (Жесткий откат): Уничтожает всё: и коммит, и правки в индексе, и сами изменения в файлах. Проект возвращается в состояние «как тогда». Опасно: незакоммиченные данные будут потеряны безвозвратно.

    git reset --hard HEAD~1
    

Безопасный откат (git revert)

Если reset буквально «отматывает» время назад, удаляя коммиты из истории, то revert — это цивилизованный способ отмены изменений. Вместо удаления старого коммита, Git создаёт новый коммит, который содержит строго противоположные изменения.

Зачем это нужно:

  1. Безопасность публичной истории: Если вы уже отправили код на сервер (push), reset ломает историю у коллег. revert же просто добавляет новый коммит в конец списка, что абсолютно безопасно.
  2. Сохранение контекста: Вы не стираете факт ошибки, а фиксируете её исправление.

Команда:

git revert <hash_of_bad_commit>

Git откроет редактор для сообщения коммита (по умолчанию: “Revert ‘Old message’”). После сохранения в истории появится новый коммит-«антипод».

Сравнение Reset vs Revert

Характеристика git reset git revert
Принцип Удаляет коммиты из текущей ветки Создаёт новый «обратный» коммит
История Переписывается (хеши меняются) Растёт только вперёд (безопасно)
Когда использовать Для локальных правок до push Для исправлений в общей ветке
Риск Высокий (можно потерять данные) Низкий (ничего не удаляется)

🎓 10. Разбор ситуаций (Case Study)

Ситуация 1: «Я ошибся, но это последний коммит»

Проблема: Вы закоммитили код, но забыли файл, оставили console.log или опечатались в сообщении.

  • Решение А (косметическое): Если нужно просто поправить мелочь — используйте --amend, как мы разбирали в разделе про фиксацию.
  • Решение Б (радикальное): Если вы поняли, что коммит вообще не должен был существовать в таком виде:
    git reset --soft HEAD~1
    
    Теперь правки снова у вас в руках (в индексе), исправляйте и коммитьте заново.

Ситуация 2: «Я ошибся, но уже произошёл Merge»

Проблема: Вы влили ветку в main, и всё сломалось. Нужно срочно вернуть проект в рабочее состояние.

  • Решение А (локальное): Если вы ещё не запушили мердж, можно просто «откатить» указатель ветки назад:
    git reset --hard <hash_before_merge>
    
  • Решение Б (публичное): Если мердж уже на сервере, используйте revert. Но помните: отмена мердж-коммита требует указания «главной» линии (обычно это ветка, в которую вливали, т.е. 1):
    git revert -m 1 <merge_commit_hash>
    
    Флаг -m 1 говорит Git рассматривать первую родительскую ветку (main) как ту, которую мы хотим оставить.

Ситуация 3: «Я ошибся, но это далёкий коммит»

Проблема: Вы нашли баг или лишний файл в истории, который был сделан 5-10 коммитов назад.

  • Решение: Используйте интерактивный rebase.
    git rebase -i HEAD~10
    
    В открывшемся списке найдите нужный коммит и замените слово pick на edit. Сохраните и закройте. Git остановит время на этом коммите. Вы вносите правки, делаете git add . и git commit --amend, а затем продолжаете путь: git rebase --continue.

✅ Заключение

Мы прошли путь от настройки конфигов до ювелирной правки истории. Git — это не просто набор команд, это ваша уверенность в том, что ни одна строчка кода не пропадёт, а любую ошибку можно локализовать и исправить.

Главное — помнить:

  1. Не делайте amend и rebase для публичной истории.
  2. Используйте --no-ff для мерджей, чтобы лог был читаемым.
  3. Чаще смотрите в git log --graph, чтобы понимать, где вы находитесь.

Credits: Иллюстрация схем слияния (Fast-forward, Three-way, Empty merge) взята из блога Noah Bieler.

💜

Полезный материал?

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

Поддержать через CloudTips