Как разные ветки хранятся локально из git на моем диске? - PullRequest
0 голосов
/ 03 декабря 2018

У меня есть только один репозиторий версий на моем локальном жестком диске, но несколько веток на Github.Разве не должно быть копий кода на ветку?какая версия кода у меня установлена ​​на локальном диске?

Ответы [ 3 ]

0 голосов
/ 03 декабря 2018

Разве не должно быть копий кода на ветку?

Нет.Это не то, как работают имена веток в Git.

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

У меня есть только один репозиторий версий на моем локальном жестком диске, но несколько веток на Github.

Обычнохранилище на вашем локальном диске - это клон хранилища на GitHub.Или мы можем также сказать, что хранилище на GitHub является клоном хранилища на вашем локальном диске.Ни один репозиторий не "лучше", чем другой, у них просто есть разные компьютеры для их хранения, и, возможно, немного разные наборы коммитов, и, скорее всего, разные ветки names в них (но поскольку Git больше заботится о фиксирует , для Git это не имеет большого значения).

Чтобы понять, как это работает, начните с фиксации.Каждый коммит, называемый каким-то большим уродливым хеш-идентификатором, таким как 8a0ba68f6dab2c8b1f297a0d46b710bb9af3237a, сохраняет полный снимок некоторого набора файлов.Наряду с этим снимком, каждый коммит имеет некоторые метаданные , некоторые данные о коммите.Например, если вы сделали коммит самостоятельно, в коммите сохраняются ваше имя и адрес электронной почты, а также отметка времени для , когда вы сделали коммит.И каждый коммит обычно хранит хэш-идентификатор своего предыдущего или parent commit.(Родитель 8a0ba68f6dab2c8b1f297a0d46b710bb9af3237a выше, например, 15cc2da0b5aaf5350f180951450e0a5318f7d34d.)

Git использует эти parent ссылки для поиска коммитов.Давайте рассмотрим крошечный репозиторий, который вы создали недавно, в котором вы сделали всего три коммита.У каждого коммита есть какой-то большой некрасивый хеш-идентификатор, но вместо этого давайте использовать заглавные буквы.Поэтому первый коммит, который вы когда-либо сделали, это коммит A.Поскольку является первым коммитом, у него есть нет родителя.

Затем, с коммитом A, вы сделали коммит B.Он имеет A в качестве родителя.Аналогично, вы использовали C для создания B, поэтому C хранит хэш-идентификатор B.

Всякий раз, когда у нас есть хеш-идентификатор некоторого коммита, мы говорим, что можем указать , что совершить.Так C указывает на B, а B указывает на A.Если мы нарисуем это, мы получим:

A <-B <-C

Теперь, с простыми заглавными буквами и этим рисунком, очевидно, что нам нужно начать с C и работать в обратном направлении.Это то, что делает Git, но способ , который Git находит в коммите C, заключается в использовании имени ветви .Поскольку мы только что запустили этот репозиторий, мы, вероятно, все еще используем master, поэтому давайте нарисуем это в:

A <-B <-C   <--master

Имя master содержит идентификатор хеша commit C, так что Git может найтиC.Оттуда Git может работать в обратном направлении, до B и затем A.

. Если мы хотим добавить new commit, мы запускаем git checkout master, что дает нам копиюиз C, над которым мы можем работать, и помнит, что мы сейчас находимся на master, который является коммитом C.Мы делаем нашу работу, git add файлы по мере необходимости и запускаем git commit.Git делает новый коммит D (из того, что есть в index , который я не собираюсь здесь определять) и устанавливает D, чтобы указать C:

A--B--C   <--master
       \
        D

Теперь происходит сложная часть: поскольку мы только что создали D, и мы находимся на master, Git сейчас обновляет master с любым реальным идентификатором хэша для нового коммитаD.Так что теперь master указывает на D, а не C:

A--B--C
       \
        D   <--master

, и мы можем выправить излом на чертеже (и мне тоже нравится немного ослаблять стрелку):

A--B--C--D   <-- master

Теперь предположим, что мы решили добавить новую ветку .Мы бежим, например, git checkout -b develop.Для этого просто добавьте новое имя , но сохраните все то же commitits .Новое имя develop указывает на выбранный нами коммит, который по умолчанию соответствует тому, который мы используем сейчас, т. Е. Commit D:

