##// END OF EJS Templates
clone: simplifying dest repo creation...
Benoit Boissinot -
r3037:3acb76f0 default
parent child Browse files
Show More
@@ -1,236 +1,231
1 # hg.py - repository classes for mercurial
1 # hg.py - repository classes for mercurial
2 #
2 #
3 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
4 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
4 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
5 #
5 #
6 # This software may be used and distributed according to the terms
6 # This software may be used and distributed according to the terms
7 # of the GNU General Public License, incorporated herein by reference.
7 # of the GNU General Public License, incorporated herein by reference.
8
8
9 from node import *
9 from node import *
10 from repo import *
10 from repo import *
11 from demandload import *
11 from demandload import *
12 from i18n import gettext as _
12 from i18n import gettext as _
13 demandload(globals(), "localrepo bundlerepo httprepo sshrepo statichttprepo")
13 demandload(globals(), "localrepo bundlerepo httprepo sshrepo statichttprepo")
14 demandload(globals(), "errno lock os shutil util merge@_merge verify@_verify")
14 demandload(globals(), "errno lock os shutil util merge@_merge verify@_verify")
15
15
16 def _local(path):
16 def _local(path):
17 return (os.path.isfile(path and util.drop_scheme('file', path)) and
17 return (os.path.isfile(path and util.drop_scheme('file', path)) and
18 bundlerepo or localrepo)
18 bundlerepo or localrepo)
19
19
20 schemes = {
20 schemes = {
21 'bundle': bundlerepo,
21 'bundle': bundlerepo,
22 'file': _local,
22 'file': _local,
23 'hg': httprepo,
23 'hg': httprepo,
24 'http': httprepo,
24 'http': httprepo,
25 'https': httprepo,
25 'https': httprepo,
26 'old-http': statichttprepo,
26 'old-http': statichttprepo,
27 'ssh': sshrepo,
27 'ssh': sshrepo,
28 'static-http': statichttprepo,
28 'static-http': statichttprepo,
29 }
29 }
30
30
31 def _lookup(path):
31 def _lookup(path):
32 scheme = 'file'
32 scheme = 'file'
33 if path:
33 if path:
34 c = path.find(':')
34 c = path.find(':')
35 if c > 0:
35 if c > 0:
36 scheme = path[:c]
36 scheme = path[:c]
37 thing = schemes.get(scheme) or schemes['file']
37 thing = schemes.get(scheme) or schemes['file']
38 try:
38 try:
39 return thing(path)
39 return thing(path)
40 except TypeError:
40 except TypeError:
41 return thing
41 return thing
42
42
43 def islocal(repo):
43 def islocal(repo):
44 '''return true if repo or path is local'''
44 '''return true if repo or path is local'''
45 if isinstance(repo, str):
45 if isinstance(repo, str):
46 try:
46 try:
47 return _lookup(repo).islocal(repo)
47 return _lookup(repo).islocal(repo)
48 except AttributeError:
48 except AttributeError:
49 return False
49 return False
50 return repo.local()
50 return repo.local()
51
51
52 repo_setup_hooks = []
52 repo_setup_hooks = []
53
53
54 def repository(ui, path=None, create=False):
54 def repository(ui, path=None, create=False):
55 """return a repository object for the specified path"""
55 """return a repository object for the specified path"""
56 repo = _lookup(path).instance(ui, path, create)
56 repo = _lookup(path).instance(ui, path, create)
57 for hook in repo_setup_hooks:
57 for hook in repo_setup_hooks:
58 hook(ui, repo)
58 hook(ui, repo)
59 return repo
59 return repo
60
60
61 def defaultdest(source):
61 def defaultdest(source):
62 '''return default destination of clone if none is given'''
62 '''return default destination of clone if none is given'''
63 return os.path.basename(os.path.normpath(source))
63 return os.path.basename(os.path.normpath(source))
64
64
65 def clone(ui, source, dest=None, pull=False, rev=None, update=True,
65 def clone(ui, source, dest=None, pull=False, rev=None, update=True,
66 stream=False):
66 stream=False):
67 """Make a copy of an existing repository.
67 """Make a copy of an existing repository.
68
68
69 Create a copy of an existing repository in a new directory. The
69 Create a copy of an existing repository in a new directory. The
70 source and destination are URLs, as passed to the repository
70 source and destination are URLs, as passed to the repository
71 function. Returns a pair of repository objects, the source and
71 function. Returns a pair of repository objects, the source and
72 newly created destination.
72 newly created destination.
73
73
74 The location of the source is added to the new repository's
74 The location of the source is added to the new repository's
75 .hg/hgrc file, as the default to be used for future pulls and
75 .hg/hgrc file, as the default to be used for future pulls and
76 pushes.
76 pushes.
77
77
78 If an exception is raised, the partly cloned/updated destination
78 If an exception is raised, the partly cloned/updated destination
79 repository will be deleted.
79 repository will be deleted.
80
80
81 Arguments:
81 Arguments:
82
82
83 source: repository object or URL
83 source: repository object or URL
84
84
85 dest: URL of destination repository to create (defaults to base
85 dest: URL of destination repository to create (defaults to base
86 name of source repository)
86 name of source repository)
87
87
88 pull: always pull from source repository, even in local case
88 pull: always pull from source repository, even in local case
89
89
90 stream: stream raw data uncompressed from repository (fast over
90 stream: stream raw data uncompressed from repository (fast over
91 LAN, slow over WAN)
91 LAN, slow over WAN)
92
92
93 rev: revision to clone up to (implies pull=True)
93 rev: revision to clone up to (implies pull=True)
94
94
95 update: update working directory after clone completes, if
95 update: update working directory after clone completes, if
96 destination is local repository
96 destination is local repository
97 """
97 """
98 if isinstance(source, str):
98 if isinstance(source, str):
99 src_repo = repository(ui, source)
99 src_repo = repository(ui, source)
100 else:
100 else:
101 src_repo = source
101 src_repo = source
102 source = src_repo.url()
102 source = src_repo.url()
103
103
104 if dest is None:
104 if dest is None:
105 dest = defaultdest(source)
105 dest = defaultdest(source)
106
106
107 def localpath(path):
107 def localpath(path):
108 if path.startswith('file://'):
108 if path.startswith('file://'):
109 return path[7:]
109 return path[7:]
110 if path.startswith('file:'):
110 if path.startswith('file:'):
111 return path[5:]
111 return path[5:]
112 return path
112 return path
113
113
114 dest = localpath(dest)
114 dest = localpath(dest)
115 source = localpath(source)
115 source = localpath(source)
116
116
117 if os.path.exists(dest):
117 if os.path.exists(dest):
118 raise util.Abort(_("destination '%s' already exists"), dest)
118 raise util.Abort(_("destination '%s' already exists"), dest)
119
119
120 class DirCleanup(object):
120 class DirCleanup(object):
121 def __init__(self, dir_):
121 def __init__(self, dir_):
122 self.rmtree = shutil.rmtree
122 self.rmtree = shutil.rmtree
123 self.dir_ = dir_
123 self.dir_ = dir_
124 def close(self):
124 def close(self):
125 self.dir_ = None
125 self.dir_ = None
126 def __del__(self):
126 def __del__(self):
127 if self.dir_:
127 if self.dir_:
128 self.rmtree(self.dir_, True)
128 self.rmtree(self.dir_, True)
129
129
130 dest_repo = None
130 dest_repo = repository(ui, dest, create=True)
131 try:
132 dest_repo = repository(ui, dest)
133 raise util.Abort(_("destination '%s' already exists." % dest))
134 except RepoError:
135 dest_repo = repository(ui, dest, create=True)
136
131
137 dest_path = None
132 dest_path = None
138 dir_cleanup = None
133 dir_cleanup = None
139 if dest_repo.local():
134 if dest_repo.local():
140 dest_path = os.path.realpath(dest_repo.root)
135 dest_path = os.path.realpath(dest_repo.root)
141 dir_cleanup = DirCleanup(dest_path)
136 dir_cleanup = DirCleanup(dest_path)
142
137
143 abspath = source
138 abspath = source
144 copy = False
139 copy = False
145 if src_repo.local() and dest_repo.local():
140 if src_repo.local() and dest_repo.local():
146 abspath = os.path.abspath(source)
141 abspath = os.path.abspath(source)
147 copy = not pull and not rev
142 copy = not pull and not rev
148
143
149 src_lock, dest_lock = None, None
144 src_lock, dest_lock = None, None
150 if copy:
145 if copy:
151 try:
146 try:
152 # we use a lock here because if we race with commit, we
147 # we use a lock here because if we race with commit, we
153 # can end up with extra data in the cloned revlogs that's
148 # can end up with extra data in the cloned revlogs that's
154 # not pointed to by changesets, thus causing verify to
149 # not pointed to by changesets, thus causing verify to
155 # fail
150 # fail
156 src_lock = src_repo.lock()
151 src_lock = src_repo.lock()
157 except lock.LockException:
152 except lock.LockException:
158 copy = False
153 copy = False
159
154
160 if copy:
155 if copy:
161 # we lock here to avoid premature writing to the target
156 # we lock here to avoid premature writing to the target
162 dest_lock = lock.lock(os.path.join(dest_path, ".hg", "lock"))
157 dest_lock = lock.lock(os.path.join(dest_path, ".hg", "lock"))
163
158
164 # we need to remove the (empty) data dir in dest so copyfiles
159 # we need to remove the (empty) data dir in dest so copyfiles
165 # can do its work
160 # can do its work
166 os.rmdir(os.path.join(dest_path, ".hg", "data"))
161 os.rmdir(os.path.join(dest_path, ".hg", "data"))
167 files = "data 00manifest.d 00manifest.i 00changelog.d 00changelog.i"
162 files = "data 00manifest.d 00manifest.i 00changelog.d 00changelog.i"
168 for f in files.split():
163 for f in files.split():
169 src = os.path.join(source, ".hg", f)
164 src = os.path.join(source, ".hg", f)
170 dst = os.path.join(dest_path, ".hg", f)
165 dst = os.path.join(dest_path, ".hg", f)
171 try:
166 try:
172 util.copyfiles(src, dst)
167 util.copyfiles(src, dst)
173 except OSError, inst:
168 except OSError, inst:
174 if inst.errno != errno.ENOENT:
169 if inst.errno != errno.ENOENT:
175 raise
170 raise
176
171
177 # we need to re-init the repo after manually copying the data
172 # we need to re-init the repo after manually copying the data
178 # into it
173 # into it
179 dest_repo = repository(ui, dest)
174 dest_repo = repository(ui, dest)
180
175
181 else:
176 else:
182 revs = None
177 revs = None
183 if rev:
178 if rev:
184 if not src_repo.local():
179 if not src_repo.local():
185 raise util.Abort(_("clone by revision not supported yet "
180 raise util.Abort(_("clone by revision not supported yet "
186 "for remote repositories"))
181 "for remote repositories"))
187 revs = [src_repo.lookup(r) for r in rev]
182 revs = [src_repo.lookup(r) for r in rev]
188
183
189 if dest_repo.local():
184 if dest_repo.local():
190 dest_repo.clone(src_repo, heads=revs, stream=stream)
185 dest_repo.clone(src_repo, heads=revs, stream=stream)
191 elif src_repo.local():
186 elif src_repo.local():
192 src_repo.push(dest_repo, revs=revs)
187 src_repo.push(dest_repo, revs=revs)
193 else:
188 else:
194 raise util.Abort(_("clone from remote to remote not supported"))
189 raise util.Abort(_("clone from remote to remote not supported"))
195
190
196 if src_lock:
191 if src_lock:
197 src_lock.release()
192 src_lock.release()
198
193
199 if dest_repo.local():
194 if dest_repo.local():
200 fp = dest_repo.opener("hgrc", "w", text=True)
195 fp = dest_repo.opener("hgrc", "w", text=True)
201 fp.write("[paths]\n")
196 fp.write("[paths]\n")
202 fp.write("default = %s\n" % abspath)
197 fp.write("default = %s\n" % abspath)
203 fp.close()
198 fp.close()
204
199
205 if dest_lock:
200 if dest_lock:
206 dest_lock.release()
201 dest_lock.release()
207
202
208 if update:
203 if update:
209 _merge.update(dest_repo, dest_repo.changelog.tip())
204 _merge.update(dest_repo, dest_repo.changelog.tip())
210 if dir_cleanup:
205 if dir_cleanup:
211 dir_cleanup.close()
206 dir_cleanup.close()
212
207
213 return src_repo, dest_repo
208 return src_repo, dest_repo
214
209
215 def update(repo, node):
210 def update(repo, node):
216 """update the working directory to node, merging linear changes"""
211 """update the working directory to node, merging linear changes"""
217 return _merge.update(repo, node)
212 return _merge.update(repo, node)
218
213
219 def clean(repo, node, wlock=None, show_stats=True):
214 def clean(repo, node, wlock=None, show_stats=True):
220 """forcibly switch the working directory to node, clobbering changes"""
215 """forcibly switch the working directory to node, clobbering changes"""
221 return _merge.update(repo, node, force=True, wlock=wlock,
216 return _merge.update(repo, node, force=True, wlock=wlock,
222 show_stats=show_stats)
217 show_stats=show_stats)
223
218
224 def merge(repo, node, force=None, remind=True, wlock=None):
219 def merge(repo, node, force=None, remind=True, wlock=None):
225 """branch merge with node, resolving changes"""
220 """branch merge with node, resolving changes"""
226 return _merge.update(repo, node, branchmerge=True, force=force,
221 return _merge.update(repo, node, branchmerge=True, force=force,
227 remind=remind, wlock=wlock)
222 remind=remind, wlock=wlock)
228
223
229 def revert(repo, node, choose, wlock):
224 def revert(repo, node, choose, wlock):
230 """revert changes to revision in node without updating dirstate"""
225 """revert changes to revision in node without updating dirstate"""
231 return _merge.update(repo, node, force=True, partial=choose,
226 return _merge.update(repo, node, force=True, partial=choose,
232 show_stats=False, wlock=wlock)
227 show_stats=False, wlock=wlock)
233
228
234 def verify(repo):
229 def verify(repo):
235 """verify the consistency of a repository"""
230 """verify the consistency of a repository"""
236 return _verify.verify(repo)
231 return _verify.verify(repo)
@@ -1,52 +1,61
1 #!/bin/sh
1 #!/bin/sh
2
2
3 # This test tries to exercise the ssh functionality with a dummy script
3 # This test tries to exercise the ssh functionality with a dummy script
4
4
5 cat <<'EOF' > dummyssh
5 cat <<'EOF' > dummyssh
6 #!/bin/sh
6 #!/bin/sh
7 # this attempts to deal with relative pathnames
7 # this attempts to deal with relative pathnames
8 cd `dirname $0`
8 cd `dirname $0`
9
9
10 # check for proper args
10 # check for proper args
11 if [ $1 != "user@dummy" ] ; then
11 if [ $1 != "user@dummy" ] ; then
12 exit -1
12 exit -1
13 fi
13 fi
14
14
15 # check that we're in the right directory
15 # check that we're in the right directory
16 if [ ! -x dummyssh ] ; then
16 if [ ! -x dummyssh ] ; then
17 exit -1
17 exit -1
18 fi
18 fi
19
19
20 echo Got arguments 1:$1 2:$2 3:$3 4:$4 5:$5 >> dummylog
20 echo Got arguments 1:$1 2:$2 3:$3 4:$4 5:$5 >> dummylog
21 $2
21 $2
22 EOF
22 EOF
23 chmod +x dummyssh
23 chmod +x dummyssh
24
24
25 echo "# creating 'local'"
25 echo "# creating 'local'"
26 hg init local
26 hg init local
27 echo this > local/foo
27 echo this > local/foo
28 hg ci --cwd local -A -m "init" -d "1000000 0"
28 hg ci --cwd local -A -m "init" -d "1000000 0"
29
29
30 echo "#test failure"
31 hg init local
32
30 echo "# init+push to remote2"
33 echo "# init+push to remote2"
31 hg init -e ./dummyssh ssh://user@dummy/remote2
34 hg init -e ./dummyssh ssh://user@dummy/remote2
32 hg incoming -R remote2 local
35 hg incoming -R remote2 local
33 hg push -R local -e ./dummyssh ssh://user@dummy/remote2
36 hg push -R local -e ./dummyssh ssh://user@dummy/remote2
34
37
35 echo "# clone to remote1"
38 echo "# clone to remote1"
36 hg clone -e ./dummyssh local ssh://user@dummy/remote1
39 hg clone -e ./dummyssh local ssh://user@dummy/remote1
37
40
41 echo "# init to existing repo"
42 hg init -e ./dummyssh ssh://user@dummy/remote1
43
44 echo "# clone to existing repo"
45 hg clone -e ./dummyssh local ssh://user@dummy/remote1
46
38 echo "# output of dummyssh"
47 echo "# output of dummyssh"
39 cat dummylog
48 cat dummylog
40
49
41 echo "# comparing repositories"
50 echo "# comparing repositories"
42 hg tip -q -R local
51 hg tip -q -R local
43 hg tip -q -R remote1
52 hg tip -q -R remote1
44 hg tip -q -R remote2
53 hg tip -q -R remote2
45
54
46 echo "# check names for repositories (clashes with URL schemes, special chars)"
55 echo "# check names for repositories (clashes with URL schemes, special chars)"
47 for i in bundle file hg http https old-http ssh static-http " " "with space"; do
56 for i in bundle file hg http https old-http ssh static-http " " "with space"; do
48 echo "# hg init \"$i\""
57 echo "# hg init \"$i\""
49 hg init "$i"
58 hg init "$i"
50 test -d "$i" -a -d "$i/.hg" -a -d "$i/.hg/data" && echo "ok" || echo "failed"
59 test -d "$i" -a -d "$i/.hg" -a -d "$i/.hg/data" && echo "ok" || echo "failed"
51 done
60 done
52
61
@@ -1,58 +1,64
1 # creating 'local'
1 # creating 'local'
2 adding foo
2 adding foo
3 #test failure
4 abort: repository local already exists!
3 # init+push to remote2
5 # init+push to remote2
4 remote: abort: repository remote2 not found!
6 remote: abort: repository remote2 not found!
5 changeset: 0:c4e059d443be
7 changeset: 0:c4e059d443be
6 tag: tip
8 tag: tip
7 user: test
9 user: test
8 date: Mon Jan 12 13:46:40 1970 +0000
10 date: Mon Jan 12 13:46:40 1970 +0000
9 summary: init
11 summary: init
10
12
11 pushing to ssh://user@dummy/remote2
13 pushing to ssh://user@dummy/remote2
12 searching for changes
14 searching for changes
13 remote: adding changesets
15 remote: adding changesets
14 remote: adding manifests
16 remote: adding manifests
15 remote: adding file changes
17 remote: adding file changes
16 remote: added 1 changesets with 1 changes to 1 files
18 remote: added 1 changesets with 1 changes to 1 files
17 # clone to remote1
19 # clone to remote1
18 remote: abort: repository remote1 not found!
20 remote: abort: repository remote1 not found!
19 searching for changes
21 searching for changes
20 remote: abort: repository remote1 not found!
21 remote: adding changesets
22 remote: adding changesets
22 remote: adding manifests
23 remote: adding manifests
23 remote: adding file changes
24 remote: adding file changes
24 remote: added 1 changesets with 1 changes to 1 files
25 remote: added 1 changesets with 1 changes to 1 files
26 # init to existing repo
27 abort: repository ssh://user@dummy/remote1 already exists!
28 # clone to existing repo
29 abort: repository ssh://user@dummy/remote1 already exists!
25 # output of dummyssh
30 # output of dummyssh
26 Got arguments 1:user@dummy 2:hg -R remote2 serve --stdio 3: 4: 5:
31 Got arguments 1:user@dummy 2:hg -R remote2 serve --stdio 3: 4: 5:
27 Got arguments 1:user@dummy 2:hg init remote2 3: 4: 5:
32 Got arguments 1:user@dummy 2:hg init remote2 3: 4: 5:
28 Got arguments 1:user@dummy 2:hg -R remote2 serve --stdio 3: 4: 5:
33 Got arguments 1:user@dummy 2:hg -R remote2 serve --stdio 3: 4: 5:
29 Got arguments 1:user@dummy 2:hg -R remote2 serve --stdio 3: 4: 5:
34 Got arguments 1:user@dummy 2:hg -R remote2 serve --stdio 3: 4: 5:
30 Got arguments 1:user@dummy 2:hg -R remote1 serve --stdio 3: 4: 5:
35 Got arguments 1:user@dummy 2:hg -R remote1 serve --stdio 3: 4: 5:
36 Got arguments 1:user@dummy 2:hg init remote1 3: 4: 5:
31 Got arguments 1:user@dummy 2:hg -R remote1 serve --stdio 3: 4: 5:
37 Got arguments 1:user@dummy 2:hg -R remote1 serve --stdio 3: 4: 5:
32 Got arguments 1:user@dummy 2:hg init remote1 3: 4: 5:
38 Got arguments 1:user@dummy 2:hg -R remote1 serve --stdio 3: 4: 5:
33 Got arguments 1:user@dummy 2:hg -R remote1 serve --stdio 3: 4: 5:
39 Got arguments 1:user@dummy 2:hg -R remote1 serve --stdio 3: 4: 5:
34 # comparing repositories
40 # comparing repositories
35 0:c4e059d443be
41 0:c4e059d443be
36 0:c4e059d443be
42 0:c4e059d443be
37 0:c4e059d443be
43 0:c4e059d443be
38 # check names for repositories (clashes with URL schemes, special chars)
44 # check names for repositories (clashes with URL schemes, special chars)
39 # hg init "bundle"
45 # hg init "bundle"
40 ok
46 ok
41 # hg init "file"
47 # hg init "file"
42 ok
48 ok
43 # hg init "hg"
49 # hg init "hg"
44 ok
50 ok
45 # hg init "http"
51 # hg init "http"
46 ok
52 ok
47 # hg init "https"
53 # hg init "https"
48 ok
54 ok
49 # hg init "old-http"
55 # hg init "old-http"
50 ok
56 ok
51 # hg init "ssh"
57 # hg init "ssh"
52 ok
58 ok
53 # hg init "static-http"
59 # hg init "static-http"
54 ok
60 ok
55 # hg init " "
61 # hg init " "
56 ok
62 ok
57 # hg init "with space"
63 # hg init "with space"
58 ok
64 ok
General Comments 0
You need to be logged in to leave comments. Login now