Изменить для вывода обновленного адреса (git branch -a
и git branch -vv
): да, что-то отсутствует . Не совсем понятно, что пошло не так, но у меня есть предположение. Эта часть git push -u
вывода:
* [new branch] newfeature/v4-json -> newfeature/v4-json
Branch 'newfeature/v4-json' set up to track remote branch 'newfeature/v4-json' from 'origin' by rebasing.
показывает, что ваш Git устанавливает origin/newfeature/v4-json
(разделенный на две части) в качестве восходящего потока для newfeature/v4-json
. Но ваш git branch -a
и git branch -vv
вывод показывают, что origin/newfeature/v4-json
там нет.
Я могу воспроизвести ключевой элемент этого поведения, создав клон single-branch . Использование git clone --depth=<em>number</em>
или git clone --single-branch
даст такой клон. Побочным эффектом этого является то, что ваш Git никогда не будет создавать имена для удаленного отслеживания ни для одной ветви , кроме той ветви, о которой вы сказали Git, что вас беспокоит. Если это , проблема заключается в том, чтобы преобразовать клон в обычный (многоотраслевой) клон. (Если вы использовали --depth
для создания аспекта с одной ветвью, также может быть целесообразно отменить клон.)
Чтобы узнать, установлен ли ваш клон origin
как одно ветвь:
$ git config --get-all remote.origin.fetch
В обычном клоне будет напечатано:
+refs/heads/*:refs/remotes/origin/*
В клоне с одной ветвью и выбранной веткой master
будет напечатано:
+refs/heads/master:refs/remotes/origin/master
, который говорит вашему Git: создать имя удаленного отслеживания для master
, а не первого создать имена удаленного отслеживания для *
, т. Е. Для всех ветвей .
Чтобы отменить единственную ветвь клона origin
:
$ git config remote.origin.fetch '+refs/heads/*:refs/remotes/origin/*'
(или отредактируйте .git/config
напрямую, например, git config --edit
, что является моим предпочтительным методом). См. Также Как мне "отменить" клон --single-branch?
Чтобы преобразовать мелкий клон в полный (не мелкий) клон, просто запустите:
$ git fetch --unshallow
Обратите внимание, что эта операция не зависит от одиночного ветвления, несмотря на то, что git clone
связывает их по умолчанию (вы можете переопределить это в git clone
время с помощью git clone --depth=<em>number</em> --no-single-branch
). В версиях Git до 2.15 нет теста командной строки на мелководье; в 2.15 или позже используйте:
git rev-parse --is-shallow-repository
но до этого вам нужно проверить наличие файла .git/shallow
:
if [ -f $(git rev-parse --git-dir)/shallow ]; then
echo true
else
echo false
fi
имитирует git rev-parse --is-shallow-repository
.
Кроме того, существует проблема с выводом, который вы хотите увидеть. Вы говорите, что хотели бы видеть newfeature
как ветвь на пульте дистанционного управления, но не может произойти , поскольку имя newfeature/v4-json
должно существовать, что исключает возможность существования newfeature
.
(Оригинальный ответ ниже строки.)
$ git push -u origin newfeature/v4-json
Это сработало именно так, как вы просили. Все хорошо в остальной части вывода, который вы показали. Так что не ясно, что вы считаете неправильным; нет ничего плохого Я перейду к другому сообщению, которое вы показали:
# Your branch and 'origin/dev' have diverged,
# and have 1 and 2 different commits each, respectively.
ниже.
Что все это значит? (Long)
Это может помочь рассмотреть, как работает Git, и некоторые довольно специфические термины Git. В частности, используемая вами фраза - ветка удаленного отслеживания - на мой взгляд, термин плохой , вводящий в заблуждение. Это - это термин Git, поэтому мы должны понимать, что люди имеют в виду, когда используют его, но это плохой термин, который означает, что люди злоупотребляют этим, и если вас смущает чье-то использование, возможно, стоит отступить и снова рассмотреть эти вопросы.
Во-первых, давайте отметим, что Git действительно все о коммитах . Комитеты являются Git's raison d'être ; без коммитов мы бы вообще не использовали Git. Итак, давайте посмотрим, что такое коммит.
EАч коммит содержит файлов, но это не , а набор файлов.Это снимок всех ваших файлов на момент создания снимка, 1 , но он также содержит некоторые метаданные: информацию о сохраненные данные.Наиболее очевидным является то, что вы видите в выводе git log
: ваше имя и адрес электронной почты, а также представление компьютера о том, каким был день и время, когда вы сделали коммит, наряду с причиной , которую вы сохранили длясделать коммит, т. е. ваше лог-сообщение.Все это предназначено для вас - или для кого-то другого - для использования в будущем: когда-нибудь, возможно, завтра, возможно, через месяцы или годы, вы можете оглянуться назад на этот сделанный вами коммит и спросить себя: почему, черт возьмия сделал что ? Ответ должен быть в вашем сообщении журнала.
Поскольку коммит сохраняет файлы - как снимок, замороженный во времени, неизменныйи жить вечно (или до тех пор, пока живет сам коммит) - они прекрасно подходят для архивирования.В любое время в будущем вы можете вернуться в прошлое и увидеть точно то, что вы тогда сохранили.Вы не можете изменить это: это в прошлом, исправлено, заморожено во времени.Даже Git не может изменить его, как мы увидим через мгновение.
Чтобы найти коммит, Git нужно имя.Эти имена не названия веток!Или, точнее, вы можете начать с использования имени ветви, но это не то имя, которое нужно Git .Истинное имя любого коммита - вместо этого идентификатор хеша .Идентификатор хэша каждого коммита кажется случайным, но на самом деле это криптографическая контрольная сумма всего содержимого коммита, исключительно чувствительная к каждому биту данных в , который фиксирует: всезамороженного снимка, а также ваше имя и отметку времени и ваше сообщение журнала.Вот почему * *1162* вы или кто-либо другой не можете изменить коммит: изменение чего-либо изменяет хэш-идентификатор, и в результате вы получаете новый и другой коммит.Никто не знает, каким будет хэш-идентификатор для нового коммита, пока он не будет сделан.В это время он получает уникальный идентификатор.Никто никогда не будет использовать этот идентификатор для любого другого коммита!И никто не может ничего изменить в коммите: Git будет знать, если вы попытаетесь, потому что идентификатор больше не будет совпадать. 2
Есть один или двапоследние ключевые части этой конкретной головоломки.Во-первых, в каждом новом коммите Git хранит хеш-идентификатор - истинное имя - предыдущего коммита как часть этих метаданных.То есть Git не только сохраняет ваше имя, время и т. Д., Но также сохраняет необработанный хэш-идентификатор коммита, который вы использовали до , чтобы сделать этот новый коммит.Git называет этот сохраненный хеш-идентификатор родительским коммита.Это означает, что каждый коммит указывает на свой родительский коммит в обратной цепочке.
Например, предположим, что у нас есть только два коммита A
и B
врепозиторий.A
- самый первый коммит, поэтому он намеренно имеет нет родителя - это особый случай.Но B
был сделан из A
, поэтому B
указывает на A
:
A <-B
Если вы извлекаете коммит B
, сделайте некоторую работу и сделайте новый коммит C
, новый коммит автоматически указывает на B
:
A <-B <-C
Что означает это , так это то, что Git должен знать только явно-случайный хэш-идентификатор последнего коммит.В этом случае это коммит C
.Если его фактический хэш-идентификатор равен cba9876...
или как-то еще, Git может использовать его для поиска содержимого из C
.Это содержимое включает в себя фактический хэш-идентификатор commit B
.Затем Git может использовать это, чтобы найти B
, содержимое которого включает в себя фактический хеш-идентификатор commit A
.Git может использовать это, чтобы найти A
, а A
не имеет родителя, так что теперь, наконец, Git может перестать работать в обратном направлении.
Этот профессионалРабота в обратном направлении от tip tip commit типа C
, идентифицируемого как имя ветки , имеет решающее значение в Git. Так история существует . История в репозитории Git - это коммитов, связанных этими стрелками, указывающими назад. Вы начинаете с конца и идете, один коммит за раз, через историю, чтобы увидеть, куда вы можете добраться, следуя стрелкам родителей.
Именно здесь появляется последняя часть головоломки, когда названия ветвей и другие подобные имена. Давайте сделаем паузу и закончим сноски здесь, а затем погрузимся в названия ветвей и рисование графиков.
1 Git на самом деле делает снимок из index , но мы не будем вдаваться в эти детали, кроме как сказать, что то, что снимается - заморожено во времени, навсегда для этого коммита - это то, что находится в индексе в то время, которое, по крайней мере, потенциально отличается от того, что вы можете видеть в вашем рабочем дереве , где вы выполняете свою работу.
2 Git действительно проверяет это, когда это кажется удобным или уместным. Это автоматически обнаруживает случайное повреждение хранилища Git, как это происходит, когда (например) вы пытаетесь сохранить в Dropbox - Dropbox иногда пытается изменить файлы за спиной (и Git), и Git его ловит. К сожалению, редко существует хороший способ восстановить поврежденный репозиторий - вместо этого Git полагается на идею, что репозитории Git реплицируются повсеместно. Возможно, у вас есть хорошая копия где-то еще, поэтому вы просто выбросите ее полностью.
Имена ветвей находят хэш-идентификаторы коммитов
Любой существующий репозиторий - ну, любой, кроме полностью пустого, свежего, нового репозитория с нет коммитов в нем - имеет некоторый набор коммитов. Эти коммиты образуют цепочки, которые мы только что видели, такие как:
A <-B <-C
Нам - и Git - нужен какой-то способ для записи хеш-идентификатора последнего коммита в этой цепочке.
Способ, которым Git достигает этого, заключается в том, что Git называет ссылками или ссылками . Существует много форм рефери, но Большая тройка:
- Названия веток, например
master
.
- Имена удаленного слежения, например
origin/master
. (Git называет эти имена веток для удаленного отслеживания или ветви для удаленного слежения , что я считаю плохим именем; я переключился на использование имен для удаленного слежения , который, я думаю, сложнее ошибиться.)
- Имена тегов, например
v1.3
.
На самом деле все они реализованы с помощью одних и тех же базовых методов, но мы просто будем рассматривать их как отдельные формы имени. Филиал Имена имеют специальное свойство; все другие имена не имеют этого свойства.
То, что упоминается в одном из этих имен, довольно просто: это просто фактический необработанный хэш-идентификатор объекта Git, обычно коммит. 3 Таким образом, имя ветви, такое как master
, указывает на последний коммит в ветви - коммит C
на этом рисунке:
A--B--C <-- master
Обратите внимание, что стрелки, соединяющие коммиты друг с другом, выходят из дочернего элемента и указывают на (неизменяемого) родителя, что дает нам этот метод обратного обхода. Нам не нужно беспокоиться, чтобы нарисовать их. Стрелки выходят из ответвлений имен, однако изменяют .
Когда мы добавляем новый коммит к master
, Git автоматически обновляет имя master
для хранения хеш-идентификатора нового коммита. Поэтому, если мы создадим новый коммит сейчас, новый коммит D
укажет на C
:
A--B--C <-- master
\
D
но Git немедленно отрегулирует master
, чтобы указывать не на C
, а на D
:
A--B--C--D <-- master
Поскольку D
указывает на C
, мы все еще можем найти все коммиты: мы начинаем в конце и работаем в обратном порядке, как обычно. C
теперь является вторым коммитом в этом процессе вместо первого.
3 Имена ветвей должны содержать хэш-идентификаторы объектов фиксации, в то время как имена тегов более гибкие.Нам не нужно заботиться об этом здесь.Поскольку значения имен удаленного отслеживания копируются из имен ветвей, имена удаленного отслеживания также содержат только хэш-идентификаторы фиксации.
Имена ветвей являются частными для каждого репозитория, но репозитории общаются.
Git - это распределенная система контроля версий.Это означает, что каждый репозиторий Git является своего рода автономным островом со всем необходимым для этого репозитория.Если существует несколько веток с большим количеством коммитов, они все в этом одном репозитории:
A--B--C--D--G--H <-- master
\
E--F <-- dev
Чтобы сделать Git действительно полезным, мы регулярно используем Git для обмена работой с другими пользователями Git.Для этого мы обмениваем коммитов .Их хеш-идентификаторы универсальны для всех Gits везде, из-за этого трюка с криптографической контрольной суммой.Учитывая снимок и метаданные, каждые Git везде будет вычислять одинаковый хэш-идентификатор .Поэтому, если мой репозиторий имеет коммиты от A
до H
, как это, - запомните, что эти заглавные буквы обозначают уникальные, большие уродливые хэш-идентификаторы - и я подключаюсь к вашему репозиторию и вам имеет коммит H
, ваш репозиторий также должен иметь тот же коммит, что и мой.
Если у вас нет коммит H
, у меня есть коммит, который вы наделите«т.Если у вас есть коммит I
или J
, , у вас есть коммит, которого I нет.В любом случае, наши Gits могут просто обмениваться хэш-идентификаторами, чтобы узнать, кто что имеет.Тот, кто отправляет коммиты, отправит их, кто получит коммиты, получит их, и отправитель даст получателю любые новые необходимые коммиты.
Допустим, вы принимаете от меня новые коммиты.У меня есть новые коммиты I
и J
, и мой новый коммит J
имеет имя , которое запоминает свой хэш-идентификатор.В моем хранилище у меня есть это:
A--B--C--D--G--H <-- master
\
E
\
I--J <-- dev
По какой-то причине я не имею коммит F
, который у вас есть на dev
.Вместо этого у меня есть I-J
коммитов на моем dev
, после (общего) коммита E
.
Именно здесь имена удаленного отслеживания входят в
Ваш Git берет мои коммитыI
и J
.Мой коммит I
имеет родителя E
.Итак, ваш репозиторий теперь имеет следующее:
A--B--C--D--G--H <-- master
\
E--F <-- dev
\
I--J <-- ???
Какое имя будет ваше Git-репозиторий используется для запоминания моего коммита I
?Лучше не использовать dev
: если ваш Git делает ваш dev
пункт для коммита I
, как вы когда-нибудь снова найдете коммит F
?Помните, что он имеет явно-случайный хэш-идентификатор.Вы никогда не сможете угадать это.
Итак, ваш Git использует имена для удаленного слежения , чтобы запомнить мои ветви,Ваш Git делает это:
A--B--C--D--G--H <-- master, origin/master
\
E--F <-- dev
\
I--J <-- origin/dev
(при условии, что мои master
очков совершают H
).
Имена origin/master
и origin/dev
в вашем репозиторий (ваше) имена для удаленного отслеживания , помня мои master
и мои dev
. 4 Более того, предположим, что вы теперь запрашиваете свой Git, прося его сравнить наборкоммитов, доступных с dev
по сравнению с origin/dev
, в обычном методе обхода назад, который использует Git.
Начиная с dev
, коммиты, которые вы будете посещать, будут F
, затем E
, затем D
и так далее до A
.Начиная с origin/dev
, коммиты, которые вы будете посещать, будут J
, затем I
, затем E
, затем D
и т. Д. До A
. Какие коммиты уникальны для какой прогулки?Сколько коммитов вы получаете с dev
, чего вы не можете достичь с origin/dev
и наоборот?
Отсчитайте их, а затем сравните с тем, что сказал вам ваш Git:
# Your branch and 'origin/dev' have diverged,
# and have 1 and 2 different commits each, respectively.
На самом деле в нашей мозаике отсутствует еще одна часть, которую мы просто опишем в последнем разделе, когда будем говорить о git push
ниже.
4 Git иногда называет это tracking , а не , помня , но это еще одно место, в котором Git сильно злоупотребляет словом. Я использовал его во фразе remote-tracking , но, по крайней мере, здесь он переносится и использует это слово в качестве прилагательного, модифицирующего remote .
git push
отличается от git fetch
Процесс выше, где ваш Git создал имена для удаленного отслеживания из имен веток, найденных в Git в origin
, относится только к git fetch
. Это происходит, когда ваш Git вызывает Git на origin
и приносит их коммитов you .
Вы, конечно, можете сделать так, чтобы ваш Git вызывал их Git в origin
и посылал коммитов. Это операция git push
, и она очень похожа. Ваш мерзавец говорит своим мерзавцам о коммитах, которые у тебя есть, а они нет. Давайте нарисуем некоторые. Начнем с этого:
A--B--C--D--G--H <-- master, origin/master
\
E--F <-- dev
\
I--J <-- origin/dev
Теперь мы запустим git checkout master
и git checkout -b newfeature/v4-json
, или проще:
git checkout -b newfeature/v4-json master
Теперь у нас есть:
A--B--C--D--G--H <-- master, origin/master, newfeature/v4-json (HEAD)
\
E--F <-- dev
\
I--J <-- origin/dev
Мы добавили специальное имя HEAD
к newfeature/v4-json
, чтобы запомнить , имя которого обновляется по мере добавления новых коммитов.
Теперь мы создадим один новый коммит. Это может быть больше одного или даже нет , но давайте просто создадим один для иллюстрации. Новый коммит получает большой уродливый хеш-идентификатор, но мы просто назовем его K
здесь:
K <-- newfeature/v4-json (HEAD)
/
A--B--C--D--G--H <-- master, origin/master
\
E--F <-- dev
\
I--J <-- origin/dev
Теперь ваш Git вызовет Git в origin
, используя:
git push -u origin newfeature/v4-json
Ваш Git набирает свой Git и объявляет, что у вас есть коммиты K
и H
. 5 У них нет K
, но у них есть H
, поэтому у них есть ваш Git отправить через фиксацию K
со своим снимком и метаданными. Ваш Git может сказать, что, поскольку у них есть H
, у них также есть G
и D
и все до этого, поэтому вам нужно только отправить им K
и его содержимое.
Затем, в конце, ваш Git спрашивает их: Пожалуйста, теперь, если все в порядке, установите ваше имя newfeature/v4-json
, чтобы оно указывало на фиксацию K
. Обратите внимание, что у вас их нет установите xpt/newfeature/v4-json
или что-нибудь подобное. У них есть их ветвь! На самом деле у них нет a newfeature/v4-json
, поэтому вполне нормально, что они установят один. Так они и делают! Теперь у них есть newfeature/v4-json
в их хранилище , указывающее на коммит K
.
Ваш Git сейчас создает ваше имя для удаленного слежения origin/newfeature/v4-json
, указывая на фиксацию K
, для запоминания их newfeature/v4-json
, указывая на commit K
. 6 Но это просто означает, что у вашего графа есть одно дополнительное имя , например:
K <-- newfeature/v4-json (HEAD), origin/newfeature/v4-json
/
A--B--C--D--G--H <-- master, origin/master
\
E--F <-- dev
\
I--J <-- origin/dev
Из-за опции -u
ваш Git сразу же запускается:
git branch --set-upstream-to=origin/newfeature/v4-json newfeature/v4-json
Устанавливает upstream для вашей ветви newfeature/v4-json
. Каждая из ваших веток может иметь one (1) upstream, и довольно типично использовать ее именно таким образом. См. Зачем мне все время делать `--set-upstream`? , чтобы узнать больше.
5 Ваш Git мог бы рассказать им о F
, но только бы сказал git push origin dev
здесь. Используя git push origin newfeature/v4-json
, с или без -u
, вы сказали своему Git: Расскажите им о коммитах K
, H
, G
, D
, C
, B
и / или A
по мере необходимости. Ваши другие неразделенные коммиты намеренно остаются частными.
6 Помните, что благодаря магии хеш-идентификаторов, коммит K
универсален для каждого Git везде . Каждый Git либо имеет K
по хэш-идентификатору, а затем , что commit; или вообще не имеет K
, так что это не имеет значения.
(Это не обязательно на 100% гарантировано. Предположим, хеш-код K
на самом деле b5101f929789889c2e536d915698f58d5c5c6b7a
. Это хеш-идентификатор коммита в Git-репозитории для самого Git. Если вы никогда не подключаетесь , ваш Git-репозиторий в Git-репозитории для Git, это нормально, что у вас и у них разные коммиты с одним и тем же хэш-идентификатором. Но если вы do когда-либо подключите свой Git-репозиторий к Git-репозиторию для Git, Происходят не так уж и большие вещи. Короткая версия заключается в том, что вы просто не получаете коммит Git, а они просто не получают ваш: два репозитория просто не могут быть объединены в данный момент. Это, вероятно, вполне устраивает и вас, и люди, которые поддерживают Git. Но смотрите также Как недавно обнаруженное столкновение SHA-1 влияет на Git? )