↓Перейти к содержанию
  1. Git: руководства и команды/

error: unable to update local ref — причины и решение

·8 минут·

Если вы когда-нибудь видели одну из этих ошибок при git fetch или git pull, вы знаете, как она раздражает:

error: cannot lock ref 'refs/remotes/origin/feature/auth':
'refs/remotes/origin/feature' exists; cannot create...

или

error: unable to update local ref 'refs/remotes/origin/master'

Эта ошибка указывает на конфликт в структуре refs (ссылок) Git. Давайте разберемся, почему это происходит и как это исправить.

Что такое refs/remotes/origin/ #

Git хранит информацию о ветках удалённого репозитория в специальной директории:

.git/refs/remotes/origin/
├── master
├── develop
├── feature
│   ├── auth      ← Это может быть файл или директория!
│   └── login
└── bugfix
    └── cache

Каждая ветка обычно представлена одним файлом, содержащим хеш последнего коммита. Но в некоторых ситуациях возникает конфликт: Git пытается создать директорию, когда уже существует файл с тем же именем (или наоборот).

Давайте посмотрим на директорию .git #

# Посмотреть содержимое
ls -la .git/refs/remotes/origin/

# Результат может быть такой:
# -rw-r--r-- feature
# drwxr-xr-x feature/

# Видна проблема? feature — одновременно и файл, и директория!

Причина 1: конфликт имён веток (самая частая) #

Это самая распространённая причина ошибки. Вот как она возникает:

Сценарий возникновения конфликта #

  1. Исходное состояние: На удалённом сервере есть ветка feature, и у вас локально есть ref:
.git/refs/remotes/origin/feature  ← Это файл
  1. На удалённом сервере удалили ветку feature, но создали ветки feature/auth и feature/login:
Удалённо (GitHub):
- feature        ← удалена
+ feature/auth   ← новая
+ feature/login  ← новая
  1. Локально у вас всё ещё есть старая ссылка:
.git/refs/remotes/origin/feature  ← Файл, указывающий на старый коммит
  1. При следующем git fetch Git пытается создать директорию feature/ для новых веток feature/auth и feature/login, но не может, потому что feature — это файл:
$ git fetch origin
error: cannot lock ref 'refs/remotes/origin/feature/auth':
'refs/remotes/origin/feature' exists; cannot create...

Визуальное представление проблемы #

ДО УДАЛЕНИЯ:                ПОСЛЕ УДАЛЕНИЯ:        ЛОКАЛЬНО:
origin/feature              origin/feature/auth    origin/feature (старая!)
  ↓ файл                       ↓ файл              ↓ файл (конфликт!)
abc1234                     cba4321

origin/feature/login
  ↓ файл
xyz9876

Решение: git remote prune origin #

Команда git remote prune origin удаляет локальные refs на ветки, которые были удалены на удалённом сервере:

# Удалить все stale (устаревшие) ветки
git remote prune origin

# Результат:
# Pruning origin
# URL: https://github.com/user/repo.git
# * [pruned] origin/feature
# * [pruned] origin/old-bugfix
# * [pruned] origin/deprecated-feature

После prune #

После очистки вы сможете нормально делать fetch:

# Теперь fetch работает
git fetch origin

# И видны новые ветки
git branch -r
# origin/develop
# origin/feature/auth
# origin/feature/login

Полный процесс решения #

# 1. Удалить stale branches
git remote prune origin

# 2. Теперь делать fetch
git fetch origin

# 3. Проверить результат
git branch -r | grep feature

# Результат:
# origin/feature/auth
# origin/feature/login

Причина 2: stale branches — что это такое #

Stale branch (устаревшая ветка) — это локальная ref на ветку, которая была удалена на удалённом сервере, но информация об этом ещё не синхронизирована локально.

Как возникают stale branches #

# Коллега создал ветку на сервере
git fetch origin
git branch -r
# origin/feature-cool  ← новая ветка

# Коллега закончил работу и удалил ветку на GitHub
# Вы не знаете об этом и по-прежнему видите:
git branch -r
# origin/feature-cool  ← всё ещё видна локально!

