Получение и слияние в Git - команда git pull

17 декабря 2025
Автор

Олег Марков

Введение

Команда git pull — одна из самых часто используемых в повседневной работе с Git. Она отвечает за получение изменений с удалённого репозитория и их автоматическое слияние с вашей текущей локальной веткой.

Смотрите, я покажу вам, как важно понимать, что именно делает git pull внутри. На первый взгляд команда кажется простой: обновить ветку с сервера. Но за этим скрываются разные стратегии слияния, возможные конфликты, настройка отслеживаемых веток и типичные ошибки, которыми легко сломать историю репозитория.

В этой статье вы разберётесь:

  • что на самом деле выполняет git pull;
  • чем git pull отличается от git fetch и git merge;
  • какие режимы слияния бывают — merge, rebase, fast-forward;
  • как правильно настраивать ветки и remotes;
  • как решать конфликты после git pull;
  • как сделать работу с git pull более безопасной и предсказуемой.

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

Что делает git pull на самом деле

Внутренний механизм: fetch + merge

Важно понять одну базовую вещь: git pull — это не отдельная магическая операция. По сути, это сокращение для двух команд:

  1. получение новых коммитов с сервера;
  2. слияние этих коммитов с вашей текущей веткой.

Если разложить git pull на шаги, получится такое:

# Шаг 1 - получить обновления из удалённого репозитория
git fetch origin

# Шаг 2 - слить изменения из удалённой ветки в текущую локальную
git merge origin/main

Комментарии к командам:

  • git fetch origin
    // Забирает все новые коммиты и ссылки веток с удалённого репозитория origin
    // При этом ваша текущая ветка и рабочие файлы не меняются

  • git merge origin/main
    // Сливает в текущую ветку изменения из удалённой ветки origin/main
    // Может создать новый merge-коммит
    // Может вызвать конфликты, если изменения пересекаются

Команда git pull делает эти шаги за вас автоматически, на основе настроек отслеживаемой ветки.

Общая форма команды:

git pull <remote> <branch>

Например:

git pull origin main

// Получить изменения из ветки main на удалённом репозитории origin
// И слить их в текущую локальную ветку

Если вы не указываете ни remote, ни ветку, Git использует настройки текущей ветки: какой remote она отслеживает и какую удалённую ветку с ней связывает.

Отслеживаемые ветки (tracking branches)

Чтобы git pull знал, откуда и во что тянуть изменения, у ветки есть пара «отслеживаемая локальная ветка — отслеживаемая удалённая ветка».

Например:

  • локальная ветка: main
  • удалённая отслеживаемая ветка: origin/main

Посмотреть настройки отслеживания можно так:

git branch -vv

// Показывает локальные ветки
// Для каждой ветки указывает, какую удалённую ветку она отслеживает
// И короткий хэш последнего коммита

Типичный вывод будет выглядеть так:

* main  a1b2c3d [origin/main] Add logging to auth service
  feature/login  e4f5g6h [origin/feature/login: ahead 2] Implement login UI

Здесь вы видите, что:

  • ветка main отслеживает origin/main;
  • ветка feature/login отслеживает origin/feature/login и «опережает» её на 2 коммита.

Когда вы выполняете:

git pull

Git использует эту информацию по ветке main:

  • remote: origin
  • upstream branch: origin/main

И поэтому выполняет эквивалент:

git pull origin main

Если отслеживание не настроено, git pull без аргументов либо не сработает, либо попросит указать remote и ветку.

Как создать отслеживаемую ветку

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

Создание новой ветки из удалённой

git checkout -b feature/login origin/feature/login

// Создаём новую локальную ветку feature/login
// Указываем, что её базой является origin/feature/login
// Git автоматически настроит отслеживание этой удалённой ветки

Теперь в ветке feature/login можно просто вызвать:

git pull

Привязка существующей локальной ветки к удалённой

Если ветка уже есть, но не привязана к remote:

git branch --set-upstream-to=origin/feature/login feature/login

// Говорим, что локальная ветка feature/login должна отслеживать origin/feature/login
// Теперь git pull внутри этой ветки будет использовать origin/feature/login

Или, если вы уже стоите в нужной ветке:

# Вы находитесь в ветке feature/login
git branch --set-upstream-to=origin/feature/login

// Настраиваем upstream-ветку для текущей ветки

Режимы работы git pull: merge, rebase, fast-forward

Git позволяет по-разному встраивать изменения из удалённой ветки в вашу локальную. Это влияет на историю коммитов и на то, как удобно её читать.

По умолчанию: merge (слияние)

Стандартное поведение git pull (если вы явно ничего не настраивали) — использовать merge.

