Как частично объединить коммиты в порядке из разветвленной ветки? - PullRequest
1 голос
/ 24 марта 2012

Хорошо, вот моя ситуация.У меня есть следующие ветки

  • development
  • development-kirby (270 коммитов до разработки)

Мой босс хотел бы, чтобы я изменился с development-kirbyслились в разработке, но он не хочет, чтобы все это произошло одним огромным слиянием.Как можно ложкой кормить коммиты (скажем, 50 за раз, по порядку) несколькими небольшими порциями?Должен ли я просто выделить несколько пунктов в ветке development-kirby и объединить их таким образом или есть что-то лучшее?

Я уверен, что этот вопрос был задан лучше и ответил, но в поискеТАК не очень много.

Ответы [ 2 ]

2 голосов
/ 24 марта 2012

Чтобы ответить напрямую, вы можете выполнить ветвление после выбранного коммита (синтаксис: git branch ), объединить ствол в эту новую ветвь, вытянуть (подтолкнуть) результат объединения в ствол, отбросить временную ветвь и перейти к следующей части коммита.

OTOH Полагаю, ваш начальник неправ: такое медленное кормление не принесет пользы, если коммиты не реорганизованы в логические группы, которые можно применять отдельно, не нарушая логику всего приложения; но в этом случае коммиты не должны просто учитываться. Вы можете реорганизовать их (переупорядочить, реструктурировать), используя интерактивный перебаз [Обновление: это не тот случай, см. Ниже]

1 голос
/ 24 марта 2012

Да, вы можете делать именно то, что просили (но не делайте; см. Ниже).Например (это довольно хрупко, поскольку я предполагаю, что число 270 является правильным, и что первое слияние будет работать; оно также полностью не проверено ...):

b=development-kirby
m=development
git checkout $m
# 270 = 50 + 50 + 50 + 50 + 50 + 20
# hence, to be done in groups of 50, the commit-points are:
# $b~219, $b~169, $b~119, $b~69, $b~19, and $b (aka $b~0)
for dist in 219 169 119 69 19 0; do
    # want to merge $b~$dist into $m, and we're (still) on $m
    git merge --no-ff $b~$dist -m "mechanical merge at $b~$dist"
done

Это должно дать вам кое-чтонапример:

   ---- K1 --...-- K50 ---- K51..K100 --- ... --- K201...K250 --- K251..K270     development-kirby
 /                        \            \              \              \      \
* --- D1 --- D2 --- D3 --- M1 --------- M2 --- ... --- M4 ----------- M5 --- M6  development

, где K1..K270 - это ваши 270 коммитов, а D1 - D3 - три примера коммитов, которые были установлены на development с тех пор, как вы начали свою работу.

(--no-ff гарантирует, что все шесть действительных коммитов слияния, от M1 до M6, окажутся в репо, в противном случае вы получите быстрые форварды и у вас просто будет M1 в вышеупомянутом, с K51 до K270 на вершине, иветвь development теперь указывает на то же место, что и ветвь development-kirby:

   ---- K1 --...-- K50 ---     --- K51..K270  development-kirby, development
 /                        \   /
* --- D1 --- D2 --- D3 --- M1

[Может быть полезно отметить, что между ними во время каждого «промежуточного ускоренного перехода вперед» development будетуказывает на некоторый коммит в промежутке K51..K269, в то время как development-kirby будет продолжать указывать на коммит K270. Только после самого последнего ускоренного перехода два имени ветви будут указывать на один и тот же коммит.])

Проблема в том, что это странно инеумная вещь, чтобы сделать.Эти промежуточные коммиты слияния, если они выбраны чисто механически («каждые 50» или что-то еще), бесполезны.Они не дают вам абсолютно ничего, что вы не могли бы получить, просто выполнив одно большое слияние с 270 коммитами.Было бы намного разумнее выбирать точки в вашей частной ветке разработки, которые «имеют смысл» (например, «полностью реализованная функция X»).Допустим, вы пошли и выбрали несколько таких моментов, например, выполнив:

git checkout $b~215
... poke around to make sure it's a good merge point,
    and maybe move to $b~213 instead ...
git tag mp1 # merge point 1
git checkout $b~170
... poke around more, move to $b~172 because that's better ...
git tag mp2 # merge point 2
git checkout ...
# add more temporary tag labels as needed
# let's say you wind up with 4 total merge points
# which I've simply numbered mp1, mp2, mp3, mp4 
git checkout development # get on target merge branch
git merge --no-ff mp1
<put in a sensible commit message this time, and resolve any merge conflicts>
git merge --no-ff mp2
<put in a sensible commit message for merge point 2>
git merge --no-ff mp3
<etc>
git merge --no-ff mp4
<etc>
git tag -d mp1 mp2 mp3 mp4 # get rid of temporary tags

