Это продолжение части 1 и части 2.
С момента написания предыдущей части прошло две недели, но за это время произошло немало событий. Зарелизились Mercurial 1.5 и TortoiseHg 1.0, а Джоэл Спольски написал туториал по mercurial. Что характерно, повествование своё, Джоэл разбавил логами виндовой консоли, что позволяет мне в тайне надеяться, что перед этим он читал мой блог :)
Впрочем, даже столь серьёзная конкуренция не отобьёт у меня желания продолжить свой убогий обзор.
Сегодня тема будет небольшая и, на первый взгляд, простая. Однако и здесь можно запутаться. Итак — головы (heads) в Mercurial.
Голова, это ревизия, не имеющая потомков. При линейной разработке (без ветвей), всё вообще кристально ясно — последняя ревизия является текущей рабочей ревизией и головой всей разработки. При наличии ветвей, всё немного усложняется.
Создадим для тестов новый рабочий каталог и репозиторий.
c:\>mkdir c:\hg\heads c:\>cd c:\hg\heads C:\hg\heads>hg init
Доведём дело до первой ревизии.
C:\hg\heads>echo "1" > 1.txt C:\hg\heads>hg add adding 1.txt C:\hg\heads>hg commit --message="1"
Теперь просмотрим список голов с помощью команды hg heads
:
C:\hg\heads>hg heads changeset: 0:6d02b12a67d2 tag: tip user: go <> date: Wed Mar 10 17:07:12 2010 +0300 summary: 1
Как видно, головой является наша первая и пока единственная ревизия.
Следующий шаг:
C:\hg\heads>echo "2" > 2.txt C:\hg\heads>hg add adding 2.txt C:\hg\heads>hg commit --message="2" C:\hg\heads>hg heads changeset: 1:4e0e603cfacf tag: tip user: go <> date: Wed Mar 10 17:16:12 2010 +0300 summary: 2
Право именоваться головой перешло к следующей ревизии. И при линейной разработке, этот титул так и будет переходить от предыдущей к следующей.
Анонимные ветки
Те ветки-бранчи, которые мы рассматривали в прошлый раз на самом деле являются лишь частным случаем ветвей (именованные ветви). Кроме них, возможны и анонимные ветви.
Анонимную ветвь создать проще простого (настолько просто, что можно создать случайно, когда она и не нужна). Откатимся на предыдущую ревизию и продолжим разработку параллельно уже сделанному.
C:\hg\heads>hg update 0 0 files updated, 0 files merged, 1 files removed, 0 files unresolved C:\hg\heads>echo "3" > 3.txt C:\hg\heads>hg add adding 3.txt C:\hg\heads>hg commit --message="3" created new head
Обратите внимание: «created new head
». Теперь у 0-й ревизии сразу две ревизии-потомка, а у них самих потомков нет — они обе головы:
C:\hg\heads>hg heads changeset: 2:e0858832eb0d tag: tip parent: 0:6d02b12a67d2 user: go <> date: Wed Mar 10 17:29:20 2010 +0300 summary: 3 changeset: 1:4e0e603cfacf user: go <> date: Wed Mar 10 17:16:12 2010 +0300 summary: 2
У нас образовалось две ветки разработки (при том, что branch по-прежнему один — default). В этих ветках можно продолжать параллельную разработку (голова каждой ветки будет переходить к следующим ревизиям), можно плодить ещё ветки, ну и самое приятное: ветки можно сливать вместе.
C:\hg\heads>hg merge 1 files updated, 0 files merged, 0 files removed, 0 files unresolved (branch merge, don't forget to commit) C:\hg\heads>hg commit --message="merge" C:\hg\heads>hg heads changeset: 3:e057a6b01c4a tag: tip parent: 1:4e0e603cfacf parent: 2:e0858832eb0d user: go <> date: Wed Mar 10 17:47:05 2010 +0300 summary: merge
Мы слили две ветки (объединили две головы в одну). Теперь обе предыдущие головы являются предками новой ревизии, которая и стала нашей текущей единственной головой.
Заметьте, что у ревизии может быть не более двух предков, то есть слить одновременно можно только две головы. Так как у нас только две головы и было, то нам хватило вызова hg merge
без параметров. Если же голов было бы больше, пришлось бы точно указывать какую из них следует слить с текущей.
Именованные ветки (branches)
Теперь посмотрим на branch’и новым взглядом.
Создадим для тестов новый репозиторий:
c:\>mkdir c:\hg\branches c:\>cd c:\hg\branches C:\hg\branches>hg init C:\hg\branches>
Сделаем пару комитов в default-ветвь:
C:\hg\branches>echo "1" > 1.txt C:\hg\branches>hg add adding 1.txt C:\hg\branches>hg commit -m "1" C:\hg\branches>echo "2" > 2.txt C:\hg\branches>hg add adding 2.txt C:\hg\branches>hg commit -m "2"
А теперь ответвим ветвь «one» и туда ещё парочку.
C:\hg\branches>hg branch one marked working directory as branch one C:\hg\branches>echo "3" > 3.txt C:\hg\branches>hg add adding 3.txt C:\hg\branches>hg commit -m "3" C:\hg\branches>echo "4" > 4.txt C:\hg\branches>hg add adding 4.txt C:\hg\branches>hg commit -m "4"
Проверим, что у нас с ветвями и головами:
C:\hg\branches>hg branches one 3:5d8341509529 default 1:7da81129f01e (inactive) C:\hg\branches>hg heads changeset: 3:5d8341509529 branch: one tag: tip user: go <> date: Wed Mar 10 18:11:42 2010 +0300 summary: 4
Вот незадача, ветви у нас теперь две, а голова всего одна.
Ревизия #1, которая является последней в ветви default
и, на первый взгляд, должна быть её головой, на самом деле ей быть не может. Просто потому, что она имеет потомка (ревизию #2, пусть она и из другой ветви). Таким образом, ветвь default
вообще осталась без головы и помечена в списке, как (inactive)
.
Так приделаем ей голову! Сначала вернёмся в default-ветку:
C:\hg\branches>hg update default 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
Обратите внимание на то, почему эти ветви называются «именованными». «Имя» default
здесь просто является синонимом последней ревизии из соответствующей ветки. То есть, мы могли ровно с тем же успехом написать hg update 1
.
C:\hg\branches>echo "5" > 5.txt C:\hg\branches>hg add adding 5.txt C:\hg\branches>hg commit -m "5" created new head
Мы создали голову в ветке default
(created new head).
Проверим, что вышло
C:\hg\branches>hg branches default 4:6ded3e912b46 one 3:5d8341509529 C:\hg\branches>hg heads changeset: 4:6ded3e912b46 tag: tip parent: 1:7da81129f01e user: go <> date: Wed Mar 10 18:29:25 2010 +0300 summary: 5 changeset: 3:5d8341509529 branch: one user: go <> date: Wed Mar 10 18:11:42 2010 +0300 summary: 4
Теперь имеется две головы, а default стала полноценной веткой.
Слив!
C:\hg\branches>hg merge 3 2 files updated, 0 files merged, 0 files removed, 0 files unresolved (branch merge, don't forget to commit) C:\hg\branches>hg commit -m "merge" C:\hg\branches>
Вместо hg merge one
я выпендрился и написал hg merge 3
с тем, чтобы опять указать на то, что one
, это всего лишь «именование» ревизии #3 (последней в именованной ветке one
).
Результаты:
C:\hg\branches>hg branches default 5:2657a30296ee one 3:5d8341509529 (inactive) C:\hg\branches>hg heads changeset: 5:2657a30296ee tag: tip parent: 4:6ded3e912b46 parent: 3:5d8341509529 user: go <> date: Wed Mar 10 18:39:03 2010 +0300 summary: merge
Головы слились в одну. Так как мы «вливали» ветку one
в ветку default
, голова эта принадлежит default
. Ветка one
, таким образом, осталась вообще без головы и стала inactive
.
Ветви с подветвями
Для чуть лучшего понимания (или окончательного запутывания), рассмотрим следующее извращение:
Мало того, что здесь у нас две именованные ветки, так ещё и в каждой из них, по две анонимные. То есть в итоге, у нас 4 головы (#3, #4, #5 и #6).
Итак, вопрос, на что указывают имена default
и test
? Ответ: они указывают на последние ревизии в своих ветках (соответственно, #5 и #6). Это видно в списке ветвей:
c:\hg\ch>hg branches test 6:3948f137a081 default 5:b195b529cb3f
В какую ревизию будут вливаться изменения при merge
? В ту, в которой находимся. Перейдём в #4 и смержимся с test
.
c:\hg\ch>hg update 4 0 files updated, 0 files merged, 0 files removed, 0 files unresolved c:\hg\ch>hg merge test 2 files updated, 0 files merged, 0 files removed, 0 files unresolved (branch merge, don't forget to commit) c:\hg\ch>hg commit -m "merge"
И что у нас получилось? А получилось вот что:
c:\hg\ch>hg branches default 7:753bb1ce4da0 test 6:3948f137a081 c:\hg\ch>hg heads changeset: 7:753bb1ce4da0 tag: tip parent: 4:75522fbdb615 parent: 6:3948f137a081 user: go <> date: Wed Mar 10 19:15:19 2010 +0300 summary: merge changeset: 5:b195b529cb3f parent: 1:ed719fdb06b3 user: go <> date: Wed Mar 10 18:53:23 2010 +0300 summary: 5 changeset: 3:f4d1c7f1f1a0 branch: test user: go <> date: Wed Mar 10 18:52:41 2010 +0300 summary: 3
Ушла только одна голова (#6) и одна голова сменила ревизию (#4 → #7). Все остальные остались, в том числе и одна из голов ветви test
, за счёт чего, та продолжает быть активной.
Можно сделать ещё много подобных бессмысленных вещей…
Так то, ребятки.
Спасибо за статью. очень полезно.
alexey_baranov, 11.03.2010, 9:58
Много букв, как-нибудь потом осилю
adw0rd, 11.03.2010, 14:48
Зато много чёрно-белых картинок для расскрашивания
vasa_c, 11.03.2010, 17:55
Только половина — это и не картинки вовсе =)
inst, 12.03.2010, 0:56
а есть что-то по автоматическому деплою через меркуриал? И когда на удаленном сервере нет ssh а есть только ftp
Абырвалг, 27.05.2010, 0:30
Круто. Прочитал все три части. Мне понравилось. Не узнав до конца SVN, познать философию веток в Mercurial — это действительно радость жизни!
kostyl, 4.06.2010, 0:04
Спасибо за отличную статью! Теперь надеюсь буду уверенно создавать ветки;)
loder, 22.06.2010, 16:05
Супер. Большое спасибо, а то после bzr слегка не въезжал и на heads, branches запутался.
После статьи решил все свои проблемы :) И даже нашел парочку там где думал что проблем нет. :)
roman, 9.07.2010, 8:15
Отличнейшая статья, в корне отличается от остальных. Во первых есть иллюстрации, во вторых все объяснено с примерами. Спасибо!
Rastler, 2.10.2010, 0:23
Статьи хорошие. Всё детально и по полочкам. Спасибо.
afgm, 24.11.2010, 21:20
Не еби мозга людям, читайте оригиналы:
http://stevelosh.com/blog/2009/08/a-guide-to-branching-in-mercurial/
pusher, 2.02.2011, 13:05
Наконец то на наше гнилое болото пришли знатоки.
Поаплодируем — pusher!
vasa_c, 2.02.2011, 13:35
Ура! Ура! Ура!
kostyl, 2.02.2011, 14:16
Очень понятно описанно в картинках.
Мне помогло разобратся с мержем.
vl, 17.02.2011, 20:49
> а есть что-то по автоматическому деплою через меркуриал? И когда на удаленном сервере нет ssh а есть только ftp
Смотри тут: http://lug.nsk.ru/lugnskru/2012/01/zagruzka-dannyih-po-ftp-s-ispolzovaniem-mercurial.html
Green, 3.01.2012, 11:03
Материал Джоэла Спольски не охватывает бранчи. С помощью ваших статей смог нормально разобраться, до этого прочитал много всего. Спасибо большое, хорошее дело делаете.
Игорь, 5.07.2012, 17:53
Игорь, спасибо на добром слове :)
vasa_c, 5.07.2012, 18:50
У меня, почему-то, после hg merge, головы у веток не пропадают, а также послушно выводятся командой hg heads. Это кажется довольно логичным, ведь коммит после слияния веток попадает в ветку первого родителя, а в ветке мы можем продолжить разработку, т.к. там осталась голова. Бранч, при этом, помечается как inactive.
stan, 20.06.2013, 8:21