Source

kick-start_fork / src / tasks / index.txt

The default branch has multiple heads

Full commit
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
.. -*- rst -*-

.. Mercurial Kick Start: http://mercurial.aragost.com/kick-start/
..
.. Copyright 2010 aragost Trifork
..
.. This software may be used and distributed according to the terms of
.. the GNU General Public License version 2 or any later version.

.. include:: ../common.rst

======================
Task Based Development
======================

In the `previous section`_, we used bookmarks to track several
concurrent lines of development (feature or topic branches). Here, we
will explore a workflow based around using named branches instead.

.. _previous section: ../bookmarks/

Named branches differ from bookmarks in several respects:

* Immutable: Named branches are immutable: when a changeset has been
  committed on a branch ``foo``, then you cannot later "move" it to
  another branch. This means no renaming or deletion of branches ---
  do not use named branches as disposable branches.

* Auditable: Since named branches cannot be deleted, it is possible to
  inspect old history and see which branch each changeset was
  committed to.

Because of these differences, a project should document how named
branches are used in the project. Whereas bookmarks can be created and
deleted with no consequence, named branches persist and share a global
name space. This means that the branches become an integral part of
the overall workflow in the project and the team members need to agree
on their use.

In the examples below, the team will use the company bug tracker as
the source of long-term stable references. While this is a common
setup, some projects only use named branches for tracking long-term
release branches --- they might use clones or bookmarks for tracking
smaller bugfix and feature branches. That way they avoid the extra
"bureaucrazy" associated with the more heavy-weight named branches.

.. contents::

Overview
========

Imagine a small team where a set of junior developers, Alice and Bob,
will work on the tasks while a senior developer, Carla, will review
and integrate the changes made by Alice and Bob.

The senior developer will be the only one who has write access to the
main repository. She pulls changes from Alice and Bob and pushes then
to the main repository after reviewing them. Alice and Bob pulls
changes from the main repository:

.. image:: flow.png
   :align: center

Using this flow, Carla can control what goes into the main repository,
from which the final builds are made.

In a real setup, the repositories will most likely be located on
different machines, but we will place all four repositories besides
one another in our examples:

.. shelltest::

   $ ## hg init main
   $ ## hg -q clone main carla
   $ ## hg -q clone carla alice
   $ ## hg -q clone carla bob
   $ ## echo "alice = ../alice" >> carla/.hg/hgrc
   $ ## echo "bob = ../bob" >> carla/.hg/hgrc
   $ ls
   alice
   bob
   carla
   main

.. shelltest::
   :name: alice

   $ ## echo "[ui]" >> $HGRCPATH
   $ ## echo "username = Alice <alice@example.net>" >> $HGRCPATH
   $ ## echo "[extensions]" >> $HGRCPATH
   $ ## echo "graphlog =" >> $HGRCPATH
   $ ## cd ../default/alice

.. shelltest::
   :name: bob

   $ ## echo "[ui]" >> $HGRCPATH
   $ ## echo "username = Bob <bob@example.net>" >> $HGRCPATH
   $ ## echo "[extensions]" >> $HGRCPATH
   $ ## echo "graphlog =" >> $HGRCPATH
   $ ## cd ../default/bob

.. shelltest::
   :name: carla

   $ ## echo "[ui]" >> $HGRCPATH
   $ ## echo "username = Carla <carla@example.net>" >> $HGRCPATH
   $ ## echo "[extensions]" >> $HGRCPATH
   $ ## echo "graphlog =" >> $HGRCPATH
   $ ## cd ../default/carla
   $ ## echo "Goodbye, World!" > hello.txt
   $ ## hg add hello.txt
   $ ## hg commit -m "Initial import." -d "2010-03-01 10:20:30"
   $ ## hg -q push

This is only to make the examples easier.

Working with a Branch
=====================

Alice begins her work by pulling in changes from the main repository:

.. shelltest::
   :name: alice

   $ hg pull ../main
   pulling from ../main
   requesting all changes
   adding changesets
   adding manifests
   adding file changes
   added 1 changesets with 1 changes to 1 files
   (run 'hg update' to get a working copy)
   $ hg update
   1 files updated, 0 files merged, 0 files removed, 0 files unresolved

