Вот как следует подумать о проблемах:
Ха sh Идентификаторы уникальны для каждого коммита: каждый коммит получает свой собственный, и любой из этих коммитов существует в этом репозитории под этим ха sh ID, или коммит просто отсутствует в этом хранилище. Каждый Git соглашается, что , что коммит получает , что ха sh ID, независимо от того, имеет ли данный Git этот коммит. Они все как-то договорились заранее (через криптографию c хитрость хеширования).
Gits find коммиты по именам: имена ветвей, которые являются локальными для этот конкретный Git репозиторий или любые другие имена. Каждый репозиторий поддерживает свой собственный набор имен. Любое имя содержит только один га sh ID.
Gits share коммитов между различными клонами одного и того же исходного репозитория Git , совершить га sh идентификаторов.
Когда Gits делятся, один Git отправляет коммиты, а другой получает. То есть вы выбираете один Git репозиторий и запускаете git fetch <em>remote</em>
или git push <em>remote</em> <em>branch</em>
. При этом один Git репозиторий вызывает другой Git репозиторий по URL-адресу, хранящемуся в имени remote
.
Удаленное имя - это простая строка, например origin
или upstream
. Каждый Git имеет свои собственные удаленные имена.
Механизмы передачи для fetch и pu sh очень похожи, с большой разницей в конце процесса - и, конечно, направления разные. С помощью git fetch
вы подключаете "ваш" Git к "их" Git и get (receive) коммитам. С git push
вы подключаете свои к их, но затем отправляет коммитов. Давайте сконцентрируемся на стороне fetch
, поскольку здесь в целом интереснее.
Также помните, что каждый коммит сам хранит необработанный идентификатор ha sh своего непосредственно предыдущего (или parent * 1052) *) совершать. Это то, что ссылки связывают вместе. Когда мы рисуем ветку следующим образом:
A--B--C <--master
, то, что мы действительно имеем, это то, что имя ветви master
содержит необработанный га sh идентификатор коммита, который мы вызываем C
здесь для удобства. Затем C
сам содержит идентификатор ha sh commit B
, который содержит идентификатор ha sh commit A
. Таким образом, мы всегда можем найти все коммиты, начиная с last - например, идентификаторы, хранящиеся в именах ветвей - и работая в обратном направлении. Git всегда должен работать в обратном направлении.
Итак: когда вы запускаете git fetch origin
, ваш Git вызывает другой Git, используя URL-адрес, хранящийся под именем origin
. В их Git перечислены все имена их филиалов и идентификаторы ha sh. Поскольку идентификаторы ha sh являются общими во всех Git репозиториях, ваш Git может быстро проверить каждый из них и посмотреть, есть ли у вас этот коммит. Если да, ваш Git говорит У меня есть sh ID H , а если нет, ваш Git говорит Я хочу ha sh ID H .
Если вам нужен коммит, их Git предлагает вам своих родителей. Ваши Git чеки: У меня есть эти идентификаторы? Если нет, ваши Git хотят их. Если они у вас уже есть, ваш Git говорит Нет, спасибо, у меня есть этот. В конце этого процесса ваши Git и их Git имеют список всех коммитов, которые они ' Я пришлю вам, и , они знают о коммитах, которые у вас уже есть (и, следовательно, версии файлов, которые у вас уже есть) Таким образом, они отправляют вам коммиты, которые вам нужны, а у вас уже есть остальные, и теперь у вас есть любые коммиты, которые они имели в своих ветках, которых вы не сделали, плюс все, что у вас уже было.
Другими словами, если у вас было:
A--B--C <-- master
теперь вы можете иметь:
A--B--C <-- master
\
D--E
, где E
- коммит, который их master
идентифицирован, E
' Родитель D
, а родитель D
C
. Но Git может найти коммиты назад с концов. Вам нужно имя , по которому можно найти коммит E
.
Ну, их имя, master
, почти достаточно. Но это автоматически конфликтует с вашим именем master
. Таким образом, ваши Git переименовывают их имена ветвей. Ваш Git превращает их master
в ваш origin/master
. Часть origin/
происходит из-за того, что вы назвали их Git под именем origin
. Эти имена стиля origin/master
являются именами удаленного отслеживания . (Git называет их названиями ветвей удаленного слежения , но я думаю, что лучше выбросить слово branch из этой фразы - это просто загромождает его и ослабляет и без того чрезмерно загруженное слово branch .)
После этого конкретного git fetch
ваш Git создаст или обновит ваш origin/master
, чтобы указать на вновь полученный коммит E
:
A--B--C <-- master
\
D--E <-- origin/master
Если вы запустите git fetch upstream
и вызовете Git с именем upstream
, у них будет несколько коммитов. Может быть, они совершили D
, например, как их master
. Но у вас есть D
сейчас, поэтому вы говорите им: Нет, спасибо, уже есть D
. Если это конец списка, ваш Git теперь создает upstream/master
для соответствия upstream
s master
:
A--B--C <-- master
\
D <-- upstream/master
\
E <-- origin/master
Обратите внимание, что каждый Git получает свой собственный набор имен . У них есть имена ветвей, у вас есть имена ветвей, а ваши Git переименовывают имена их ветвей в имена для удаленного отслеживания. Ваши имена для удаленного слежения строятся путем прикрепления вашего remote name, origin
или upstream
перед их ответвлениями имен (плюс sla sh, чтобы они не работает вместе).
Если вы хотите, чтобы они - кем бы они ни были, где бы ни находились эти репозитории - изменили их имена ветвей, ну, вы можете это сделать. Как вы делаете, это варьируется. Самый простой способ - git push
, но git push
требует, чтобы у вас было разрешение на запись в эти репозитории.
Если у вас нет разрешения на запись в их репозитории, вы ' они понадобятся - кем бы они ни были - чтобы либо дать вам разрешение, либо сделать обновление самостоятельно. Именно здесь приходит разветвление с GitHub и другими провайдерами. Вилка - это, по сути, клон, но с некоторой дополнительной связью, которую провайдер сохраняет. Если у вас есть репозиторий, в который вы не можете писать, вы можете превратить его в свою собственную копию. Вы можете написать на этом экземпляре. Итак, теперь у вас есть хранилище на GitHub (или другом провайдере), в которое вы можете написать.
Вы можете git fetch
из исходного хранилища - большинство людей называют это upstream
- но вы не можете git push
к нему. Вы можете git fetch
со своего форка-клона, который вы, вероятно, назовете upstream
, и вы можете git push
с этой копией. И, наконец, у вас есть ваш собственный Git репозиторий на вашем компьютере (не на GitHub). Этот последний имеет ваши ветви и все эти имена для удаленного отслеживания.
Теперь мы можем вернуться к git push
. Эта команда так же близка, как Git к противоположности fetch
(и наоборот). Вы запускаете, например:
git push origin master
, и ваш Git вызывает Git в источнике - например, ваш форк на GitHub - и предлагает GitHub любые коммиты, которые им понадобятся, но не есть, так же, как fetch предлагает вам любые коммиты, которые вам нужны, но не имеют. Но как только вы дадите им эти коммиты, все остальное станет другим.
Их Git - ваш GitHub-репозиторий, тот на GitHub, которым GitHub управляет для вас, который вы называете origin
- имеет свою собственную ветку имена. (Эти имена были первоначально скопированы из репозитория, который вы разветвили. 1 ) Так что, возможно, у них есть:
A--B--C--D--E <-- master
, и вы, в своем репозитории, переставили свое так, чтобы читать так:
A--B--C--D--E <-- origin/master
\
F <-- master
Таким образом, вы отправите им коммит F
, который вам нужен, но затем попросите их установить их master
, чтобы он указывал прямо на F
. Они не устанавливают имя для удаленного слежения! Они устанавливают одно из своих ответвлений имен.
Теперь, они в этом случае - своего рода вы. Это хранилище на GitHub, но GitHub управляет им для вас . Тем не менее, вот как вы должны сделать этот шаг: вы отправляете коммит F
со своего Git на свой ноутбук, скажем. Затем вы просите репозиторий GitHub Git -for-you установить его master
, чтобы указать на этот новый коммит F
. Поскольку вам разрешено это делать, и оно отвечает всем другим требованиям для пу sh, они это делают. Теперь они имеют цепочку A-B-C-D-E-F
и они имеют свои master
, указывающие на F
.
Ваш Git на вашем ноутбуке видит, что они - origin
- приняли запрос на обновление своих master
. Итак, ваш Git обновляет ваш origin/master
сейчас, и теперь у вас есть:
A--B--C--D--E--F <-- master, origin/master
Если у вас есть upstream/master
метка в вашем Git хранилище, ничего не происходит с это здесь. Вы можете запустить git fetch upstream
в в любое время , и пусть ваш Git вызовет их Git - тот, что на GitHub, который не ваш, - и посмотрите, не переместили ли они свои имена ветвей и / или есть новые коммиты, которых у вас нет.
Если у них do есть новые коммиты, вы можете забрать их. На этом этапе вы можете делать с ними все, что захотите, в том числе отправлять их на origin
. Но, конечно, для этого вам нужно установить имена веток origin
, чтобы они указывали на эти коммиты. Вы сделаете это с git push origin
. Здесь немного сложнее. (То есть все предыдущие вещи были просты! ? По крайней мере, для сравнения.)
1 * 1265 Процесс клонирования * GitHub немного отличается от вашего: когда вы клонируете репозиторий, ваш Git переименовывает их имена веток в ваши имена для удаленного отслеживания, а затем создает только одно имя ветви в вашем клоне. Когда вы используете кнопку GitHub "fork a repository", они клонируют другой репозиторий, но создают в этом клоне ровно столько же имен веток, сколько было в разветвленном репозитории. Все эти имена в настоящее время содержат те же самые идентификаторы ha sh, что и разветвленный репозиторий. Но так как это клон, эти имена теперь являются частными для этой вилки: любые изменения, сделанные кем-то еще в оригинале, здесь не отражены.
Как обновить origin/master
из upstream/master
Предположим, что вы сначала разветвляете их хранилище, а затем клонируете свой разветвитель и добавляете к нему upstream
, и все три синхронизируются c. У вас есть это в вашем локальном репозитории Git:
A--B--C <-- master, origin/master, upstream/master
Теперь upstream
сделайте новый коммит, который мы назовем D
. Вы запускаете git fetch upstream
и поднимаете его:
A--B--C <-- master, origin/master
\
D <-- upstream/master
Теперь вы можете продвинуть свой master
, чтобы указать на D
, а затем git push origin master
. Это отправит D
на origin
и попросит origin
указать master
на D
.
Но что, если вы еще не готовы сделать это по какой-либо причине? Ну, вместо git push origin master
, вы можете запустить:
git push origin upstream/master:master
Имя слева от двоеточия :
здесь ваше upstream/master
, которое идентифицирует коммит D
. Это ваш коммит, который им origin
нужен. Ваш Git отправляет этот коммит в Git, который добавляет его в свой (origin
) репозиторий. Затем ваш Git просит их Git задать имя справа от двоеточия: ваш Git просит их origin
установить их master
для указания на фиксацию D
.
Если и когда все это работает, ваш Git теперь имеет:
A--B--C <-- master
\
D <-- upstream/master, origin/master
, который представляет состояние всех трех репозиториев: у каждого есть все четыре коммита, вы назовите третьего master
, а они - origin
и upstream
- назовите четвертого master
.
Когда вы будете готовы, вы можете переместить master
на указывает на коммит D
тоже.
Это до вам , когда перемещать каждое имя вокруг. Они твои имена! Даже имена для удаленного отслеживания принадлежат вам, но имеет смысл только заставить ваш Git перемещать их в зависимости от результата операции git fetch
или git push
, потому что весь смысл этих имен заключается в том, чтобы помнить, что в их репозиториях.
Опять же, ключ здесь в том, что это коммиты, которые являются общими . имена полезны только для поиска коммитов. В частности, имя ветви находит последний коммит, который является частью этой ветви. Поэтому названия ветвей изменяются со временем. Обычно они двигаются так, что в них содержится больше коммитов. С силой можно вырвать имя «назад», чтобы потом было трудно найти последующий коммит. Некоторые операции, такие как git commit --amend
, создают новый коммит, а затем вырывают имя как бы вбок:
I <-- master (HEAD)
/
...--G--H <-- origin/master
В этот момент вы можете решить, что коммит I
плох, и используйте git commit --amend
, чтобы исправить это. Это не может не может - на самом деле изменить I
. Универсально согласованный трюк с идентификатором ha sh не позволит этому случиться. Таким образом, ваш Git просто делает новый и улучшенный коммит, который мы можем назвать J
или, может быть, I'
, чтобы указать, что это новый и улучшенный I
. Вместо того, чтобы I'
указывать назад на I
, ваш Git делает I'
указанием назад на H
:
I [abandoned]
/
...--G--H <-- origin/master
\
I' <-- master (HEAD)
Фиксация I
на самом деле не go далеко - на по крайней мере, на какое-то время. 2 Но ваше имя master
как бы дернуло боком, чтобы указывать на новый и улучшенный I'
. Пока вы никогда не отправляли коммит I
нигде и не показывали кому-либо свой га sh ID, никто больше не может даже знать, что вы это сделали. Они не видели, как это происходит, вы их не показываете, и им никогда не нужно заботиться.
Если вы уже отправили кому-то еще совершить I
, тем не менее, они могут иметь это можно найти по некоторым из их имен. Это когда вещи становятся сложными. Вы можете попросить их, кто бы они ни были, выбросить плохие коммиты. Если вы управляете репозиторием, как ваша origin
форк на GitHub, вы можете git push --force
выбросить плохие коммиты. Однако, как правило, это путь, которого следует избегать: по крайней мере, он полон раздражения, а иногда и более серьезной боли. В определенных c обстоятельствах, когда все заранее согласились на все это, раздражение незначительно, и это нормально.
Использование git rebase
приводит к такой же раздражающей ситуации: хорошо, если вы все согласились на это заранее, или, если никто еще не видел эти коммиты. В противном случае, вам решать, стоит ли раздражать выполнение новых и улучшенных коммитов.
Наконец, есть запросов на получение , которые являются указанными хост-провайдером c предмет. Они не являются частью Git! Вы делаете это, отправляя коммиты на ваш форк, а затем с помощью щелкающих кнопок веб-интерфейса заставляете GitHub отправлять эти коммиты владельцам разветвленного репозитория, прося их (людей) включить эти коммиты. Коммиты, которые вы отправляете в этом процессе, должны быть на вашей вилке, поэтому вы обычно делаете это с последовательностью: git push origin ...
с последующим использованием веб-интерфейса.
Если и когда люди кто управляет репозиторием GitHub, который вы называете upstream
принимать ваши коммиты, ваш git fetch upstream
будет видеть ваши коммиты (с их оригинальными идентификаторами ha sh) и обновлять ваши upstream/*
имена для удаленного слежения. Или они могут не принимать ваши коммиты как есть: они могут создавать свои собственные модифицированные версии с различными га sh идентификаторами. Это, по крайней мере, слегка раздражает, как и вся ситуация с изменениями или перебазированием: теперь вам, возможно, придется отказаться от своих оригинальных коммитов и перестроить любую новую работу на их копиях.
2 Commits это не может быть достигнуто, начиная с какого-то имени и работая в обратном направлении, в конце концов пожнётся Гримом Жнец Коллекционер, git gc
.