##// END OF EJS Templates
Show the destionation for clone if not specified manually.
Thomas Arendsen Hein -
r3841:aaeb7f5d default
parent child Browse files
Show More
@@ -1,256 +1,257
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(util.drop_scheme('file', path)) and
17 return (os.path.isfile(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='', create=False):
54 def repository(ui, path='', 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 ui.status(_("destination directory: %s\n") % dest)
106
107
107 def localpath(path):
108 def localpath(path):
108 if path.startswith('file://'):
109 if path.startswith('file://'):
109 return path[7:]
110 return path[7:]
110 if path.startswith('file:'):
111 if path.startswith('file:'):
111 return path[5:]
112 return path[5:]
112 return path
113 return path
113
114
114 dest = localpath(dest)
115 dest = localpath(dest)
115 source = localpath(source)
116 source = localpath(source)
116
117
117 if os.path.exists(dest):
118 if os.path.exists(dest):
118 raise util.Abort(_("destination '%s' already exists") % dest)
119 raise util.Abort(_("destination '%s' already exists") % dest)
119
120
120 class DirCleanup(object):
121 class DirCleanup(object):
121 def __init__(self, dir_):
122 def __init__(self, dir_):
122 self.rmtree = shutil.rmtree
123 self.rmtree = shutil.rmtree
123 self.dir_ = dir_
124 self.dir_ = dir_
124 def close(self):
125 def close(self):
125 self.dir_ = None
126 self.dir_ = None
126 def __del__(self):
127 def __del__(self):
127 if self.dir_:
128 if self.dir_:
128 self.rmtree(self.dir_, True)
129 self.rmtree(self.dir_, True)
129
130
130 dest_repo = repository(ui, dest, create=True)
131 dest_repo = repository(ui, dest, create=True)
131
132
132 dir_cleanup = None
133 dir_cleanup = None
133 if dest_repo.local():
134 if dest_repo.local():
134 dir_cleanup = DirCleanup(os.path.realpath(dest_repo.root))
135 dir_cleanup = DirCleanup(os.path.realpath(dest_repo.root))
135
136
136 abspath = source
137 abspath = source
137 copy = False
138 copy = False
138 if src_repo.local() and dest_repo.local():
139 if src_repo.local() and dest_repo.local():
139 abspath = os.path.abspath(source)
140 abspath = os.path.abspath(source)
140 copy = not pull and not rev
141 copy = not pull and not rev
141
142
142 src_lock, dest_lock = None, None
143 src_lock, dest_lock = None, None
143 if copy:
144 if copy:
144 try:
145 try:
145 # we use a lock here because if we race with commit, we
146 # we use a lock here because if we race with commit, we
146 # can end up with extra data in the cloned revlogs that's
147 # can end up with extra data in the cloned revlogs that's
147 # not pointed to by changesets, thus causing verify to
148 # not pointed to by changesets, thus causing verify to
148 # fail
149 # fail
149 src_lock = src_repo.lock()
150 src_lock = src_repo.lock()
150 except lock.LockException:
151 except lock.LockException:
151 copy = False
152 copy = False
152
153
153 if copy:
154 if copy:
154 # we lock here to avoid premature writing to the target
155 # we lock here to avoid premature writing to the target
155 src_store = os.path.realpath(src_repo.spath)
156 src_store = os.path.realpath(src_repo.spath)
156 dest_store = os.path.realpath(dest_repo.spath)
157 dest_store = os.path.realpath(dest_repo.spath)
157 dest_lock = lock.lock(os.path.join(dest_store, "lock"))
158 dest_lock = lock.lock(os.path.join(dest_store, "lock"))
158
159
159 files = ("data",
160 files = ("data",
160 "00manifest.d", "00manifest.i",
161 "00manifest.d", "00manifest.i",
161 "00changelog.d", "00changelog.i")
162 "00changelog.d", "00changelog.i")
162 for f in files:
163 for f in files:
163 src = os.path.join(src_store, f)
164 src = os.path.join(src_store, f)
164 dst = os.path.join(dest_store, f)
165 dst = os.path.join(dest_store, f)
165 try:
166 try:
166 util.copyfiles(src, dst)
167 util.copyfiles(src, dst)
167 except OSError, inst:
168 except OSError, inst:
168 if inst.errno != errno.ENOENT:
169 if inst.errno != errno.ENOENT:
169 raise
170 raise
170
171
171 # 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
172 # into it
173 # into it
173 dest_repo = repository(ui, dest)
174 dest_repo = repository(ui, dest)
174
175
175 else:
176 else:
176 revs = None
177 revs = None
177 if rev:
178 if rev:
178 if 'lookup' not in src_repo.capabilities:
179 if 'lookup' not in src_repo.capabilities:
179 raise util.Abort(_("src repository does not support revision "
180 raise util.Abort(_("src repository does not support revision "
180 "lookup and so doesn't support clone by "
181 "lookup and so doesn't support clone by "
181 "revision"))
182 "revision"))
182 revs = [src_repo.lookup(r) for r in rev]
183 revs = [src_repo.lookup(r) for r in rev]
183
184
184 if dest_repo.local():
185 if dest_repo.local():
185 dest_repo.clone(src_repo, heads=revs, stream=stream)
186 dest_repo.clone(src_repo, heads=revs, stream=stream)
186 elif src_repo.local():
187 elif src_repo.local():
187 src_repo.push(dest_repo, revs=revs)
188 src_repo.push(dest_repo, revs=revs)
188 else:
189 else:
189 raise util.Abort(_("clone from remote to remote not supported"))
190 raise util.Abort(_("clone from remote to remote not supported"))
190
191
191 if src_lock:
192 if src_lock:
192 src_lock.release()
193 src_lock.release()
193
194
194 if dest_repo.local():
195 if dest_repo.local():
195 fp = dest_repo.opener("hgrc", "w", text=True)
196 fp = dest_repo.opener("hgrc", "w", text=True)
196 fp.write("[paths]\n")
197 fp.write("[paths]\n")
197 fp.write("default = %s\n" % abspath)
198 fp.write("default = %s\n" % abspath)
198 fp.close()
199 fp.close()
199
200
200 if dest_lock:
201 if dest_lock:
201 dest_lock.release()
202 dest_lock.release()
202
203
203 if update:
204 if update:
204 _update(dest_repo, dest_repo.changelog.tip())
205 _update(dest_repo, dest_repo.changelog.tip())
205 if dir_cleanup:
206 if dir_cleanup:
206 dir_cleanup.close()
207 dir_cleanup.close()
207
208
208 return src_repo, dest_repo
209 return src_repo, dest_repo
209
210
210 def _showstats(repo, stats):
211 def _showstats(repo, stats):
211 stats = ((stats[0], _("updated")),
212 stats = ((stats[0], _("updated")),
212 (stats[1], _("merged")),
213 (stats[1], _("merged")),
213 (stats[2], _("removed")),
214 (stats[2], _("removed")),
214 (stats[3], _("unresolved")))
215 (stats[3], _("unresolved")))
215 note = ", ".join([_("%d files %s") % s for s in stats])
216 note = ", ".join([_("%d files %s") % s for s in stats])
216 repo.ui.status("%s\n" % note)
217 repo.ui.status("%s\n" % note)
217
218
218 def _update(repo, node): return update(repo, node)
219 def _update(repo, node): return update(repo, node)
219
220
220 def update(repo, node):
221 def update(repo, node):
221 """update the working directory to node, merging linear changes"""
222 """update the working directory to node, merging linear changes"""
222 stats = _merge.update(repo, node, False, False, None, None)
223 stats = _merge.update(repo, node, False, False, None, None)
223 _showstats(repo, stats)
224 _showstats(repo, stats)
224 if stats[3]:
225 if stats[3]:
225 repo.ui.status(_("There are unresolved merges with"
226 repo.ui.status(_("There are unresolved merges with"
226 " locally modified files.\n"))
227 " locally modified files.\n"))
227 return stats[3]
228 return stats[3]
228
229
229 def clean(repo, node, wlock=None, show_stats=True):
230 def clean(repo, node, wlock=None, show_stats=True):
230 """forcibly switch the working directory to node, clobbering changes"""
231 """forcibly switch the working directory to node, clobbering changes"""
231 stats = _merge.update(repo, node, False, True, None, wlock)
232 stats = _merge.update(repo, node, False, True, None, wlock)
232 if show_stats: _showstats(repo, stats)
233 if show_stats: _showstats(repo, stats)
233 return stats[3]
234 return stats[3]
234
235
235 def merge(repo, node, force=None, remind=True, wlock=None):
236 def merge(repo, node, force=None, remind=True, wlock=None):
236 """branch merge with node, resolving changes"""
237 """branch merge with node, resolving changes"""
237 stats = _merge.update(repo, node, True, force, False, wlock)
238 stats = _merge.update(repo, node, True, force, False, wlock)
238 _showstats(repo, stats)
239 _showstats(repo, stats)
239 if stats[3]:
240 if stats[3]:
240 pl = repo.parents()
241 pl = repo.parents()
241 repo.ui.status(_("There are unresolved merges,"
242 repo.ui.status(_("There are unresolved merges,"
242 " you can redo the full merge using:\n"
243 " you can redo the full merge using:\n"
243 " hg update -C %s\n"
244 " hg update -C %s\n"
244 " hg merge %s\n")
245 " hg merge %s\n")
245 % (pl[0].rev(), pl[1].rev()))
246 % (pl[0].rev(), pl[1].rev()))
246 elif remind:
247 elif remind:
247 repo.ui.status(_("(branch merge, don't forget to commit)\n"))
248 repo.ui.status(_("(branch merge, don't forget to commit)\n"))
248 return stats[3]
249 return stats[3]
249
250
250 def revert(repo, node, choose, wlock):
251 def revert(repo, node, choose, wlock):
251 """revert changes to revision in node without updating dirstate"""
252 """revert changes to revision in node without updating dirstate"""
252 return _merge.update(repo, node, False, True, choose, wlock)[3]
253 return _merge.update(repo, node, False, True, choose, wlock)[3]
253
254
254 def verify(repo):
255 def verify(repo):
255 """verify the consistency of a repository"""
256 """verify the consistency of a repository"""
256 return _verify.verify(repo)
257 return _verify.verify(repo)
@@ -1,11 +1,12
1 abort: repository a not found!
1 abort: repository a not found!
2 255
2 255
3 abort: error: Connection refused
3 abort: error: Connection refused
4 255
4 255
5 abort: repository a not found!
5 abort: repository a not found!
6 255
6 255
7 abort: destination '../a' already exists
7 abort: destination '../a' already exists
8 255
8 255
9 abort: repository a not found!
9 abort: repository a not found!
10 255
10 255
11 destination directory: q
11 abort: destination 'q' already exists
12 abort: destination 'q' already exists
@@ -1,15 +1,16
1 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
2 a
2 a
3 checking changesets
3 checking changesets
4 checking manifests
4 checking manifests
5 crosschecking files in changesets and manifests
5 crosschecking files in changesets and manifests
6 checking files
6 checking files
7 1 files, 1 changesets, 1 total revisions
7 1 files, 1 changesets, 1 total revisions
8 a not present
8 a not present
9 checking changesets
9 checking changesets
10 checking manifests
10 checking manifests
11 crosschecking files in changesets and manifests
11 crosschecking files in changesets and manifests
12 checking files
12 checking files
13 1 files, 1 changesets, 1 total revisions
13 1 files, 1 changesets, 1 total revisions
14 destination directory: a
14 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
15 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
15 a
16 a
General Comments 0
You need to be logged in to leave comments. Login now