Невозможно понять определенное поведение мерзавца - PullRequest
0 голосов
/ 20 сентября 2019

Я не могу понять, какое поведение демонстрирует git.

У меня есть локальная ветка, скажем, myBranch, которая синхронизируется с удаленной веткой на gitlab с тем же именем.На самом деле я сначала создал ветку локально, а затем отправил ее на удаленный компьютер, используя:

git push -u origin myBranch

Я так понимаю, это также означает, что origin /myBranch также находится в том же коммите, скажем, C1, что и myBranch (надеюсь, я здесь прав).

Теперь я делаю некоторые локальные изменения, а затем добавляю их с помощью поправки.

gitcommit --amend --no-edit.

Таким образом, я изменяю существующий коммит C1, и я ожидаю, что myBranch и origin / myBranch должны указывать на C1.

Но когда ясделать статус git, я получаю:

На ветке myBranch

Ваша ветка и 'origin / myBranch' разошлись, и имеют 1 и 1 разных коммитов, соответственно.(используйте «git pull», чтобы объединить удаленную ветку с вашей)

Почему я должен получить это, когда myBranch и origin / myBranch указывают на C1?

Ответы [ 3 ]

4 голосов
/ 20 сентября 2019

Добавление коммита в локальную ветку не приводит к автоматическому обновлению ветки удаленного отслеживания.Чтобы обновить ветку удаленного отслеживания с помощью дополнительных коммитов в локальной ветке, используйте git push (при условии, что вы уже настроили ветку удаленного отслеживания, что вы сделали, указав -u для начального git push).

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

Причина этого в том, что Git использует криптографиюХэши SHA-1 для уникальной идентификации каждого коммита (вы можете увидеть их в истории коммитов, например, выполнив git log), и некоторые из метаданных коммитов, такие как сообщение и метка времени, также хэшируются вместе с содержимым коммита.По сути, когда вы исправили этот коммит, вы создали новый коммит с другим хешем и заменили старый коммитом.Таким образом, ваша локальная ветвь указывает на эту фиксацию и находится за удаленной, потому что она не содержит старой, а удаленная ветвь находится за вашей локальной веткой, потому что она не содержит новой.

Вам нужно будет принудительно нажать (git push -f), чтобы отправить изменения в удаленный репозиторий - заменив коммит, на который указывает удаленная ветвь, новым.Проблема в том, что если какой-либо другой разработчик перенес эту ветку на свою локальную машину, им будет очень сложно синхронизировать свои изменения, поскольку вы изменили ветку из-под них.

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

2 голосов
/ 20 сентября 2019

Опция --amend, похоже, изменяет существующий коммит. Это не так.

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

...--F--G--H   <-- myBranch

Когда вы делаете новый коммит, высделайте это сначала, выполнив git checkout нужной ветви.Это добавляет специальное имя HEAD к имени этой ветви, и извлекает конкретный коммит - в данном случае H - на который указывает имя.

Затем вы вносите любые изменения, которыеwant (в вашем рабочем дереве), используйте git add (чтобы скопировать обновленные файлы обратно в область index / staging-) и запустите git commit (без --amend).На этом этапе Git:

  • сохраняет файлы индекса / промежуточной области в качестве нового снимка для нового коммита;
  • устанавливает для вас автора и коммиттера нового коммита, а также егоотметки даты и времени для «сейчас»;
  • записывает ваше сообщение в журнал для новой фиксации, а
  • записывает необработанный идентификатор хэша из текущий коммит (для которого мы используем букву H) в новый коммит в качестве его родителя.

Так что теперь у нас есть:

...--F--G--H   <-- myBranch (HEAD)
            \
             I

гдеI это новый коммит.Последний шаг - это сложная часть: ваш Git теперь записывает новый хэш-идентификатор нового коммита (который был просто создан путем создания коммита - мы называем его I здесь, но опять же это какой-то большой уродливый хэш-идентификатор) в имя myBranch, так как это то, к которому прикреплено специальное имя HEAD:

...--F--G--H
            \
             I   <-- myBranch (HEAD)

Если вы теперь git push, то это к другому Git, который вы называете originВаш Git помнит, что его Git myBranch указывает на коммит I:

...--F--G--H
            \
             I   <-- myBranch (HEAD), origin/myBranch

Но теперь вы решаете, что что-то не так с коммитом I.Таким образом, вы вносите некоторые изменения в файлы и git add их, и запускаете git commit --amend. Это не меняет коммит I. Вместо этого он создает еще один новый коммит - назовем его J, но на этот раз вместо установки родительского элемента J в I, Git устанавливаетРодитель J снова H.Затем он записывает фактический хэш-идентификатор J, что бы это ни было, в myBranch:

             J   <-- myBranch (HEAD)
            /
...--F--G--H
            \
             I   <-- origin/myBranch

Это то, что находится в вашем хранилище.Другой репозиторий, в origin, имеет имя myBranch, указывающее на коммит I - тот, который вы дали ему ранее.

Теперь вы должны исправить представление другого репозитория Git о том, где его myBranch должен указать.Вы должны передать ему J коммит, а затем сказать ему: забудьте коммит I, просто сделайте свой myBranch коммит имени J. После того, как вы это сделаете, ваш репозиторий и их репозиторий больше не сможет найти коммит I.Ваш Git будет иметь следующее:

             J   <-- myBranch (HEAD), origin/myBranch
            /
...--F--G--H
            \
             I   [abandoned]

Обратите внимание, что коммит I еще не ушел .Он останется на некоторое время - по крайней мере, на 30 дней по умолчанию - на тот случай, если вы решите, что хотите вернуть его.Тем не менее, будет трудно найти .Я использовал I для определения его действительного хеш-идентификатора.Но что является его фактическим хеш-идентификатором?Как вы будете догадываться, каким может быть его хеш-идентификатор?(Есть способ, но оставьте его для другого вопроса :-))

Как сказал Энди в своем ответе , вам нужно будет использовать git push -f, чтобы заставить сервер забыть зафиксироватьI при запоминании нового коммита замены J.Это нормально, если каждый, кто может использовать имя сервера myBranch, заранее соглашается с тем, что этими именами манипулируют таким образом.Если вы единственный, кто использует серверное хранилище, вы должны согласиться с самим собой, что это нормально.Если другие люди используют его, убедитесь, что они понимают, что вы делаете в первую очередь!

1 голос
/ 20 сентября 2019

Итак, я изменяю существующий коммит C1, и я ожидаю, что myBranch и origin / myBranch должны указывать на C1.

Вы отредактировали коммит в ветви C1 и git переместил ветку на новый коммит.Нет никаких оснований ожидать, что git переместит другую ветку, локальную или удаленную.

Ссылка origin/myBranch (это не совсем ветвь, это ссылка) остается в старой фиксации, потому что удаленнаяссылка отслеживания перемещается только когда вы получаете / тянуть / толкать.Сама идея ссылок удаленного отслеживания состоит в том, чтобы помнить, где находились их восходящие удаленные ветви, когда вы общались с удаленным репозиторием.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...