##// END OF EJS Templates
hg: support for auto sharing stores when cloning...
Gregory Szorc -
r25761:0d37b9b2 default
parent child Browse files
Show More
@@ -3,10 +3,42 b''
3 # This software may be used and distributed according to the terms of the
3 # This software may be used and distributed according to the terms of the
4 # GNU General Public License version 2 or any later version.
4 # GNU General Public License version 2 or any later version.
5
5
6 '''share a common history between several working directories'''
6 '''share a common history between several working directories
7
8 Automatic Pooled Storage for Clones
9 -----------------------------------
10
11 When this extension is active, :hg:`clone` can be configured to
12 automatically share/pool storage across multiple clones. This
13 mode effectively converts :hg:`clone` to :hg:`clone` + :hg:`share`.
14 The benefit of using this mode is the automatic management of
15 store paths and intelligent pooling of related repositories.
16
17 The following ``share.`` config options influence this feature:
18
19 ``pool``
20 Filesystem path where shared repository data will be stored. When
21 defined, :hg:`clone` will automatically use shared repository
22 storage instead of creating a store inside each clone.
23
24 ``poolnaming``
25 How directory names in ``share.pool`` are constructed.
26
27 "identity" means the name is derived from the first changeset in the
28 repository. In this mode, different remotes share storage if their
29 root/initial changeset is identical. In this mode, the local shared
30 repository is an aggregate of all encountered remote repositories.
31
32 "remote" means the name is derived from the source repository's
33 path or URL. In this mode, storage is only shared if the path or URL
34 requested in the :hg:`clone` command matches exactly to a repository
35 that was cloned before.
36
37 The default naming mode is "identity."
38 '''
7
39
8 from mercurial.i18n import _
40 from mercurial.i18n import _
9 from mercurial import cmdutil, hg, util, extensions, bookmarks
41 from mercurial import cmdutil, commands, hg, util, extensions, bookmarks
10 from mercurial.hg import repository, parseurl
42 from mercurial.hg import repository, parseurl
11 import errno
43 import errno
12
44
@@ -75,10 +107,24 b' def unshare(ui, repo):'
75 # update store, spath, svfs and sjoin of repo
107 # update store, spath, svfs and sjoin of repo
76 repo.unfiltered().__init__(repo.baseui, repo.root)
108 repo.unfiltered().__init__(repo.baseui, repo.root)
77
109
110 # Wrap clone command to pass auto share options.
111 def clone(orig, ui, source, *args, **opts):
112 pool = ui.config('share', 'pool', None)
113 if pool:
114 pool = util.expandpath(pool)
115
116 opts['shareopts'] = dict(
117 pool=pool,
118 mode=ui.config('share', 'poolnaming', 'identity'),
119 )
120
121 return orig(ui, source, *args, **opts)
122
78 def extsetup(ui):
123 def extsetup(ui):
79 extensions.wrapfunction(bookmarks.bmstore, 'getbkfile', getbkfile)
124 extensions.wrapfunction(bookmarks.bmstore, 'getbkfile', getbkfile)
80 extensions.wrapfunction(bookmarks.bmstore, 'recordchange', recordchange)
125 extensions.wrapfunction(bookmarks.bmstore, 'recordchange', recordchange)
81 extensions.wrapfunction(bookmarks.bmstore, 'write', write)
126 extensions.wrapfunction(bookmarks.bmstore, 'write', write)
127 extensions.wrapcommand(commands.table, 'clone', clone)
82
128
83 def _hassharedbookmarks(repo):
129 def _hassharedbookmarks(repo):
84 """Returns whether this repo has shared bookmarks"""
130 """Returns whether this repo has shared bookmarks"""
@@ -1421,7 +1421,8 b' def clone(ui, source, dest=None, **opts)'
1421 stream=opts.get('uncompressed'),
1421 stream=opts.get('uncompressed'),
1422 rev=opts.get('rev'),
1422 rev=opts.get('rev'),
1423 update=opts.get('updaterev') or not opts.get('noupdate'),
1423 update=opts.get('updaterev') or not opts.get('noupdate'),
1424 branch=opts.get('branch'))
1424 branch=opts.get('branch'),
1425 shareopts=opts.get('shareopts'))
1425
1426
1426 return r is None
1427 return r is None
1427
1428
@@ -284,8 +284,50 b' def copystore(ui, srcrepo, destpath):'
284 release(destlock)
284 release(destlock)
285 raise
285 raise
286
286
287 def clonewithshare(ui, peeropts, sharepath, source, srcpeer, dest, pull=False,
288 rev=None, update=True, stream=False):
289 """Perform a clone using a shared repo.
290
291 The store for the repository will be located at <sharepath>/.hg. The
292 specified revisions will be cloned or pulled from "source". A shared repo
293 will be created at "dest" and a working copy will be created if "update" is
294 True.
295 """
296 revs = None
297 if rev:
298 if not srcpeer.capable('lookup'):
299 raise util.Abort(_("src repository does not support "
300 "revision lookup and so doesn't "
301 "support clone by revision"))
302 revs = [srcpeer.lookup(r) for r in rev]
303
304 basename = os.path.basename(sharepath)
305
306 if os.path.exists(sharepath):
307 ui.status(_('(sharing from existing pooled repository %s)\n') %
308 basename)
309 else:
310 ui.status(_('(sharing from new pooled repository %s)\n') % basename)
311 # Always use pull mode because hardlinks in share mode don't work well.
312 # Never update because working copies aren't necessary in share mode.
313 clone(ui, peeropts, source, dest=sharepath, pull=True,
314 rev=rev, update=False, stream=stream)
315
316 sharerepo = repository(ui, path=sharepath)
317 share(ui, sharerepo, dest=dest, update=update, bookmarks=False)
318
319 # We need to perform a pull against the dest repo to fetch bookmarks
320 # and other non-store data that isn't shared by default. In the case of
321 # non-existing shared repo, this means we pull from the remote twice. This
322 # is a bit weird. But at the time it was implemented, there wasn't an easy
323 # way to pull just non-changegroup data.
324 destrepo = repository(ui, path=dest)
325 exchange.pull(destrepo, srcpeer, heads=revs)
326
327 return srcpeer, peer(ui, peeropts, dest)
328
287 def clone(ui, peeropts, source, dest=None, pull=False, rev=None,
329 def clone(ui, peeropts, source, dest=None, pull=False, rev=None,
288 update=True, stream=False, branch=None):
330 update=True, stream=False, branch=None, shareopts=None):
289 """Make a copy of an existing repository.
331 """Make a copy of an existing repository.
290
332
291 Create a copy of an existing repository in a new directory. The
333 Create a copy of an existing repository in a new directory. The
@@ -320,6 +362,13 b' def clone(ui, peeropts, source, dest=Non'
320 anything else is treated as a revision)
362 anything else is treated as a revision)
321
363
322 branch: branches to clone
364 branch: branches to clone
365
366 shareopts: dict of options to control auto sharing behavior. The "pool" key
367 activates auto sharing mode and defines the directory for stores. The
368 "mode" key determines how to construct the directory name of the shared
369 repository. "identity" means the name is derived from the node of the first
370 changeset in the repository. "remote" means the name is derived from the
371 remote's path/URL. Defaults to "identity."
323 """
372 """
324
373
325 if isinstance(source, str):
374 if isinstance(source, str):
@@ -352,6 +401,36 b' def clone(ui, peeropts, source, dest=Non'
352 elif destvfs.listdir():
401 elif destvfs.listdir():
353 raise util.Abort(_("destination '%s' is not empty") % dest)
402 raise util.Abort(_("destination '%s' is not empty") % dest)
354
403
404 shareopts = shareopts or {}
405 sharepool = shareopts.get('pool')
406 sharenamemode = shareopts.get('mode')
407 if sharepool:
408 sharepath = None
409 if sharenamemode == 'identity':
410 # Resolve the name from the initial changeset in the remote
411 # repository. This returns nullid when the remote is empty. It
412 # raises RepoLookupError if revision 0 is filtered or otherwise
413 # not available. If we fail to resolve, sharing is not enabled.
414 try:
415 rootnode = srcpeer.lookup('0')
416 if rootnode != node.nullid:
417 sharepath = os.path.join(sharepool, node.hex(rootnode))
418 else:
419 ui.status(_('(not using pooled storage: '
420 'remote appears to be empty)\n'))
421 except error.RepoLookupError:
422 ui.status(_('(not using pooled storage: '
423 'unable to resolve identity of remote)\n'))
424 elif sharenamemode == 'remote':
425 sharepath = os.path.join(sharepool, util.sha1(source).hexdigest())
426 else:
427 raise util.Abort('unknown share naming mode: %s' % sharenamemode)
428
429 if sharepath:
430 return clonewithshare(ui, peeropts, sharepath, source, srcpeer,
431 dest, pull=pull, rev=rev, update=update,
432 stream=stream)
433
355 srclock = destlock = cleandir = None
434 srclock = destlock = cleandir = None
356 srcrepo = srcpeer.local()
435 srcrepo = srcpeer.local()
357 try:
436 try:
@@ -674,4 +674,342 b' Test clone from the repository in (emula'
674 $ hg clone -U -q src dst
674 $ hg clone -U -q src dst
675 $ hg -R dst log -q
675 $ hg -R dst log -q
676 0:e1bab28bca43
676 0:e1bab28bca43
677
678 Create repositories to test auto sharing functionality
679
680 $ cat >> $HGRCPATH << EOF
681 > [extensions]
682 > share=
683 > EOF
684
685 $ hg init empty
686 $ hg init source1a
687 $ cd source1a
688 $ echo initial1 > foo
689 $ hg -q commit -A -m initial
690 $ echo second > foo
691 $ hg commit -m second
677 $ cd ..
692 $ cd ..
693
694 $ hg init filteredrev0
695 $ cd filteredrev0
696 $ cat >> .hg/hgrc << EOF
697 > [experimental]
698 > evolution=createmarkers
699 > EOF
700 $ echo initial1 > foo
701 $ hg -q commit -A -m initial0
702 $ hg -q up -r null
703 $ echo initial2 > foo
704 $ hg -q commit -A -m initial1
705 $ hg debugobsolete c05d5c47a5cf81401869999f3d05f7d699d2b29a e082c1832e09a7d1e78b7fd49a592d372de854c8
706 $ cd ..
707
708 $ hg -q clone --pull source1a source1b
709 $ cd source1a
710 $ hg bookmark bookA
711 $ echo 1a > foo
712 $ hg commit -m 1a
713 $ cd ../source1b
714 $ hg -q up -r 0
715 $ echo head1 > foo
716 $ hg commit -m head1
717 created new head
718 $ hg bookmark head1
719 $ hg -q up -r 0
720 $ echo head2 > foo
721 $ hg commit -m head2
722 created new head
723 $ hg bookmark head2
724 $ hg -q up -r 0
725 $ hg branch branch1
726 marked working directory as branch branch1
727 (branches are permanent and global, did you want a bookmark?)
728 $ echo branch1 > foo
729 $ hg commit -m branch1
730 $ hg -q up -r 0
731 $ hg branch branch2
732 marked working directory as branch branch2
733 $ echo branch2 > foo
734 $ hg commit -m branch2
735 $ cd ..
736 $ hg init source2
737 $ cd source2
738 $ echo initial2 > foo
739 $ hg -q commit -A -m initial2
740 $ echo second > foo
741 $ hg commit -m second
742 $ cd ..
743
744 Clone with auto share from an empty repo should not result in share
745
746 $ mkdir share
747 $ hg --config share.pool=share clone empty share-empty
748 (not using pooled storage: remote appears to be empty)
749 updating to branch default
750 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
751 $ ls share
752 $ test -d share-empty/.hg/store
753 $ test -f share-empty/.hg/sharedpath
754 [1]
755
756 Clone with auto share from a repo with filtered revision 0 should not result in share
757
758 $ hg --config share.pool=share clone filteredrev0 share-filtered
759 (not using pooled storage: unable to resolve identity of remote)
760 requesting all changes
761 adding changesets
762 adding manifests
763 adding file changes
764 added 1 changesets with 1 changes to 1 files
765 updating to branch default
766 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
767
768 Clone from repo with content should result in shared store being created
769
770 $ hg --config share.pool=share clone source1a share-dest1a
771 (sharing from new pooled repository b5f04eac9d8f7a6a9fcb070243cccea7dc5ea0c1)
772 requesting all changes
773 adding changesets
774 adding manifests
775 adding file changes
776 added 3 changesets with 3 changes to 1 files
777 updating working directory
778 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
779 searching for changes
780 no changes found
781 adding remote bookmark bookA
782
783 The shared repo should have been created
784
785 $ ls share
786 b5f04eac9d8f7a6a9fcb070243cccea7dc5ea0c1
787
788 The destination should point to it
789
790 $ cat share-dest1a/.hg/sharedpath; echo
791 $TESTTMP/share/b5f04eac9d8f7a6a9fcb070243cccea7dc5ea0c1/.hg
792
793 The destination should have bookmarks
794
795 $ hg -R share-dest1a bookmarks
796 bookA 2:e5bfe23c0b47
797
798 The default path should be the remote, not the share
799
800 $ hg -R share-dest1a config paths.default
801 $TESTTMP/source1a
802
803 Clone with existing share dir should result in pull + share
804
805 $ hg --config share.pool=share clone source1b share-dest1b
806 (sharing from existing pooled repository b5f04eac9d8f7a6a9fcb070243cccea7dc5ea0c1)
807 updating working directory
808 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
809 searching for changes
810 adding changesets
811 adding manifests
812 adding file changes
813 added 4 changesets with 4 changes to 1 files (+4 heads)
814 adding remote bookmark head1
815 adding remote bookmark head2
816
817 $ ls share
818 b5f04eac9d8f7a6a9fcb070243cccea7dc5ea0c1
819
820 $ cat share-dest1b/.hg/sharedpath; echo
821 $TESTTMP/share/b5f04eac9d8f7a6a9fcb070243cccea7dc5ea0c1/.hg
822
823 We only get bookmarks from the remote, not everything in the share
824
825 $ hg -R share-dest1b bookmarks
826 head1 3:4a8dc1ab4c13
827 head2 4:99f71071f117
828
829 Default path should be source, not share.
830
831 $ hg -R share-dest1b config paths.default
832 $TESTTMP/source1a
833
834 Clone from unrelated repo should result in new share
835
836 $ hg --config share.pool=share clone source2 share-dest2
837 (sharing from new pooled repository 22aeff664783fd44c6d9b435618173c118c3448e)
838 requesting all changes
839 adding changesets
840 adding manifests
841 adding file changes
842 added 2 changesets with 2 changes to 1 files
843 updating working directory
844 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
845 searching for changes
846 no changes found
847
848 $ ls share
849 22aeff664783fd44c6d9b435618173c118c3448e
850 b5f04eac9d8f7a6a9fcb070243cccea7dc5ea0c1
851
852 remote naming mode works as advertised
853
854 $ hg --config share.pool=shareremote --config share.poolnaming=remote clone source1a share-remote1a
855 (sharing from new pooled repository 195bb1fcdb595c14a6c13e0269129ed78f6debde)
856 requesting all changes
857 adding changesets
858 adding manifests
859 adding file changes
860 added 3 changesets with 3 changes to 1 files
861 updating working directory
862 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
863 searching for changes
864 no changes found
865 adding remote bookmark bookA
866
867 $ ls shareremote
868 195bb1fcdb595c14a6c13e0269129ed78f6debde
869
870 $ hg --config share.pool=shareremote --config share.poolnaming=remote clone source1b share-remote1b
871 (sharing from new pooled repository c0d4f83847ca2a873741feb7048a45085fd47c46)
872 requesting all changes
873 adding changesets
874 adding manifests
875 adding file changes
876 added 6 changesets with 6 changes to 1 files (+4 heads)
877 updating working directory
878 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
879 searching for changes
880 no changes found
881 adding remote bookmark head1
882 adding remote bookmark head2
883
884 $ ls shareremote
885 195bb1fcdb595c14a6c13e0269129ed78f6debde
886 c0d4f83847ca2a873741feb7048a45085fd47c46
887
888 request to clone a single revision is respected in sharing mode
889
890 $ hg --config share.pool=sharerevs clone -r 4a8dc1ab4c13 source1b share-1arev
891 (sharing from new pooled repository b5f04eac9d8f7a6a9fcb070243cccea7dc5ea0c1)
892 adding changesets
893 adding manifests
894 adding file changes
895 added 2 changesets with 2 changes to 1 files
896 updating working directory
897 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
898 no changes found
899 adding remote bookmark head1
900
901 $ hg -R share-1arev log -G
902 @ changeset: 1:4a8dc1ab4c13
903 | bookmark: head1
904 | tag: tip
905 | user: test
906 | date: Thu Jan 01 00:00:00 1970 +0000
907 | summary: head1
908 |
909 o changeset: 0:b5f04eac9d8f
910 user: test
911 date: Thu Jan 01 00:00:00 1970 +0000
912 summary: initial
913
914
915 making another clone should only pull down requested rev
916
917 $ hg --config share.pool=sharerevs clone -r 99f71071f117 source1b share-1brev
918 (sharing from existing pooled repository b5f04eac9d8f7a6a9fcb070243cccea7dc5ea0c1)
919 updating working directory
920 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
921 searching for changes
922 adding changesets
923 adding manifests
924 adding file changes
925 added 1 changesets with 1 changes to 1 files (+1 heads)
926 adding remote bookmark head1
927 adding remote bookmark head2
928
929 $ hg -R share-1brev log -G
930 o changeset: 2:99f71071f117
931 | bookmark: head2
932 | tag: tip
933 | parent: 0:b5f04eac9d8f
934 | user: test
935 | date: Thu Jan 01 00:00:00 1970 +0000
936 | summary: head2
937 |
938 | @ changeset: 1:4a8dc1ab4c13
939 |/ bookmark: head1
940 | user: test
941 | date: Thu Jan 01 00:00:00 1970 +0000
942 | summary: head1
943 |
944 o changeset: 0:b5f04eac9d8f
945 user: test
946 date: Thu Jan 01 00:00:00 1970 +0000
947 summary: initial
948
949
950 Request to clone a single branch is respected in sharing mode
951
952 $ hg --config share.pool=sharebranch clone -b branch1 source1b share-1bbranch1
953 (sharing from new pooled repository b5f04eac9d8f7a6a9fcb070243cccea7dc5ea0c1)
954 adding changesets
955 adding manifests
956 adding file changes
957 added 2 changesets with 2 changes to 1 files
958 updating working directory
959 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
960 no changes found
961
962 $ hg -R share-1bbranch1 log -G
963 o changeset: 1:5f92a6c1a1b1
964 | branch: branch1
965 | tag: tip
966 | user: test
967 | date: Thu Jan 01 00:00:00 1970 +0000
968 | summary: branch1
969 |
970 @ changeset: 0:b5f04eac9d8f
971 user: test
972 date: Thu Jan 01 00:00:00 1970 +0000
973 summary: initial
974
975
976 $ hg --config share.pool=sharebranch clone -b branch2 source1b share-1bbranch2
977 (sharing from existing pooled repository b5f04eac9d8f7a6a9fcb070243cccea7dc5ea0c1)
978 updating working directory
979 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
980 searching for changes
981 adding changesets
982 adding manifests
983 adding file changes
984 added 1 changesets with 1 changes to 1 files (+1 heads)
985
986 $ hg -R share-1bbranch2 log -G
987 o changeset: 2:6bacf4683960
988 | branch: branch2
989 | tag: tip
990 | parent: 0:b5f04eac9d8f
991 | user: test
992 | date: Thu Jan 01 00:00:00 1970 +0000
993 | summary: branch2
994 |
995 | o changeset: 1:5f92a6c1a1b1
996 |/ branch: branch1
997 | user: test
998 | date: Thu Jan 01 00:00:00 1970 +0000
999 | summary: branch1
1000 |
1001 @ changeset: 0:b5f04eac9d8f
1002 user: test
1003 date: Thu Jan 01 00:00:00 1970 +0000
1004 summary: initial
1005
1006
1007 -U is respected in share clone mode
1008
1009 $ hg --config share.pool=share clone -U source1a share-1anowc
1010 (sharing from existing pooled repository b5f04eac9d8f7a6a9fcb070243cccea7dc5ea0c1)
1011 searching for changes
1012 no changes found
1013 adding remote bookmark bookA
1014
1015 $ ls share-1anowc
General Comments 0
You need to be logged in to leave comments. Login now