Используйте git rebase --onto
. Это все еще немного сложно, но вам не нужно быть интерактивным и конкретно указывать Git drop
конкретные коммиты: вы выбираете через командную строку , какие коммиты копируются, а какие нет.
На самом деле, вы делаете это все время, потому что git rebase
в основном работает путем копирования некоторого набора коммитов. То есть git rebase
имеет три или четыре фазы, в зависимости от того, как вы считаете:
Перечислите коммиты ha sh ID коммитов для копирования. Они становятся pick
командами, когда вы используете интерактивное изменение стиля, так что вы на самом деле уже знаете, как работает эта часть.
Используйте git checkout --detach
(или эквивалентный) для go в отсоединенный режим HEAD при конкретном целевом коммите. Целевой коммит зависит от вас: вы сообщаете Git через командную строку, что коммит для проверки здесь.
Повторно запускайте git cherry-pick
на каждом коммите, который будет выбран. (Точные детали этого конкретного шага сильно различаются в зависимости от стиля перебазирования, который вы используете.)
Теперь, когда нужные коммиты скопированы, возьмите название ветви, которое мы отказались на шаге 2 - который перезагружает записи в файле - и заставляет имя этой ветви указывать на текущую (HEAD
) фиксацию, и повторно присоединяет HEAD, чтобы мы вернулись к ветви.
Если я могу немного перерисовать ваш пример, вы на самом деле начинаете с этого:
A--B--C <-- origin/master
\
D [refs/for/master in the Gerrit repo at `origin`]
\
E--F <-- devel
Когда вы используете --amend
или какую-либо другую операцию, это не совсем измените D
вообще, как вы уже видели: он просто делает новый коммит D'
, чей родитель C
и чей снимок учитывает любые обновления, которые вы хотели. Итак, теперь у вас есть:
D' [whatever name you like can go here]
/
A--B--C <-- origin/master
\
D [refs/for/master in the Gerrit repo at `origin`]
\
E--F <-- devel
Чтобы скопировать E-F
в автоматическом режиме, вам нужен способ назвать коммит D
. Его фактический идентификатор ha sh всегда будет работать, но идентификаторы ha sh большие, уродливые и раздражающие. Это работает намного лучше, если вы вставите в свой собственный репозиторий имя, подойдет любой тип имени, которое вы сможете запомнить.
Доступны следующие "виды" имен:
- имена ветвей:
git branch
создает и удаляет их; - имена тегов:
git tag
создает и удаляет их; и - любое другое название вашего изобретения: это (легкая) боль в области тела, так как вы должны использовать их полные имена, включая префиксы, такие как
refs/for/
или refs/xyzzy/
. Пространство имен Gerrit refs/for/
является одним из этих изобретений: оно принадлежит не вам, а Gerrit, но это всего лишь целая категория, в которой каждый может прикреплять имена, и если каждый оставляет refs/for/
Gerrit и изобретает свои собственные личные вещи что не refs/for/
, они не будут сталкиваться.
Из них названия филиалов, вероятно, являются лучшим выбором, но решать только вам. В остальном я предполагаю, что вы используете имена веток. (Имена тегов тоже хорошо работают, и я экспериментировал с их использованием для своего собственного использования. Только будьте осторожны, чтобы не git push
ошибочно их вводить, поскольку теги начинают быстро захламлять репозитории других людей!)
Итак, Предположим, у вас есть:
D' <-- in-review/master/2
/
A--B--C <-- origin/master
\
D <-- in-review/master/1
\
E--F <-- devel
, где in-review/master/<em>number</em>
- ваш личный способ запомнить Я отправил этот коммит с помощью git push origin refs/for/master
. Так как вы сделали это дважды, у нас есть два разных числа. (Я только что изобрел эту систему именования для этого ответа, так что это может быть ужасно. Выберите ту, которая подходит для you .)
Когда вы запускаете интерактивную перебазировку, используя:
git checkout devel
git rebase -i origin/master
коммитов, которые git rebase
перечисляет для копирования: D-E-F
.
Это потому, что он фактически перечисляет F-E-D-C-B-A
- каждый коммит, который можно найти, запустив в F
коммит, названный через devel
и работающий в обратном направлении. Затем отдельно перечисляются C-B-A
: каждый коммит, который можно найти, начиная с C
, коммит с именем origin/master
и работая в обратном направлении. Он выбивает любой коммит в списке second из списка first , оставляя F-E-D
, который затем возвращается к необходимому порядку выбора вишни.
Список коммитов:
- те, которые достижимы из текущей ветви (
devel
), минус - те, которые достижимы из
upstream
аргумента, который вы даете git rebase
: origin/master
, в данном случае .
На этом завершается шаг 1. (На самом деле все сложнее: больше коммитов можно выбить из списка. По умолчанию коммиты слияния выбрасываются автоматически. Дополнительные коммиты могут быть отбрасывается с помощью сопоставления идентификатора патча и режима перебазировки. Но давайте просто проигнорируем все это здесь.)
Аргумент upstream
также предоставляет целевой коммит, который Git На шаге 2 будет использоваться git checkout
, отсоединяющий HEAD.
Если бы вы могли просто сказать Git:
- , используйте commit
C
в качестве цели ... - , но используйте commit
D
в качестве конца списка коммитов, чтобы выбить
, которые бы выполняли эту работу, без необходимости использовать git rebase -i
и ручное редактирование. Оказывается, это легко сделать:
git rebase --onto in-review/master/2 in-review/master/1
Аргумент --onto
отделяет часть target от upstream
, освобождая upstream
аргумент, означающий просто , обязывает не копировать .
Именно поэтому мы дали интересные коммиты, указывающие c имена. В ваш более сложный сценарий, вы начнете с:
... если для проверки было выдвинуто более одного коммита ...
В этом случае мы будем иметь:
...--G--H <-- origin/master
\
I--J--K <-- in-review/master/1
\
L <-- feature/xyz
Если для коммита J
необходимо внести изменения, вы проверяете коммит K
и присваиваете ему новое имя ветки in-review/master/2
:
git checkout -b in-review/master/2 in-review/master/1
, что дает вам это :
...--G--H <-- origin/master
\
I--J--K <-- in-review/master/1, in-review/master/2 (HEAD)
\
L <-- feature/xyz
Теперь вы можете запустить git rebase -i origin/master
и изменить второй коммит на edit
. Когда перебазирование закончено, вы можете - в зависимости от того, решили ли вы также редактировать I
и / или использовали --force
- иметь:
I'-J'-K' <-- in-review/master/2 (HEAD)
/
...--G--H <-- origin/master
\
I--J--K <-- in-review/master/1
\
L <-- feature/xyz
или:
...--G--H <-- origin/master
\
I--J'--K' <-- in-review/master/2
\
J--K <-- in-review/master/1
\
L <-- feature/xyz
Теперь вы можете git checkout feature/xyz; git rebase --onto in-review/master/2 in-review/master/1
, точно так же, как и раньше.
Есть случаи, когда эта техника падает. Git скорее нужен своего рода инструмент перебазирования по нескольким ветвям, и у него его нет (и создать его, который хорошо работает и не смешно сложен в использовании, сложно, поэтому никто не сделал этого). Подумайте:
...--G--H <-- origin/master
\
I <-- in-review/master/1
\
J <-- in-review/feature/tall/1
\
K <-- feature/short
\
L <-- feature/long
Возможно, вам придется что-то делать с любым из промежуточных коммитов. Поскольку любое изменение происхождения и снимка любого коммита приводит к его копированию, если вы вынуждены изменить коммит I
на новый I'
, вы должны придумать новый J'
и K'
и L'
(и представить новый отзыв для J'
, предположительно).
Обратите внимание, что после копирования I
в I'
одна git checkout feature/long; git rebase --onto in-review/master/2 in-review/master/1
копирует J-K-L
в J'-K'-L'
, но теперь есть три метки для перемещения. Это недостающий инструмент, который перемещает более одной метки. Но эта картинка слишком проста, как вы могли бы иметь:
...--G--H <-- origin/master
\
I <-- in-review/master/1
\
J <-- in-review/feature/tall/1
\
K--L <-- feature/short
\
M <-- feature/long
, и теперь перебазирование feature/long
само по себе не будет работать, поскольку оно не будет копировать L
; и не будет перебазировать feature/short
в одиночку, поскольку это скопирует L
, но не M
. Таким образом, инструмент multi-rebase должен знать:
- , какие ветви интересны, и
- , где перебазировать их как группу
, и он должен затем выясните, кто совершает копирование, создайте отображение из старого коммита ha sh в новый, пока группа как целое не будет полностью скопировано, и только затем переместите все имена ветвей в свой новый коммит ha sh Идентификаторы. Режим сохранения слияния (а-ля Git git rebase --rebase-merges
) также будет правильным режимом по умолчанию для этого инструмента, так как здесь у нескольких ветвей могут быть шаблоны ветвления и слияния внутри их подграфов (ветвление и слияние друг с другом, или независимо друг от друга, или с обоими).
Новый код rebase-merges является большей частью пути к этому необходимому инструменту, но в нем все еще отсутствует метод указания более чем одного имени ветви ( и, следовательно, по крайней мере, потенциально, несколько коммитов-подсказок) и код, который понадобится для настройки имен нескольких ветвей в конце всего процесса.