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