Здесь есть куча концепций, которые вам нужно держать отдельно в своей голове;и затем возникает проблема терминологии.
Это понятия:
- имена ветвей подобно
master
; - удаленное отслеживание имена - часто называемые удаленное отслеживание ветвей - подобно
origin/master
; - идея в восходящем направлении; и
- фиксирует , которые идентифицируются по их хэш-идентификаторам; хранят файлы плюс некоторые метаданные; и через эти метаданные образуют цепочки.
Первые два - имена веток и имена удаленного отслеживания - довольно тесно связаны, и вместе с именами тегов, такими как v2.1
, все сгруппированы в одну концепцию, которую Git называет reference .
Проблема терминологии заключается в том, что иногда некоторые ветви - некоторые имена, такие как master
- говорят, что они отслеживают .Другие имена, такие как origin/master
, называются ветви удаленного отслеживания , что выглядит так же.Это не!Вот почему я называю последние имена для удаленного отслеживания , чтобы избежать слова branch , и поэтому я рекомендую вместо глагола tracking думать о ветвиимена типа master
как с восходящим потоком или без восходящего .Это обходит хитрое слово track (которое имеет еще одно значение применительно к файлам в вашем рабочем дереве).
Давайте перейдем к вашим действиям:
Вот краткий пример, чтобы показать мою проблему:
На данный момент у вас есть два отдельных хранилища.Один на сервере GitLab и один на вашем компьютере.Мы назовем GitLab один «их», а другой на вашем компьютере - «ваш», хотя в некотором смысле они оба ваши.Ваш репозиторий очень похож на их, но не совсем такой: это клон, и у него есть способ идентифицировать его как копию вместо оригинала.
Мы вернемся к вашему репозиторию,Ваш компьютер, немного.Следующие несколько шагов происходят в их репозитории.
- Затем я создал новую ветку в веб-интерфейсе gitlab (с помощью кнопки '+'):
my_server_branch
ОК, так что на данный момент, их репозиторий Git имеет ветку, о которой ваш не знает.
- Я поместил один файл в эту ветку (также с веб-интерфейсом gitlab)
Технически, вы не можете поместить файл в репозиторий, напримерэтот.То, что вы сделали, это добавили новый коммит , с новым коммитом, содержащим файл.
Это важно, потому что способ работы имен веток заключается в запоминании хеш-идентификатора last коммит который идет в ветке.Когда вы добавляете новый коммит, идентификатор хеша, сохраненный в имени ветви, изменяется, чтобы запомнить новый коммит.Новый коммит запоминает предыдущий последний коммит.
Если мы вытянем их, используя одинарные заглавные буквы для замены реальных хеш-идентификаторов, мы получим картинку, подобную этой, для простого репозитория с тремя коммитами.только с веткой master
:
A <-B <-C <-- master
Здесь name master
запоминает фактический хеш-идентификатор commit C
.Этот коммит сам запоминает фактический хеш-идентификатор коммита B
, который запоминает хэш-идентификатор коммита A
.Так что Git нужно только иметь имя master
запомнить идентификатор коммита C
: остальное он находит, просматривая сами коммиты.
(Мыу master
указывает на C
, C
указывает на B
и B
указывает на A
.Поскольку A
- самый первый коммит, когда-либо сделанный в хранилище, он нигде не указывает: это то, что говорит нам и Git, что мы можем остановиться и отдохнуть.Там нет предыдущей истории для изучения.Коммиты являются историей, а история идет C, затем B, затем A .)
Чтобы добавить новую ветвь в хранилище, мы обычно выбираем какой-то существующий коммит внутрихранилище и проверьте его, чтобы это был текущий коммит, затем добавьте новое имя, которое указывает на тот же коммит:
A--B--C <-- master, my_server_branch (HEAD)
Git должен знать какое имя обновлять примы делаем новые коммиты, поэтому Git присоединяет специальное имя HEAD
(например, в верхнем регистре) к имени ветви.Если мы используем локальный компьютер (а не веб-интерфейс), мы должны создать файл, использовать git add
, чтобы добавить его, и запустить git commit
, чтобы сделать новый коммит.Если мы используем веб-интерфейс, GitLab делает то же самое в их хранилище, оно просто скрыто за их веб-интерфейсом.В итоге они набирают:
A--B--C <-- master
\
D <-- my_server_branch (HEAD)
, хотя они могут сделать это таким образом, что они все равно оставят HEAD
присоединенным к master
.Вот как GitHub сделал бы это, например, без перемещения HEAD
.В любом случае, поскольку это их HEAD
, а не ваши, сейчас это не так уж важно.
Клоны получают свои собственные ветви
Теперь пришло время посмотретьвернуться в свой собственный репозиторий.Когда вы запустили:
git clone <url>
, ваш компьютер создал новый пустой репозиторий Git с no коммитами, no ответвлениями, в основном ничего, кроме пустого репозиторияракушка.Затем ваш Git на вашем компьютере заполнил эту оболочку, взяв все коммиты из их Git.Поэтому, если у них было три простых коммита:
A--B--C <-- master
ваш Git получил эти три коммита:
A--B--C
(внутренние стрелки, обращенные назад, слишком раздражают, чтобы рисовать, но они 'все еще там: C
указывает на B
и B
указывает на A
).
Все идентификаторы хеша совпадают: каждый Git во вселенной согласится с тем, что находится внутри коммита C
, что делает его хэш-идентификатором C
хэш-идентификатор.Таким образом, ваш Git и его Git могут определить, какой Git имеет какие коммиты, просто взглянув на эти хэш-идентификаторы.Но у вашего Git все еще нет ветвей.
Ваш Git спрашивает у Git, каковы все их ветви и имена тегов, и они говорят: My master
идентификатор хеша для коммита C
. Итак, ваш Git теперь создает, а не master
, а origin/master
, указывая на коммит C
:
A--B--C <-- origin/master
Коммитов нет иветок не осталось, поэтому ваш Git завершил копирование.Ваш Git теперь выполняет последний шаг git clone
, который должен быть запущен:
git checkout master
Вы можете заставить свой Git использовать другое имя, и если вы этого не сделаете, ваш Git запрашивает их Git, какое имя использовать;но это обычный, обычный случай: ваш Git пытается проверить ваш master
.
У вас нет master
. И все же,эта проверка в любом случае успешна.Причиной этого является то, что их Git имеет master
и ваш Git скопировал это в ваш origin/master
.Так что ваш Git, вместо того, чтобы просто провалить проверку, говорит сам себе: Хм, нет master
, но есть origin/master
... который очень похож на master
, держу пари, вы имели в виду, что я должен make master
с использованием origin/master
. Итак, ваш Git делает это:
git checkout --track master origin/master
, который создает вашего собственного master
и устанавливает его вверх по течению до origin/master
.Итак, теперь у вас есть это:
A--B--C <-- master (HEAD), origin/master
Ваша ветка master
существует и имеет origin/master
в качестве восходящего потока.
Боковая панель: если вы не уверены, будьте уверены, этосбивает с толку!
Запутанный способ объяснить это тем, что ваша ветка master
(1) теперь отслеживает (2) вашу ветку удаленного отслеживания (3, 4, 5) origin/master
с удаленного (6) origin
. Здесь в точках (1) и (5) оба слова или фразы используют слово branch , но оба означают что-то другое. В (2) и (4) у нас есть слово tracking , оба означающие что-то другое. В (3) и (6) у нас есть слово remote , оба означающие что-то другое. Вы можете понять, почему мне не нравятся эти слова, и я предпочитаю называть это именем ветви master
, с восходящим origin/master
, с origin/master
, являющимся имя удаленного отслеживания , связанное с remote origin
. Мне все еще приходится использовать слово remote дважды, но «дистанционное отслеживание» как минимум переносится.
Один правильный и хороший способ получить my_server_branch
локально
Сделав my_server_branch
в их Git и добавив туда коммит D
, теперь вы можете выполнить команду:
git fetch
в вашем собственном Git на вашем компьютере. (Вы можете использовать git fetch origin
, если хотите быть явным.) Это заставляет ваш Git вызывать Git и снова запрашивать у него список названий веток. На этот раз они говорят: У меня есть master
, на коммите C
. У меня есть my_server_branch
, при коммите D
. Ваш Git говорит: Ах, у меня уже есть коммит C
, так что никаких проблем нет. Дайте мне совершить D
хотя. Они делают это, и теперь разговор между вашим мерзавцем и их мерзавцем завершен. Теперь ваш Git обновляет ваш origin/master
так, чтобы он указывал на C
- что вообще не изменится - и создает ваш origin/my_server_branch
, указывая на новый коммит D
. Итак, теперь у вас есть:
A--B--C <-- master (HEAD), origin/master
\
D <-- origin/my_server_branch
Теперь вы можете запустить git checkout my_server_branch
. Как и раньше, у вас нет my_server_branch
на данный момент, но вместо просто неудачи ваш Git скажет: Ага, у меня нет my_server_branch
. Но у меня есть origin/my_server_branch
. Я создам my_server_branch
, указывая на коммит D
. Я установлю восходящий поток my_server_branch
равным origin/my_server_branch
. Тогда я сделаю заказ, о котором вы просили. Результат:
A--B--C <-- master, origin/master
\
D <-- my_server_branch (HEAD), origin/my_server_branch
Вам почти никогда не нужно использовать git checkout --track
Единственный раз, когда вам нужно git checkout --track
, это когда git checkout
не сделает для вас правильную вещь. Это происходит в двух случаях:
Предположим, у вас есть более одного пульта , например, если у вас есть origin
плюс второй пульт fred
для получения материала из хранилища Фреда. Предположим далее, что у вас есть собственный origin/hello
, скопированный из ветви hello
в источнике, и у Фреда есть Фреда hello
, который теперь скопирован в ваш fred/hello
. Если вы попытаетесь git checkout hello
, ваш Git найдет двух кандидатов - fred/hello
и origin/hello
- и не знает, какой из них использовать. Так что теперь вы можете запустить вместо:
git checkout --track fred/hello
если вы действительно хотели использовать Фреда, или:
git checkout --track origin/hello
если вы действительно хотели использовать origin.
Или, если по какой-то странной причине у вас есть, скажем, origin/my_server_branch
, но в вашем хранилище вы хотите назвать это bob_server_branch
. Использование git checkout my_server_branch
дает вам my_server_branch
; и, конечно же, с помощью git checkout bob_server_branch
пытается найти origin/bob_server_branch
. Итак, здесь вам нужна длинная форма:
git checkout --track bob_server_branch origin/my_server_branch
О git pull
Команда git pull
является сокращением для:
- пробег
git fetch
; тогда, при условии, что это успешно
- запустить вторую команду Git, обычно
git merge
.
Поскольку git fetch
будет (при запуске с правильными параметрами, в любом случае) создавать и / или обновлять ваши origin/*
имена для удаленного отслеживания из ветвей origin
, это первая половина git pull
это сделало origin/my_server_branch
для вас.
Вторая команда - git merge
, или, если вы скажете ей использовать git rebase
, git rebase
- принимает коммиты, введенные с помощью первой команды, и использует их для слияния, или перебазировать.
Я неКак и команда git pull
, по ряду причин, некоторые из которых носят чисто исторический характер (git pull
использовался, чтобы иногда уничтожать вашу локальную работу в нескольких редких, но не случайных случаях, и я случился со мной вне реже одного раза).Наиболее практичное возражение довольно простое: пока вы не увидите, что git fetch
извлечено, как вы узнаете, хотите ли вы запустить git merge
, git rebase
или что-то еще целиком? Поэтому я предпочитаюизбегать git pull
: сначала я запускаю git fetch
, затем возможно запускаю git merge
или git rebase
или, возможно, делаю что-то еще полностью.Что делать, зависит от того, что я видел из git fetch
(а также, конечно, от того, что я делаю с этим конкретным репозиторием).
Есть несколько исключений, особенно с репозиториями, которые я использую для чтения-только - я просто хочу их последний коммит плюс его историю, поэтому git pull
это , вероятно нормально, пока они ведут себя хорошо - или где я контролирую оба конца, например,origin
репозиторий действительно мой на GitHub, и я знаю , что я туда вставил.Но даже в последнем случае я стараюсь избегать git pull
, потому что иногда я забываю, что я положил в какой репозиторий.Использование git fetch
позволяет сначала проверить.