Сценарий:

  1. Ваша локальная ветка main
  2. Коллега сделал несколько коммитов в origin/main
  3. Вы сделали локальные коммиты в main
  4. Выполняете git pull

Git:

  • забирает новые коммиты с сервера (fetch);
  • создаёт новый merge-коммит, у которого два родителя:
    • последний локальный коммит;
    • последний удалённый коммит.

Выглядит это примерно так:

A --- B --- C --- D (origin/main)
       \
        E --- F (main до pull)
               \
                M (merge-коммит после git pull)

Здесь:

  • D — последний коммит на сервере;
  • F — последний ваш локальный коммит;
  • M — новый merge-коммит, который создаёт git pull.

Основные особенности режима merge:

  • История остаётся разветвлённой, вы видите, как ветки сходятся в merge-коммитах.
  • Не переписываются существующие коммиты ни у вас, ни у коллег.
  • При активной разработке история может выглядеть «шумно» из-за множества merge-коммитов.

Выполнить git pull в merge-режиме можно явно:

git pull --merge

// Выполнить pull с использованием merge
// Поведение аналогично умолчанию во многих конфигурациях

Режим rebase: линейная история

Многим разработчикам удобнее смотреть на линейную историю без лишних merge-коммитов. Для этого в Git есть режим rebase.

Схема работы git pull с rebase такая:

  1. Git забирает новые коммиты с сервера.
  2. Ваши локальные коммиты «переписываются» так, как будто они были сделаны поверх обновлённой удалённой ветки.

Посмотрим на пример. До pull история такая:

A --- B --- C --- D (origin/main)
       \
        E --- F (main локальная)

После:

git pull --rebase

История станет:

A --- B --- C --- D --- E' --- F' (main локальная)
              ^
              origin/main (может быть обновлён дальше)

Что здесь произошло:

  • Коммиты E и F не просто «добавились» к D.
  • Git создал новые версии этих коммитов E' и F', основанные на D.
  • Старые E и F больше не используются в основной истории (но пока ещё есть в репозитории).

Таким образом:

  • История становится линейной: все коммиты идут один за другим.
  • При просмотре лога проще понять развитие кода.
  • Но: ваши локальные коммиты получили новые хэши, то есть буквально переписаны.

Выполнить pull с rebase можно так:

git pull --rebase

// Получить изменения с сервера
// Поверх них «переиграть» ваши локальные коммиты
// Сохранить линейную историю

Этот вариант популярен в командах, которые ценят аккуратный лог коммитов.

Важно: не стоит делать rebase публичных веток, которые уже использует кто-то ещё. В случае git pull --rebase это обычно не проблема, потому что вы «подстраиваете» свои локальные коммиты под общую ветку, а не ломаете её.

Только fast-forward: без merge-коммитов

Есть ещё один важный режим: fast-forward only. Он не переписывает ваши коммиты, а просто обновляет указатель ветки, если это возможно без merge.

Поясню на примере.

Ситуация 1 — вы не делали локальных коммитов:

A --- B --- C (origin/main)
       ^
       main локальная

После:

git pull --ff-only

История:

A --- B --- C (origin/main, main)

Здесь не требуется слияние, достаточно «перемотать» main до C. Это и есть fast-forward.

Ситуация 2 — вы сделали локальный коммит:

A --- B --- C (origin/main)
       \
        D (main локальная)

После:

git pull --ff-only

Git скажет, что fast-forward невозможен, потому что есть ответвление (коммит D), и прервёт операцию. Никаких автослияний, никаких merge-коммитов.

Использование:

git pull --ff-only

// Получить изменения
// Обновить ветку только если это можно сделать fast-forward
// В противном случае — остановиться с ошибкой

Этот режим делает поведение git pull более безопасным: вы осознанно управляете моментом слияния и не получаете неожиданные merge-коммиты.

Как выбрать режим по умолчанию

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

# Сделать rebase стандартным при git pull
git config --global pull.rebase true

# Вернуть стандартное merge-поведение
git config --global pull.rebase false

# Заставить git pull по умолчанию использовать режим fast-forward only
git config --global pull.ff only

Комментарии:

  • pull.rebase true
    // Теперь git pull без флагов будет работать как git pull --rebase

  • pull.ff only
    // Теперь Git не будет делать merge, если нельзя сделать fast-forward
    // Попытается либо fast-forward, либо завершит операцию с ошибкой

Вы можете комбинировать эти настройки или настраивать их для конкретных репозиториев без --global.

Практические сценарии использования git pull

Обновление основной ветки разработки