Alice now had an up-to-date clone of the main repository. The project
consist of a single file, ``hello.txt``:

.. shelltest::
   :name: alice

   $ cat hello.txt
   Goodbye, World!

There is clearly a bug here, and Carla has put it in the company bug
tracker as Issue 1. She tells Alice to fix it by changing the text to
``Hello, World!``, which seems more appropriate.

Creating a Branch
-----------------

Alice makes a new branch for her work:

.. shelltest::
   :name: alice

   $ hg branch issue-1
   marked working directory as branch issue-1
   (branches are permanent and global, did you want a bookmark?)
   $ hg commit -m "Starting branch for Issue 1." ## -d "2010-03-15 10:25:00"

The `hg branch` command does not change the history or working copy in
itself, it only ensure that the next commit is "stamped" with
``issue-1``.

The message issued is just a warning to remind you that you cannot
delete the branch name later (though we will see that you can hide a
branch if you don't need it any longer). As described in the
introduction, named branches are persistent, and it is therefore not
recommended to use them unless you really want to permanently track
where a changeset was made. In this project, the team uses the bug
tracker as their reference and names the branches according to the
issue they're working on --- that way it's easy to find all commits
related to a given bug later.

Normally, you cannot make an empty commit, but changing the branch
name counts as a change, so Alice was able to make the commit. The
repository has just two changesets:

.. shelltest::
   :name: alice

   $ ## snap-tortoisehg "$DSTDIR/alice-branched.png"

.. image:: alice-branched.png
   :align: center

Notice the branch names in the summary column. The names are only
shown for the changesets that make up *branch heads*. The changeset on
the ``default`` branch is drawn with a light blue cirle and the
changeset on the ``issue-1`` branch is drawn with a green circle.

She then begins hacking:

.. shelltest::
   :name: alice

   $ echo "Hey, World!" > hello.txt
   $ hg diff## | sed -e 's|\+\+\+ b/hello\.txt.*'\
                         '|+++ b/hello.txt Mon Mar 15 10:28:00 2010 +0000|'
   diff -r eb6db6528ec2 hello.txt
   --- a/hello.txt Mon Mar 15 10:25:00 2010 +0000
   +++ b/hello.txt Mon Mar 15 10:28:00 2010 +0000
   @@ -1,1 +1,1 @@
   -Goodbye, World!
   +Hey, World!
   $ hg commit -m "Fixed Issue 1." ## -d "2010-03-15 10:30:00"

The world now looks like this:

.. shelltest::
   :name: alice

   $ hg glog
   @  changeset:   2:2cdcbb0a5bfd
   |  branch:      issue-1
   |  tag:         tip
   |  user:        Alice <alice@example.net>
   |  date:        Mon Mar 15 10:30:00 2010 +0000
   |  summary:     Fixed Issue 1.
   |
   o  changeset:   1:eb6db6528ec2
   |  branch:      issue-1
   |  user:        Alice <alice@example.net>
   |  date:        Mon Mar 15 10:25:00 2010 +0000
   |  summary:     Starting branch for Issue 1.
   |
   o  changeset:   0:e9eb044d45e0
      user:        Carla <carla@example.net>
      date:        Mon Mar 01 10:20:30 2010 +0000
      summary:     Initial import.
   $ ## snap-tortoisehg "$DSTDIR/alice-fixed-issue-1.png"

The two tip-most changesets are on the ``issue-1`` branch, the root
changeset is on a branch called ``default``, despite no branch name
being printed. Changesets are always on a branch in Mercurial, but we
do not display it when they are on the ``default`` branch to reduce
clutter. Note that changesets on different branches share the same
changeset graph --- the branch names simply give you an easy way to
refer to the branch heads: instead of ``2cdcbb0a5bfd`` you write
``issue-1`` and instead of ``e9eb044d45e0`` you write ``default``. In
this sense, branches work very much like "floating tags" that always
point to the tip-most changeset on a particular branch. These tip-most
changesets are exactly the ones highlighted with a light green circle
in `thg log`:

.. image:: alice-fixed-issue-1.png
   :align: center

Alice comments in the bug tracker that she has fixed the bug and that
Carla should pull her changes.

Merging a Branch
----------------

Carla has been working too, while Alice fixed the bug, and has added a
README file:

.. shelltest::
   :name: carla

   $ ## echo "Classic Greetings!"   > README
   $ ## hg add README
   $ ## hg commit -m "Added README." -d "2010-03-02 14:30:00"
   $ hg glog
   @  changeset:   1:550bad1893cf
   |  tag:         tip
   |  user:        Carla <carla@example.net>
   |  date:        Tue Mar 02 14:30:00 2010 +0000
   |  summary:     Added README.
   |
   o  changeset:   0:e9eb044d45e0
      user:        Carla <carla@example.net>
      date:        Mon Mar 01 10:20:30 2010 +0000
      summary:     Initial import.

Carla pulls from Alice:

.. shelltest::
   :name: carla

   $ hg pull ../alice
   pulling from ../alice
   searching for changes
   adding changesets
   adding manifests
   adding file changes
   added 2 changesets with 1 changes to 1 files (+1 heads)
   (run 'hg heads' to see heads)

Mercurial tells her that she has a new head in the repository. This is
visible with the `hg heads` command:

.. shelltest::
   :name: carla

   $ hg heads
   changeset:   3:2cdcbb0a5bfd
   branch:      issue-1
   tag:         tip
   user:        Alice <alice@example.net>
   date:        Mon Mar 15 10:30:00 2010 +0000
   summary:     Fixed Issue 1.

   changeset:   1:550bad1893cf
   user:        Carla <carla@example.net>
   date:        Tue Mar 02 14:30:00 2010 +0000
   summary:     Added README.

There are now also two branches in the repository:

.. shelltest::
   :name: carla

   $ hg branches
   issue-1                        3:2cdcbb0a5bfd
   default                        1:550bad1893cf
   $ ## snap-tortoisehg "$DSTDIR/carla-pull-issue-1.png"

Using `thg log`, Carla can see how the new branch is attached to the
``default`` branch:

.. image:: carla-pull-issue-1.png
   :align: center

The two heads are clearly visible and Carla updates to the ``issue-1``
branch:

.. shelltest::
   :name: carla

   $ hg update issue-1
   1 files updated, 0 files merged, 1 files removed, 0 files unresolved

Her working directory now looks like Alice's working directory and
Carla can examine the file. She is not entirely happy with the
"modern" greeting chosen by Alice --- Carla wants the good old classic
"Hello" instead. They discuss this on the bug tracker and Alice agrees
to make the change. Alice first checks that she is still on the
``issue-1`` branch and she then fixes the bug:

.. shelltest::
   :name: alice

   $ hg branch
   issue-1
   $ echo "Hello, World!" > hello.txt
   $ hg commit -m "Really fix Issue 1." ## -d "2010-03-15 13:20:00"

Carla pulls the new change:

.. shelltest::
   :name: carla

   $ hg pull ../alice
   pulling from ../alice
   searching for changes
   adding changesets
   adding manifests
   adding file changes
   added 1 changesets with 1 changes to 1 files
   (run 'hg update' to get a working copy)

She can use `hg update` to go to the tip of her current branch
(``issue-1``) or she can be explicit and say `hg update issue-1`
again:

.. shelltest::
   :name: carla

   $ hg update
   1 files updated, 0 files merged, 0 files removed, 0 files unresolved

She agrees that the bug is finally fixed.

Closing a Branch
----------------

The branch is no longer needed, so she marks it as closed:

.. shelltest::
   :name: carla

   $ hg commit --close-branch -m "Close branch, Issue 1 is fixed." \
     ## -d "2010-03-15 13:24:00"

This makes the ``issue-1`` branch disappear from the `hg branches`
list:

.. shelltest::
   :name: carla

   $ hg branches
   default                        1:550bad1893cf

If she had not closed it, the `hg branches` list could easily become
unwieldy. Next she updates to the ``default`` branch and merges with
``issue-1``:

.. shelltest::
   :name: carla

   $ hg update default
   2 files updated, 0 files merged, 0 files removed, 0 files unresolved
   $ hg merge issue-1
   1 files updated, 0 files merged, 0 files removed, 0 files unresolved
   (branch merge, don't forget to commit)
   $ hg commit -m "Merged fix for Issue 1." ## -d "2010-03-15 13:25:00"

By updating to ``default`` first, she ensures that the merge changeset
is created on ``default`` and not on ``issue-1``, where it does not
belong. Note how merging a branch is no different from merging any
other two changesets --- Mercurial has a very uniform history model.


Pushing a Branch
----------------

She will now push the whole thing to the main repository. She must use
a `--new-branch` flag since she is pushing new named branches. This is to
prevent accidentally pushing a named branch prematurely. Without the
flag she gets a friendly reminder:

.. shelltest::
   :name: carla

   $ hg push ../main
   pushing to ../main
   searching for changes
   abort: push creates new remote branches: issue-1!
   (use 'hg push --new-branch' to create new remote branches)

.. note::

   The `--new-branch` flag was introduced in Mercurial 1.6. Older
   versions of Mercurial required you to use `--force` instead. The
   danger of using `--force` is that it disables all checks, whereas
   `--new-branch` wont let you do things like pushing multiple heads.

Sure enough, adding the flag makes the push go through:

.. shelltest::
   :name: carla

   $ hg push --new-branch ../main
   pushing to ../main
   searching for changes
   adding changesets
   adding manifests
   adding file changes
   added 6 changesets with 3 changes to 2 files
   $ ## snap-tortoisehg "$DSTDIR/carla-merge-issue-1.png"

The main repository is now in this state:

.. image:: carla-merge-issue-1.png
   :align: center


Longer Term Branches
====================

While Alice was working on fixing Issue 1, Carla asked Bob to look
into translating the ``hello.txt`` file into more languages. They
agree to do Danish, German, and French and create Issue 2 for this.
Like Alice, Bob will work on his own branch for this and he begins by
pulling changes from the main repository:

.. shelltest::
   :name: bob

   $ hg pull ../main ## -r 1
   pulling from ../main
   adding changesets
   adding manifests
   adding file changes
   added 2 changesets with 2 changes to 2 files
   (run 'hg update' to get a working copy)
   $ hg update
   2 files updated, 0 files merged, 0 files removed, 0 files unresolved
   $ hg glog
   @  changeset:   1:550bad1893cf
   |  tag:         tip
   |  user:        Carla <carla@example.net>
   |  date:        Tue Mar 02 14:30:00 2010 +0000
   |  summary:     Added README.
   |
   o  changeset:   0:e9eb044d45e0
      user:        Carla <carla@example.net>
      date:        Mon Mar 01 10:20:30 2010 +0000
      summary:     Initial import.

At this point, Alice's branch had not yet been merged into the main
repository, so Bob only sees the two first changesets made by Carla.
He creates a branch and renames the ``hello.txt`` file to reflect its
language:

.. shelltest::
   :name: bob

   $ hg branch issue-2
   marked working directory as branch issue-2
   (branches are permanent and global, did you want a bookmark?)
   $ hg commit -m "Began Issue 2 branch." ## -d "2010-03-09 11:30:00"
   $ hg rename hello.txt hello.en.txt
   $ hg commit -m "English translation." ## -d "2010-03-09 11:32:00"

He quickly creates a Danish and a German version and commits those too:

.. shelltest::
   :name: bob

   $ echo "Hej, verden!" > hello.da.txt
   $ echo "Hallo, Welt!" > hello.de.txt
   $ hg add
   adding hello.da.txt
   adding hello.de.txt
   $ hg commit -m "Danish and German translations." \
     ## -d "2010-03-09 11:45:00"
   $ ## snap-tortoisehg "$DSTDIR/bob-first-commits.png"

He needs some more time to research the French translation, so he
postpones that change. His changeset graph is shown below and you'll
notice that his ``issue-2`` branch has shown up in red:

.. image:: bob-first-commits.png
   :align: center



Merging ``default`` into a Branch
---------------------------------

Meanwhile, Carla has merged Alice's bugfix into ``default``:

.. image:: carla-merge-issue-1.png
   :align: center

This bugfix is also appropriate for Bob's branch, so he pulls from the
main repository:

.. shelltest::
   :name: bob

   $ hg pull ../main
   pulling from ../main
   searching for changes
   adding changesets
   adding manifests
   adding file changes
   added 5 changesets with 2 changes to 1 files (+1 heads)
   (run 'hg heads' to see heads)
   $ ## snap-tortoisehg "$DSTDIR/bob-pull-main.png"

.. image:: bob-pull-main.png
   :align: center

He now merges ``default`` into ``issue-2`` in order to bring the
bugfix to his branch:

.. shelltest::
   :name: bob

   $ hg merge default
   merging hello.en.txt and hello.txt to hello.en.txt
   0 files updated, 1 files merged, 0 files removed, 0 files unresolved
   (branch merge, don't forget to commit)
   $ hg commit -m "Merge in bugfix from default." \
     ## -d "2010-03-09 12:35:00"
   $ ## snap-tortoisehg "$DSTDIR/bob-merge-bugfix.png"

Notice how smoothly Alice's change to ``hello.txt`` was merged into
the renamed ``hello.en.txt``. Just like a good movie soundtrack, a
good merge implementation is characterized by the fact that you do not
notice it --- it should just work and do the right thing. We will
compare this to how Subversion handles this `below`__. The resulting
changeset graph looks like this:

.. image:: bob-merge-bugfix.png
   :align: center

Using the colored branches, you can quickly see that the green
``issue-1`` branch was merged into the blue ``default`` branch, and
that the whole thing was merged into the pink ``issue-2`` branch. The
colors are huge help when you look at complex histories.

Bob has also finally figured out how to translate the file into
French, so he makes a final commit before asking Carla to review the
branch.

.. shelltest::
   :name: bob

   $ echo "Bonjour tout le monde !" > hello.fr.txt
   $ hg add hello.fr.txt
   $ hg commit -m "French translation." \
     ## -d "2010-03-09 12:45:00"

.. __: `Merging Renamed Files in Subversion`_

Happy with his work, he submits it to Carla, who pulls it into her
repository:

.. shelltest::
   :name: carla

   $ hg pull ../bob
   pulling from ../bob
   searching for changes
   adding changesets
   adding manifests
   adding file changes
   added 5 changesets with 5 changes to 4 files
   (run 'hg update' to get a working copy)
   $ ## snap-tortoisehg "$DSTDIR/carla-pull-issue-2.png"

Her repository now looks like this:

.. image:: carla-pull-issue-2.png
   :align: center

Carla double-checks the files and is decides to merge his work now.
She updates to his branch to close it, and back to ``default`` to
merge:

.. shelltest::
   :name: carla

   $ hg update issue-2
   4 files updated, 0 files merged, 1 files removed, 0 files unresolved
   $ hg commit --close-branch -m "Close branch, Issue 2 is fixed." \
     ## -d "2010-03-16 10:10:00"
   $ hg update default
   1 files updated, 0 files merged, 4 files removed, 0 files unresolved
   $ hg merge issue-2
   4 files updated, 0 files merged, 1 files removed, 0 files unresolved
   (branch merge, don't forget to commit)
   $ hg commit -m "Merged fix for Issue 2." ## -d "2010-03-16 10:15:00"
   $ ## snap-tortoisehg "$DSTDIR/carla-merge-issue-2.png"

The final changeset graph shows nicely how the ``default`` branch (in
black) was merged into the ``issue-2`` branch (in red), before
``issue-2`` was merged into ``default`` again:

.. image:: carla-merge-issue-2.png
   :align: center

This is a very typical pattern for longer lived branches: the
``default`` branch is periodically merged into the more experimental
branch in order to propagate the latest bugfixes and changes. Keeping
the branches in sync help make the merges easier. On each merge,
Mercurial will find the nearest common ancestor and apply the changes
since that point. With periodic merges, this ancestor point will not
lie too far in the past, and the branches wont have drifted too far
away from each other.


Merging Renamed Files in Subversion
-----------------------------------

When merging two branches, it is crucial to be able to find the common
ancestor between two branches. It is the changes made *since* this
ancestor that needs to be merged into the target branch. Mercurial
tracks this ancestor information automatically --- it is an integral
part of our history model and you have seen again and again in `thg
log`.

The history in Subversion is not based on a graph like in Mercurial
and for a very long time, Subversion had no support for tracking
merges. This meant that you had to manually dig out the revision
ranges you wanted to merge when invoking ``svn merge``. Subversion 1.5
and later does track merges, but the support is incomplete. The
implementation of merge tracking is described as `extremely complex`__
in Version Control with Subversion (the "SVN Book"). In particular,
Subversion handles `merges involving renamed files`__ poorly.

.. __: http://svnbook.red-bean.com/en/1.5/svn-book.html
              #svn.branchmerge.advanced.finalword

.. __: http://svnbook.red-bean.com/en/1.5/svn-book.html
              #svn.branchmerge.advanced.moves

Above, we merged the ``issue-2`` branch into ``default``. On
``default``, the ``hello.txt`` file had been changed and on
``issue-2``, the ``hello.txt`` file had been renamed to
``hello.en.txt``. Mercurial did the right thing and applied the change
to ``hello.en.txt`` while merging. Subversion, on the other hand,
bails out with a message about a conflict:

.. shelltest::

   $ ## svnadmin create $HOME/hello
   $ ## ln -s /bin/true $HOME/hello/hooks/pre-revprop-change
   $ ## svn -q checkout file://$HOME/hello test
   $ ## cd test
   $ ## mkdir trunk branches
   $ ## echo "Goodbye, World!" > trunk/hello.txt
   $ ## svn -q add trunk branches
   $ ## svn -q commit -m "Initial import."
   $ ## svn -q copy file://$HOME/hello/trunk \
                    file://$HOME/hello/branches/issue-2 -m "Branch."
   $ ## cd ..
   $ ## svn -q checkout file://$HOME/hello/trunk trunk
   $ ## cd trunk
   $ ## echo "Hello, World!" > hello.txt
   $ ## svn -q commit -m "Update on trunk."
   $ ## cd ..
   $ ## svn -q checkout file://$HOME/hello/branches/issue-2 issue-2
   $ ## cd issue-2
   $ ## svn -q rename hello.txt hello.en.txt
   $ ## svn -q commit -m "Rename on branch."
   $ ## cd ../trunk
   $ ## svn -q update
   $ svn merge --reintegrate file://$HOME/hello/branches/issue-2
   --- Merging differences between repository URLs into '.':
   A    hello.en.txt
      C hello.txt
   Summary of conflicts:
     Tree conflicts: 1

Note also that one must use a special flag in Subversion when a branch
is merged back into the trunk. No such flag is needed in Mercurial
where there is nothing inherently special about the ``default`` branch
(except that it is, well…, the default branch).

Grafting Changes
----------------

When named branches are in use for a longer time, it happens that
changesets are needed on more than one branch. Above, Bob could simply
pull all the changes from the main repository, but this is often not
desirable because the change you want is mixed with other changes.

The typical scenario is a bugfix that is committed to ``default``,
when it should really have been committed to a branch for the last
stable release. Consider a repository that has this graph:

.. shelltest::
   :name: carla

   $ ## hg init  ../graft
   $ ## cd ../graft
   $ ## echo > a.txt
   $ ## hg add a.txt
   $ ## hg -q commit -m "First version." -d "2010-03-18 10:15:00"
   $ ## hg -q branch stable
   $ ## hg -q commit -m "Started stable branch." -d "2010-03-18 10:20:00"
   $ ## hg -q tag 1.0 -d "2010-03-18 10:25:00"
   $ ## hg -q update default
   $ ## echo >> a.txt
   $ ## hg -q commit -m "New feature." -d "2010-03-18 10:30:00"
   $ ## echo >> a.txt
   $ ## hg -q commit -m "Documentation." -d "2010-03-18 10:35:00"
   $ ## hg -q update stable
   $ ## echo > b.txt
   $ ## hg add b.txt
   $ ## hg -q commit -m "Important bugfix." -d "2010-03-18 10:40:00"
   $ ## hg -q update default
   $ ## hg -q merge stable
   $ ## hg -q commit -m "Merge with stable." -d "2010-03-18 10:45:00"
   $ ## echo >> a.txt
   $ ## hg -q commit -m "Fix new feature." -d "2010-03-18 10:50:00"
   $ ## hg -q update stable
   $ ## hg -q merge default
   $ ## hg -q commit -m "Merge default into stable." -d "2010-03-18 10:55:00"
   $ ## hg -q tag 2.0 -d "2010-03-18 11:00:00"
   $ ## hg -q update default
   $ ## hg -q merge stable
   $ ## hg -q commit -m "Merge with stable." -d "2010-03-18 11:05:00"
   $ ## hg -q commit -m "Cool feature." -d "2010-03-18 11:10:00"
   $ ## echo >> a.txt
   $ ## hg -q commit -m "Experimental feature." -d "2010-03-18 11:15:00"
   $ ## echo > c.txt
   $ ## hg add c.txt
   $ ## hg -q commit -m "Fixed old bug." -d "2010-03-18 11:20:00"
   $ ## hg -q update stable
   $ ## snap-tortoisehg "$DSTDIR/carla-graft-before.png"

.. image:: carla-graft-before.png
   :align: center

Here two branches are used: ``default`` (in blue) for the ongoing
development, and ``stable`` (in green) for bugfixes and releases. You
can see how ``stable`` is merged into ``default`` after a bugfix in
order to propagate bugfixes into the current development. When a
release is made, the merge is made in the other direction --- from
``default`` into ``stable`` --- and a tag is added. The result is two
parallel tracks of development, where the green ``stable`` branch
always contain a subset of the changesets on the black ``default``
branch.

The final commit fixes an old bug and a little later, Carla realizes
that this bug is also present in the 2.0 release they made earlier.
The changeset should therefore really have been made on the ``stable``
branch. It is too late to simply merge ``default`` into ``stable``,
since that would also merge the experimental change.

The solution is to use `hg graft` to copy the changeset. While being
on the ``stable`` branch, she does:

.. shelltest::
   :name: carla

   $ hg graft tip
   grafting revision 12
   $ ## snap-tortoisehg "$DSTDIR/carla-graft-after.png"

.. note::

   The graft command was introduced in Mercurial 2.0. In earlier
   versions you can use the `transplant extension`__ to copy
   changesets. It is a standard extension and you enable it by
   putting::

     [extensions]
     transplant =

   in your configuration file. The transplan extension works by
   exporting the changeset as a patch and then applying this patch
   onto the target. If this fails, then you'll have to resolve the
   conflict yourself based on a reject (``.rej``) file.

This copies the changeset to her branch:

.. image:: carla-graft-after.png
   :align: center

Since the duplicate changeset has a different past than the original,
it will get a new changeset hash. The copying is done by running
three-way merge internally and so Carla will be able to resolve
conflicts using her favorite three-way merge program. Most small
bugfixes can be grafted cleanly, however. She can now merge the
``stable`` branch back into ``default``:

.. shelltest::
   :name: carla

   $ hg update default
   1 files updated, 0 files merged, 0 files removed, 0 files unresolved
   $ hg merge stable
   0 files updated, 0 files merged, 0 files removed, 0 files unresolved
   (branch merge, don't forget to commit)
   $ hg commit -m "Merge with stable." ## -d "2010-03-18 11:25:00"
   $ ## snap-tortoisehg "$DSTDIR/carla-graft-merged.png"

Note that changesets are not really moved with this technique. In a
distributed environment, it is close to impossible to destroy history,
so Mercurial will by default only let you append to your history. This
means that fixing mistakes involve making a new changeset that undoes
the mistake. In our case, we fixed the mistake by duplicating the
changeset. When merging, these duplicates are automatically
reconciled:

.. image:: carla-graft-merged.png
   :align: center

.. __: http://mercurial.selenic.com/wiki/TransplantExtension

Exercises
=========

We will now split you into groups of five. Each group picks one who
should play the role of Carla, i.e., who should pull from the others
and decide what to merge. We will call this person C.

Now follow these steps:

1. C starts by cloning an example repository from::

     https://bitbucket.org/aragost/cantons/

   When it has been cloned, C starts the built-in webserver in
   Mercurial with `hg serve`.

2. The other group members make a clone from C. You'll need to
   exchange IP addresses at this point. You can also enable the
   `zeroconf extension`__, wait a second, and then do `hg paths`.
   Hopefully you will be able to see each other's repositories in the
   list.

   .. __: http://mercurial.selenic.com/wiki/ZeroconfExtension

   On Windows, you should beware that since `hg serve` makes the
   repository available on port 8000 by default, Mercurial will try to
   name the clone something like ``172.16.0.1:8000``, which is an
   illegal filename on Windows. So you should specify a second
   argument to `hg clone` in order to give the clone a suitable name.

3. In the clone, you will find a file named ``cantons.txt``. It has a
   list of Swiss canton abbreviations. The abbreviations need to be
   expanded, e.g., the line saying::

     * FR

   should be changed to say::

     * FR: Fribourg

   C will appoint a range of letters to each of the other group
   members.

4. Make a branch for your range, e.g., with `hg branch range-a-b`.
   Then expand each abbreviation. Make a commit after expanding
   the abbreviations for a single letter.

5. While people are busy expanding abbreviations, C will add another
   section to the file. It should look like this::

     By Language
     ===========

     The official languages of Switzerland are French, German, Italian, and
     Romansh:

     French
     ------

     French is spoken in: VD, ...


     German
     ------

     German is spoken in: FR, ...


     Italian
     -------

     Italian is spoken in: GR, ...


     Romansh
     -------

     Romansh is spoken in: GR.

   Fill in the missing cantons for French, German, and Italian and
   commit the change. Wait for the others to finish expanding their
   abbreviations.

6. When someone is finished expanding his abbreviations, he should use
   `hg serve` and let C know where to find his work, just like we did
   it above.

7. C will now pull from other group members and merge their changes
   into ``default``. If everybody has paid attention and edited their
   section only, then there should be no conflicts.

8. Finally, note that Wikipedia is `there to help you`__ if you get
   stuck ``:-)``

   .. __: http://en.wikipedia.org/wiki/Cantons_of_Switzerland

Next we will experiment with resolving conflicts:

1. Have two group members edit the same line --- one can add a line
   with the French canton name, the other with the Italian name.

2. The conflict will be discovered when C pulls the changes and runs
   `hg merge`. Depending on the exact configuration of Mercurial, a
   merge tool will be run. Under Windows and under Linux, KDiff3 is
   the default tool. It looks like this when started:

   .. image:: cantons-merge-before.png

   The bottom pane holds the result of the merge, and there is
   currently three merge conflicts. You can right-click on a ``<Merge
   Conflict>`` line and choose to resolve it by including lines from
   file B (your local version) or C (the other version).
   In our case we want to include both changes:

   .. image:: cantons-merge-after.png

   Save the output and exit KDiff3. Mercurial will notice that the
   merge tool exited successfully and mark the files as resolved.
   Check with `hg resolve --list`.

   If Mercurial is not configured to start a merge tool, then you will
   have to edit the file yourself to remove the conflict markers
   inserted by Mercurial. Use `hg resolve --mark cantons.txt` when
   done to mark the file as resolved.

3. Commit the result of the merge as normal.


Summary
=======

We have shown you how named branches can be used to add structure to a
Mercurial repository. They make it easy to work with several distinct
branches of development at the same time, without mixing them up.


..  LocalWords:  hg bugfix thg svn bugfixes zeroconf