Git не совсем о ответвлениях . Это действительно около коммитов . Имена ветвей - это просто метки, которые мы (и Git) используем, чтобы найти коммитов.
Вывод журнала, который вы нарисовали:
/| merge branch A into master (commit id a.into.m)
| | commit (id a2) branch A
| | commit (id 3) branch master
| | commit (id a1) branch A
\| new branch A commit (id a0)
| commit 2 master (id m2)
| commit 1 master (id m1)
отчасти близок к ASCII-арт-графики, которые git log --decorate --oneline --graph
dr aws, но не содержат маленьких звездочек, которые используются для маркировки коммитов:
* 4291fbf Merge dotfiles installer
|\
| * 4f5b84b dotfiles: add --tar option
| * 8a45ff6 dotfiles: refuse to install over special files
| * e750c89 dotfiles: reshuffle work-to-do code
| * 36aa02c dotfiles: s/--install-to/--homedir/
| * 93c597c dotfiles: simplify FileInfo
| * 4771f69 dotfiles: manipulate home dir .foorc etc
|/
* 6c9a43a add prototype Dot-files
Обратите внимание, что каждый коммит также имеет уникальный идентификатор ha sh (здесь они укоротили) Эти идентификаторы ha sh являются настоящими именами коммитов.
Branch names просто действуют как указатели , указывающие на эти коммиты. Мне нравится рисовать эти вещи в стороны, а не вертикально, для сообщений StackOverflow. Я также люблю давать им заглавные буквы вместо больших уродливых идентификаторов ha sh. Результат:
...--F--G--H <-- branch1
\
I--J <-- branch2
Идея в том, что каждое имя ветви указывает на один коммит. Сами коммиты также указывают (назад) один шаг, так что J
указывает на I
, I
указывает на H
, H
указывает на G
и т. Д.
Коммиты, которые находятся «на» ветви в Git, - это те, к которым вы можете обратиться, начав с имени ветви, следуя за его стрелкой к его фиксации, а затем работая в обратном направлении от этой фиксации. Поэтому в некотором смысле лучше сказать, что эти коммиты содержатся в ветви, потому что коммиты до H
включены или содержатся в обеих ветвях, в то время как коммиты I
и J
в настоящее время содержат только branch2
.
Теперь, что нужно знать об этих коммитах и именах веток:
Никаких коммитов не может быть изменилось. Самое большее, вы можете взять существующий коммит и скопировать его в новую улучшенную версию, которая получит новый и другой идентификатор ha sh.
Как мы только что сказали Множество коммитов, содержащихся в некоторой ветви, получают, начиная с точки ветвления, а затем переходя от коммита к коммиту, назад, по одному шагу за раз. Сами имена просто содержат коммит ha sh ID любого коммита, который мы говорим, должен быть последний коммит в ветке.
Имена ветвей поэтому двигаться . Их обычное продвижение вперед: мы начинаем с некоторого набора коммитов, подобных приведенным выше, а затем добавляем новый коммит. Например, если мы git checkout branch1
, мы получим commit H
. Если мы сделаем новый коммит, он получит новый идентификатор, который мы назовем K
(I
и J
используются):
...--F--G--H---K <-- branch1 (HEAD)
\
I--J <-- branch2
(Мы можем использовать слово HEAD
, прикрепив его к какой-то такой ветке, чтобы запомнить, на какой ветке мы находимся.)
Обычное движение, как мы только что видели, это добавление. Давайте слить branch2
в branch1
. Не беспокоясь о том, как Git выполняет это, давайте нарисуем результат:
...--F--G--H---K--L <-- branch1 (HEAD)
\ /
I--J <-- branch2
Новый коммит L
- это коммит слияния , то есть один с двумя родителями (или более чем два, но мы не увидим этого здесь).
Возвращаясь к вашему вопросу
Так как имена веток просто указывают на указанные c коммиты, мы можем добавить другие имена и / или заставляют имена двигаться в направлениях, которые они обычно не делают:
...--F--G--H---K--L <-- branch1 (HEAD), master
\ /
I--J <-- branch2
Давайте заставим имя master
указывать на фиксацию K
вместо L
:
$ git branch -f master <hash-of-K>
и нарисуйте результат:
K <-- master
/ \_
/ \
...--F--G--H------L <-- branch1 (HEAD)
\ /
I--J <-- branch2
Ничего не изменилось изменилось с точки зрения фиксации, но метки сместились.
Но master
двигался в «неправильном» направлении. Если мы соединяем наши Git с некоторыми другими Git и их master
точками для фиксации L
, и мы просим их переместить своего мастера в точку для фиксации K
, как наша они обычно говорят нет . Причина, по которой они скажут «нет», заключается в том, что это движение метки ветки идет в неправильном направлении. Термин Git означает, что это не ускоренная перемотка вперед .
Мы можем использовать команду git push --force
to , чтобы переместить их master
нравится. Они могут или не могут подчиняться. Другие клоны нашего или их репозитория могут также иметь master
, который указывает «впереди» коммит K
, например, L
, и нам придется убедить их переместить то же самое way.
Итак:
Я пытаюсь создать несколько ветвей этого и сбросить мастер в предыдущее состояние. ветвь new_calculator из commit (id 3) ветвь new_form из commit (id a2) ветвь rollback_master commit (id m1)
Вы можете сделать это в своем собственном Git хранилище где вы все контролируете. Просто поменяйте ярлыки и делайте новые ярлыки по мере необходимости. Все существующие коммиты остаются на месте; двигаются только метки . См. Ответ Михаэля Бера для получения более подробной информации о том, как это сделать (с git reset
и / или git branch -f
).
Вы не обязательно сможете изменить кого-либо репозиторий Git. Сюда входит любая копия вашего собственного Git репозитория, которую вы поместили на GitHub. В конце концов, это репозиторий GitHub, а не ваш: они просто позволяют вам контролировать его, и они сами решают, какой контроль вам предоставить.
Если вы владеете им, чтобы GitHub предоставил вам полный контроль, и никто больше не имеет Git репозиториев, о которых вам нужно беспокоиться, вы можете git push --force
обновлять свой репозиторий GitHub (он же «репозиторий GitHub, который они хранят для вас»).
Если не , то, что вы обычно должны сделать, это добавить new коммитов в репозиторий, чтобы новые коммиты расширяли существующие цепочки коммитов в этом "перемещении" вперед "манера. Новые коммиты просто записывают новое положение вещей. Все Gits везде понимают концепцию продвижения вперед, и новые коммиты ссылаются на существующие коммиты, поэтому все предыдущие зафиксированные файлы все еще находятся в своих ранее зафиксированных версиях.
Когда я делаю вытащить запрос rollback_master в master ...
Запросы на извлечение не являются частью Git. Это расширение, предлагаемое такими сервисами, как GitHub.
Когда вы делаете запрос на извлечение, вы прикрепляете метку - фактическая метка зависит от сервиса - к некоторому коммиту в каком-то хранилище, которое существует на сервере. Этот коммит имеет некоторый идентификатор ha sh, как и все коммиты. Затем сервер доставляет запрос на удаление (в комплекте с внутренней прикрепленной меткой) тому, кому он управляет другим хранилищем - тем, с которого вы ответили. (Сервер где-то записал этот репозиторий. В некоторых случаях вы выбираете этот «другой» репозиторий, этот репозиторий; тогда кто-то является владельцем этого репозитория.) Этот человек получает пользовательский интерфейс, который позволяет ему:
- запустите слегка измененный
git merge
на сервере GitHub, или - запустите слегка измененный
git rebase
на сервере GitHub, или - запустите слегка измененный
git merge --squash
на сервере GitHub.
Ни один из этих шагов не включает git push --force
операцию «произвольно переместить метку». Все они либо вообще, либо специально создают новый коммит, который добавляет к существующим коммитам. Таким образом, все операции, выполняемые с помощью запросов на получение, обязательно очень отличаются от вида git push --force
, который перемещает метку назад.
Иными словами, если вы go идете по этому пути, «вы не можете получить там отсюда ". : -)