Самый частый сценарий — вы работаете в ветке main или develop и периодически обновляете её из удалённого репозитория.

Базовый вариант:

# Убедитесь, что вы в нужной ветке
git checkout main

# Обновите ветку с сервера
git pull

// Получить изменения из origin/main
// Влить их в локальную main согласно вашей настройке pull.rebase и pull.ff

Рекомендуется:

  • перед началом рабочего дня обновлять основную ветку;
  • перед созданием feature-ветки сделать git pull, чтобы ответвляться от актуального состояния.

Обновление feature-ветки перед push

Сценарий:

  1. Вы создали ветку feature/login от main.
  2. Пока вы работали, в main появилось много новых коммитов.
  3. Вы хотите отправить свою ветку в репозиторий, но сначала стоит подтянуть актуальные изменения и разрешить конфликты локально.

Один из подходов:

# Перейти в основную ветку
git checkout main

# Обновить основную ветку
git pull --ff-only

# Вернуться к своей задаче
git checkout feature/login

# Перебазировать свои изменения поверх обновлённой main
git rebase main

Но если ваша ветка отслеживает удалённую ветку (origin/feature/login), вы можете обновить именно её:

# Находитесь в ветке feature/login
git pull --rebase

// Получить обновления из origin/feature/login
// Переписать ваши локальные коммиты поверх обновлённой ветки

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

git push

Если Git сообщит, что push невозможен из-за расхождений, вам может понадобиться:

git push --force-with-lease

// Аккуратно перезаписать удалённую ветку
// --force-with-lease безопаснее, чем просто --force
// Git проверит, что на сервере нет чужих новых коммитов

Обновление сразу всех веток и тегов

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

Для этого есть:

# Обновить все ветки из origin
git fetch --all --prune

// Забрать данные по всем remotes
// Удалить локальные ссылки на удалённые ветки, которые были удалены на сервере

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

git pull сам по себе обновляет только:

  • текущую локальную ветку;
  • и только от одного удалённого репозитория (обычно origin).

Конфликты при git pull и их решение

Когда возникают конфликты

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

Это может случиться:

  • при merge-режиме (git pull или git pull --merge);
  • при rebase-режиме (git pull --rebase);
  • как при обновлении основной ветки, так и при работе с feature-веткой.

Типичная ситуация:

  1. Коллега меняет функцию calculateTotal и пушит это в origin/main.
  2. Вы, не обновив ветку, тоже меняете calculateTotal.
  3. При git pull Git пытается совместить изменения и видит конфликт.

Как выглядит конфликт в файлах

После неудачного git pull Git пометит конфликтующие файлы специальными маркерами. Например, в файле order.go вы увидите:

// calculateTotal.go

func calculateTotal(order Order) float64 {
<<<<<<< HEAD
    // Ваша локальная версия функции
    total := 0.0
    for _, item := range order.Items {
        total += item.Price * float64(item.Quantity)
    }
    return total
=======
    // Версия, пришедшая из origin/main
    total := 0.0
    for _, item := range order.Items {
        total += item.PriceWithDiscount()
    }
    return total
>>>>>>> origin/main
}

Комментарии к маркерам:

  • <<<<<<< HEAD
    // Начало блока вашей локальной версии (HEAD — это текущий коммит)

  • =======
    // Разделитель между вашими и удалёнными изменениями

  • >>>>>>> origin/main
    // Конец блока, показывающий, что ниже была версия из origin/main

Ваша задача — вручную отредактировать эти места и оставить только итоговый корректный код.

Пошаговое разрешение конфликта

Давайте разберёмся на практике, как действовать.

  1. Выполняете git pull, получаете сообщение о конфликте:
Auto-merging order.go
CONFLICT (content): Merge conflict in order.go
Automatic merge failed; fix conflicts and then commit the result.
  1. Проверяете список файлов с конфликтами:
git status

// Покажет файлы в состоянии unmerged
// Они будут отмечены как both modified

  1. Открываете файл и решаете, какая версия нужна, либо объединяете изменения. Например, объединённая версия может выглядеть так:
func calculateTotal(order Order) float64 {
    // Итоговая версия после ручного слияния
    total := 0.0
    for _, item := range order.Items {
        // Используем скидку, как в удалённой версии
        total += item.PriceWithDiscount()
    }

    // Допустим, вы хотите добавить логирование из своей версии
    // logTotal(total)

    return total
}

// Здесь вы извлекли полезное из обеих версий
// Комментарии и логика остались осмысленными

Важно: удалите все маркеры <<<<<<<, =======, >>>>>>>. Они не должны остаться в коде.

  1. Отмечаете файл как исправленный:
