Могу ли я перебазировать точку ветки ветки без явного указания родителя? - PullRequest
0 голосов
/ 10 ноября 2018

Я часто использую git rebase -i, чтобы очистить свою историю перед публикацией. Обычно я хочу редактировать коммиты туда, где разветвляется текущая ветвь, не меняя точку ветвления. Я делаю это примерно так: git rebase -i $(git show-branch --merge-base $PARENT_BRANCH HEAD)

Это ужасная команда, и я пытаюсь найти лучший способ. Пока я в этом, я бы хотел, чтобы git автоматически определил правильного родителя.

I думаю, , что я хочу, это псевдоним для git rebase -i --fork-point $(something), где something находит ветвь с самым последним общим предком текущей ветки. Это не должно быть пуленепробиваемым. Если это работает для линейной тематической ветви, этого достаточно для моих целей.

Ответы [ 2 ]

0 голосов
/ 10 ноября 2018

Во-первых, --fork-point предназначено для удаленного отслеживания имен. Он работает так, что использует reflog для поставляемого апстрима. Подробнее об этом см., Например, Git rebase - фиксация выбора в режиме точки ветвления .

Во-вторых, но, возможно, важнее, вы можете запустить git rebase <em>upstream</em>, git rebase --onto <em>newbase</em> <em>upstream</em> или даже просто git rebase. Используя форму с двумя аргументами, вы получаете большую свободу:

  • Ограничения аргумента upstream, которые будут скопированы. Мы еще поговорим об этом чуть позже.

  • Аргумент newbase выбирает, какой коммит является точкой, после которой должны быть добавлены копии. Фактический хеш-код здесь очень важен.

То есть, когда вы делаете , используете --onto, вы должны выбрать точную фиксацию для аргумента --onto newbase. Это означает, что вы можете быть очень не уверены в том, что вы используете в качестве аргумента upstream. Но когда вы не используете --onto, upstream, вы используете для обеих целей , поэтому параметр upstream это тот, который требует много тщательности. Одна цель свободна и свободна, а другая нет.

Что это означает, что вы можете использовать два аргумента, чтобы восстановить любую лишнюю слабость, которую вы хотите (но может и не понадобиться), или нет аргументов, чтобы получить удобство.

(Следующая часть построена странно из-за обновления вопроса.)

git show-branch --merge-base X Y = git merge-base X Y

Команда git show-branch теперь принимает --merge-base или --independent при наличии нескольких аргументов. Имея ровно два аргумента, он делает то же самое, что и git merge-base X Y, то есть находит базу (и) слияния ревизий X и Y. (git merge-base также теперь занимает --independent, вместо того, чтобы предполагать стратегию осьминога, но это применимо только при использовании трех или более спецификаторов коммитов.)

Я предпочитаю git merge-base здесь, так как я думаю, что это более очевидно (и, конечно, немного короче, чтобы набрать).

Без аргументов upstream исходит из настроек вашего филиала

Каждая ветвь может иметь одну (но только одну) восходящую настройку.

Настройка восходящего потока для любой ветви B обычно origin/<em>B</em>, потому что мы склонны передавать их на GitHub или Bitbucket или какой-либо корпоративный веб-сервер, URL которого мы придерживаемся под именем origin, чтобы мы не должны печатать это все время. Если вы израсходовали свой апстрим на origin/ и хотите установить секундный параметр, который git rebase будет использовать автоматически, вам не повезло (но читайте дальше). Но если вы не не использовали один восходящий поток, просто установите для восходящего потока то, что вы хотели бы git rebase для автоматического использования. Например, если вы сейчас на feature-X и хотите, чтобы он перебазировал на develop:

$ git branch --set-upstream-to=develop

и теперь feature имеет develop в качестве восходящего. Выполнение git rebase-i или без него) перезапустится, как если бы вы выполнили ту же команду с develop, что и ее аргумент upstream.

Если вы использовали вышестоящий поток, вы можете создать свой собственный псевдоним:

alias.name = !git rebase "$@" $(git config --get branch.$(git symbolic-ref --short HEAD).base) #

(выберите какое-нибудь имя): это позволяет вам настроить, используя git config, дополнительное имя, branch.feature-X.base, для develop. $(git symbolic-ref --short) извлекает текущее имя ветви, git config --get получает настройку, а затем rebase использует ее в качестве одного вышестоящего аргумента.

