##// END OF EJS Templates
Update branch on checkout/update
Matt Mackall -
r3416:145a8fde default
parent child Browse files
Show More
@@ -1,256 +1,260 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 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
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 = repository(ui, dest, create=True)
130 dest_repo = repository(ui, dest, create=True)
131
131
132 dest_path = None
132 dest_path = None
133 dir_cleanup = None
133 dir_cleanup = None
134 if dest_repo.local():
134 if dest_repo.local():
135 dest_path = os.path.realpath(dest_repo.root)
135 dest_path = os.path.realpath(dest_repo.root)
136 dir_cleanup = DirCleanup(dest_path)
136 dir_cleanup = DirCleanup(dest_path)
137
137
138 abspath = source
138 abspath = source
139 copy = False
139 copy = False
140 if src_repo.local() and dest_repo.local():
140 if src_repo.local() and dest_repo.local():
141 abspath = os.path.abspath(source)
141 abspath = os.path.abspath(source)
142 copy = not pull and not rev
142 copy = not pull and not rev
143
143
144 src_lock, dest_lock = None, None
144 src_lock, dest_lock = None, None
145 if copy:
145 if copy:
146 try:
146 try:
147 # 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
148 # 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
149 # not pointed to by changesets, thus causing verify to
149 # not pointed to by changesets, thus causing verify to
150 # fail
150 # fail
151 src_lock = src_repo.lock()
151 src_lock = src_repo.lock()
152 except lock.LockException:
152 except lock.LockException:
153 copy = False
153 copy = False
154
154
155 if copy:
155 if copy:
156 # we lock here to avoid premature writing to the target
156 # we lock here to avoid premature writing to the target
157 dest_lock = lock.lock(os.path.join(dest_path, ".hg", "lock"))
157 dest_lock = lock.lock(os.path.join(dest_path, ".hg", "lock"))
158
158
159 # 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
160 # can do its work
160 # can do its work
161 os.rmdir(os.path.join(dest_path, ".hg", "data"))
161 os.rmdir(os.path.join(dest_path, ".hg", "data"))
162 files = "data 00manifest.d 00manifest.i 00changelog.d 00changelog.i"
162 files = "data 00manifest.d 00manifest.i 00changelog.d 00changelog.i"
163 for f in files.split():
163 for f in files.split():
164 src = os.path.join(source, ".hg", f)
164 src = os.path.join(source, ".hg", f)
165 dst = os.path.join(dest_path, ".hg", f)
165 dst = os.path.join(dest_path, ".hg", f)
166 try:
166 try:
167 util.copyfiles(src, dst)
167 util.copyfiles(src, dst)
168 except OSError, inst:
168 except OSError, inst:
169 if inst.errno != errno.ENOENT:
169 if inst.errno != errno.ENOENT:
170 raise
170 raise
171
171
172 # 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
173 # into it
173 # into it
174 dest_repo = repository(ui, dest)
174 dest_repo = repository(ui, dest)
175
175
176 else:
176 else:
177 revs = None
177 revs = None
178 if rev:
178 if rev:
179 if not src_repo.local():
179 if not src_repo.local():
180 raise util.Abort(_("clone by revision not supported yet "
180 raise util.Abort(_("clone by revision not supported yet "
181 "for remote repositories"))
181 "for remote repositories"))
182 revs = [src_repo.lookup(r) for r in rev]
182 revs = [src_repo.lookup(r) for r in rev]
183
183
184 if dest_repo.local():
184 if dest_repo.local():
185 dest_repo.clone(src_repo, heads=revs, stream=stream)
185 dest_repo.clone(src_repo, heads=revs, stream=stream)
186 elif src_repo.local():
186 elif src_repo.local():
187 src_repo.push(dest_repo, revs=revs)
187 src_repo.push(dest_repo, revs=revs)
188 else:
188 else:
189 raise util.Abort(_("clone from remote to remote not supported"))
189 raise util.Abort(_("clone from remote to remote not supported"))
190
190
191 if src_lock:
191 if src_lock:
192 src_lock.release()
192 src_lock.release()
193
193
194 if dest_repo.local():
194 if dest_repo.local():
195 fp = dest_repo.opener("hgrc", "w", text=True)
195 fp = dest_repo.opener("hgrc", "w", text=True)
196 fp.write("[paths]\n")
196 fp.write("[paths]\n")
197 fp.write("default = %s\n" % abspath)
197 fp.write("default = %s\n" % abspath)
198 fp.close()
198 fp.close()
199
199
200 if dest_lock:
200 if dest_lock:
201 dest_lock.release()
201 dest_lock.release()
202
202
203 if update:
203 if update:
204 _update(dest_repo, dest_repo.changelog.tip())
204 _update(dest_repo, dest_repo.changelog.tip())
205 if dir_cleanup:
205 if dir_cleanup:
206 dir_cleanup.close()
206 dir_cleanup.close()
207
207
208 return src_repo, dest_repo
208 return src_repo, dest_repo
209
209
210 def _showstats(repo, stats):
210 def _showstats(repo, stats):
211 stats = ((stats[0], _("updated")),
211 stats = ((stats[0], _("updated")),
212 (stats[1], _("merged")),
212 (stats[1], _("merged")),
213 (stats[2], _("removed")),
213 (stats[2], _("removed")),
214 (stats[3], _("unresolved")))
214 (stats[3], _("unresolved")))
215 note = ", ".join([_("%d files %s") % s for s in stats])
215 note = ", ".join([_("%d files %s") % s for s in stats])
216 repo.ui.status("%s\n" % note)
216 repo.ui.status("%s\n" % note)
217
217
218 def _update(repo, node): return update(repo, node)
218 def _update(repo, node): return update(repo, node)
219
219
220 def update(repo, node):
220 def update(repo, node):
221 """update the working directory to node, merging linear changes"""
221 """update the working directory to node, merging linear changes"""
222 stats = _merge.update(repo, node, False, False, None, None)
222 stats = _merge.update(repo, node, False, False, None, None)
223 _showstats(repo, stats)
223 _showstats(repo, stats)
224 branch = repo.changectx(node).branch()
225 repo.opener("branch", "w").write(branch + "\n")
224 if stats[3]:
226 if stats[3]:
225 repo.ui.status(_("There are unresolved merges with"
227 repo.ui.status(_("There are unresolved merges with"
226 " locally modified files.\n"))
228 " locally modified files.\n"))
227 return stats[3]
229 return stats[3]
228
230
229 def clean(repo, node, wlock=None, show_stats=True):
231 def clean(repo, node, wlock=None, show_stats=True):
230 """forcibly switch the working directory to node, clobbering changes"""
232 """forcibly switch the working directory to node, clobbering changes"""
231 stats = _merge.update(repo, node, False, True, None, wlock)
233 stats = _merge.update(repo, node, False, True, None, wlock)
234 branch = repo.changectx(node).branch()
235 repo.opener("branch", "w").write(branch + "\n")
232 if show_stats: _showstats(repo, stats)
236 if show_stats: _showstats(repo, stats)
233 return stats[3]
237 return stats[3]
234
238
235 def merge(repo, node, force=None, remind=True, wlock=None):
239 def merge(repo, node, force=None, remind=True, wlock=None):
236 """branch merge with node, resolving changes"""
240 """branch merge with node, resolving changes"""
237 stats = _merge.update(repo, node, True, force, False, wlock)
241 stats = _merge.update(repo, node, True, force, False, wlock)
238 _showstats(repo, stats)
242 _showstats(repo, stats)
239 if stats[3]:
243 if stats[3]:
240 pl = repo.parents()
244 pl = repo.parents()
241 repo.ui.status(_("There are unresolved merges,"
245 repo.ui.status(_("There are unresolved merges,"
242 " you can redo the full merge using:\n"
246 " you can redo the full merge using:\n"
243 " hg update -C %s\n"
247 " hg update -C %s\n"
244 " hg merge %s\n"
248 " hg merge %s\n"
245 % (pl[0].rev(), pl[1].rev())))
249 % (pl[0].rev(), pl[1].rev())))
246 elif remind:
250 elif remind:
247 repo.ui.status(_("(branch merge, don't forget to commit)\n"))
251 repo.ui.status(_("(branch merge, don't forget to commit)\n"))
248 return stats[3]
252 return stats[3]
249
253
250 def revert(repo, node, choose, wlock):
254 def revert(repo, node, choose, wlock):
251 """revert changes to revision in node without updating dirstate"""
255 """revert changes to revision in node without updating dirstate"""
252 return _merge.update(repo, node, False, True, choose, wlock)[3]
256 return _merge.update(repo, node, False, True, choose, wlock)[3]
253
257
254 def verify(repo):
258 def verify(repo):
255 """verify the consistency of a repository"""
259 """verify the consistency of a repository"""
256 return _verify.verify(repo)
260 return _verify.verify(repo)
General Comments 0
You need to be logged in to leave comments. Login now