Но существует ли общий способ создания ветви baz
из foo
и отслеживания того же пульта, что и foo
, независимо от того, что это за пульт?
Да, но это немного сложно. Будь то, что есть, есть около двенадцати дюжин 1 разных способов сделать это, но мы просто выберем один здесь.
Сначала давайте рассмотрим все важные части этого:
- В вашем примере есть ваши собственные локальные имена филиалов,
foo
и baz
.
- Существуют имена для удаленного отслеживания, такие как
origin/bar
(полное имя refs/remotes/origin/bar
).
- Идентификатор хэша коммита, на который указывает имя каждой ветви или имя удаленного отслеживания.
И есть дополнительная восходящая настройка для любого (локального) имени ветви. Обычно восходящий поток какой-то локальной ветки - это имя удаленного отслеживания (Git называет это «удаленным отслеживанием») с тем же базовым именем, но с префиксом удаленного имени. Например, master
обычно имеет origin/master
в качестве восходящего потока. Однако, как в вашем примере, исходящий поток ветви не должен совпадать: исходящий поток foo
может быть origin/bar
.
(Также возможно установить локальную ветвь в качестве восходящего потока другой локальной ветки; это ведет себя так, как вы ожидаете. Внутренне это реализуется установкой половины remote
из двух частей настройка upstream на .
, что означает ваш собственный репозиторий. Но мы проигнорируем эти детали здесь.)
Как упоминалось fphillipe , у нас есть git branch --set-upstream-to
, который может установить восходящий канал любой ветви в любое время. Если уже есть какой-то восходящий набор, он заменяет его. Если не было никакого набора вверх по течению, теперь есть.
У нас также есть два разных способа создания нового имени ветви:
git branch <em>name</em> [<em>start-point</em>]
создает новую (локальную) ветвь с именем name
. Исходный хеш-идентификатор, хранящийся в этом имени, задается как start-point
; если вы опустите start-point
, начальный идентификатор хэша будет таким, который git rev-parse HEAD
выдаст.
git checkout -b <em>name</em> [<em>start-point</em>]
создает новую (локальную) ветвь с именем name
, а затем делает git checkout
этого имени, все в одном. По сути это эквивалентно запуску git branch
, за которым следует git checkout
.
1 "Ew, gross" ?
Цель
В этом случае вы хотите создать локальную ветвь с именем baz
, установить ее хеш в соответствии с foo
и установить ее восходящий поток в foo
восходящий, что origin/bar
. То есть после создания baz
вы бы хотели:
git rev-parse baz
для получения того же результата, что и:
git rev-parse foo
- это означает, что один и тот же коммит является коммитом-наконечником каждой ветви - но вы хотите:
git rev-parse --symbolic-full-name baz@{upstream}
для производства refs/remotes/origin/bar
.
Обе команды создания могут, но не всегда, также устанавливать восходящий поток недавно созданной ветви. Вы можете использовать аргумент --track
, чтобы заставить их установить его, и вы можете использовать аргумент --no-track
, чтобы заставить их не устанавливать его. Флаг --track
использует имя удаленного слежения, поэтому, если вы сначала выясните, что восходящий поток foo
равен origin/bar
, вы можете написать:
git checkout -b baz origin/bar
(или то же самое с git branch
, если вы не хотите также проверять новую ветку). Но это имеет две ошибки:
- Требуется, чтобы вы вручную находили восходящий поток
foo
. Вы хотите автоматизировать это.
- Возможно, более важно, он создает
baz
, указывая на тот же коммит, что и origin/bar
. Если foo
указывает на другой коммит, это нарушает одно из желаний.
HРабота должна состоять из нескольких частей. Сначала вы создадите ветку (и, если хотите, переключитесь на нее), используя локальное имя foo
, чтобы установить начальную точку. И git branch
, и git checkout
будут не устанавливать восходящий поток новой ветви в этом случае, по крайней мере по умолчанию, но вы можете быть полностью явным с помощью --no-track
, или вы можете включить foo
в необработанный хэш-идентификатор. Обычно все это не нужно, 2 , но если вы хотите использовать необработанный хэш-идентификатор, вот фрагмент оболочки, чтобы сделать это:
name=baz
start=foo
hash=$(git rev-parse ${start}^{commit}) || exit
Если git rev-parse
не удается выполнить синтаксический анализ foo
для необработанного хеш-идентификатора или foo
не идентифицирует фиксацию, git rev-parse
выдаст сообщение об ошибке для stderr и выйдет с ненулевым значением, и || exit
будет иметь в этот момент ваш фрагмент оболочки завершился.
Тогда:
git branch $name $hash || exit
попытается создать имя ветви $name
, указывающее на нужный хеш.
Нам также нужно найти вышестоящее имя, для которого git rev-parse
снова является командой для использования:
upstream=$(git rev-parse --symbolic-full-name ${start}@{upstream}) || exit
Как и прежде, у нас есть наш фрагмент оболочки, выходящий из строя, если git rev-parse
не удался, позволяя git rev-parse
напечатать соответствующую ошибку. Теперь, когда у нас есть восходящий поток, мы можем установить его, используя git branch --set-upstream-to
:
git branch --set-upstream-to $name $upstream
2 В командной строке вы просто запускаете команду и наблюдаете: если она сделала то, что вы хотите, хорошо, если нет, вы исправляете по ходу дела. Однако из сценария часто трудно понять, что произошло или может произойти. Лучше всего использовать то, что имеет как можно меньше побочных эффектов. Поэтому мы по возможности разбиваем каждую команду Git на прямые команды. Я не довожу это до крайности, в этом примере, так как это слишком раздражает, но я покажу операции git rev-parse
.
код
Объединение частей в правильном порядке, плюс небольшая проверка аргументов и использование -e
, чтобы избежать всех || exit
s, дает нам полный (но полностью непроверенный) сценарий оболочки:
#! /bin/sh -e
usage() {
echo "usage: $0 branch-name start-point"
}
case "$#" in
2) ;;
*) usage 1>&2; exit 1;;
esac
name="$1"
start="$2"
hash=$(git rev-parse ${start}^{commit})
upstream=$(git rev-parse --symbolic-full-name ${start}@{upstream})
git branch $name $hash
git branch --set-upstream-to $name $upstream
После тестирования напишите его как исполняемый скрипт оболочки где-то в вашем $PATH
, назвав его (например) git-branch-with-upstream
, а затем:
git branch-with-upstream baz foo
вызовет скрипт, который создаст baz
, указывающий на тот же коммит, что и foo
, и имеющий foo
в восходящем направлении как baz
в восходящем.
Теперь вы знаете, как писать новые команды Git!