Недостаток одного аргумента ограничитель-и-цель / newbase

Недостаток здесь в том, что, учитывая график вида:

             o--o   <-- develop
            /
...--o--o--o
            \
             A--B--C   <-- feature-X (HEAD)

вы получите копии, которые идут после кончика develop:

                  A'-B'-C'  <-- feature-X (HEAD)
                 /
             o--o   <-- develop
            /
...--o--o--o
            \
             A--B--C   [abandoned]

когда вы хотите сохранить копии в одном и том же месте, просто возитесь с текстом их коммита или, может быть, сложите два вместе или что-то в этом роде:

             o--o   <-- develop
            /
...--o--o--o--A'-B'-C'  <-- feature-X (HEAD)
            \
             A--B--C   [abandoned]

Использование --onto

В форме с двумя аргументами параметр --onto выбирает целевой коммит:

             o--o   <-- develop or whatever
            /
...--o--o--*    [pick this commit as target]
            \
             A--B--C   <-- feature-X (HEAD)

Копии будут идти после *. Набор коммитов, подлежащих копированию, определяется с помощью <em>upstream</em>..feature-X, то есть коммитов, достижимых начиная с feature-X и работающих в обратном направлении, но исключая коммиты, достижимые начиная с upstream и работает в обратном направлении.

Теперь вам нужно только найти коммит *. Если у вас есть два имени, таких как feature-X и develop, вы можете использовать синтаксис gitrevisions с тремя точками , develop...feature-X или feature-X...develop (синтаксис симметричен, когда существует только одно слияние база, как это), чтобы указать коммит *. Это работает только в довольно новых версиях Git: в более старых версиях используйте git show-branch или git merge-base (с двумя хэш-идентификаторами коммитов они оба ведут себя одинаково).

Указав коммит * в качестве цели --onto, вы снова можете разрешить восходящему потоку ветви работать в качестве ограничителя. То есть вы можете опустить явное значение upstream, поскольку оно по умолчанию соответствует фактическому восходящему потоку. А так как вы можете использовать синтаксис @{upstream} или @{u}, вы можете сделать очень короткий и простой псевдоним, как вы сделали в своем собственном ответе.

Если вы хотите сохранить отдельный восходящий поток (origin/feature-X) и базу, вы можете вернуться к идее настройки дополнительного значения для имени ветви. В этом случае вам нужно будет использовать его дважды, поэтому вместо псевдонима вам может понадобиться полноценный скрипт, где вы можете выполнить проверку ошибок:

#! /bin/sh
# git-base - rebase on the current branch's base setting
. git-sh-setup
branch=$(git symbolic-ref --short HEAD) || exit
base=$(git config --get branch.$branch.base) || die "branch.$branch.base is not set"
mbase=$(git merge-base $base HEAD) || die "there is no merge base"
git rebase --onto $mbase "$@" $base

Назовите это git-base, поместите его на свой путь, и теперь вы можете запустить git base.

0 голосов
/ 10 ноября 2018

Через час или около того я придумал что-то достаточно хорошее. Эта команда делает то, что я хочу , если ветвь имеет установленный восходящий поток, и если упомянутый восходящий поток является тем, с которым я хочу сравнить:

$ git rebase -i --onto @{upstream}...HEAD

Эти тройные точки делают не то же самое, что в git-log и аналогичных командах. Из документации git-rebase :

В качестве особого случая вы можете использовать «A ... B» в качестве ярлыка для базы слияния A и B, если существует ровно одна база слияния.

Итак, это говорит: "найдите базу слияния HEAD и ее собственного восходящего потока и сделайте упор против этого". Обычно у локальных веток нет восходящего потока, но это можно установить, и есть опция gitconfig (autoSetupMerge), которая будет делать это автоматически для новых веток. Таким образом:

$ git config --global branch.autoSetupMerge always
$ git config --global alias.fixup 'rebase -i --onto @{upstream}...HEAD'
$ git branch childbranch -u parentbranch  # Repeat for other branches as needed.

После этого я могу легко редактировать историю обратно в точку ветвления с помощью:

git fixup

И это работает для всех будущих ветвей.

(примечание: см. ответ Торека для подробного объяснения того, что здесь происходит под капотом)

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