A--B--C--D   <-- master, develop

Теперь нам нужен способ рисованиякакое название ветви мы используем.Для этого мы приложим слово HEAD (заглавными буквами) к одной из двух ветвей.Поскольку мы сделали git checkout, чтобы присоединить HEAD к новому develop, давайте нарисуем это:

A--B--C--D   <-- master, develop (HEAD)

Теперь пришло время сделать еще один новый коммит.Не делая ничего другого, мы модифицируем некоторые файлы, используем git add, чтобы скопировать обновленные файлы в индекс, и запускаем git commit, чтобы сделать новый коммит, который мы назовем E (но который получит, как обычно,какой-то непонятный хеш-идентификатор).Когда Git обновляет ветку name , она обновляет имя, к которому прикреплено HEAD, поэтому график теперь выглядит так:

A--B--C--D   <-- master
          \
           E   <-- develop (HEAD)

На этом этапепредположим, я клонирую ваш репозиторий (напрямую с вашего компьютера или из точной копии, которую вы отправляете на GitHub, с теми же коммитами и теми же именами веток).Мой Git сначала сделает это:

A--B--C--D   <-- origin/master
          \
           E   <-- origin/develop

Здесь у меня нет каких-либо веток.У меня все те же коммиты , но что запоминает их концы, это не имена ветвей , а скорее имена для удаленного слежения .Вместо master у меня есть origin/master для запоминания хеш-идентификатора коммита D.Вместо develop у меня есть origin/develop, чтобы запомнить хэш-идентификатор E.

В качестве последнего шага моего клона мой собственный Git пытается git checkout master.Вместо неудачи, потому что у меня нет a master, на самом деле создает мой master, используя мой origin/master, который мой Git скопировал с вашего Git'а master.Итак, теперь у меня есть:

A--B--C--D   <-- origin/master, master (HEAD)
          \
           E   <-- origin/develop

Если я теперь запусту git checkout develop, мой Git просмотрит мой репозиторий и не найдет develop, но найдет найдет origin/develop.Мой Git затем создаст новое имя , develop, указывающее на фиксацию E, и присоединит HEAD к этому имени вместо master:

A--B--C--D   <-- origin/master, master
          \
           E   <-- origin/develop, develop (HEAD)

Обратите внимание, что коммиты не были скопированы .Git просто добавил новое имя , указывающее на существующий коммит.Существующий коммит уже был там, в моем локальном репозитории.

Вы подключаете два репозитория Git с fetch и push

Так вы получаете обновления из репозитория GitHub.Если у них есть новые коммиты, которых у вас нет, вы можете запустить git fetch в своем репозитории Git.Ваш Git использует имя origin, которое ваш Git создал во время исходной операции git clone, чтобы найти URL для GitHub.Затем ваш Git вызывает GitHub, а ваш Git и их Git немного беседуют.

С git fetch ваш Git спрашивает у Git свои имена веток и окончательные коммиты для каждого из этих имен.Их Git может сказать: my master указывает на какой-то коммит с большим уродливым хешем - давайте просто назовем это H, вместо того, чтобы пытаться угадать фактический хеш-идентификатор.Ваш Git смотрит на ваш репозиторий и говорит себе: У меня нет H, я бы лучше попросил его. Ваш Git спрашивает у своего Git о H;они говорят, что родитель H - G, чей родитель F, чей родитель D.У вас обоих D, так что эта часть разговора завершена.Их Git может также сказать: my develop указывает на коммит E. Ваш Git уже имеет E, так что эта часть разговора также выполнена.Больше не нужно беспокоиться о других именах, так что теперь ваш Git отправляет свои Git по коммитам F, G и H, которые ваш Git сохраняет в своем хранилище:

           F--G--H   <-- origin/master
          /
A--B--C--D   <-- master
          \
           E   <-- origin/develop, develop (HEAD)

