Другое

Исправление ошибки Git push: удаленный сервер отклонил ветку master

Исправление ошибки Git push 'remote rejected master -> master (branch is currently checked out)' с практическими решениями для небазовых репозиториев. Узнайте несколько подходов, включая преобразование в базовый репозиторий и параметры конфигурации.

Как исправить ошибку Git push “remote rejected master -> master (branch is currently checked out)” при отправке изменений в небезветвенный репозиторий?

Я столкнулся с ошибкой Git push при попытке отправить изменения с локальной машины на удаленный репозиторий. Сообщение об ошибке гласит:

error: refusing to update checked out branch: refs/heads/master
error: By default, updating the current branch in a non-bare repository
error: is denied, because it will make the index and work tree inconsistent
error: with what you pushed, and will require 'git reset --hard' to match
error: the work tree to HEAD.

Моя настройка:

  • Исходная машина: 192.168.1.2 (версия Git 1.7)
  • Целевая машина: 192.168.1.1 (версия Git 1.5)
  • Я могу успешно клонировать репозиторий с исходной машины на целевую
  • После внесения изменений и коммита, отправка (push) завершается ошибкой, указанной выше

Какие возможные решения существуют для устранения этой проблемы? Нужно ли настраивать удаленный репозиторий иначе, или следует использовать другой подход?

Ошибка Git push “remote rejected master -> master (branch is currently checked out)” возникает потому, что Git предотвращает прямые push-и в текущую ветку в небезрепозиториях, чтобы защитить рабочую директорию от повреждений. Это поведение по умолчанию гарантирует, что индекс и рабочее дерево остаются согласованными с состоянием репозитория. Вы можете решить эту проблему, преобразовав удаленный репозиторий в bare-репозиторий, настроив его для принятия push-ей или используя альтернативные рабочие процессы, которые избегают этого конфликта.

Содержание

Понимание ошибки

Сообщение об ошибке, с которым вы столкнулись, является фундаментальным механизмом защиты Git:

error: refusing to update checked out branch: refs/heads/master
error: By default, updating the current branch in a non-bare repository
error: is denied, because it will make the index and work tree inconsistent
error: with what you pushed, and will require 'git reset --hard' to match
error: the work tree to HEAD.

Это происходит потому, что Git защищает небезрепозитории от прямых push-ей в текущую ветку. Эта защита предотвращает повреждение рабочей директории, гарантируя, что индекс и рабочее дерево остаются согласованными с состоянием репозитория.

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

Ошибка возникает именно потому, что:

  • Ваш удаленный репозиторий является небезрепозиторием (имеет рабочую директорию)
  • Ветка master в данный момент извлечена в удаленном репозитории
  • Git предотвращает прямые обновления для избежания несогласованности состояния

Решение 1: Преобразование в bare-репозиторий

Самым распространенным и рекомендуемым решением является преобразование вашего удаленного репозитория в bare-репозиторий. Bare-репозитории не имеют рабочих директорий, поэтому они могут безопасно принимать push-и в любую ветку.

Шаги для преобразования:

  1. Остановите все процессы, использующие репозиторий на целевой машине
  2. Создайте резервную копию вашего текущего репозитория
  3. Инициализируйте новый bare-репозиторий:
    bash
    cd /path/to/your/repo
    git init --bare --shared
    
  4. Обновите URL локального удаленного репозитория:
    bash
    git remote set-url origin /path/to/new/bare/repo.git
    

Примечание: Согласно Stack Overflow, bare-репозитории никогда не имеют извлеченных веток, поэтому вы всегда можете отправлять push в любую ветку bare-репозитория.

Преимущества:

  • Четкое разделение хранения репозитория и рабочей директории
  • Стандартная практика для удаленных репозиториев
  • Нет риска повреждения рабочей директории

Недостатки:

  • Нельзя напрямую редактировать файлы в репозитории
  • Требуется дополнительная настройка для рабочих директорий

Решение 2: Настройка receive.denyCurrentBranch

Вы можете настроить удаленный репозиторий для принятия push-ей, изменив параметр receive.denyCurrentBranch.

Доступные варианты:

  1. updateInstead (Рекомендуется для небезрепозиториев):

    bash
    cd /path/to/remote/repo
    git config receive.denyCurrentBranch updateInstead
    
  2. ignore (Менее безопасно):

    bash
    git config receive.denyCurrentBranch ignore
    
  3. warn (Разрешает push, но показывает предупреждение):

    bash
    git config receive.denyCurrentBranch warn
    

Важно: Как упоминается в GitHub gist, установка receive.denyCurrentBranch в ignore или warn позволяет отправлять push в текущую ветку удаленного репозитория, но может потребоваться git reset --hard для согласования рабочей директории с HEAD.

Поведение updateInstead:

  • Автоматически обновляет рабочую директорию при получении push-ей
  • Поддерживает согласованность между репозиторием и рабочей директорией
  • Доступно с версии Git 2.3.0 (февраль 2015)

Примечание: Некоторые старые версии Git могут не поддерживать updateInstead. Как отмечено на форуме Atlassian, если вы столкнулись с ошибкой “fatal: bad config value for ‘receive.denycurrentbranch’”, вам может потребоваться использовать refuse или ignore вместо этого.


Решение 3: Push в другие ветки

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

Шаги:

  1. Отправьте push во временную ветку:

    bash
    git push origin master:temp-branch
    
  2. На удаленной машине переключитесь на целевую ветку и выполните слияние:

    bash
    git checkout master
    git merge temp-branch
    git branch -d temp-branch  # Опционально: удалить временную ветку
    

Альтернативный подход: Как предложено на Stack Overflow, вы можете использовать git push origin master:foo и выполнить слияние в удаленном репозитории.