Вот главное, что нужно знать о «git merge», в данном конкретном случае, когда всегда есть фактический коммит слияниявход (т.е. не ускоренная перемотка вперед): то, что вы делаете, когда запускаете git merge, добавляет - к вашей текущей ветке - один новый коммит, чьи родители:

  • текущий HEADветки, в которой вы находитесь (в данном случае лучше development), и
  • идентификатора коммита, который вы называете в команде git merge (может быть любой commit-ID где угодно).

( содержимое вашего нового коммита, конечно, является результатом слияния "содержимого коммита HEAD" с "контентом именованного коммита", что подразумевает нахождение того, что изменилось между этими двумя коммитами и т. д., и иногда получение помощи в разрешении конфликтов слияния.) После того, как ваш новый коммит входит, текущая подсказка ветвления перенаправляется на новый коммит слияния.В этом случае development перемещается вперед к вновь добавленному слиянию.

Следовательно, все, что вам нужно сделать, это запускать git merge --no-ff (для принудительного выполнения реальных слияний, а не ускоренных перемоток вперед), каждый развремя указания одного подходящего идентификатора фиксации в вашей ветви development-kirby - в «топологическом порядке», то есть mp1 ~ выше, но самый лучший (для некоторого значения «лучший») способ - найти отдельные «хорошие» (git checkout)), затем пометьте их (git tag), чтобы вам не приходилось запоминать длинные строки цифр.


Сноска: вы можете использовать git rev-list --no-walk --topo-order, чтобы увидеть, находится ли какой-то список идентификаторов коммитов в правильном порядке, но это немного болезненно.Я не знаю лучшего способа, и быстрый поиск в Google ничего не дал, поэтому я написал короткий скрипт:
#! /bin/sh
#
# check a list of IDs to see if they're in "topo order"
usage()
{
    echo "usage: $0 id [...]"
}

case $# in
0) usage 1>&2; exit 1;;
esac

TF1=$(mktemp)
TF2=$(mktemp)
trap "rm -f $TF1 $TF2; exit" 0 1 2 3 15

# parse the arguments into one file
git rev-parse $@ > $TF1 || exit 1
# and topo-sort the arguments into another
git rev-list --topo-order --no-walk --reverse $@ > $TF2 || exit 1
# If the list is in the correct order the files will be the same
cmp -s $TF1 $TF2 || {
    # If the files differ, it's possible that some argument(s) name
    # the same rev...
    [ $(wc -l < $TF1) -eq $(wc -l < $TF2) ] || {
        echo "ERROR: there are repeats in $@"
        # finding them is a pain, we don't bother trying
        exit 1
    }
    echo "ERROR: $@ NOT in topo order"
    echo "use instead:"
    # read the topo-ordered raw IDs
    while read sha1; do
        # and find the (single) arg in $@ that names this one
        for i; do
            if [ $(git rev-parse $i) = $sha1 ]; then
                printf ' %s' $i
                break
            fi
        done
    done < $TF2
    echo
    exit 1
}
echo "$@ in topo order"
exit 0

После создания набора тегов точек слияния, которые вы запустите,Например:

check-topo-order mp1 mp2 mp3 mp4

, и вам будет указано, mp1 Кроме того, хотя вы и не просили об этом, вполне может быть разумным свернуть («раздавить») некоторые из этих 270 коммитов.Все зависит от того, как вашей компании нравится делать вещи, но гораздо более привычным является объединение цепочки из 270 коммитов в новую цепочку «добавить функцию F, добавить функцию G, добавить функцию H»это может занять от 5 до 10 коммитов.

Например, вы можете получить 3 коммита, которые «добавляют функцию F» - возможно, «очистить, чтобы подготовиться к F», затем «добавить инфраструктуру для F», а затем «включить функцию F» - затем только 1 для G и 2или 3 коммита для добавления функции H. Это даст вам ветвь длиной в 6 коммитов (например, назовите ее devel-kirby-squashed).На этом этапе у вас есть довольно чистая цепочка разработки, которая может быть встроена непосредственно в ветвь с именем development.

. Конечно, иногда является причиной сохранения всехэта промежуточная работа, в этом случае серия --no-ff слияния является разумной.

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