Кажется, здесь нет ничего неправильно .Тем не менее, в вашем выводе есть что-то слегка подозрительное, что говорит о том, что, возможно, вы используете Git версии 1.8.3 или более ранней, а может и нет.(Вы можете легко проверить это с помощью git --version
. Если ваш Git до версии 2.0, возможно, пришло время обновить его. Текущий выпуск v2.21 на этот ответ.)
Интересная часть этого бита:
➜ dir git:(master) git fetch boilerplate phaser3
From https://github.com/lean/phaser-es6-webpack
* branch phaser3 -> FETCH_HEAD
Сравните это с тем, что я получаю, когда я git fetch origin master
в моем репозитории Linux сегодня днем:
$ git fetch origin master
remote: Counting objects: 235, done.
remote: Compressing objects: 100% (160/160), done.
remote: Total 235 (delta 120), reused 124 (delta 71)
Receiving objects: 100% (235/235), 273.62 KiB | 230.00 KiB/s, done.
Resolving deltas: 100% (120/120), done.
From git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux
* branch master -> FETCH_HEAD
9e98c678c2d6..54c490164523 master -> origin/master
Помимо дополнительной детализации (считая,сжатие, получение и т. д.), последние две строки очень важны:
* branch master -> FETCH_HEAD
9e98c678c2d6..54c490164523 master -> origin/master
Они показывают, что мой Git записал новый хэш-идентификатор в origin/master
.В частности, до я бегал git fetch
Я получил:
$ git rev-parse origin/master
9e98c678c2d6ae3a17cb2de55d17f69dddaa231b
После я бежал git fetch
, я получил:
$ git rev-parse origin/master
54c490164523de90c42b1d89e7de3befe3284d1b
Это потому, что git fetch origin master
обновлено два важных элементов в моем собственном репозитории Git.Одним из них является мое имя для удаленного отслеживания , refs/remotes/origin/master
, обычно сокращенно origin/master
.Другой файл - это специальный файл .git/FETCH_HEAD
, который git fetch
всегда пишет, основываясь на том, что вы сказали ему выбрать и что он получил от другого Git.
Ваш вывод показывает, что ваш Git обновляется только один из этих пунктов, а именно файл .git/FETCH_HEAD
.Но это может быть нормально и не признак старой версии Git, потому что, если я сразу же после этого перезапущу тот же git fetch
, это произойдет:
$ git fetch origin master
From git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux
* branch master -> FETCH_HEAD
и это выглядит так же, как ваш собственный вывод.Поэтому я подозреваю, что ваш git fetch
по сути ничего не сделал, так же, как мой второй git fetch
, потому что не было ничего общего.Это привело бы к остальному поведению, которое вы видите.
Что здесь происходит
Ключ к пониманию того, что происходит - что делает git fetch
, заключается в осознании этого имена веток не очень важны для Git .Для важно важно для Git каждый коммит .Каждый коммит уникально идентифицируется по его хеш-идентификатору , который выглядит случайным, но на самом деле является криптографической контрольной суммой его содержимого.
Коммит Git хранит снимок всех файлов вместе с некоторыми метаданными- некоторые данные о коммите.Метаданные включают имя и адрес электронной почты того, кто сделал коммит, , когда они сделали это (отметка времени), почему они сделали это (свое сообщение журнала), и - что крайне важно родительский хеш-код коммита .
Вот пример коммита из этого конкретного репозитория.Его хэш-идентификатор равен d1f0301b3333eef5efbfa1fe0f0edbea01863d5d
:
$ git cat-file -p d1f0301b3333eef5efbfa1fe0f0edbea01863d5d | sed 's/@/ /'
tree 86c897e94952090eee579ed47f49cc7006c41fd6
parent 6b4703768268d09ac928c64474fd686adf4574f9
author Thomas Gleixner <tglx linutronix.de> 1533300299 +0200
committer Thomas Gleixner <tglx linutronix.de> 1533302341 +0200
genirq: Make force irq threading setup more robust
The support of force threading interrupts which are set up with both a
primary and a threaded handler wreckaged the setup of regular requested
threaded interrupts (primary handler == NULL).
[snip]
Это не тот способ, которым мы обычно смотрим на коммиты - в большинстве случаев мы можем использовать, например, git show
или git log
, и эти сравнить коммиты с более ранними коммитами, чтобы мы могли видеть различия в двух снимках.Это все равно, что сравнивать, насколько сегодня тепло или дождливо, с тем, как вчера было тепло или дождливо: это говорит нам о том, что изменило , что иногда для нас важнее, чем абсолютное состояние.Но это точно показывает, что находится в коммите: у него есть дерево (с некоторым внутренним идентификатором хэша Git), parent с хэш-идентификатором другого коммита, а также автор, коммиттер и журналсообщение.(У некоторых коммитов больше материала.) Дерево - это то, как Git хранит снимок.Строка parent
сообщает нам, какой коммит наступает за до этого коммита.
В результате, учитывая некоторый хэш-идентификатор коммита Git, Git всегда может найти коммит, который приходит до , которые совершают.Фактически родительская строка указывает на другую, более раннюю фиксацию.Поэтому, если у нас есть длинная цепочка коммитов:
... <-F <-G <-H ...
, мы можем выбрать один из этих коммитов и следовать за его стрелкой назад к предыдущему коммиту: от H
до G
и затем F
и т. д.
проблема в том, что эти хэш-идентификаторы кажутся совершенно случайными и частично зависят от того, в какое время, с точностью до секунды, кто-то их создал. Например, Томас Глейкснер сделал вышеупомянутый коммит во время 1533300299 +0200
. 1 Вся эта деталь входит в хэш-идентификатор коммита. Это означает, что у нас нет возможности узнать, каким будет хеш-идентификатор - это эффективно случайно. Итак, как мы узнаем, какой коммит является последним коммитом?
Ответ в том, что Git помогает нам, позволяя us создавать имена ветвей . Мы говорим Git: хранить новейший / последний идентификатор хеша коммита под некоторым именем, например master
или phaser3
. Учитывая ID , Git может найти коммит . Этот коммит хранит идентификатор родителя, поэтому Git также может найти родителя. Родитель хранит другой родительский идентификатор - как бы прародителя этого коммита - так что Git может найти деда, а этот коммит сохраняет другой родительский идентификатор и т. Д. В истории.
История, по сути, является не чем иным, как набором коммитов, найденных при переходе от каждого коммита к его родителю (-ям). Посещенные коммиты, начиная с некоторого набора начальных точек и работающих в обратном направлении, являются историей в репозитории Git. Начальными точками являются все имена в хранилище: имена веток, имена тегов, такие как v2.1
и имена удаленного отслеживания , такие как origin/master
или boilerplate/phaser3
. 2
1 Ну, вот тогда он создал (оригинальную версию) коммит, но потом он совершил коммит в 1533302341, 34 минуты и 2 секунды спустя.
2 Хранилище может и после различных операций, таких как git rebase
или git commit --amend
, будет , содержать коммиты, которые не посещаются этим пройти все достижимые коммиты, начав с какого-то процесса . Эти оставшиеся или ненужные коммиты в конечном итоге очищаются и выбрасываются сборщиком мусора Git , git gc
. Обратите внимание, что git gc
делает много других дел, связанных с ведением домашнего хозяйства и улучшением производительности, или, по крайней мере, для улучшения производительности. Все это делается автоматически - обычно нет необходимости запускать его вручную.
Checkout создает ветки, иногда
Вы запустили git checkout phaser3
, и ваш Git сказал это:
Branch 'phaser3' set up to track remote branch 'phaser3' from 'boilerplate'.
Switched to a new branch 'phaser3'
Обычно, когда вы говорите git checkout <em>somebranch</em>
, Git просматривает в вашем хранилище для вашей ветви с именем somebranch
. Это имя ветви идентифицирует новейший / последний коммит, который следует считать tip ветви. Ваш Git извлекает данные, которые фиксируются в вашем индексе и рабочем дереве, чтобы вы могли видеть его и работать с ним.
Иногда, однако, вы говорите Git проверить ветку, а у вас нет этой ветви . Вместо того, чтобы просто потерпеть неудачу - что все еще может произойти:
$ git checkout nosuchbranch
error: pathspec 'nosuchbranch' did not match any file(s) known to git
- ваш Git начинает с перечисления ваших собственных веток, а git branch
:
$ git branch
* master
Запрошенного вами имени нет в списке, но Git еще не готов отказаться. Теперь он проверяет все ваши имена для удаленного отслеживания , которые вы можете перечислить с помощью git branch -r
. Результат этого зависит от того, какие пультов у вас есть - у большинства людей есть только один, с именем origin
, в своих хранилищах - и от каких имен ваш Git запоминает из эти пульты:
$ git branch -r
origin/develop
origin/feature2
origin/master
upstream/master
upstream/develop
Я попросил nosuchbranch
, и ни один из них не выглядит так. Но что, если я попросил, скажем, feature2
?
Git просканирует их, и будет точно одно имя, соответствующее feature2
, а именно origin/feature2
. Так что мой Git сказал бы: Ага, вы хотите, чтобы я создал новый feature2
, используя origin/feature2
в качестве коммита! И теперь у меня может быть:
... <-H <-I <-J <-- feature2 (HEAD), origin/feature2
с именами feature2
и origin/feature2
оба с запоминанием хеш-идентификатора коммита J
.
Обратите внимание, чтов этой конкретной ситуации - где у меня нет develop
, но у обоих origin
и upstream
есть один - если я запускаю:
git checkout develop
мой Git сдается, потому что он не знает, должен ли он сделать новый develop
из origin/develop
или из upstream/develop
. Даже если они оба идентифицируют один и тот же коммит, скажем, коммит L
, который я не нарисовал здесь на диаграмме - мой Git не знает, какой из этих двух использовать в качестве вверх по течению настройка для моего нового develop
. Вместо этого я могу сделать следующее:
git checkout --track origin/develop
, который сообщает моему Git: Используя origin/develop
, создайте новый develop
, где develop
обновляется и отправляется origin
* develop
.
Мы продвинулись очень быстро и не очень подробно , как все это работает, но я еще раз упомяну здесь, что это устанавливает настройку upstream недавно созданной ветви. Восходящий поток ветки - это то, как Git решает сказать «впереди» и / или «позади», когда вы запускаете git status
, и как Git знает, какой коммит использовать, если вы запускаете git merge
или git rebase
без аргументов. Восходящий поток определяет, что будут делать git pull
или git push
, если вы не предоставите дополнительных аргументов, и какой пульт git fetch
будет использовать, когда у вас более одного пульта. Таким образом, настройка upstream довольно полезна.
Резюме
git pull --rebase
ничего не делает, потому что новых коммитов нет: ваш график выглядит так, когда я рисую так, как мне нравится рисовать коммиты в StackOverflow:
...--G--H <-- phaser3 (HEAD), boilerplate/phaser3
или, возможно, так:
...--G--H <-- boilerplate/phaser3
\
I <-- phaser3
Работа, которую выполняет git rebase
, заключается в копировании коммитов, которые у вас есть, таких как - I
на этой диаграмме, - в новые коммиты, которые приходят после их последнего коммита. В этом случае это будет копировать I
в новый коммит, который идет сразу после H
вместо того, где I
сейчас , но I
уже сразу после H
, поэтому нет необходимости делать такое копирование. (И в ситуации, когда оба имени указывают на один и тот же коммит H
, нет никакого коммита для копирования, что также означает, что не нужно выполнять какую-либо работу.)
вверх по течению из phaser3
равно boilerplate/phaser3
. Имя boilerplate
само по себе - обычно - просто короткое имя для https://github.com/lean/phaser-es6-webpack
; Git называет это удаленным .
Есть полный репозиторий Git на https://github.com/lean/phaser-es6-webpack
; у него четыре собственные ветви. Ваш репозиторий скопировал свою ветвь phaser3
- хэш-идентификатор c9584e75521a3b94d4f883a246aad134b83dc12d
, хранящийся под этим именем, то есть - в ваше собственное имя remote-tracking , boilerplate/phaser3
.
Команды git fetch
и git push
- это команды, которые передают коммиты из одного хранилища в другое. В общем, отправитель отправляет коммиты, которых у получателя еще нет, так что, в конце концов, у получателя есть все, что есть у отправителя, плюс, может быть, больше, если у него уже есть вещи, которых у отправителя нет. Отправив коммиты от отправителя к получателю, теперь получателю необходимо обновить имя или имена , чтобы запомнить новые коммиты.
При использовании git fetch
получатель - вы! - вернее, ваш Git - обновляет ваши имена для удаленного слежения . Вы берете их имя, phaser3
, и добавляете префикс к вашему удаленному имени, boilerplate
, и вставляете косую черту между ними. Это дает вам boilerplate/phaser3
как способ запомнить их коммит.
При использовании git push
отправитель - вы снова - отвечает за запрос получателя установить какое-либо имя в получателе. Вы выбираете, какое имя вы просите их установить Если это имя, скажем, phaser3
, вы просите их изменить их phaser3
. У них нет имени для удаленного слежения: здесь нет quantum_spaghetti/phaser3
. Вы просите их изменить их имя. В общем, вы должны убедиться, что независимо от того, какие новые коммиты вы им отправили, эти новые коммиты имеют свои существующие коммиты как часть истории.