##// END OF EJS Templates
clone: checkout the "default" branch if available
Matt Mackall -
r4477:6cbfa740 default
parent child Browse files
Show More
@@ -1,281 +1,285 b''
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 i18n import _
11 from i18n import _
12 import localrepo, bundlerepo, httprepo, sshrepo, statichttprepo
12 import localrepo, bundlerepo, httprepo, sshrepo, statichttprepo
13 import errno, lock, os, shutil, util
13 import errno, lock, os, shutil, util
14 import merge as _merge
14 import merge as _merge
15 import verify as _verify
15 import verify as _verify
16
16
17 def _local(path):
17 def _local(path):
18 return (os.path.isfile(util.drop_scheme('file', path)) and
18 return (os.path.isfile(util.drop_scheme('file', path)) and
19 bundlerepo or localrepo)
19 bundlerepo or localrepo)
20
20
21 schemes = {
21 schemes = {
22 'bundle': bundlerepo,
22 'bundle': bundlerepo,
23 'file': _local,
23 'file': _local,
24 'hg': httprepo,
24 'hg': httprepo,
25 'http': httprepo,
25 'http': httprepo,
26 'https': httprepo,
26 'https': httprepo,
27 'old-http': statichttprepo,
27 'old-http': statichttprepo,
28 'ssh': sshrepo,
28 'ssh': sshrepo,
29 'static-http': statichttprepo,
29 'static-http': statichttprepo,
30 }
30 }
31
31
32 def _lookup(path):
32 def _lookup(path):
33 scheme = 'file'
33 scheme = 'file'
34 if path:
34 if path:
35 c = path.find(':')
35 c = path.find(':')
36 if c > 0:
36 if c > 0:
37 scheme = path[:c]
37 scheme = path[:c]
38 thing = schemes.get(scheme) or schemes['file']
38 thing = schemes.get(scheme) or schemes['file']
39 try:
39 try:
40 return thing(path)
40 return thing(path)
41 except TypeError:
41 except TypeError:
42 return thing
42 return thing
43
43
44 def islocal(repo):
44 def islocal(repo):
45 '''return true if repo or path is local'''
45 '''return true if repo or path is local'''
46 if isinstance(repo, str):
46 if isinstance(repo, str):
47 try:
47 try:
48 return _lookup(repo).islocal(repo)
48 return _lookup(repo).islocal(repo)
49 except AttributeError:
49 except AttributeError:
50 return False
50 return False
51 return repo.local()
51 return repo.local()
52
52
53 repo_setup_hooks = []
53 repo_setup_hooks = []
54
54
55 def repository(ui, path='', create=False):
55 def repository(ui, path='', create=False):
56 """return a repository object for the specified path"""
56 """return a repository object for the specified path"""
57 repo = _lookup(path).instance(ui, path, create)
57 repo = _lookup(path).instance(ui, path, create)
58 ui = getattr(repo, "ui", ui)
58 ui = getattr(repo, "ui", ui)
59 for hook in repo_setup_hooks:
59 for hook in repo_setup_hooks:
60 hook(ui, repo)
60 hook(ui, repo)
61 return repo
61 return repo
62
62
63 def defaultdest(source):
63 def defaultdest(source):
64 '''return default destination of clone if none is given'''
64 '''return default destination of clone if none is given'''
65 return os.path.basename(os.path.normpath(source))
65 return os.path.basename(os.path.normpath(source))
66
66
67 def clone(ui, source, dest=None, pull=False, rev=None, update=True,
67 def clone(ui, source, dest=None, pull=False, rev=None, update=True,
68 stream=False):
68 stream=False):
69 """Make a copy of an existing repository.
69 """Make a copy of an existing repository.
70
70
71 Create a copy of an existing repository in a new directory. The
71 Create a copy of an existing repository in a new directory. The
72 source and destination are URLs, as passed to the repository
72 source and destination are URLs, as passed to the repository
73 function. Returns a pair of repository objects, the source and
73 function. Returns a pair of repository objects, the source and
74 newly created destination.
74 newly created destination.
75
75
76 The location of the source is added to the new repository's
76 The location of the source is added to the new repository's
77 .hg/hgrc file, as the default to be used for future pulls and
77 .hg/hgrc file, as the default to be used for future pulls and
78 pushes.
78 pushes.
79
79
80 If an exception is raised, the partly cloned/updated destination
80 If an exception is raised, the partly cloned/updated destination
81 repository will be deleted.
81 repository will be deleted.
82
82
83 Arguments:
83 Arguments:
84
84
85 source: repository object or URL
85 source: repository object or URL
86
86
87 dest: URL of destination repository to create (defaults to base
87 dest: URL of destination repository to create (defaults to base
88 name of source repository)
88 name of source repository)
89
89
90 pull: always pull from source repository, even in local case
90 pull: always pull from source repository, even in local case
91
91
92 stream: stream raw data uncompressed from repository (fast over
92 stream: stream raw data uncompressed from repository (fast over
93 LAN, slow over WAN)
93 LAN, slow over WAN)
94
94
95 rev: revision to clone up to (implies pull=True)
95 rev: revision to clone up to (implies pull=True)
96
96
97 update: update working directory after clone completes, if
97 update: update working directory after clone completes, if
98 destination is local repository
98 destination is local repository
99 """
99 """
100 if isinstance(source, str):
100 if isinstance(source, str):
101 src_repo = repository(ui, source)
101 src_repo = repository(ui, source)
102 else:
102 else:
103 src_repo = source
103 src_repo = source
104 source = src_repo.url()
104 source = src_repo.url()
105
105
106 if dest is None:
106 if dest is None:
107 dest = defaultdest(source)
107 dest = defaultdest(source)
108 ui.status(_("destination directory: %s\n") % dest)
108 ui.status(_("destination directory: %s\n") % dest)
109
109
110 def localpath(path):
110 def localpath(path):
111 if path.startswith('file://'):
111 if path.startswith('file://'):
112 return path[7:]
112 return path[7:]
113 if path.startswith('file:'):
113 if path.startswith('file:'):
114 return path[5:]
114 return path[5:]
115 return path
115 return path
116
116
117 dest = localpath(dest)
117 dest = localpath(dest)
118 source = localpath(source)
118 source = localpath(source)
119
119
120 if os.path.exists(dest):
120 if os.path.exists(dest):
121 raise util.Abort(_("destination '%s' already exists") % dest)
121 raise util.Abort(_("destination '%s' already exists") % dest)
122
122
123 class DirCleanup(object):
123 class DirCleanup(object):
124 def __init__(self, dir_):
124 def __init__(self, dir_):
125 self.rmtree = shutil.rmtree
125 self.rmtree = shutil.rmtree
126 self.dir_ = dir_
126 self.dir_ = dir_
127 def close(self):
127 def close(self):
128 self.dir_ = None
128 self.dir_ = None
129 def __del__(self):
129 def __del__(self):
130 if self.dir_:
130 if self.dir_:
131 self.rmtree(self.dir_, True)
131 self.rmtree(self.dir_, True)
132
132
133 dir_cleanup = None
133 dir_cleanup = None
134 if islocal(dest):
134 if islocal(dest):
135 dir_cleanup = DirCleanup(dest)
135 dir_cleanup = DirCleanup(dest)
136
136
137 abspath = source
137 abspath = source
138 copy = False
138 copy = False
139 if src_repo.local() and islocal(dest):
139 if src_repo.local() and islocal(dest):
140 abspath = os.path.abspath(source)
140 abspath = os.path.abspath(source)
141 copy = not pull and not rev
141 copy = not pull and not rev
142
142
143 src_lock, dest_lock = None, None
143 src_lock, dest_lock = None, None
144 if copy:
144 if copy:
145 try:
145 try:
146 # 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
147 # 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
148 # not pointed to by changesets, thus causing verify to
148 # not pointed to by changesets, thus causing verify to
149 # fail
149 # fail
150 src_lock = src_repo.lock()
150 src_lock = src_repo.lock()
151 except lock.LockException:
151 except lock.LockException:
152 copy = False
152 copy = False
153
153
154 if copy:
154 if copy:
155 def force_copy(src, dst):
155 def force_copy(src, dst):
156 try:
156 try:
157 util.copyfiles(src, dst)
157 util.copyfiles(src, dst)
158 except OSError, inst:
158 except OSError, inst:
159 if inst.errno != errno.ENOENT:
159 if inst.errno != errno.ENOENT:
160 raise
160 raise
161
161
162 src_store = os.path.realpath(src_repo.spath)
162 src_store = os.path.realpath(src_repo.spath)
163 if not os.path.exists(dest):
163 if not os.path.exists(dest):
164 os.mkdir(dest)
164 os.mkdir(dest)
165 dest_path = os.path.realpath(os.path.join(dest, ".hg"))
165 dest_path = os.path.realpath(os.path.join(dest, ".hg"))
166 os.mkdir(dest_path)
166 os.mkdir(dest_path)
167 if src_repo.spath != src_repo.path:
167 if src_repo.spath != src_repo.path:
168 dest_store = os.path.join(dest_path, "store")
168 dest_store = os.path.join(dest_path, "store")
169 os.mkdir(dest_store)
169 os.mkdir(dest_store)
170 else:
170 else:
171 dest_store = dest_path
171 dest_store = dest_path
172 # copy the requires file
172 # copy the requires file
173 force_copy(src_repo.join("requires"),
173 force_copy(src_repo.join("requires"),
174 os.path.join(dest_path, "requires"))
174 os.path.join(dest_path, "requires"))
175 # we lock here to avoid premature writing to the target
175 # we lock here to avoid premature writing to the target
176 dest_lock = lock.lock(os.path.join(dest_store, "lock"))
176 dest_lock = lock.lock(os.path.join(dest_store, "lock"))
177
177
178 files = ("data",
178 files = ("data",
179 "00manifest.d", "00manifest.i",
179 "00manifest.d", "00manifest.i",
180 "00changelog.d", "00changelog.i")
180 "00changelog.d", "00changelog.i")
181 for f in files:
181 for f in files:
182 src = os.path.join(src_store, f)
182 src = os.path.join(src_store, f)
183 dst = os.path.join(dest_store, f)
183 dst = os.path.join(dest_store, f)
184 force_copy(src, dst)
184 force_copy(src, dst)
185
185
186 # we need to re-init the repo after manually copying the data
186 # we need to re-init the repo after manually copying the data
187 # into it
187 # into it
188 dest_repo = repository(ui, dest)
188 dest_repo = repository(ui, dest)
189
189
190 else:
190 else:
191 dest_repo = repository(ui, dest, create=True)
191 dest_repo = repository(ui, dest, create=True)
192
192
193 revs = None
193 revs = None
194 if rev:
194 if rev:
195 if 'lookup' not in src_repo.capabilities:
195 if 'lookup' not in src_repo.capabilities:
196 raise util.Abort(_("src repository does not support revision "
196 raise util.Abort(_("src repository does not support revision "
197 "lookup and so doesn't support clone by "
197 "lookup and so doesn't support clone by "
198 "revision"))
198 "revision"))
199 revs = [src_repo.lookup(r) for r in rev]
199 revs = [src_repo.lookup(r) for r in rev]
200
200
201 if dest_repo.local():
201 if dest_repo.local():
202 dest_repo.clone(src_repo, heads=revs, stream=stream)
202 dest_repo.clone(src_repo, heads=revs, stream=stream)
203 elif src_repo.local():
203 elif src_repo.local():
204 src_repo.push(dest_repo, revs=revs)
204 src_repo.push(dest_repo, revs=revs)
205 else:
205 else:
206 raise util.Abort(_("clone from remote to remote not supported"))
206 raise util.Abort(_("clone from remote to remote not supported"))
207
207
208 if src_lock:
208 if src_lock:
209 src_lock.release()
209 src_lock.release()
210
210
211 if dest_repo.local():
211 if dest_repo.local():
212 fp = dest_repo.opener("hgrc", "w", text=True)
212 fp = dest_repo.opener("hgrc", "w", text=True)
213 fp.write("[paths]\n")
213 fp.write("[paths]\n")
214 fp.write("default = %s\n" % abspath)
214 fp.write("default = %s\n" % abspath)
215 fp.close()
215 fp.close()
216
216
217 if dest_lock:
217 if dest_lock:
218 dest_lock.release()
218 dest_lock.release()
219
219
220 if update:
220 if update:
221 _update(dest_repo, dest_repo.changelog.tip())
221 try:
222 checkout = dest_repo.lookup("default")
223 except:
224 checkout = dest_repo.changelog.tip()
225 _update(dest_repo, checkout)
222 if dir_cleanup:
226 if dir_cleanup:
223 dir_cleanup.close()
227 dir_cleanup.close()
224
228
225 return src_repo, dest_repo
229 return src_repo, dest_repo
226
230
227 def _showstats(repo, stats):
231 def _showstats(repo, stats):
228 stats = ((stats[0], _("updated")),
232 stats = ((stats[0], _("updated")),
229 (stats[1], _("merged")),
233 (stats[1], _("merged")),
230 (stats[2], _("removed")),
234 (stats[2], _("removed")),
231 (stats[3], _("unresolved")))
235 (stats[3], _("unresolved")))
232 note = ", ".join([_("%d files %s") % s for s in stats])
236 note = ", ".join([_("%d files %s") % s for s in stats])
233 repo.ui.status("%s\n" % note)
237 repo.ui.status("%s\n" % note)
234
238
235 def _update(repo, node): return update(repo, node)
239 def _update(repo, node): return update(repo, node)
236
240
237 def update(repo, node):
241 def update(repo, node):
238 """update the working directory to node, merging linear changes"""
242 """update the working directory to node, merging linear changes"""
239 pl = repo.parents()
243 pl = repo.parents()
240 stats = _merge.update(repo, node, False, False, None, None)
244 stats = _merge.update(repo, node, False, False, None, None)
241 _showstats(repo, stats)
245 _showstats(repo, stats)
242 if stats[3]:
246 if stats[3]:
243 repo.ui.status(_("There are unresolved merges with"
247 repo.ui.status(_("There are unresolved merges with"
244 " locally modified files.\n"))
248 " locally modified files.\n"))
245 if stats[1]:
249 if stats[1]:
246 repo.ui.status(_("You can finish the partial merge using:\n"))
250 repo.ui.status(_("You can finish the partial merge using:\n"))
247 else:
251 else:
248 repo.ui.status(_("You can redo the full merge using:\n"))
252 repo.ui.status(_("You can redo the full merge using:\n"))
249 # len(pl)==1, otherwise _merge.update() would have raised util.Abort:
253 # len(pl)==1, otherwise _merge.update() would have raised util.Abort:
250 repo.ui.status(_(" hg update %s\n hg update %s\n")
254 repo.ui.status(_(" hg update %s\n hg update %s\n")
251 % (pl[0].rev(), repo.changectx(node).rev()))
255 % (pl[0].rev(), repo.changectx(node).rev()))
252 return stats[3]
256 return stats[3]
253
257
254 def clean(repo, node, wlock=None, show_stats=True):
258 def clean(repo, node, wlock=None, show_stats=True):
255 """forcibly switch the working directory to node, clobbering changes"""
259 """forcibly switch the working directory to node, clobbering changes"""
256 stats = _merge.update(repo, node, False, True, None, wlock)
260 stats = _merge.update(repo, node, False, True, None, wlock)
257 if show_stats: _showstats(repo, stats)
261 if show_stats: _showstats(repo, stats)
258 return stats[3]
262 return stats[3]
259
263
260 def merge(repo, node, force=None, remind=True, wlock=None):
264 def merge(repo, node, force=None, remind=True, wlock=None):
261 """branch merge with node, resolving changes"""
265 """branch merge with node, resolving changes"""
262 stats = _merge.update(repo, node, True, force, False, wlock)
266 stats = _merge.update(repo, node, True, force, False, wlock)
263 _showstats(repo, stats)
267 _showstats(repo, stats)
264 if stats[3]:
268 if stats[3]:
265 pl = repo.parents()
269 pl = repo.parents()
266 repo.ui.status(_("There are unresolved merges,"
270 repo.ui.status(_("There are unresolved merges,"
267 " you can redo the full merge using:\n"
271 " you can redo the full merge using:\n"
268 " hg update -C %s\n"
272 " hg update -C %s\n"
269 " hg merge %s\n")
273 " hg merge %s\n")
270 % (pl[0].rev(), pl[1].rev()))
274 % (pl[0].rev(), pl[1].rev()))
271 elif remind:
275 elif remind:
272 repo.ui.status(_("(branch merge, don't forget to commit)\n"))
276 repo.ui.status(_("(branch merge, don't forget to commit)\n"))
273 return stats[3]
277 return stats[3]
274
278
275 def revert(repo, node, choose, wlock):
279 def revert(repo, node, choose, wlock):
276 """revert changes to revision in node without updating dirstate"""
280 """revert changes to revision in node without updating dirstate"""
277 return _merge.update(repo, node, False, True, choose, wlock)[3]
281 return _merge.update(repo, node, False, True, choose, wlock)[3]
278
282
279 def verify(repo):
283 def verify(repo):
280 """verify the consistency of a repository"""
284 """verify the consistency of a repository"""
281 return _verify.verify(repo)
285 return _verify.verify(repo)
General Comments 0
You need to be logged in to leave comments. Login now