Преимущества:

  • Не требуются изменения в конфигурации
  • Безопасно - избегает повреждения рабочей директории
  • Работает с любой версией Git

Недостатки:

  • Требуются дополнительные шаги
  • Ручной процесс, который может быть подвержен ошибкам

Решение 4: Использование post-receive хуков

Для более сложных рабочих процессов вы можете использовать Git хуки для автоматизации процесса обновления.

Шаги настройки:

  1. Создайте post-receive хук:

    bash
    cd /path/to/remote/repo/.git/hooks
    cat > post-receive << 'EOF'
    #!/bin/sh
    unset GIT_INDEX_FILE
    unset GIT_WORK_TREE
    git checkout -f
    EOF
    
  2. Сделайте хук исполняемым:

    bash
    chmod +x post-receive
    
  3. Настройте репозиторий (при необходимости):

    bash
    git config core.bare false
    git config receive.denyCurrentBranch ignore
    

Примечание: Как упоминается в ответе на Super User, вы можете настроить хуки для автоматической обработки процесса извлечения.

Пример расширенного хука:

bash
#!/bin/sh
# Этот хук будет автоматически обновлять рабочую директорию
# при получении push

# Сброс рабочей директории в соответствии с новым HEAD
git reset --hard

Решение 5: Принудительный push с осторожностью

В некоторых случаях вам может потребоваться использовать принудительный push, но это следует делать с крайней осторожностью.

Команда принудительного push:

bash
git push -f origin master

Предупреждение: Как отмечено на Stack Overflow, принудительный push требует, чтобы denyCurrentBranch был проигнорирован. Это может привести к потере данных, если не обращаться с этим должным образом.

Когда использовать принудительный push:

  • Только при крайней необходимости
  • После обеспечения отсутствия незафиксированных изменений на удаленном репозитории
  • Когда вы понимаете связанные риски

Лучшие практики и рекомендации

Рекомендуемое решение в зависимости от вашей настройки:

Учитывая вашу настройку с разными версиями Git (1.7 и 1.5), я рекомендую:

  1. В долгосрочной перспективе: Преобразовать в bare-репозиторий (Решение 1)
  2. Для немедленных нужд: Использовать receive.denyCurrentBranch updateInstead (Решение 2)

Соображения совместимости версий:

  • Git 1.5: Может не поддерживать updateInstead - используйте ignore вместо этого
  • Git 1.7: Поддерживает updateInstead - это предпочтительный вариант
  • Современный Git (2.3+): Полная поддержка updateInstead

Рекомендации для производственной среды:

Согласно Linux Hint, разработчикам следует учитывать следующие лучшие практики:

  • Используйте bare-репозитории для удаленного хранения
  • Храните рабочие директории отдельно от хранилища репозитория
  • Используйте правильные рабочие процессы Git, которые избегают прямых push-ей в извлеченные ветки

Альтернативный рабочий процесс:

Для вашей конкретной настройки рассмотрите этот рабочий процесс:

  1. На целевой машине (192.168.1.1):

    bash
    git init --bare ~/project.git
    
  2. На исходной машине (192.168.1.2):

    bash
    git remote set-url origin user@192.168.1.1:~/project.git
    
  3. Создайте рабочую директорию на целевой машине:

    bash
    git clone ~/project.git ~/project-working
    cd ~/project-working
    

Эта настройка обеспечивает лучшее из двух миров: bare-репозиторий для безопасных push-ей и отдельную рабочую директорию для разработки.

Заключение

Ошибка Git push “remote rejected master -> master (branch is currently checked out)” является механизмом защиты, который предотвращает повреждение рабочей директории. Для решения этой проблемы у вас есть несколько жизнеспособных вариантов:

  1. Преобразовать в bare-репозиторий - Самый надежный вариант для удаленных репозиториев
  2. Настроить receive.denyCurrentBranch - Быстрое исправление для небезрепозиториев
  3. Отправлять push в другие ветки - Безопасный обходной путь без изменений в конфигурации
  4. Использовать post-receive хуки - Расширенная автоматизация для сложных рабочих процессов
  5. Принудительный push с осторожностью - Вариант последней инстанции со значительными рисками

Для вашей конкретной настройки с разными версиями Git я рекомендую преобразовать удаленную машину (192.168.1.1) в bare-репозиторий, сохраняя при этом рабочую директорию отдельно. Этот подход обеспечивает наиболее надежное и поддерживаемое решение, которое работает с разными версиями Git и предотвращает будущие проблемы с согласованностью рабочей директории.

Источники

  1. [Git push error ‘remote rejected] master -> master (branch is currently checked out)’ - Stack Overflow](https://stackoverflow.com/questions/2816369/git-push-error-remote-rejected-master-master-branch-is-currently-checked)
  2. Git Push error: refusing to update checked out branch - Stack Overflow
  3. [How to Fix Git Push Error ‘remote rejected] main’? – Linux Hint](https://linuxhint.com/fix-git-push-error-remote-rejected-master/)
  4. git-denyCurrentBranch-updateInstead.md · GitHub
  5. Push-to-deploy: A nice git workflow for updating server code
  6. A Better Way to Git Push to Deploy (updateInstead & push-to-checkout)
  7. repository - How to setup git repo in order to push as currently the remote reject it? - Super User
  8. Problems with denycurrentbranch setting when pushing commits to origin - Atlassian Community
  9. git - Why doesn’t github throw an error ! [remote rejected] master -> master (branch is currently checked out) and a remote server does? - Stack Overflow
  10. making a remote repo update when changes are pushed to it
Авторы
Проверено модерацией
Модерация