Как мне отправить исправленный коммит в удаленный репозиторий Git? - PullRequest
586 голосов
/ 31 октября 2008

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

> git commit --amend

К сожалению, фиксация не может быть перенесена обратно в хранилище. Отклонено так:

> git push origin
To //my.remote.repo.com/stuff.git/
 ! [rejected]        master -> master (non-fast forward)
error: failed to push some refs to '//my.remote.repo.com/stuff.git/'

Что мне делать? (Я могу получить доступ к удаленному хранилищу.)

Ответы [ 14 ]

459 голосов
/ 11 января 2009

Я на самом деле однажды нажал на --force и .git хранилище и получил от Линуса БОЛЬШОЕ ВРЕМЯ . В целом это создаст много проблем для других людей. Простой ответ: «Не делай этого».

Я вижу, что другие так или иначе дали рецепт для этого, поэтому я не буду повторять их здесь. Но вот совет для восстановления ситуации после , когда вы вытолкнули исправленный коммит с помощью --force (или + master).

  1. Используйте git reflog, чтобы найти старый коммит, который вы изменили (назовите его old, и мы назовем новый коммит, который вы создали, изменив new).
  2. Создать слияние между old и new, записав дерево new, как git checkout new && git merge -s ours old.
  3. Объедините это с вашим мастером с git merge master
  4. Обновите ваш мастер с результатом с git push . HEAD:master
  5. Подтолкнуть результат.

Тогда люди, которые были достаточно неудачливы, чтобы основывать свою работу на коммите, который вы уничтожили, исправив и форсировав толчок, увидят, что в результате слияния вы увидите, что вы поддерживаете new над old. Их более поздние слияния не приведут к конфликтам между old и new, возникшим в результате вашего исправления, поэтому им не придется страдать.

253 голосов
/ 01 ноября 2008

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

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

Если вы знаете, что вы единственный, кто нажимает, и хотите отправить исправленный коммит или выдать коммит, который свернет ветку, вы можете «заставить» Git обновить удаленную ветку с помощью переключателя -f.

git push -f origin master

Даже это может не сработать, так как Git позволяет удаленным репозиториям отклонять не-быстрые форсировки на дальнем конце, используя переменную конфигурации receive.denynonfastforwards. В этом случае причина отказа будет выглядеть следующим образом (обратите внимание на часть «удаленный отказ»):

 ! [remote rejected] master -> master (non-fast forward)

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

git push origin :master
git push origin master

Как правило, последний параметр git push использует формат <local_ref>:<remote_ref>, где local_ref - это имя ветви в локальном хранилище, а remote_ref - имя ветви в удаленном хранилище. Эта пара команд использует два сокращения. :master имеет нулевой local_ref, что означает перемещение нулевой ветви на удаленную сторону master, т.е. удаление удаленной ветви. Имя ветви без : означает передачу локальной ветви с заданным именем в удаленную ветку с тем же именем. master в этой ситуации коротко для master:master.

201 голосов
/ 22 сентября 2009

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

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

После разрешения любых конфликтов вы можете нажать еще раз.

Итак:

git pull

Если вы получаете ошибки в pull, возможно, что-то не так в вашей конфигурации локального репозитория (у меня был неправильный ref в разделе ветки .git / config).

и после

git push

Может быть, вы получите дополнительный коммит с темой, рассказывающей о «тривиальном слиянии».

97 голосов
/ 31 октября 2008

Краткий ответ: не выдвигайте исправленные коммиты в публичное репо.

Длинный ответ: Несколько команд Git, таких как git commit --amend и git rebase, на самом деле переписывают граф истории. Это нормально до тех пор, пока вы не опубликовали свои изменения, но как только вы это сделаете, вам действительно не стоит копаться в истории, потому что, если кто-то уже получил ваши изменения, тогда, когда они попытаются повторить попытку, это может привести к неудаче , Вместо внесения изменений в коммит, вы должны просто сделать новый коммит с изменениями.

Однако, если вы действительно, действительно хотите отправить исправленный коммит, вы можете сделать это следующим образом:

$ git push origin +master:master

Ведущий знак + заставит произойти толчок, даже если он не приведет к фиксации "ускоренной перемотки вперед". (Ускоренная фиксация происходит, когда изменения, которые вы отправляете, являются прямым потомком изменений, уже находящихся в публичном репо.)

43 голосов
/ 21 июня 2015