Обратите внимание, что кроме добавления new коммитов в ваш собственный репозиторий, единственное, что сделал ваш Git, это обновил все ваши origin/* имена так, чтобы они совпадали с именами других Git.То есть ваш origin/master переместился из общего коммита D в общий коммит H.

Выборка всегда безопасна, но git push отличается

Всегда безопасно запускать git fetch: это соединяет ваш Git с каким-то другим Git, получает любые новые коммиты от них,и обновляет ваши имена для удаленного отслеживания .Поскольку эти имена просто помнят их работу, а не вашу работу, это безопасно.Если вы сделали новые коммиты, ваши новые коммиты все еще находятся в ваших ветвях , которые не являются их ветвями, кроме тех, которые имеют общие коммиты у вас обоих.

Когда вы используете git push, ваш Git вызывает свой Git, и два Git ведут очень похожий разговор.Ваш Git предлагает своим Git несколько новых коммитов, если у вас есть новые.Но затем вместо того, чтобы ваш Git обновил ваши имена для удаленного слежения , ваш Git предлагает Git вежливый запрос: Пожалуйста, если все в порядке, обновите ваш master, чтобы он соответствовал моим master, или пожалуйста, если все в порядке, обновите ваш develop, чтобы он соответствовал моему develop - или, возможно, даже обоим.(Вы можете нажать столько названий веток, сколько захотите за один раз.)

Предположим, что после более раннего git fetch у вас было следующее:

           F--G--H   <-- origin/master
          /
A--B--C--D   <-- master
          \
           E   <-- origin/develop, develop (HEAD)

Вы остались на вашем develop и сделал несколько новых коммитов, которые мы назовем I и J, так что теперь у вас есть это:

           F--G--H   <-- origin/master
          /
A--B--C--D   <-- master
          \
           E   <-- origin/develop
            \
             I--J   <-- develop (HEAD)

Но, без вашего ведома, кто-то еще добавилновый коммит их develop.Этот коммит имеет хеш-идентификатор, которого у вас нет нигде.Давайте назовем это K в их Git, так что они имеют это:

           F--G--H   <-- master
          /
A--B--C--D
          \
           E--K   <-- develop

Вы отправляете их I и J, так чтотеперь у них есть это:

           F--G--H   <-- master
          /
A--B--C--D
          \
           E--K   <-- develop
            \
             I--J   <-- (polite request to make develop go here)

Если они примут ваш запрос и переместят их develop, что произойдет с их коммитом K?В итоге они получают следующее:

           F--G--H   <-- master
          /
A--B--C--D
          \
           E--K   ???
            \
             I--J   <-- develop

Как мы уже говорили выше, Git работает с коммитами и именами веток так, что он использует имя ветки для поиска last commit, а затемработает в обратном направлении.Работая в обратном направлении с J, их Git перейдет на JС J они вернутся к I, затем E, затем D, C, B и A.Это означает, что коммит K потерян!

В результате их Git скажет no на ваш вежливый запрос: Нет, еслиЯ перенес свой develop, я бы потерял некоторые коммиты. (Ваш Git сообщает об этом как "отклоненный" и "не ускоренный переход".)

В этом случае, что вынужно git fetch взять их новый коммит K, а затем сделать что-то в своем собственном хранилище, чтобы приспособить его.Однако это еще один набор вопросов (на все они уже даны ответы в StackOverflow).

0 голосов
/ 03 декабря 2018

TL; DR: git хранит все ветви, теги, коммиты и заметки в одном репозитории.Он также может отображать комбинированные представления нескольких репозиториев (например, локальных и удаленных) и fetch и push вещи между репозиториями.Каждая ветвь указывает на определенный конкретный коммит в репозитории, и каждый коммит указывает на ноль или более родительских коммитов.Обычные файлы (то есть все, кроме содержимого в подкаталоге .git), которое вы видите, называются рабочим пространством или рабочим деревом, и вы можете отобразить состояние своего рабочего пространства, вызвав git status.Чтобы увидеть список всех известных на данный момент веток (как локальных, так и удаленных), вы можете использовать git branch --all.Чтобы выбрать, какие файлы есть в вашем рабочем пространстве, используйте git checkout.

Длинный ответ: посмотрите ответ по @torek и прочитайте https://jwiegley.github.io/git-from-the-bottom-up/

0 голосов
/ 03 декабря 2018

Запустите git branch, чтобы просмотреть список локальных веток и текущую ветку, в которой вы находитесь.Если вы хотите извлекать удаленные ветви локально, запустите git fetch <origin> <branch name>

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