##// END OF EJS Templates
merge with -stable
Benoit Boissinot -
r5186:0d5d0384 merge default
parent child Browse files
Show More
@@ -1,281 +1,282 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, cmdutil, extensions
13 import errno, lock, os, shutil, util, cmdutil, 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 schemes = {
21 schemes = {
22 'bundle': bundlerepo,
22 'bundle': bundlerepo,
23 'file': _local,
23 'file': _local,
24 'http': httprepo,
24 'http': httprepo,
25 'https': httprepo,
25 'https': httprepo,
26 'ssh': sshrepo,
26 'ssh': sshrepo,
27 'static-http': statichttprepo,
27 'static-http': statichttprepo,
28 }
28 }
29
29
30 def _lookup(path):
30 def _lookup(path):
31 scheme = 'file'
31 scheme = 'file'
32 if path:
32 if path:
33 c = path.find(':')
33 c = path.find(':')
34 if c > 0:
34 if c > 0:
35 scheme = path[:c]
35 scheme = path[:c]
36 thing = schemes.get(scheme) or schemes['file']
36 thing = schemes.get(scheme) or schemes['file']
37 try:
37 try:
38 return thing(path)
38 return thing(path)
39 except TypeError:
39 except TypeError:
40 return thing
40 return thing
41
41
42 def islocal(repo):
42 def islocal(repo):
43 '''return true if repo or path is local'''
43 '''return true if repo or path is local'''
44 if isinstance(repo, str):
44 if isinstance(repo, str):
45 try:
45 try:
46 return _lookup(repo).islocal(repo)
46 return _lookup(repo).islocal(repo)
47 except AttributeError:
47 except AttributeError:
48 return False
48 return False
49 return repo.local()
49 return repo.local()
50
50
51 def repository(ui, path='', create=False):
51 def repository(ui, path='', create=False):
52 """return a repository object for the specified path"""
52 """return a repository object for the specified path"""
53 repo = _lookup(path).instance(ui, path, create)
53 repo = _lookup(path).instance(ui, path, create)
54 ui = getattr(repo, "ui", ui)
54 ui = getattr(repo, "ui", ui)
55 for hook in extensions.setuphooks:
55 for hook in extensions.setuphooks:
56 hook(ui, repo)
56 hook(ui, repo)
57 return repo
57 return repo
58
58
59 def defaultdest(source):
59 def defaultdest(source):
60 '''return default destination of clone if none is given'''
60 '''return default destination of clone if none is given'''
61 return os.path.basename(os.path.normpath(source))
61 return os.path.basename(os.path.normpath(source))
62
62
63 def clone(ui, source, dest=None, pull=False, rev=None, update=True,
63 def clone(ui, source, dest=None, pull=False, rev=None, update=True,
64 stream=False):
64 stream=False):
65 """Make a copy of an existing repository.
65 """Make a copy of an existing repository.
66
66
67 Create a copy of an existing repository in a new directory. The
67 Create a copy of an existing repository in a new directory. The
68 source and destination are URLs, as passed to the repository
68 source and destination are URLs, as passed to the repository
69 function. Returns a pair of repository objects, the source and
69 function. Returns a pair of repository objects, the source and
70 newly created destination.
70 newly created destination.
71
71
72 The location of the source is added to the new repository's
72 The location of the source is added to the new repository's
73 .hg/hgrc file, as the default to be used for future pulls and
73 .hg/hgrc file, as the default to be used for future pulls and
74 pushes.
74 pushes.
75
75
76 If an exception is raised, the partly cloned/updated destination
76 If an exception is raised, the partly cloned/updated destination
77 repository will be deleted.
77 repository will be deleted.
78
78
79 Arguments:
79 Arguments:
80
80
81 source: repository object or URL
81 source: repository object or URL
82
82
83 dest: URL of destination repository to create (defaults to base
83 dest: URL of destination repository to create (defaults to base
84 name of source repository)
84 name of source repository)
85
85
86 pull: always pull from source repository, even in local case
86 pull: always pull from source repository, even in local case
87
87
88 stream: stream raw data uncompressed from repository (fast over
88 stream: stream raw data uncompressed from repository (fast over
89 LAN, slow over WAN)
89 LAN, slow over WAN)
90
90
91 rev: revision to clone up to (implies pull=True)
91 rev: revision to clone up to (implies pull=True)
92
92
93 update: update working directory after clone completes, if
93 update: update working directory after clone completes, if
94 destination is local repository
94 destination is local repository
95 """
95 """
96
96
97 origsource = source
97 origsource = source
98 source, rev = cmdutil.parseurl(ui.expandpath(source), rev)
98 source, rev = cmdutil.parseurl(ui.expandpath(source), rev)
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 src_lock = dest_lock = dir_cleanup = None
133 src_lock = dest_lock = dir_cleanup = None
134 try:
134 try:
135 if islocal(dest):
135 if islocal(dest):
136 dir_cleanup = DirCleanup(dest)
136 dir_cleanup = DirCleanup(dest)
137
137
138 abspath = origsource
138 abspath = origsource
139 copy = False
139 copy = False
140 if src_repo.local() and islocal(dest):
140 if src_repo.local() and islocal(dest):
141 abspath = os.path.abspath(origsource)
141 abspath = os.path.abspath(origsource)
142 copy = not pull and not rev
142 copy = not pull and not rev
143
143
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 dir_cleanup:
209 dir_cleanup.close()
210
208 if dest_repo.local():
211 if dest_repo.local():
209 fp = dest_repo.opener("hgrc", "w", text=True)
212 fp = dest_repo.opener("hgrc", "w", text=True)
210 fp.write("[paths]\n")
213 fp.write("[paths]\n")
211 fp.write("default = %s\n" % abspath)
214 fp.write("default = %s\n" % abspath)
212 fp.close()
215 fp.close()
213
216
214 if update:
217 if update:
215 try:
218 try:
216 checkout = dest_repo.lookup("default")
219 checkout = dest_repo.lookup("default")
217 except:
220 except:
218 checkout = dest_repo.changelog.tip()
221 checkout = dest_repo.changelog.tip()
219 _update(dest_repo, checkout)
222 _update(dest_repo, checkout)
220 if dir_cleanup:
221 dir_cleanup.close()
222
223
223 return src_repo, dest_repo
224 return src_repo, dest_repo
224 finally:
225 finally:
225 del src_lock, dest_lock, dir_cleanup
226 del src_lock, dest_lock, dir_cleanup
226
227
227 def _showstats(repo, stats):
228 def _showstats(repo, stats):
228 stats = ((stats[0], _("updated")),
229 stats = ((stats[0], _("updated")),
229 (stats[1], _("merged")),
230 (stats[1], _("merged")),
230 (stats[2], _("removed")),
231 (stats[2], _("removed")),
231 (stats[3], _("unresolved")))
232 (stats[3], _("unresolved")))
232 note = ", ".join([_("%d files %s") % s for s in stats])
233 note = ", ".join([_("%d files %s") % s for s in stats])
233 repo.ui.status("%s\n" % note)
234 repo.ui.status("%s\n" % note)
234
235
235 def _update(repo, node): return update(repo, node)
236 def _update(repo, node): return update(repo, node)
236
237
237 def update(repo, node):
238 def update(repo, node):
238 """update the working directory to node, merging linear changes"""
239 """update the working directory to node, merging linear changes"""
239 pl = repo.parents()
240 pl = repo.parents()
240 stats = _merge.update(repo, node, False, False, None)
241 stats = _merge.update(repo, node, False, False, None)
241 _showstats(repo, stats)
242 _showstats(repo, stats)
242 if stats[3]:
243 if stats[3]:
243 repo.ui.status(_("There are unresolved merges with"
244 repo.ui.status(_("There are unresolved merges with"
244 " locally modified files.\n"))
245 " locally modified files.\n"))
245 if stats[1]:
246 if stats[1]:
246 repo.ui.status(_("You can finish the partial merge using:\n"))
247 repo.ui.status(_("You can finish the partial merge using:\n"))
247 else:
248 else:
248 repo.ui.status(_("You can redo the full merge using:\n"))
249 repo.ui.status(_("You can redo the full merge using:\n"))
249 # len(pl)==1, otherwise _merge.update() would have raised util.Abort:
250 # len(pl)==1, otherwise _merge.update() would have raised util.Abort:
250 repo.ui.status(_(" hg update %s\n hg update %s\n")
251 repo.ui.status(_(" hg update %s\n hg update %s\n")
251 % (pl[0].rev(), repo.changectx(node).rev()))
252 % (pl[0].rev(), repo.changectx(node).rev()))
252 return stats[3]
253 return stats[3]
253
254
254 def clean(repo, node, show_stats=True):
255 def clean(repo, node, show_stats=True):
255 """forcibly switch the working directory to node, clobbering changes"""
256 """forcibly switch the working directory to node, clobbering changes"""
256 stats = _merge.update(repo, node, False, True, None)
257 stats = _merge.update(repo, node, False, True, None)
257 if show_stats: _showstats(repo, stats)
258 if show_stats: _showstats(repo, stats)
258 return stats[3]
259 return stats[3]
259
260
260 def merge(repo, node, force=None, remind=True):
261 def merge(repo, node, force=None, remind=True):
261 """branch merge with node, resolving changes"""
262 """branch merge with node, resolving changes"""
262 stats = _merge.update(repo, node, True, force, False)
263 stats = _merge.update(repo, node, True, force, False)
263 _showstats(repo, stats)
264 _showstats(repo, stats)
264 if stats[3]:
265 if stats[3]:
265 pl = repo.parents()
266 pl = repo.parents()
266 repo.ui.status(_("There are unresolved merges,"
267 repo.ui.status(_("There are unresolved merges,"
267 " you can redo the full merge using:\n"
268 " you can redo the full merge using:\n"
268 " hg update -C %s\n"
269 " hg update -C %s\n"
269 " hg merge %s\n")
270 " hg merge %s\n")
270 % (pl[0].rev(), pl[1].rev()))
271 % (pl[0].rev(), pl[1].rev()))
271 elif remind:
272 elif remind:
272 repo.ui.status(_("(branch merge, don't forget to commit)\n"))
273 repo.ui.status(_("(branch merge, don't forget to commit)\n"))
273 return stats[3]
274 return stats[3]
274
275
275 def revert(repo, node, choose):
276 def revert(repo, node, choose):
276 """revert changes to revision in node without updating dirstate"""
277 """revert changes to revision in node without updating dirstate"""
277 return _merge.update(repo, node, False, True, choose)[3]
278 return _merge.update(repo, node, False, True, choose)[3]
278
279
279 def verify(repo):
280 def verify(repo):
280 """verify the consistency of a repository"""
281 """verify the consistency of a repository"""
281 return _verify.verify(repo)
282 return _verify.verify(repo)
General Comments 0
You need to be logged in to leave comments. Login now