TL; DR
Вы хотите git checkout -b featureB master
или подобное.
Long
Одна из многих вещей, которые нужно узнать о Git, заключается в том, что Git не совсем о ветках . Git действительно о коммитах . Ветви - или, точнее, ветви names - это всего лишь метод, которым вы, и Git, находите , фиксируете.
Давайте нарисуем вашу существующую ситуацию. Нарисовать график набора существующих коммитов - хорошее упражнение, и вы должны выполнять его часто.
Я клонировал репо ...
Когда вы впервые клонируете некоторые существующий репозиторий, вы получаете все его коммитов . Каждый коммит имеет какой-то большой уродливый идентификатор sh, уникальный для этого коммита. Каждый репозиторий Git везде использует один и тот же га sh ID для указанных c коммитов.
Когда вы делаете это git clone
, в самом начале вы не получить любые веток других Git. Но они вам не нужны, потому что Git не о ветвях. Вам просто нужно имен , чтобы вы могли найти коммиты. Таким образом, ваши Git берут свои имена ветвей и сохраняют их в своем хранилище как ваши имена для удаленного слежения , такие как origin/master
и origin/develop
. То есть ваши Git запоминают их имена ветвей, как ваши имена для удаленного слежения.
Последний шаг процесса git clone
заключается в том, что ваш Git создает в вашем хранилище одно имя ветки. Это имя одной ветви должно указывать на один из коммитов , полученных вами от их Git. Какой из этих коммитов должен использовать ваш Git? Вероятно, есть сотни или тысячи коммитов; как ваш Git выберет правильный один? Вы можете сказать это, но обычно по умолчанию: Используйте имя origin/master
, которое мы получили из другого Git репозитория в origin
, чтобы выбрать коммит; заставить мое новое имя ветки master
идентифицировать тот же самый коммит.
Итак, если у них есть серия коммитов, заканчивающаяся на некотором га sh ID H
, мы можем нарисовать это так:
... <-F <-G <-H <-- master, origin/master
Последний из их master
коммитов это ha sh H
. (На самом деле ha sh - это какая-то большая уродливая строка букв и цифр - мы просто используем H
для краткости.) Этот коммит хранит внутри него идентификатор ha sh некоторого другого, более раннего коммита, который мы ' позвоню G
. Коммит G
хранит в нем идентификатор ha sh некоторого другого более раннего коммита, который мы назовем F
.
Когда вы запускаете git log
, Git начинается с чтения имени master
найти га sh H
. Затем он показывает, что вы совершаете H
. Затем он использует commit H
, чтобы найти ha sh G
, и показывает, что вы делаете commit G
. Он использует это, чтобы найти F
, и показывает вам F
. Это повторяется вплоть до самого первого коммита . Этот первый коммит не больше не указывает назад, потому что не может, и вот как Git знает, как его остановить.
... и извлечение для перехода featureA.
Здесь есть две возможности: featureA
существовал на origin
ранее или не существовал. У вашего собственного Git еще нет featureA
- у него есть master
- поэтому, если вы запустите:
git checkout featureA
ваш собственный Git обычно просто скажет I не имеет featureA
. Но вместо этого ваш Git проверит ваши origin/*
имена, чтобы узнать, есть ли у вас origin/featureA
. Если это так, ваш Git найдет коммит, который назван вашим origin/featureA
, и создаст свой собственный новый featureA
, указывая на этот коммит.
Если у вас нет либо origin/featureA
, то вы должны указать Git создать featureA
сейчас, используя либо git branch
, либо git checkout -b
. Вы можете сказать своему Git: Создать новое имя featureA
, указывая на тот же коммит, на который указывает мое текущее имя ветви master
. Это дает вам:
...--F--G--H <-- master, featureA, origin/master
Теперь, когда у вас есть два имени ветви (плюс все имена удаленного отслеживания origin/*
), нам нужно каким-то образом запомнить какое имя ветви ваше Git использует . Чтобы сделать это, мы прикрепим специальное имя HEAD
в верхнем регистре, как это, к одному из двух имен ветвей.
Обратите внимание, что оба имени ветви в настоящее время оба указывают на одно и то же commit ha sh ID (H
в этом примере)! Это прекрасно. Это просто означает, что обе ветви заканчиваются коммитом H
.
Я работаю над веткой featureA и pu sh двумя коммитами.
Предположительно, что вы имеете в виду здесь вы создали два новых коммита, а затем использовали git push
, чтобы получить другой Git репозиторий, в origin
, чтобы получить два новых коммита и создать или обновить его featureA
имя .
Давайте нарисуем этот процесс create-two-new-commits. Мы начинаем, как мы видели, с:
...--F--G--H <-- master, featureA (HEAD), origin/master
Сначала вы сделали один новый коммит (обычным способом). Этот новый коммит получил новый уникальный идентификатор ha sh, отличный от идентификатора ha sh любого другого коммита в любой точке мира. 1 Мы просто назовем его I
, и нарисуйте его:
...--F--G--H <-- master, origin/master
\
I <-- featureA (HEAD)
Обратите внимание, как новый коммит I
указывает на существующий коммит H
, и что имя featureA
теперь указывает вместо I
H
. Никакой существующий коммит не изменился! Вы только что добавили новый коммит с новым ha sh ID и переместили имя ветви . Вот как растут ветви: в Git.
Затем вы сделали второй новый коммит:
...--F--G--H <-- master, origin/master
\
I--J <-- featureA (HEAD)
И наконец, вы использовали git push
для отправки новых коммитов, I
и J
, к другому Git в origin
. Вы попросили Git установить в своем репозитории имя featureA
, указывающее на фиксацию J
. Учитывая, что он сказал ОК, я сделал это ваш Git затем создал ваш origin/featureA
, чтобы помнить, что их Git, в origin
, теперь имеет featureA
:
...--F--G--H <-- master, origin/master
\
I--J <-- featureA (HEAD), origin/featureA
Затем я переключаюсь на featureB и хочу получить sh два новых коммита, но коммиты в featureA являются частью featureB. Как мне переключиться на новую ветку featureB и начать «afre sh», которая была в состоянии до того, как я нажал два коммита?
Теперь, когда у нас есть все эти графические рисунки, ответ гораздо яснее , Нам просто нужно создать новое имя featureB
так, чтобы оно указывало на существующий коммит H
, такой же, как у master
и origin/master
name.
Два обычных способы создания нового имени ветви - использовать git branch
или git checkout -b
. Разница в том, что git branch
создает имя и затем останавливается, в то время как git checkout -b
создает имя, а затем git checkout
для имени. Поскольку вам также нужен шаг git checkout
, имеет смысл использовать комбинированную команду, но давайте выделим ее как отдельные шаги.
По умолчанию обе команды создают new label - имя новой ветви - указывает на текущий коммит . Git находит текущий коммит, используя специальное имя HEAD
. Если это имя присоединено к имени ветви, как обычно, Git следует за стрелкой, выходящей из имени ветви, чтобы найти фиксацию.
Прямо сейчас, HEAD
присоединено к featureA
и featureA
очков для фиксации J
. Поэтому, если вы просто создадите имя ветви с git branch featureB
, новое имя будет указывать на коммит J
. Но вы хотите, чтобы он указывал на фиксацию H
.
Вы можете легко указать git branch
использовать любой существующий коммит . Все, что вам нужно, это его sh ID или любое имя, которое указывает на этот коммит. Таким образом, вы можете запустить git log
и найти фактический идентификатор * *1226* коммита H
и ввести его - что довольно сложно сделать, хотя вырезать и вставить проще сделать правильно - или вы можете просто используйте любое из имен, которые все еще указывают на фиксацию H
, например, master
или origin/master
:
git branch featureB master
. Это приводит к:
...--F--G--H <-- master, featureB, origin/master
\
I--J <-- featureA (HEAD), origin/featureA
Обратите внимание, что HEAD
все еще присоединен к featureA
, который все еще указывает на фиксацию J
.
Теперь, когда существует featureB
, мы можем использовать git checkout
чтобы переключиться на него. Это заменит ваш index (который мы здесь не упомянули, так как этот ответ касается только названий ветвей) и рабочее дерево (о котором мы действительно не упомянули здесь) с содержимым коммита H
при добавлении имени HEAD
к имени featureB
:
...--F--G--H <-- master, featureB (HEAD), origin/master
\
I--J <-- featureA, origin/featureA
Ваш текущий коммит теперь H
; ваше имя текущей ветки теперь featureB
; и вы готовы делать новые коммиты, в результате чего featureB
будет включать новые коммиты, которые вы делаете.
1 Эта уникальность почему Git га sh ID такие большие и некрасивые. Они действительно уникальны на практике, но на самом деле только имеют , чтобы быть уникальными во всех Git хранилищах, которые когда-либо "встречались". Однако, поскольку SHA-1 считается небезопасным и «сломанным», Git перемещается в SHA-256, чтобы помочь сохранить это «уникальное значение для всех коммитов на Земле и в любом месте солнечной системы или галактики в будущем».