Как проверить stale branches #

# Стандартное отображение может быть обманчиво:
git branch -r

# Лучше проверить детально:
git branch -r --list

# Или посмотреть напрямую в файловой системе:
ls -la .git/refs/remotes/origin/

Почему Git не удаляет их автоматически #

Git не удаляет stale branches автоматически по нескольким причинам:

  1. Безопасность — не случайно удалить важные ссылки
  2. Локальная работа — может быть локальная ветка, основанная на этой ref
  3. Контроль — вы сами решаете, когда очищать

Решение: git fetch –prune #

Команда git fetch --prune (или -p) комбинирует fetch и prune в одно действие:

# Сделать fetch и удалить stale refs в одно действие
git fetch --prune

# Короткая форма
git fetch -p

# С указанием удалённого репозитория
git fetch --prune origin

Разница между командами #

КомандаЧто делает
git remote prune originУдаляет stale refs, но не делает fetch
git fetch --pruneДелает fetch И удаляет stale refs
git fetch originТолько fetch, stale refs остаются

Пример использования #

# Сценарий:
# 1. Коллега удалил feature/old на GitHub
# 2. Вы работаете локально в своей ветке

git fetch --prune
# Fetching from github
# Pruning refs/remotes/origin
# * [deleted] origin/feature/old

# Теперь ваши локальные refs синхронизированы с удалённым
git branch -r
# origin/develop
# origin/feature/auth
# (feature/old больше не видна)

Профилактика: автоматическая очистка при fetch #

Вместо того чтобы каждый раз помнить о --prune, настройте автоматическую очистку:

# Сделать прунинг по умолчанию при fetch
git config --global fetch.prune true

# Только для текущего репозитория:
git config fetch.prune true

После этого каждый git fetch будет автоматически удалять stale branches:

# Автоматически с прунингом
git fetch

# Эквивалентно:
# git fetch --prune

Проверить текущую настройку #

# Проверить глобальную настройку
git config --global fetch.prune

# Проверить для репозитория
git config fetch.prune

Причина 3: lock файл от упавшего процесса #

Иногда Git процесс крашится во время работы, оставляя .lock файлы:

.git/refs/remotes/origin/feature.lock  ← lock файл

Git не может работать с файлом, пока существует .lock файл.

Как найти и удалить lock файлы #

# Найти все lock файлы в репозитории
find .git -name "*.lock"

# Результат:
# .git/refs/heads/master.lock
# .git/refs/remotes/origin/feature.lock
# .git/index.lock

# Удалить их (убедитесь, что нет других git процессов!)
find .git -name "*.lock" -delete

# Или вручную удалить конкретный файл:
rm .git/refs/remotes/origin/feature.lock

Осторожно! #

Перед удалением .lock файлов убедитесь, что нет запущенных git процессов:

# Проверить процессы Git
ps aux | grep git

# Если процессы есть, дождитесь их завершения или убейте их:
pkill git

Причина 4: проблемы с правами доступа #

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

Проверить права доступа #

# Посмотреть права на директорию refs
ls -la .git/refs/remotes/origin/

# Результат (обратите внимание на rwx):
# drwxr-xr-x  origin  ← может быть проблема если нет w (write)

# Проверить права на сам .git:
ls -la .git/

Исправить права доступа #

# Дать права на запись для текущего пользователя
chmod -R u+rw .git/refs/remotes/

# Или для всей папки .git:
chmod -R u+rw .git/

В Docker контейнерах #

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

# Проверить владельца
ls -la .git/refs/

# Изменить владельца
chown -R $(id -u):$(id -g) .git/

Универсальный рецепт диагностики #

Если у вас есть ошибка с ref, используйте этот пошаговый процесс:

# Шаг 1: Попытайтесь прунить stale refs
git remote prune origin
git fetch origin

# Если не помогло, переходим к шагу 2

# Шаг 2: Проверьте наличие lock файлов
find .git -name "*.lock" -ls

# Если найдены, удалите их:
find .git -name "*.lock" -delete

# Попытайтесь fetch снова
git fetch origin

# Если не помогло, шаг 3