git add order.go

// Сообщаете Git, что конфликт в этом файле решён

  1. Если конфликт был во время merge (стандартный git pull):
git commit

// Завершаете merge, создавая merge-коммит с вашими решениями конфликтов

Если конфликт был во время git pull --rebase:

git rebase --continue

// Говорите Git продолжить rebase после того, как вы исправили конфликт
// При необходимости повторяете шаги, если конфликты есть в следующих коммитах

Отмена git pull, если всё пошло не так

Иногда проще откатить попытку git pull и начать заново.

Если это был merge-пулл

Если вы ещё не завершили merge-коммит (то есть не сделали commit после конфликта), можно выполнить:

git merge --abort

// Отменить текущий процесс слияния
// Вернуться к состоянию ветки до git pull

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

Если это был rebase-пулл

При git pull --rebase в случае проблем можно сделать:

git rebase --abort

// Отменить процесс rebase
// Вернуться к состоянию до git pull --rebase

Если вы уже завершили pull (commit или завершённый rebase), для отката потребуется использовать git reflog и git reset, но это уже более продвинутая тема.

Настройки и расширенные возможности git pull

Работа с несколькими remotes

В одном локальном репозитории может быть несколько удалённых:

  • origin — основной репозиторий;
  • upstream — исходный репозиторий, из которого был форк;
  • ещё какие-то дополнительные репозитории команды.

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

git remote -v

// Показывает все удалённые репозитории с их URL
// Для каждого remote есть адрес для fetch и для push

Например:

origin  git@github.com:your-org/app.git (fetch)
origin  git@github.com:your-org/app.git (push)
upstream  git@github.com:main-org/app.git (fetch)
upstream  git@github.com:main-org/app.git (push)

Теперь вы можете получать изменения с нужного remote:

# Получить изменения из origin для текущей ветки
git pull origin main

# Получить изменения из upstream для текущей ветки
git pull upstream main

// В обоих случаях изменения будут влиты в вашу текущую локальную ветку
// Но источник изменений будет разный

Важно понимать, что git pull всегда обновляет только текущую ветку, а не все ветки сразу.

Управление поведением с помощью конфигурации

Помимо глобальных настроек pull.rebase и pull.ff, можно настраивать поведение git pull по веткам.

Например, установить для конкретной ветки выполнение pull с rebase:

# Для текущей ветки
git config branch.main.rebase true

// Теперь в ветке main команда git pull будет работать как git pull --rebase
// Но это не затронет другие ветки

Проверить текущие настройки можно так:

git config --show-origin pull.rebase
git config --show-origin pull.ff

// Покажет, где именно задана настройка (глобально или в локальном конфиге репозитория)

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

Есть несколько ситуаций, когда лучше разделить fetch и merge вручную:

  1. Вам нужно сначала посмотреть, что изменилось на сервере, и только потом решать, вливать это или нет.

    Тогда вы делаете:

    git fetch origin
    git log HEAD..origin/main
    

    // Сначала забираете изменения
    // Потом смотрите список новых коммитов между вашей веткой и origin/main
    // Только после анализа принимаете решение, как и когда объединять

  2. Вы хотите использовать нестандартную стратегию merge, например ours, theirs, или дополнительные опции.

    Тогда после fetch вы вызываете merge с нужными параметрами:

    git fetch origin
    git merge --strategy=ours origin/main
    

    // Пример - стратегия ours берёт конфликтующие участки из вашей ветки
    // и игнорирует конфликтующие изменения из origin/main

  3. Вам нужно обновить несколько веток пошагово, а не только текущую.

В таких случаях удобнее сначала использовать git fetch, а потом вручную управлять слияниями или rebase.

Рекомендованные практики при работе с git pull

Явно задавайте желаемое поведение

Чтобы избежать неожиданных merge-коммитов и переписанной истории, имеет смысл сознательно выбрать один из стилей работы:

  • предпочитаете аккуратную линейную историю — используйте rebase;
  • предпочитаете строгое fast-forward без автослияний — используйте ff-only;
  • хотите видеть все точки слияния — используйте merge.

Примеры настройки:

# Линейная история через rebase для всех репозиториев
git config --global pull.rebase true

# Или более строгий вариант - только fast-forward
git config --global pull.ff only

Обновляйте основную ветку перед созданием feature-веток

Последовательность:

git checkout main
git pull --ff-only   # или git pull --rebase, если так удобнее
git checkout -b feature/new-report

// Так вы создаёте новую ветку от самого свежего состояния main
// Меньше шансов получить конфликты позже

Не смешивайте незакоммиченные изменения с git pull

