Eugene Burtsev's blog

Объединение истории репозиториев Mercurial

Жил-был один проект. Жил он тихо мирно в своем репозитории и постепенно обзаводился друзьями-проектами, жившими в своих репозиториях. И вот захотели они сьехать на одну квартиру, дабы экономить на квартплате и чаще видеться друг с другом, и позвали девелопера что бы он им помог.

Вот такая вот сказочка. Собственно задача такова: создать родительский проект maven содержащий историю всех подпроектов, находившихся ранее в отдельных репозиториях. В принципе это очень просто делается по этой инструкции. Но если следовать данной инструкции у репозитория получится несколько хвостов (как на рисунке ниже).

1
2
3
4
5
6
7
8
9
10
   @
   |
   o-----------o
   |           |
   .           .
   .           .
   |           |
   o           o
  tail        tail
   #1          #2

Мне бы хотелось видеть историю в виде примерно такого графа:

1
2
3
4
5
6
7
8
9
10
11
12
   @
   |
   o-----------o
   |           |
   . default   .
   .           . Feature
   |           | branch
   o           o
   |           |
   o-----------o
   |
   o

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

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

1
2
3
4
5
6
7
8
9
10
PROJECT=project1

cd ${PROJECT}
hg export -r0:tip -o ${PROJECT}.patch

# Modify patch (Move all files to subdirectory)

sed -i.bak -e "s/\(diff -r [0-9a-f]* -r [0-9a-f]*\) \(.*\)/\1 ${PROJECT}\/\2/g" ${PROJECT}.patch
sed -i.bak -e "s/--- a/\0\/${PROJECT}/g" ${PROJECT}.patch
sed -i.bak -e "s/+++ b/\0\/${PROJECT}/g" ${PROJECT}.patch

Для каждого из проектов делаем тоже самое (достаточно поменять только переменную PROJECT в начале скрипта)

Если вместо того что бы плодить безымянные ветки вы захотите разнести историю отдельных проетов по именованым веткам, то достаточно для всех проектов кроме первого выполнить следующие команды, для добавления информации об имени ветки в патч:

1
2
BRANCH=${PROJECT} branch
sed -i.bak -e "s/# Date \(.*\)/# Date \1\n# Branch $BRANCH/g" ${PROJECT}.patch

После того как мы создали патчи для каждого из проектов, сольем их в один. В первую очередь инициализируем новый репозиторий агрегирующий историю всех проектов.

1
2
hg init combined
hg import ../project1/project1.patch

Для всех остальных проектов делаем следующие махинации:

  1. Обновляем репозиторий до ревизии из которой будет начинаться проект
  2. Импортируем соответствующий патч (Если было указано имя бранча то не стоит забывать про ключ --import-branch)
  3. Мержим ветки
  4. Повторяем для оставшихся патчей

Этот подход проиллюстрирован ниже. Для способа без использования именованых веток действий нужно сделать немного меньше и в принципе можно легко автоматизировать процесс:

1
2
3
hg up <parent-revision>
hg import ../project1/project1.patch --import-branch
hg merge

Если же использовались именованые ветки то стоит делать так:

1
2
3
4
5
6
7
hg up <parent-revision>
hg import ../project1/project1.patch --import-branch
hg up <latest-patch-revision> # переходим на последнюю ревизию импортированного проекта
hg commit --close-branch
hg up default
hg merge <latest-patch-revision>
hg commit -m "Merge"

По мотивам данной статьи был создан скрипт:

modify-patch.sh Download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#!/bin/bash

PROJECT=$1

pushd ${PROJECT}

hg export -r0:tip -o ${PROJECT}.patch

# Modify patch (Move all files to subdirectory)

sed -i.bak -e "s/\(diff -r [0-9a-f]* -r [0-9a-f]*\) \(.*\)/\1 ${PROJECT}\/\2/g" ${PROJECT}.patch
sed -i.bak -e "s/--- a/\0\/${PROJECT}/g" ${PROJECT}.patch
sed -i.bak -e "s/+++ b/\0\/${PROJECT}/g" ${PROJECT}.patch

popd

Буду рад если кому нибудь поможет эта статья. Замечания приветствуются.

Comments