Вот очень простой и понятный способ отправить ваши изменения после того, как вы уже сделали commit --amend:

git reset --soft HEAD^
git stash
git push -f origin master
git stash pop
git commit -a
git push origin master

Что делает следующее:

  • Сброс заголовка ветви до родительского коммита.
  • Спрятать этот последний коммит.
  • Принудительное нажатие на пульт. Удаленный теперь не имеет последнего коммита.
  • Скинь свой сундук.
  • Совершайте чисто.
  • Нажмите на пульте.

Не забудьте изменить «origin» и «master», если применяете это к другой ветке или к удаленному.

21 голосов
/ 24 сентября 2012

Я решил это, отбросив свой локальный исправленный коммит и добавив новые изменения сверху:

# Rewind to commit before conflicting
git reset --soft HEAD~1

# Pull the remote version
git pull

# Add the new commit on top
git add ...
git commit
git push
7 голосов
/ 20 февраля 2014

У меня была такая же проблема.

  • Случайно изменил последний коммит, который уже был передан
  • Сделано много локальных изменений, совершено около пяти раз
  • Пытался нажать, получил ошибку, запаниковал, объединил удаленный, получил много не-моих файлов, нажал, потерпел неудачу и т.д.

Будучи новичком в Git, я думал, что он закончен FUBAR .

Решение: В некотором смысле @bara предложил + создал локальную резервную ветвь

# Rewind to commit just before the pushed-and-amended one.
# Replace <hash> with the needed hash.
# --soft means: leave all the changes there, so nothing is lost.
git reset --soft <hash>

# Create new branch, just for a backup, still having all changes in it.
# The branch was feature/1234, new one - feature/1234-gone-bad
git checkout -b feature/1234-gone-bad

# Commit all the changes (all the mess) not to lose it & not to carry around
git commit -a -m "feature/1234 backup"

# Switch back to the original branch
git checkout feature/1234

# Pull the from remote (named 'origin'), thus 'repairing' our main problem
git pull origin/feature/1234

# Now you have a clean-and-non-diverged branch and a backup of the local changes.
# Check the needed files from the backup branch
git checkout feature/1234-gone-bad -- the/path/to/file.php

Возможно, это не быстрое и чистое решение, и я потерял свою историю (1 коммит вместо 5), но это спасло работу за день.

5 голосов
/ 07 июня 2016

Если вы знаете, что никто не снял ваш неизмененный коммит, используйте параметр --force-with-lease git push.

В TortoiseGit вы можете сделать то же самое в «Push ...» опциях «Force: May Discard» и проверяя «известные изменения».

Force (может отменить известные изменения) позволяет удаленному репозиторию принимать более безопасное ускорение без ускоренной пересылки. Это может привести к потере коммитов в удаленном репозитории; используйте это с осторожностью. Это может предотвратить потерю неизвестных изменений от других людей на пульте. Он проверяет, указывает ли ветвь сервера на тот же коммит, что и на ветку удаленного отслеживания (известные изменения). Если да, будет выполнен принудительный толчок. В противном случае он будет отклонен. Поскольку в git нет тегов удаленного отслеживания, теги нельзя перезаписать с помощью этой опции.

5 голосов
/ 13 января 2015

Если вы не отправили код в удаленную ветку (GitHub / Bitbucket), вы можете изменить сообщение коммита в командной строке, как показано ниже.

 git commit --amend -m "Your new message"

Если вы работаете над определенной веткой, сделайте это:

git commit --amend -m "BRANCH-NAME: new message"

Если вы уже отправили код с неправильным сообщением, вам следует быть осторожным при изменении сообщения. Т.е. после того, как вы измените сообщение коммита и попытаетесь нажать его снова, у вас возникнут проблемы. Чтобы сделать это гладко, выполните следующие шаги.

Пожалуйста, прочитайте весь ответ, прежде чем делать это

git commit --amend -m "BRANCH-NAME : your new message"

git push -f origin BRANCH-NAME                # Not a best practice. Read below why?

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

 git commit --amend -m "BRANCH-NAME : your new message"
 git pull origin BRANCH-NAME
 git push -f origin BRANCH-NAME

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

2 голосов
/ 21 января 2016

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

git push -f origin branch_name

Также убедитесь, что вы извлекаете код из удаленного пункта, поскольку кто-то из вашей команды мог отправить его в ту же ветку.

git pull origin branch_name

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

...