# Шаг 3: Проверьте права доступа
ls -la .git/refs/

# Если права неправильные:
chmod -R u+rw .git/refs/

# Шаг 4: Полная очистка Git данных
git gc --prune=now

# Шаг 5: Если ничего не помогло, переинициализируйте кеш отсылки
rm -rf .git/refs/remotes/
git fetch origin

Полный пример решения конфликта #

Давайте пройдём через реальный сценарий:

# Вы видите ошибку при fetch:
$ git fetch origin
error: cannot lock ref 'refs/remotes/origin/feature/auth'

# Шаг 1: Посмотрите, в чём проблема
$ ls -la .git/refs/remotes/origin/ | head -10
-rw-r--r--  feature      ← Это файл
drwxr-xr-x  feature      ← Это директория (конфликт!)

# Шаг 2: Удалите старый файл feature
$ git remote prune origin
Pruning origin
* [pruned] origin/feature

# Шаг 3: Проверьте структуру
$ ls -la .git/refs/remotes/origin/ | head -10
drwxr-xr-x  feature      ← Теперь только директория

# Шаг 4: Делайте fetch
$ git fetch origin
From github.com:user/repo
* [new branch]      feature/auth -> origin/feature/auth
* [new branch]      feature/login -> origin/feature/login

# Успех!

Часто задаваемые вопросы (FAQ) #

В: Что такое stale branches?

О: Stale branches — это локальные ссылки на ветки, которые были удалены на удалённом сервере. Git сохраняет информацию о них локально для безопасности, но это может привести к конфликтам. Удалите их с помощью git remote prune origin или git fetch --prune.

В: В чём разница между git fetch --prune и git remote prune origin?

О:

  • git fetch --prune — делает fetch И удаляет stale refs одновременно
  • git remote prune origin — только удаляет stale refs, не делает fetch

Используйте git fetch --prune для быстроты.

В: Эта ошибка только локальная или она затрагивает всех в команде?

О: Это только локальная проблема. Ошибка возникает на вашей машине из-за конфликта в локальной структуре refs. Для других разработчиков в команде эта проблема может не возникнуть (или они могут их решить независимо).

В: Безопасно ли установить fetch.prune = true глобально?

О: Да, это абсолютно безопасно! Настройка git config --global fetch.prune true просто удаляет stale refs автоматически. Это не влияет на ваши локальные ветки, только на remote-tracking ветки (refs/remotes/origin/*).

В: После git remote prune некоторые ветки исчезли. Как их восстановить?

О: Если вы удалили важные refs, их можно восстановить из рефлога:

# Посмотреть reflog для удалённых веток
git reflog show origin/important-branch

# Если ветка есть в reflog, вы можете восстановить её
git branch important-branch <commit-hash>

# Если её вообще нет, проверьте на удалённом сервере
git fetch origin important-branch

Однако если ветка была действительно удалена на сервере, восстановить её локально невозможно.

Практические команды для частого использования #

Вот набор команд, которые полезно знать:

# Полная диагностика и исправление
git fetch --prune
git gc --prune=now

# Увидеть все remote branches
git branch -r

# Удалить конкретную remote branch локально
git branch -r -d origin/old-branch

# Установить автоматический prune при fetch
git config --global fetch.prune true

# Просмотреть все stale branches
git branch -r --list | while read branch; do
  if ! git ls-remote --heads origin "${branch#origin/}" | grep -q "${branch#origin/}"; then
    echo "$branch is stale"
  fi
done

Внутренние ссылки #

Изучите связанные темы:

  • /ru/git/git-fetch/ — полное руководство по git fetch
  • /ru/git/udalennye-vetki-git/ — работа с удалёнными ветками
  • /ru/git/git-prune/ — детально о git prune

Заключение #

Ошибка “cannot lock ref” — это обычно результат конфликта в структуре refs из-за удаления веток на сервере. Решение простое:

  1. Использовать git fetch --prune — это решит большинство случаев
  2. Включить автоматический prune — git config --global fetch.prune true
  3. При необходимости вручную удалить lock файлы — find .git -name "*.lock" -delete

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