Если у вас есть незакоммиченные изменения и вы вызываете git pull, есть риск:

  • получить конфликт сразу в грязном рабочем дереве;
  • запутаться, где ваши локальные несохранённые правки, а где изменения из удалённого репозитория.

Лучше сделать так:

  1. Закоммитить изменения:

    git add .
    git commit -m "Describe your changes"
    git pull --rebase
    
  2. Или временно спрятать изменения:

    git stash        # Спрятать незакоммиченные изменения
    git pull         # Обновиться с сервера
    git stash pop    # Вернуть изменения поверх обновлённой ветки
    

Всегда читайте сообщения Git при ошибках

Если git pull завершился с ошибкой, Git почти всегда даёт понятную подсказку:

  • предложит выполнить git merge --abort или git rebase --abort;
  • покажет, какие файлы в конфликте;
  • подскажет, какой режим можно включить в конфигурации.

Не игнорируйте эти сообщения, в них часто есть точное решение.

Заключение

Команда git pull — это удобное сокращение для двух операций: получения изменений с удалённого репозитория и их слияния с вашей текущей веткой. Понимая, что за ней стоят git fetch и git merge или rebase, вы можете гораздо точнее управлять историей коммитов и избегать неожиданных конфликтов.

Ключевые моменты:

  • git pull использует настройки отслеживаемых веток, поэтому важно правильно настраивать upstream.
  • Существует несколько режимов работы: merge, rebase и fast-forward only, и их можно задать через флаги или конфиг.
  • Конфликты при git pull — нормальная часть разработки, главное уметь их последовательно и аккуратно решать.
  • В сложных сценариях лучше разделять fetch и merge, чтобы сначала увидеть изменения, а затем осознанно их влить.

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

Частозадаваемые технические вопросы по теме

1. Как запретить git pull создавать merge-коммиты вообще

Если вы хотите, чтобы git pull никогда не создавал merge-коммитов, а либо делал fast-forward, либо завершался с ошибкой, настройте:

git config --global pull.ff only

// Теперь git pull будет работать как git pull --ff-only
// Если fast-forward невозможен, вы увидите ошибку и сможете сами решить, что делать дальше

2. Как сделать, чтобы только одна конкретная ветка использовала rebase при pull

Допустим, вы хотите, чтобы main работала с rebase, а остальные ветки — нет. Сделайте так:

git checkout main
git config branch.main.rebase true

// Для ветки main включён rebase при pull
// Для всех остальных поведение останется по умолчанию

3. Почему после git pull origin main я вижу сообщение You have divergent branches

Это значит, что локальная ветка и origin/main разошлись: у каждой есть свои уникальные коммиты. Git не может автоматически решить, использовать merge, rebase или прервать. Чтобы исправить:

# Явно выбираете стратегию
git pull --rebase origin main
# или
git pull --merge origin main
# или настраиваете поведение в конфиге, как предлагает сообщение об ошибке

4. Как обновить локальную ветку так, чтобы она полностью совпала с origin, игнорируя локальные изменения

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

git fetch origin
git checkout main
git reset --hard origin/main

// Локальная ветка main будет точно соответствовать origin/main
// Все локальные неотправленные коммиты в main пропадут

5. Что делать, если после git pull я хочу вернуться к состоянию до него, но уже успел закоммитить merge

Если вы уже создали merge-коммит, поможет git reflog:

git reflog

// Найдите запись с состоянием до git pull, например HEAD@{2}

Затем:

git reset --hard HEAD@{2}

// Вернёт ветку к выбранному состоянию до pull
// Будьте осторожны - локальные изменения после этого шага будут потеряны

Стрелочка влевоОтправка изменений git pushПолучение изменений в Git с помощью git fetchСтрелочка вправо

Постройте личный план изучения Git до уровня Middle — бесплатно!

Git — часть карты развития Frontend

  • step100+ шагов развития
  • lessons30 бесплатных лекций
  • lessons300 бонусных рублей на счет

Бесплатные лекции

Все гайды по Git

Открыть базу знаний

Лучшие курсы по теме

изображение курса

Основы Git

Антон Ларичев
AI-тренажеры
Гарантия
Бонусы
иконка звёздочки рейтинга4.9
3 999 ₽ 6 990 ₽
Подробнее
изображение курса

TypeScript с нуля

Антон Ларичев
AI-тренажеры
Практика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.8
3 999 ₽ 6 990 ₽
Подробнее
изображение курса

Next.js - с нуля

Антон Ларичев
AI-тренажеры
Практика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.7
3 999 ₽ 6 990 ₽
Подробнее

Отправить комментарий