##// END OF EJS Templates
push: make locking of source optional (issue3684)...
Pierre-Yves David -
r19097:3f5e75c2 stable
parent child Browse files
Show More
@@ -1,2587 +1,2608
1 # localrepo.py - read/write repository class for mercurial
1 # localrepo.py - read/write repository class for mercurial
2 #
2 #
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7 from node import hex, nullid, short
7 from node import hex, nullid, short
8 from i18n import _
8 from i18n import _
9 import peer, changegroup, subrepo, discovery, pushkey, obsolete, repoview
9 import peer, changegroup, subrepo, discovery, pushkey, obsolete, repoview
10 import changelog, dirstate, filelog, manifest, context, bookmarks, phases
10 import changelog, dirstate, filelog, manifest, context, bookmarks, phases
11 import lock, transaction, store, encoding
11 import lock, transaction, store, encoding
12 import scmutil, util, extensions, hook, error, revset
12 import scmutil, util, extensions, hook, error, revset
13 import match as matchmod
13 import match as matchmod
14 import merge as mergemod
14 import merge as mergemod
15 import tags as tagsmod
15 import tags as tagsmod
16 from lock import release
16 from lock import release
17 import weakref, errno, os, time, inspect
17 import weakref, errno, os, time, inspect
18 import branchmap
18 import branchmap
19 propertycache = util.propertycache
19 propertycache = util.propertycache
20 filecache = scmutil.filecache
20 filecache = scmutil.filecache
21
21
22 class repofilecache(filecache):
22 class repofilecache(filecache):
23 """All filecache usage on repo are done for logic that should be unfiltered
23 """All filecache usage on repo are done for logic that should be unfiltered
24 """
24 """
25
25
26 def __get__(self, repo, type=None):
26 def __get__(self, repo, type=None):
27 return super(repofilecache, self).__get__(repo.unfiltered(), type)
27 return super(repofilecache, self).__get__(repo.unfiltered(), type)
28 def __set__(self, repo, value):
28 def __set__(self, repo, value):
29 return super(repofilecache, self).__set__(repo.unfiltered(), value)
29 return super(repofilecache, self).__set__(repo.unfiltered(), value)
30 def __delete__(self, repo):
30 def __delete__(self, repo):
31 return super(repofilecache, self).__delete__(repo.unfiltered())
31 return super(repofilecache, self).__delete__(repo.unfiltered())
32
32
33 class storecache(repofilecache):
33 class storecache(repofilecache):
34 """filecache for files in the store"""
34 """filecache for files in the store"""
35 def join(self, obj, fname):
35 def join(self, obj, fname):
36 return obj.sjoin(fname)
36 return obj.sjoin(fname)
37
37
38 class unfilteredpropertycache(propertycache):
38 class unfilteredpropertycache(propertycache):
39 """propertycache that apply to unfiltered repo only"""
39 """propertycache that apply to unfiltered repo only"""
40
40
41 def __get__(self, repo, type=None):
41 def __get__(self, repo, type=None):
42 return super(unfilteredpropertycache, self).__get__(repo.unfiltered())
42 return super(unfilteredpropertycache, self).__get__(repo.unfiltered())
43
43
44 class filteredpropertycache(propertycache):
44 class filteredpropertycache(propertycache):
45 """propertycache that must take filtering in account"""
45 """propertycache that must take filtering in account"""
46
46
47 def cachevalue(self, obj, value):
47 def cachevalue(self, obj, value):
48 object.__setattr__(obj, self.name, value)
48 object.__setattr__(obj, self.name, value)
49
49
50
50
51 def hasunfilteredcache(repo, name):
51 def hasunfilteredcache(repo, name):
52 """check if a repo has an unfilteredpropertycache value for <name>"""
52 """check if a repo has an unfilteredpropertycache value for <name>"""
53 return name in vars(repo.unfiltered())
53 return name in vars(repo.unfiltered())
54
54
55 def unfilteredmethod(orig):
55 def unfilteredmethod(orig):
56 """decorate method that always need to be run on unfiltered version"""
56 """decorate method that always need to be run on unfiltered version"""
57 def wrapper(repo, *args, **kwargs):
57 def wrapper(repo, *args, **kwargs):
58 return orig(repo.unfiltered(), *args, **kwargs)
58 return orig(repo.unfiltered(), *args, **kwargs)
59 return wrapper
59 return wrapper
60
60
61 MODERNCAPS = set(('lookup', 'branchmap', 'pushkey', 'known', 'getbundle'))
61 MODERNCAPS = set(('lookup', 'branchmap', 'pushkey', 'known', 'getbundle'))
62 LEGACYCAPS = MODERNCAPS.union(set(['changegroupsubset']))
62 LEGACYCAPS = MODERNCAPS.union(set(['changegroupsubset']))
63
63
64 class localpeer(peer.peerrepository):
64 class localpeer(peer.peerrepository):
65 '''peer for a local repo; reflects only the most recent API'''
65 '''peer for a local repo; reflects only the most recent API'''
66
66
67 def __init__(self, repo, caps=MODERNCAPS):
67 def __init__(self, repo, caps=MODERNCAPS):
68 peer.peerrepository.__init__(self)
68 peer.peerrepository.__init__(self)
69 self._repo = repo.filtered('served')
69 self._repo = repo.filtered('served')
70 self.ui = repo.ui
70 self.ui = repo.ui
71 self._caps = repo._restrictcapabilities(caps)
71 self._caps = repo._restrictcapabilities(caps)
72 self.requirements = repo.requirements
72 self.requirements = repo.requirements
73 self.supportedformats = repo.supportedformats
73 self.supportedformats = repo.supportedformats
74
74
75 def close(self):
75 def close(self):
76 self._repo.close()
76 self._repo.close()
77
77
78 def _capabilities(self):
78 def _capabilities(self):
79 return self._caps
79 return self._caps
80
80
81 def local(self):
81 def local(self):
82 return self._repo
82 return self._repo
83
83
84 def canpush(self):
84 def canpush(self):
85 return True
85 return True
86
86
87 def url(self):
87 def url(self):
88 return self._repo.url()
88 return self._repo.url()
89
89
90 def lookup(self, key):
90 def lookup(self, key):
91 return self._repo.lookup(key)
91 return self._repo.lookup(key)
92
92
93 def branchmap(self):
93 def branchmap(self):
94 return self._repo.branchmap()
94 return self._repo.branchmap()
95
95
96 def heads(self):
96 def heads(self):
97 return self._repo.heads()
97 return self._repo.heads()
98
98
99 def known(self, nodes):
99 def known(self, nodes):
100 return self._repo.known(nodes)
100 return self._repo.known(nodes)
101
101
102 def getbundle(self, source, heads=None, common=None):
102 def getbundle(self, source, heads=None, common=None):
103 return self._repo.getbundle(source, heads=heads, common=common)
103 return self._repo.getbundle(source, heads=heads, common=common)
104
104
105 # TODO We might want to move the next two calls into legacypeer and add
105 # TODO We might want to move the next two calls into legacypeer and add
106 # unbundle instead.
106 # unbundle instead.
107
107
108 def lock(self):
108 def lock(self):
109 return self._repo.lock()
109 return self._repo.lock()
110
110
111 def addchangegroup(self, cg, source, url):
111 def addchangegroup(self, cg, source, url):
112 return self._repo.addchangegroup(cg, source, url)
112 return self._repo.addchangegroup(cg, source, url)
113
113
114 def pushkey(self, namespace, key, old, new):
114 def pushkey(self, namespace, key, old, new):
115 return self._repo.pushkey(namespace, key, old, new)
115 return self._repo.pushkey(namespace, key, old, new)
116
116
117 def listkeys(self, namespace):
117 def listkeys(self, namespace):
118 return self._repo.listkeys(namespace)
118 return self._repo.listkeys(namespace)
119
119
120 def debugwireargs(self, one, two, three=None, four=None, five=None):
120 def debugwireargs(self, one, two, three=None, four=None, five=None):
121 '''used to test argument passing over the wire'''
121 '''used to test argument passing over the wire'''
122 return "%s %s %s %s %s" % (one, two, three, four, five)
122 return "%s %s %s %s %s" % (one, two, three, four, five)
123
123
124 class locallegacypeer(localpeer):
124 class locallegacypeer(localpeer):
125 '''peer extension which implements legacy methods too; used for tests with
125 '''peer extension which implements legacy methods too; used for tests with
126 restricted capabilities'''
126 restricted capabilities'''
127
127
128 def __init__(self, repo):
128 def __init__(self, repo):
129 localpeer.__init__(self, repo, caps=LEGACYCAPS)
129 localpeer.__init__(self, repo, caps=LEGACYCAPS)
130
130
131 def branches(self, nodes):
131 def branches(self, nodes):
132 return self._repo.branches(nodes)
132 return self._repo.branches(nodes)
133
133
134 def between(self, pairs):
134 def between(self, pairs):
135 return self._repo.between(pairs)
135 return self._repo.between(pairs)
136
136
137 def changegroup(self, basenodes, source):
137 def changegroup(self, basenodes, source):
138 return self._repo.changegroup(basenodes, source)
138 return self._repo.changegroup(basenodes, source)
139
139
140 def changegroupsubset(self, bases, heads, source):
140 def changegroupsubset(self, bases, heads, source):
141 return self._repo.changegroupsubset(bases, heads, source)
141 return self._repo.changegroupsubset(bases, heads, source)
142
142
143 class localrepository(object):
143 class localrepository(object):
144
144
145 supportedformats = set(('revlogv1', 'generaldelta'))
145 supportedformats = set(('revlogv1', 'generaldelta'))
146 supported = supportedformats | set(('store', 'fncache', 'shared',
146 supported = supportedformats | set(('store', 'fncache', 'shared',
147 'dotencode'))
147 'dotencode'))
148 openerreqs = set(('revlogv1', 'generaldelta'))
148 openerreqs = set(('revlogv1', 'generaldelta'))
149 requirements = ['revlogv1']
149 requirements = ['revlogv1']
150 filtername = None
150 filtername = None
151
151
152 def _baserequirements(self, create):
152 def _baserequirements(self, create):
153 return self.requirements[:]
153 return self.requirements[:]
154
154
155 def __init__(self, baseui, path=None, create=False):
155 def __init__(self, baseui, path=None, create=False):
156 self.wvfs = scmutil.vfs(path, expandpath=True, realpath=True)
156 self.wvfs = scmutil.vfs(path, expandpath=True, realpath=True)
157 self.wopener = self.wvfs
157 self.wopener = self.wvfs
158 self.root = self.wvfs.base
158 self.root = self.wvfs.base
159 self.path = self.wvfs.join(".hg")
159 self.path = self.wvfs.join(".hg")
160 self.origroot = path
160 self.origroot = path
161 self.auditor = scmutil.pathauditor(self.root, self._checknested)
161 self.auditor = scmutil.pathauditor(self.root, self._checknested)
162 self.vfs = scmutil.vfs(self.path)
162 self.vfs = scmutil.vfs(self.path)
163 self.opener = self.vfs
163 self.opener = self.vfs
164 self.baseui = baseui
164 self.baseui = baseui
165 self.ui = baseui.copy()
165 self.ui = baseui.copy()
166 # A list of callback to shape the phase if no data were found.
166 # A list of callback to shape the phase if no data were found.
167 # Callback are in the form: func(repo, roots) --> processed root.
167 # Callback are in the form: func(repo, roots) --> processed root.
168 # This list it to be filled by extension during repo setup
168 # This list it to be filled by extension during repo setup
169 self._phasedefaults = []
169 self._phasedefaults = []
170 try:
170 try:
171 self.ui.readconfig(self.join("hgrc"), self.root)
171 self.ui.readconfig(self.join("hgrc"), self.root)
172 extensions.loadall(self.ui)
172 extensions.loadall(self.ui)
173 except IOError:
173 except IOError:
174 pass
174 pass
175
175
176 if not self.vfs.isdir():
176 if not self.vfs.isdir():
177 if create:
177 if create:
178 if not self.wvfs.exists():
178 if not self.wvfs.exists():
179 self.wvfs.makedirs()
179 self.wvfs.makedirs()
180 self.vfs.makedir(notindexed=True)
180 self.vfs.makedir(notindexed=True)
181 requirements = self._baserequirements(create)
181 requirements = self._baserequirements(create)
182 if self.ui.configbool('format', 'usestore', True):
182 if self.ui.configbool('format', 'usestore', True):
183 self.vfs.mkdir("store")
183 self.vfs.mkdir("store")
184 requirements.append("store")
184 requirements.append("store")
185 if self.ui.configbool('format', 'usefncache', True):
185 if self.ui.configbool('format', 'usefncache', True):
186 requirements.append("fncache")
186 requirements.append("fncache")
187 if self.ui.configbool('format', 'dotencode', True):
187 if self.ui.configbool('format', 'dotencode', True):
188 requirements.append('dotencode')
188 requirements.append('dotencode')
189 # create an invalid changelog
189 # create an invalid changelog
190 self.vfs.append(
190 self.vfs.append(
191 "00changelog.i",
191 "00changelog.i",
192 '\0\0\0\2' # represents revlogv2
192 '\0\0\0\2' # represents revlogv2
193 ' dummy changelog to prevent using the old repo layout'
193 ' dummy changelog to prevent using the old repo layout'
194 )
194 )
195 if self.ui.configbool('format', 'generaldelta', False):
195 if self.ui.configbool('format', 'generaldelta', False):
196 requirements.append("generaldelta")
196 requirements.append("generaldelta")
197 requirements = set(requirements)
197 requirements = set(requirements)
198 else:
198 else:
199 raise error.RepoError(_("repository %s not found") % path)
199 raise error.RepoError(_("repository %s not found") % path)
200 elif create:
200 elif create:
201 raise error.RepoError(_("repository %s already exists") % path)
201 raise error.RepoError(_("repository %s already exists") % path)
202 else:
202 else:
203 try:
203 try:
204 requirements = scmutil.readrequires(self.vfs, self.supported)
204 requirements = scmutil.readrequires(self.vfs, self.supported)
205 except IOError, inst:
205 except IOError, inst:
206 if inst.errno != errno.ENOENT:
206 if inst.errno != errno.ENOENT:
207 raise
207 raise
208 requirements = set()
208 requirements = set()
209
209
210 self.sharedpath = self.path
210 self.sharedpath = self.path
211 try:
211 try:
212 vfs = scmutil.vfs(self.vfs.read("sharedpath").rstrip('\n'),
212 vfs = scmutil.vfs(self.vfs.read("sharedpath").rstrip('\n'),
213 realpath=True)
213 realpath=True)
214 s = vfs.base
214 s = vfs.base
215 if not vfs.exists():
215 if not vfs.exists():
216 raise error.RepoError(
216 raise error.RepoError(
217 _('.hg/sharedpath points to nonexistent directory %s') % s)
217 _('.hg/sharedpath points to nonexistent directory %s') % s)
218 self.sharedpath = s
218 self.sharedpath = s
219 except IOError, inst:
219 except IOError, inst:
220 if inst.errno != errno.ENOENT:
220 if inst.errno != errno.ENOENT:
221 raise
221 raise
222
222
223 self.store = store.store(requirements, self.sharedpath, scmutil.vfs)
223 self.store = store.store(requirements, self.sharedpath, scmutil.vfs)
224 self.spath = self.store.path
224 self.spath = self.store.path
225 self.svfs = self.store.vfs
225 self.svfs = self.store.vfs
226 self.sopener = self.svfs
226 self.sopener = self.svfs
227 self.sjoin = self.store.join
227 self.sjoin = self.store.join
228 self.vfs.createmode = self.store.createmode
228 self.vfs.createmode = self.store.createmode
229 self._applyrequirements(requirements)
229 self._applyrequirements(requirements)
230 if create:
230 if create:
231 self._writerequirements()
231 self._writerequirements()
232
232
233
233
234 self._branchcaches = {}
234 self._branchcaches = {}
235 self.filterpats = {}
235 self.filterpats = {}
236 self._datafilters = {}
236 self._datafilters = {}
237 self._transref = self._lockref = self._wlockref = None
237 self._transref = self._lockref = self._wlockref = None
238
238
239 # A cache for various files under .hg/ that tracks file changes,
239 # A cache for various files under .hg/ that tracks file changes,
240 # (used by the filecache decorator)
240 # (used by the filecache decorator)
241 #
241 #
242 # Maps a property name to its util.filecacheentry
242 # Maps a property name to its util.filecacheentry
243 self._filecache = {}
243 self._filecache = {}
244
244
245 # hold sets of revision to be filtered
245 # hold sets of revision to be filtered
246 # should be cleared when something might have changed the filter value:
246 # should be cleared when something might have changed the filter value:
247 # - new changesets,
247 # - new changesets,
248 # - phase change,
248 # - phase change,
249 # - new obsolescence marker,
249 # - new obsolescence marker,
250 # - working directory parent change,
250 # - working directory parent change,
251 # - bookmark changes
251 # - bookmark changes
252 self.filteredrevcache = {}
252 self.filteredrevcache = {}
253
253
254 def close(self):
254 def close(self):
255 pass
255 pass
256
256
257 def _restrictcapabilities(self, caps):
257 def _restrictcapabilities(self, caps):
258 return caps
258 return caps
259
259
260 def _applyrequirements(self, requirements):
260 def _applyrequirements(self, requirements):
261 self.requirements = requirements
261 self.requirements = requirements
262 self.sopener.options = dict((r, 1) for r in requirements
262 self.sopener.options = dict((r, 1) for r in requirements
263 if r in self.openerreqs)
263 if r in self.openerreqs)
264
264
265 def _writerequirements(self):
265 def _writerequirements(self):
266 reqfile = self.opener("requires", "w")
266 reqfile = self.opener("requires", "w")
267 for r in sorted(self.requirements):
267 for r in sorted(self.requirements):
268 reqfile.write("%s\n" % r)
268 reqfile.write("%s\n" % r)
269 reqfile.close()
269 reqfile.close()
270
270
271 def _checknested(self, path):
271 def _checknested(self, path):
272 """Determine if path is a legal nested repository."""
272 """Determine if path is a legal nested repository."""
273 if not path.startswith(self.root):
273 if not path.startswith(self.root):
274 return False
274 return False
275 subpath = path[len(self.root) + 1:]
275 subpath = path[len(self.root) + 1:]
276 normsubpath = util.pconvert(subpath)
276 normsubpath = util.pconvert(subpath)
277
277
278 # XXX: Checking against the current working copy is wrong in
278 # XXX: Checking against the current working copy is wrong in
279 # the sense that it can reject things like
279 # the sense that it can reject things like
280 #
280 #
281 # $ hg cat -r 10 sub/x.txt
281 # $ hg cat -r 10 sub/x.txt
282 #
282 #
283 # if sub/ is no longer a subrepository in the working copy
283 # if sub/ is no longer a subrepository in the working copy
284 # parent revision.
284 # parent revision.
285 #
285 #
286 # However, it can of course also allow things that would have
286 # However, it can of course also allow things that would have
287 # been rejected before, such as the above cat command if sub/
287 # been rejected before, such as the above cat command if sub/
288 # is a subrepository now, but was a normal directory before.
288 # is a subrepository now, but was a normal directory before.
289 # The old path auditor would have rejected by mistake since it
289 # The old path auditor would have rejected by mistake since it
290 # panics when it sees sub/.hg/.
290 # panics when it sees sub/.hg/.
291 #
291 #
292 # All in all, checking against the working copy seems sensible
292 # All in all, checking against the working copy seems sensible
293 # since we want to prevent access to nested repositories on
293 # since we want to prevent access to nested repositories on
294 # the filesystem *now*.
294 # the filesystem *now*.
295 ctx = self[None]
295 ctx = self[None]
296 parts = util.splitpath(subpath)
296 parts = util.splitpath(subpath)
297 while parts:
297 while parts:
298 prefix = '/'.join(parts)
298 prefix = '/'.join(parts)
299 if prefix in ctx.substate:
299 if prefix in ctx.substate:
300 if prefix == normsubpath:
300 if prefix == normsubpath:
301 return True
301 return True
302 else:
302 else:
303 sub = ctx.sub(prefix)
303 sub = ctx.sub(prefix)
304 return sub.checknested(subpath[len(prefix) + 1:])
304 return sub.checknested(subpath[len(prefix) + 1:])
305 else:
305 else:
306 parts.pop()
306 parts.pop()
307 return False
307 return False
308
308
309 def peer(self):
309 def peer(self):
310 return localpeer(self) # not cached to avoid reference cycle
310 return localpeer(self) # not cached to avoid reference cycle
311
311
312 def unfiltered(self):
312 def unfiltered(self):
313 """Return unfiltered version of the repository
313 """Return unfiltered version of the repository
314
314
315 Intended to be overwritten by filtered repo."""
315 Intended to be overwritten by filtered repo."""
316 return self
316 return self
317
317
318 def filtered(self, name):
318 def filtered(self, name):
319 """Return a filtered version of a repository"""
319 """Return a filtered version of a repository"""
320 # build a new class with the mixin and the current class
320 # build a new class with the mixin and the current class
321 # (possibly subclass of the repo)
321 # (possibly subclass of the repo)
322 class proxycls(repoview.repoview, self.unfiltered().__class__):
322 class proxycls(repoview.repoview, self.unfiltered().__class__):
323 pass
323 pass
324 return proxycls(self, name)
324 return proxycls(self, name)
325
325
326 @repofilecache('bookmarks')
326 @repofilecache('bookmarks')
327 def _bookmarks(self):
327 def _bookmarks(self):
328 return bookmarks.bmstore(self)
328 return bookmarks.bmstore(self)
329
329
330 @repofilecache('bookmarks.current')
330 @repofilecache('bookmarks.current')
331 def _bookmarkcurrent(self):
331 def _bookmarkcurrent(self):
332 return bookmarks.readcurrent(self)
332 return bookmarks.readcurrent(self)
333
333
334 def bookmarkheads(self, bookmark):
334 def bookmarkheads(self, bookmark):
335 name = bookmark.split('@', 1)[0]
335 name = bookmark.split('@', 1)[0]
336 heads = []
336 heads = []
337 for mark, n in self._bookmarks.iteritems():
337 for mark, n in self._bookmarks.iteritems():
338 if mark.split('@', 1)[0] == name:
338 if mark.split('@', 1)[0] == name:
339 heads.append(n)
339 heads.append(n)
340 return heads
340 return heads
341
341
342 @storecache('phaseroots')
342 @storecache('phaseroots')
343 def _phasecache(self):
343 def _phasecache(self):
344 return phases.phasecache(self, self._phasedefaults)
344 return phases.phasecache(self, self._phasedefaults)
345
345
346 @storecache('obsstore')
346 @storecache('obsstore')
347 def obsstore(self):
347 def obsstore(self):
348 store = obsolete.obsstore(self.sopener)
348 store = obsolete.obsstore(self.sopener)
349 if store and not obsolete._enabled:
349 if store and not obsolete._enabled:
350 # message is rare enough to not be translated
350 # message is rare enough to not be translated
351 msg = 'obsolete feature not enabled but %i markers found!\n'
351 msg = 'obsolete feature not enabled but %i markers found!\n'
352 self.ui.warn(msg % len(list(store)))
352 self.ui.warn(msg % len(list(store)))
353 return store
353 return store
354
354
355 @storecache('00changelog.i')
355 @storecache('00changelog.i')
356 def changelog(self):
356 def changelog(self):
357 c = changelog.changelog(self.sopener)
357 c = changelog.changelog(self.sopener)
358 if 'HG_PENDING' in os.environ:
358 if 'HG_PENDING' in os.environ:
359 p = os.environ['HG_PENDING']
359 p = os.environ['HG_PENDING']
360 if p.startswith(self.root):
360 if p.startswith(self.root):
361 c.readpending('00changelog.i.a')
361 c.readpending('00changelog.i.a')
362 return c
362 return c
363
363
364 @storecache('00manifest.i')
364 @storecache('00manifest.i')
365 def manifest(self):
365 def manifest(self):
366 return manifest.manifest(self.sopener)
366 return manifest.manifest(self.sopener)
367
367
368 @repofilecache('dirstate')
368 @repofilecache('dirstate')
369 def dirstate(self):
369 def dirstate(self):
370 warned = [0]
370 warned = [0]
371 def validate(node):
371 def validate(node):
372 try:
372 try:
373 self.changelog.rev(node)
373 self.changelog.rev(node)
374 return node
374 return node
375 except error.LookupError:
375 except error.LookupError:
376 if not warned[0]:
376 if not warned[0]:
377 warned[0] = True
377 warned[0] = True
378 self.ui.warn(_("warning: ignoring unknown"
378 self.ui.warn(_("warning: ignoring unknown"
379 " working parent %s!\n") % short(node))
379 " working parent %s!\n") % short(node))
380 return nullid
380 return nullid
381
381
382 return dirstate.dirstate(self.opener, self.ui, self.root, validate)
382 return dirstate.dirstate(self.opener, self.ui, self.root, validate)
383
383
384 def __getitem__(self, changeid):
384 def __getitem__(self, changeid):
385 if changeid is None:
385 if changeid is None:
386 return context.workingctx(self)
386 return context.workingctx(self)
387 return context.changectx(self, changeid)
387 return context.changectx(self, changeid)
388
388
389 def __contains__(self, changeid):
389 def __contains__(self, changeid):
390 try:
390 try:
391 return bool(self.lookup(changeid))
391 return bool(self.lookup(changeid))
392 except error.RepoLookupError:
392 except error.RepoLookupError:
393 return False
393 return False
394
394
395 def __nonzero__(self):
395 def __nonzero__(self):
396 return True
396 return True
397
397
398 def __len__(self):
398 def __len__(self):
399 return len(self.changelog)
399 return len(self.changelog)
400
400
401 def __iter__(self):
401 def __iter__(self):
402 return iter(self.changelog)
402 return iter(self.changelog)
403
403
404 def revs(self, expr, *args):
404 def revs(self, expr, *args):
405 '''Return a list of revisions matching the given revset'''
405 '''Return a list of revisions matching the given revset'''
406 expr = revset.formatspec(expr, *args)
406 expr = revset.formatspec(expr, *args)
407 m = revset.match(None, expr)
407 m = revset.match(None, expr)
408 return [r for r in m(self, list(self))]
408 return [r for r in m(self, list(self))]
409
409
410 def set(self, expr, *args):
410 def set(self, expr, *args):
411 '''
411 '''
412 Yield a context for each matching revision, after doing arg
412 Yield a context for each matching revision, after doing arg
413 replacement via revset.formatspec
413 replacement via revset.formatspec
414 '''
414 '''
415 for r in self.revs(expr, *args):
415 for r in self.revs(expr, *args):
416 yield self[r]
416 yield self[r]
417
417
418 def url(self):
418 def url(self):
419 return 'file:' + self.root
419 return 'file:' + self.root
420
420
421 def hook(self, name, throw=False, **args):
421 def hook(self, name, throw=False, **args):
422 return hook.hook(self.ui, self, name, throw, **args)
422 return hook.hook(self.ui, self, name, throw, **args)
423
423
424 @unfilteredmethod
424 @unfilteredmethod
425 def _tag(self, names, node, message, local, user, date, extra={}):
425 def _tag(self, names, node, message, local, user, date, extra={}):
426 if isinstance(names, str):
426 if isinstance(names, str):
427 names = (names,)
427 names = (names,)
428
428
429 branches = self.branchmap()
429 branches = self.branchmap()
430 for name in names:
430 for name in names:
431 self.hook('pretag', throw=True, node=hex(node), tag=name,
431 self.hook('pretag', throw=True, node=hex(node), tag=name,
432 local=local)
432 local=local)
433 if name in branches:
433 if name in branches:
434 self.ui.warn(_("warning: tag %s conflicts with existing"
434 self.ui.warn(_("warning: tag %s conflicts with existing"
435 " branch name\n") % name)
435 " branch name\n") % name)
436
436
437 def writetags(fp, names, munge, prevtags):
437 def writetags(fp, names, munge, prevtags):
438 fp.seek(0, 2)
438 fp.seek(0, 2)
439 if prevtags and prevtags[-1] != '\n':
439 if prevtags and prevtags[-1] != '\n':
440 fp.write('\n')
440 fp.write('\n')
441 for name in names:
441 for name in names:
442 m = munge and munge(name) or name
442 m = munge and munge(name) or name
443 if (self._tagscache.tagtypes and
443 if (self._tagscache.tagtypes and
444 name in self._tagscache.tagtypes):
444 name in self._tagscache.tagtypes):
445 old = self.tags().get(name, nullid)
445 old = self.tags().get(name, nullid)
446 fp.write('%s %s\n' % (hex(old), m))
446 fp.write('%s %s\n' % (hex(old), m))
447 fp.write('%s %s\n' % (hex(node), m))
447 fp.write('%s %s\n' % (hex(node), m))
448 fp.close()
448 fp.close()
449
449
450 prevtags = ''
450 prevtags = ''
451 if local:
451 if local:
452 try:
452 try:
453 fp = self.opener('localtags', 'r+')
453 fp = self.opener('localtags', 'r+')
454 except IOError:
454 except IOError:
455 fp = self.opener('localtags', 'a')
455 fp = self.opener('localtags', 'a')
456 else:
456 else:
457 prevtags = fp.read()
457 prevtags = fp.read()
458
458
459 # local tags are stored in the current charset
459 # local tags are stored in the current charset
460 writetags(fp, names, None, prevtags)
460 writetags(fp, names, None, prevtags)
461 for name in names:
461 for name in names:
462 self.hook('tag', node=hex(node), tag=name, local=local)
462 self.hook('tag', node=hex(node), tag=name, local=local)
463 return
463 return
464
464
465 try:
465 try:
466 fp = self.wfile('.hgtags', 'rb+')
466 fp = self.wfile('.hgtags', 'rb+')
467 except IOError, e:
467 except IOError, e:
468 if e.errno != errno.ENOENT:
468 if e.errno != errno.ENOENT:
469 raise
469 raise
470 fp = self.wfile('.hgtags', 'ab')
470 fp = self.wfile('.hgtags', 'ab')
471 else:
471 else:
472 prevtags = fp.read()
472 prevtags = fp.read()
473
473
474 # committed tags are stored in UTF-8
474 # committed tags are stored in UTF-8
475 writetags(fp, names, encoding.fromlocal, prevtags)
475 writetags(fp, names, encoding.fromlocal, prevtags)
476
476
477 fp.close()
477 fp.close()
478
478
479 self.invalidatecaches()
479 self.invalidatecaches()
480
480
481 if '.hgtags' not in self.dirstate:
481 if '.hgtags' not in self.dirstate:
482 self[None].add(['.hgtags'])
482 self[None].add(['.hgtags'])
483
483
484 m = matchmod.exact(self.root, '', ['.hgtags'])
484 m = matchmod.exact(self.root, '', ['.hgtags'])
485 tagnode = self.commit(message, user, date, extra=extra, match=m)
485 tagnode = self.commit(message, user, date, extra=extra, match=m)
486
486
487 for name in names:
487 for name in names:
488 self.hook('tag', node=hex(node), tag=name, local=local)
488 self.hook('tag', node=hex(node), tag=name, local=local)
489
489
490 return tagnode
490 return tagnode
491
491
492 def tag(self, names, node, message, local, user, date):
492 def tag(self, names, node, message, local, user, date):
493 '''tag a revision with one or more symbolic names.
493 '''tag a revision with one or more symbolic names.
494
494
495 names is a list of strings or, when adding a single tag, names may be a
495 names is a list of strings or, when adding a single tag, names may be a
496 string.
496 string.
497
497
498 if local is True, the tags are stored in a per-repository file.
498 if local is True, the tags are stored in a per-repository file.
499 otherwise, they are stored in the .hgtags file, and a new
499 otherwise, they are stored in the .hgtags file, and a new
500 changeset is committed with the change.
500 changeset is committed with the change.
501
501
502 keyword arguments:
502 keyword arguments:
503
503
504 local: whether to store tags in non-version-controlled file
504 local: whether to store tags in non-version-controlled file
505 (default False)
505 (default False)
506
506
507 message: commit message to use if committing
507 message: commit message to use if committing
508
508
509 user: name of user to use if committing
509 user: name of user to use if committing
510
510
511 date: date tuple to use if committing'''
511 date: date tuple to use if committing'''
512
512
513 if not local:
513 if not local:
514 for x in self.status()[:5]:
514 for x in self.status()[:5]:
515 if '.hgtags' in x:
515 if '.hgtags' in x:
516 raise util.Abort(_('working copy of .hgtags is changed '
516 raise util.Abort(_('working copy of .hgtags is changed '
517 '(please commit .hgtags manually)'))
517 '(please commit .hgtags manually)'))
518
518
519 self.tags() # instantiate the cache
519 self.tags() # instantiate the cache
520 self._tag(names, node, message, local, user, date)
520 self._tag(names, node, message, local, user, date)
521
521
522 @filteredpropertycache
522 @filteredpropertycache
523 def _tagscache(self):
523 def _tagscache(self):
524 '''Returns a tagscache object that contains various tags related
524 '''Returns a tagscache object that contains various tags related
525 caches.'''
525 caches.'''
526
526
527 # This simplifies its cache management by having one decorated
527 # This simplifies its cache management by having one decorated
528 # function (this one) and the rest simply fetch things from it.
528 # function (this one) and the rest simply fetch things from it.
529 class tagscache(object):
529 class tagscache(object):
530 def __init__(self):
530 def __init__(self):
531 # These two define the set of tags for this repository. tags
531 # These two define the set of tags for this repository. tags
532 # maps tag name to node; tagtypes maps tag name to 'global' or
532 # maps tag name to node; tagtypes maps tag name to 'global' or
533 # 'local'. (Global tags are defined by .hgtags across all
533 # 'local'. (Global tags are defined by .hgtags across all
534 # heads, and local tags are defined in .hg/localtags.)
534 # heads, and local tags are defined in .hg/localtags.)
535 # They constitute the in-memory cache of tags.
535 # They constitute the in-memory cache of tags.
536 self.tags = self.tagtypes = None
536 self.tags = self.tagtypes = None
537
537
538 self.nodetagscache = self.tagslist = None
538 self.nodetagscache = self.tagslist = None
539
539
540 cache = tagscache()
540 cache = tagscache()
541 cache.tags, cache.tagtypes = self._findtags()
541 cache.tags, cache.tagtypes = self._findtags()
542
542
543 return cache
543 return cache
544
544
545 def tags(self):
545 def tags(self):
546 '''return a mapping of tag to node'''
546 '''return a mapping of tag to node'''
547 t = {}
547 t = {}
548 if self.changelog.filteredrevs:
548 if self.changelog.filteredrevs:
549 tags, tt = self._findtags()
549 tags, tt = self._findtags()
550 else:
550 else:
551 tags = self._tagscache.tags
551 tags = self._tagscache.tags
552 for k, v in tags.iteritems():
552 for k, v in tags.iteritems():
553 try:
553 try:
554 # ignore tags to unknown nodes
554 # ignore tags to unknown nodes
555 self.changelog.rev(v)
555 self.changelog.rev(v)
556 t[k] = v
556 t[k] = v
557 except (error.LookupError, ValueError):
557 except (error.LookupError, ValueError):
558 pass
558 pass
559 return t
559 return t
560
560
561 def _findtags(self):
561 def _findtags(self):
562 '''Do the hard work of finding tags. Return a pair of dicts
562 '''Do the hard work of finding tags. Return a pair of dicts
563 (tags, tagtypes) where tags maps tag name to node, and tagtypes
563 (tags, tagtypes) where tags maps tag name to node, and tagtypes
564 maps tag name to a string like \'global\' or \'local\'.
564 maps tag name to a string like \'global\' or \'local\'.
565 Subclasses or extensions are free to add their own tags, but
565 Subclasses or extensions are free to add their own tags, but
566 should be aware that the returned dicts will be retained for the
566 should be aware that the returned dicts will be retained for the
567 duration of the localrepo object.'''
567 duration of the localrepo object.'''
568
568
569 # XXX what tagtype should subclasses/extensions use? Currently
569 # XXX what tagtype should subclasses/extensions use? Currently
570 # mq and bookmarks add tags, but do not set the tagtype at all.
570 # mq and bookmarks add tags, but do not set the tagtype at all.
571 # Should each extension invent its own tag type? Should there
571 # Should each extension invent its own tag type? Should there
572 # be one tagtype for all such "virtual" tags? Or is the status
572 # be one tagtype for all such "virtual" tags? Or is the status
573 # quo fine?
573 # quo fine?
574
574
575 alltags = {} # map tag name to (node, hist)
575 alltags = {} # map tag name to (node, hist)
576 tagtypes = {}
576 tagtypes = {}
577
577
578 tagsmod.findglobaltags(self.ui, self, alltags, tagtypes)
578 tagsmod.findglobaltags(self.ui, self, alltags, tagtypes)
579 tagsmod.readlocaltags(self.ui, self, alltags, tagtypes)
579 tagsmod.readlocaltags(self.ui, self, alltags, tagtypes)
580
580
581 # Build the return dicts. Have to re-encode tag names because
581 # Build the return dicts. Have to re-encode tag names because
582 # the tags module always uses UTF-8 (in order not to lose info
582 # the tags module always uses UTF-8 (in order not to lose info
583 # writing to the cache), but the rest of Mercurial wants them in
583 # writing to the cache), but the rest of Mercurial wants them in
584 # local encoding.
584 # local encoding.
585 tags = {}
585 tags = {}
586 for (name, (node, hist)) in alltags.iteritems():
586 for (name, (node, hist)) in alltags.iteritems():
587 if node != nullid:
587 if node != nullid:
588 tags[encoding.tolocal(name)] = node
588 tags[encoding.tolocal(name)] = node
589 tags['tip'] = self.changelog.tip()
589 tags['tip'] = self.changelog.tip()
590 tagtypes = dict([(encoding.tolocal(name), value)
590 tagtypes = dict([(encoding.tolocal(name), value)
591 for (name, value) in tagtypes.iteritems()])
591 for (name, value) in tagtypes.iteritems()])
592 return (tags, tagtypes)
592 return (tags, tagtypes)
593
593
594 def tagtype(self, tagname):
594 def tagtype(self, tagname):
595 '''
595 '''
596 return the type of the given tag. result can be:
596 return the type of the given tag. result can be:
597
597
598 'local' : a local tag
598 'local' : a local tag
599 'global' : a global tag
599 'global' : a global tag
600 None : tag does not exist
600 None : tag does not exist
601 '''
601 '''
602
602
603 return self._tagscache.tagtypes.get(tagname)
603 return self._tagscache.tagtypes.get(tagname)
604
604
605 def tagslist(self):
605 def tagslist(self):
606 '''return a list of tags ordered by revision'''
606 '''return a list of tags ordered by revision'''
607 if not self._tagscache.tagslist:
607 if not self._tagscache.tagslist:
608 l = []
608 l = []
609 for t, n in self.tags().iteritems():
609 for t, n in self.tags().iteritems():
610 r = self.changelog.rev(n)
610 r = self.changelog.rev(n)
611 l.append((r, t, n))
611 l.append((r, t, n))
612 self._tagscache.tagslist = [(t, n) for r, t, n in sorted(l)]
612 self._tagscache.tagslist = [(t, n) for r, t, n in sorted(l)]
613
613
614 return self._tagscache.tagslist
614 return self._tagscache.tagslist
615
615
616 def nodetags(self, node):
616 def nodetags(self, node):
617 '''return the tags associated with a node'''
617 '''return the tags associated with a node'''
618 if not self._tagscache.nodetagscache:
618 if not self._tagscache.nodetagscache:
619 nodetagscache = {}
619 nodetagscache = {}
620 for t, n in self._tagscache.tags.iteritems():
620 for t, n in self._tagscache.tags.iteritems():
621 nodetagscache.setdefault(n, []).append(t)
621 nodetagscache.setdefault(n, []).append(t)
622 for tags in nodetagscache.itervalues():
622 for tags in nodetagscache.itervalues():
623 tags.sort()
623 tags.sort()
624 self._tagscache.nodetagscache = nodetagscache
624 self._tagscache.nodetagscache = nodetagscache
625 return self._tagscache.nodetagscache.get(node, [])
625 return self._tagscache.nodetagscache.get(node, [])
626
626
627 def nodebookmarks(self, node):
627 def nodebookmarks(self, node):
628 marks = []
628 marks = []
629 for bookmark, n in self._bookmarks.iteritems():
629 for bookmark, n in self._bookmarks.iteritems():
630 if n == node:
630 if n == node:
631 marks.append(bookmark)
631 marks.append(bookmark)
632 return sorted(marks)
632 return sorted(marks)
633
633
634 def branchmap(self):
634 def branchmap(self):
635 '''returns a dictionary {branch: [branchheads]}'''
635 '''returns a dictionary {branch: [branchheads]}'''
636 branchmap.updatecache(self)
636 branchmap.updatecache(self)
637 return self._branchcaches[self.filtername]
637 return self._branchcaches[self.filtername]
638
638
639
639
640 def _branchtip(self, heads):
640 def _branchtip(self, heads):
641 '''return the tipmost branch head in heads'''
641 '''return the tipmost branch head in heads'''
642 tip = heads[-1]
642 tip = heads[-1]
643 for h in reversed(heads):
643 for h in reversed(heads):
644 if not self[h].closesbranch():
644 if not self[h].closesbranch():
645 tip = h
645 tip = h
646 break
646 break
647 return tip
647 return tip
648
648
649 def branchtip(self, branch):
649 def branchtip(self, branch):
650 '''return the tip node for a given branch'''
650 '''return the tip node for a given branch'''
651 if branch not in self.branchmap():
651 if branch not in self.branchmap():
652 raise error.RepoLookupError(_("unknown branch '%s'") % branch)
652 raise error.RepoLookupError(_("unknown branch '%s'") % branch)
653 return self._branchtip(self.branchmap()[branch])
653 return self._branchtip(self.branchmap()[branch])
654
654
655 def branchtags(self):
655 def branchtags(self):
656 '''return a dict where branch names map to the tipmost head of
656 '''return a dict where branch names map to the tipmost head of
657 the branch, open heads come before closed'''
657 the branch, open heads come before closed'''
658 bt = {}
658 bt = {}
659 for bn, heads in self.branchmap().iteritems():
659 for bn, heads in self.branchmap().iteritems():
660 bt[bn] = self._branchtip(heads)
660 bt[bn] = self._branchtip(heads)
661 return bt
661 return bt
662
662
663 def lookup(self, key):
663 def lookup(self, key):
664 return self[key].node()
664 return self[key].node()
665
665
666 def lookupbranch(self, key, remote=None):
666 def lookupbranch(self, key, remote=None):
667 repo = remote or self
667 repo = remote or self
668 if key in repo.branchmap():
668 if key in repo.branchmap():
669 return key
669 return key
670
670
671 repo = (remote and remote.local()) and remote or self
671 repo = (remote and remote.local()) and remote or self
672 return repo[key].branch()
672 return repo[key].branch()
673
673
674 def known(self, nodes):
674 def known(self, nodes):
675 nm = self.changelog.nodemap
675 nm = self.changelog.nodemap
676 pc = self._phasecache
676 pc = self._phasecache
677 result = []
677 result = []
678 for n in nodes:
678 for n in nodes:
679 r = nm.get(n)
679 r = nm.get(n)
680 resp = not (r is None or pc.phase(self, r) >= phases.secret)
680 resp = not (r is None or pc.phase(self, r) >= phases.secret)
681 result.append(resp)
681 result.append(resp)
682 return result
682 return result
683
683
684 def local(self):
684 def local(self):
685 return self
685 return self
686
686
687 def cancopy(self):
687 def cancopy(self):
688 return self.local() # so statichttprepo's override of local() works
688 return self.local() # so statichttprepo's override of local() works
689
689
690 def join(self, f):
690 def join(self, f):
691 return os.path.join(self.path, f)
691 return os.path.join(self.path, f)
692
692
693 def wjoin(self, f):
693 def wjoin(self, f):
694 return os.path.join(self.root, f)
694 return os.path.join(self.root, f)
695
695
696 def file(self, f):
696 def file(self, f):
697 if f[0] == '/':
697 if f[0] == '/':
698 f = f[1:]
698 f = f[1:]
699 return filelog.filelog(self.sopener, f)
699 return filelog.filelog(self.sopener, f)
700
700
701 def changectx(self, changeid):
701 def changectx(self, changeid):
702 return self[changeid]
702 return self[changeid]
703
703
704 def parents(self, changeid=None):
704 def parents(self, changeid=None):
705 '''get list of changectxs for parents of changeid'''
705 '''get list of changectxs for parents of changeid'''
706 return self[changeid].parents()
706 return self[changeid].parents()
707
707
708 def setparents(self, p1, p2=nullid):
708 def setparents(self, p1, p2=nullid):
709 copies = self.dirstate.setparents(p1, p2)
709 copies = self.dirstate.setparents(p1, p2)
710 pctx = self[p1]
710 pctx = self[p1]
711 if copies:
711 if copies:
712 # Adjust copy records, the dirstate cannot do it, it
712 # Adjust copy records, the dirstate cannot do it, it
713 # requires access to parents manifests. Preserve them
713 # requires access to parents manifests. Preserve them
714 # only for entries added to first parent.
714 # only for entries added to first parent.
715 for f in copies:
715 for f in copies:
716 if f not in pctx and copies[f] in pctx:
716 if f not in pctx and copies[f] in pctx:
717 self.dirstate.copy(copies[f], f)
717 self.dirstate.copy(copies[f], f)
718 if p2 == nullid:
718 if p2 == nullid:
719 for f, s in sorted(self.dirstate.copies().items()):
719 for f, s in sorted(self.dirstate.copies().items()):
720 if f not in pctx and s not in pctx:
720 if f not in pctx and s not in pctx:
721 self.dirstate.copy(None, f)
721 self.dirstate.copy(None, f)
722
722
723 def filectx(self, path, changeid=None, fileid=None):
723 def filectx(self, path, changeid=None, fileid=None):
724 """changeid can be a changeset revision, node, or tag.
724 """changeid can be a changeset revision, node, or tag.
725 fileid can be a file revision or node."""
725 fileid can be a file revision or node."""
726 return context.filectx(self, path, changeid, fileid)
726 return context.filectx(self, path, changeid, fileid)
727
727
728 def getcwd(self):
728 def getcwd(self):
729 return self.dirstate.getcwd()
729 return self.dirstate.getcwd()
730
730
731 def pathto(self, f, cwd=None):
731 def pathto(self, f, cwd=None):
732 return self.dirstate.pathto(f, cwd)
732 return self.dirstate.pathto(f, cwd)
733
733
734 def wfile(self, f, mode='r'):
734 def wfile(self, f, mode='r'):
735 return self.wopener(f, mode)
735 return self.wopener(f, mode)
736
736
737 def _link(self, f):
737 def _link(self, f):
738 return self.wvfs.islink(f)
738 return self.wvfs.islink(f)
739
739
740 def _loadfilter(self, filter):
740 def _loadfilter(self, filter):
741 if filter not in self.filterpats:
741 if filter not in self.filterpats:
742 l = []
742 l = []
743 for pat, cmd in self.ui.configitems(filter):
743 for pat, cmd in self.ui.configitems(filter):
744 if cmd == '!':
744 if cmd == '!':
745 continue
745 continue
746 mf = matchmod.match(self.root, '', [pat])
746 mf = matchmod.match(self.root, '', [pat])
747 fn = None
747 fn = None
748 params = cmd
748 params = cmd
749 for name, filterfn in self._datafilters.iteritems():
749 for name, filterfn in self._datafilters.iteritems():
750 if cmd.startswith(name):
750 if cmd.startswith(name):
751 fn = filterfn
751 fn = filterfn
752 params = cmd[len(name):].lstrip()
752 params = cmd[len(name):].lstrip()
753 break
753 break
754 if not fn:
754 if not fn:
755 fn = lambda s, c, **kwargs: util.filter(s, c)
755 fn = lambda s, c, **kwargs: util.filter(s, c)
756 # Wrap old filters not supporting keyword arguments
756 # Wrap old filters not supporting keyword arguments
757 if not inspect.getargspec(fn)[2]:
757 if not inspect.getargspec(fn)[2]:
758 oldfn = fn
758 oldfn = fn
759 fn = lambda s, c, **kwargs: oldfn(s, c)
759 fn = lambda s, c, **kwargs: oldfn(s, c)
760 l.append((mf, fn, params))
760 l.append((mf, fn, params))
761 self.filterpats[filter] = l
761 self.filterpats[filter] = l
762 return self.filterpats[filter]
762 return self.filterpats[filter]
763
763
764 def _filter(self, filterpats, filename, data):
764 def _filter(self, filterpats, filename, data):
765 for mf, fn, cmd in filterpats:
765 for mf, fn, cmd in filterpats:
766 if mf(filename):
766 if mf(filename):
767 self.ui.debug("filtering %s through %s\n" % (filename, cmd))
767 self.ui.debug("filtering %s through %s\n" % (filename, cmd))
768 data = fn(data, cmd, ui=self.ui, repo=self, filename=filename)
768 data = fn(data, cmd, ui=self.ui, repo=self, filename=filename)
769 break
769 break
770
770
771 return data
771 return data
772
772
773 @unfilteredpropertycache
773 @unfilteredpropertycache
774 def _encodefilterpats(self):
774 def _encodefilterpats(self):
775 return self._loadfilter('encode')
775 return self._loadfilter('encode')
776
776
777 @unfilteredpropertycache
777 @unfilteredpropertycache
778 def _decodefilterpats(self):
778 def _decodefilterpats(self):
779 return self._loadfilter('decode')
779 return self._loadfilter('decode')
780
780
781 def adddatafilter(self, name, filter):
781 def adddatafilter(self, name, filter):
782 self._datafilters[name] = filter
782 self._datafilters[name] = filter
783
783
784 def wread(self, filename):
784 def wread(self, filename):
785 if self._link(filename):
785 if self._link(filename):
786 data = self.wvfs.readlink(filename)
786 data = self.wvfs.readlink(filename)
787 else:
787 else:
788 data = self.wopener.read(filename)
788 data = self.wopener.read(filename)
789 return self._filter(self._encodefilterpats, filename, data)
789 return self._filter(self._encodefilterpats, filename, data)
790
790
791 def wwrite(self, filename, data, flags):
791 def wwrite(self, filename, data, flags):
792 data = self._filter(self._decodefilterpats, filename, data)
792 data = self._filter(self._decodefilterpats, filename, data)
793 if 'l' in flags:
793 if 'l' in flags:
794 self.wopener.symlink(data, filename)
794 self.wopener.symlink(data, filename)
795 else:
795 else:
796 self.wopener.write(filename, data)
796 self.wopener.write(filename, data)
797 if 'x' in flags:
797 if 'x' in flags:
798 self.wvfs.setflags(filename, False, True)
798 self.wvfs.setflags(filename, False, True)
799
799
800 def wwritedata(self, filename, data):
800 def wwritedata(self, filename, data):
801 return self._filter(self._decodefilterpats, filename, data)
801 return self._filter(self._decodefilterpats, filename, data)
802
802
803 def transaction(self, desc):
803 def transaction(self, desc):
804 tr = self._transref and self._transref() or None
804 tr = self._transref and self._transref() or None
805 if tr and tr.running():
805 if tr and tr.running():
806 return tr.nest()
806 return tr.nest()
807
807
808 # abort here if the journal already exists
808 # abort here if the journal already exists
809 if self.svfs.exists("journal"):
809 if self.svfs.exists("journal"):
810 raise error.RepoError(
810 raise error.RepoError(
811 _("abandoned transaction found - run hg recover"))
811 _("abandoned transaction found - run hg recover"))
812
812
813 self._writejournal(desc)
813 self._writejournal(desc)
814 renames = [(vfs, x, undoname(x)) for vfs, x in self._journalfiles()]
814 renames = [(vfs, x, undoname(x)) for vfs, x in self._journalfiles()]
815
815
816 tr = transaction.transaction(self.ui.warn, self.sopener,
816 tr = transaction.transaction(self.ui.warn, self.sopener,
817 self.sjoin("journal"),
817 self.sjoin("journal"),
818 aftertrans(renames),
818 aftertrans(renames),
819 self.store.createmode)
819 self.store.createmode)
820 self._transref = weakref.ref(tr)
820 self._transref = weakref.ref(tr)
821 return tr
821 return tr
822
822
823 def _journalfiles(self):
823 def _journalfiles(self):
824 return ((self.svfs, 'journal'),
824 return ((self.svfs, 'journal'),
825 (self.vfs, 'journal.dirstate'),
825 (self.vfs, 'journal.dirstate'),
826 (self.vfs, 'journal.branch'),
826 (self.vfs, 'journal.branch'),
827 (self.vfs, 'journal.desc'),
827 (self.vfs, 'journal.desc'),
828 (self.vfs, 'journal.bookmarks'),
828 (self.vfs, 'journal.bookmarks'),
829 (self.svfs, 'journal.phaseroots'))
829 (self.svfs, 'journal.phaseroots'))
830
830
831 def undofiles(self):
831 def undofiles(self):
832 return [vfs.join(undoname(x)) for vfs, x in self._journalfiles()]
832 return [vfs.join(undoname(x)) for vfs, x in self._journalfiles()]
833
833
834 def _writejournal(self, desc):
834 def _writejournal(self, desc):
835 self.opener.write("journal.dirstate",
835 self.opener.write("journal.dirstate",
836 self.opener.tryread("dirstate"))
836 self.opener.tryread("dirstate"))
837 self.opener.write("journal.branch",
837 self.opener.write("journal.branch",
838 encoding.fromlocal(self.dirstate.branch()))
838 encoding.fromlocal(self.dirstate.branch()))
839 self.opener.write("journal.desc",
839 self.opener.write("journal.desc",
840 "%d\n%s\n" % (len(self), desc))
840 "%d\n%s\n" % (len(self), desc))
841 self.opener.write("journal.bookmarks",
841 self.opener.write("journal.bookmarks",
842 self.opener.tryread("bookmarks"))
842 self.opener.tryread("bookmarks"))
843 self.sopener.write("journal.phaseroots",
843 self.sopener.write("journal.phaseroots",
844 self.sopener.tryread("phaseroots"))
844 self.sopener.tryread("phaseroots"))
845
845
846 def recover(self):
846 def recover(self):
847 lock = self.lock()
847 lock = self.lock()
848 try:
848 try:
849 if self.svfs.exists("journal"):
849 if self.svfs.exists("journal"):
850 self.ui.status(_("rolling back interrupted transaction\n"))
850 self.ui.status(_("rolling back interrupted transaction\n"))
851 transaction.rollback(self.sopener, self.sjoin("journal"),
851 transaction.rollback(self.sopener, self.sjoin("journal"),
852 self.ui.warn)
852 self.ui.warn)
853 self.invalidate()
853 self.invalidate()
854 return True
854 return True
855 else:
855 else:
856 self.ui.warn(_("no interrupted transaction available\n"))
856 self.ui.warn(_("no interrupted transaction available\n"))
857 return False
857 return False
858 finally:
858 finally:
859 lock.release()
859 lock.release()
860
860
861 def rollback(self, dryrun=False, force=False):
861 def rollback(self, dryrun=False, force=False):
862 wlock = lock = None
862 wlock = lock = None
863 try:
863 try:
864 wlock = self.wlock()
864 wlock = self.wlock()
865 lock = self.lock()
865 lock = self.lock()
866 if self.svfs.exists("undo"):
866 if self.svfs.exists("undo"):
867 return self._rollback(dryrun, force)
867 return self._rollback(dryrun, force)
868 else:
868 else:
869 self.ui.warn(_("no rollback information available\n"))
869 self.ui.warn(_("no rollback information available\n"))
870 return 1
870 return 1
871 finally:
871 finally:
872 release(lock, wlock)
872 release(lock, wlock)
873
873
874 @unfilteredmethod # Until we get smarter cache management
874 @unfilteredmethod # Until we get smarter cache management
875 def _rollback(self, dryrun, force):
875 def _rollback(self, dryrun, force):
876 ui = self.ui
876 ui = self.ui
877 try:
877 try:
878 args = self.opener.read('undo.desc').splitlines()
878 args = self.opener.read('undo.desc').splitlines()
879 (oldlen, desc, detail) = (int(args[0]), args[1], None)
879 (oldlen, desc, detail) = (int(args[0]), args[1], None)
880 if len(args) >= 3:
880 if len(args) >= 3:
881 detail = args[2]
881 detail = args[2]
882 oldtip = oldlen - 1
882 oldtip = oldlen - 1
883
883
884 if detail and ui.verbose:
884 if detail and ui.verbose:
885 msg = (_('repository tip rolled back to revision %s'
885 msg = (_('repository tip rolled back to revision %s'
886 ' (undo %s: %s)\n')
886 ' (undo %s: %s)\n')
887 % (oldtip, desc, detail))
887 % (oldtip, desc, detail))
888 else:
888 else:
889 msg = (_('repository tip rolled back to revision %s'
889 msg = (_('repository tip rolled back to revision %s'
890 ' (undo %s)\n')
890 ' (undo %s)\n')
891 % (oldtip, desc))
891 % (oldtip, desc))
892 except IOError:
892 except IOError:
893 msg = _('rolling back unknown transaction\n')
893 msg = _('rolling back unknown transaction\n')
894 desc = None
894 desc = None
895
895
896 if not force and self['.'] != self['tip'] and desc == 'commit':
896 if not force and self['.'] != self['tip'] and desc == 'commit':
897 raise util.Abort(
897 raise util.Abort(
898 _('rollback of last commit while not checked out '
898 _('rollback of last commit while not checked out '
899 'may lose data'), hint=_('use -f to force'))
899 'may lose data'), hint=_('use -f to force'))
900
900
901 ui.status(msg)
901 ui.status(msg)
902 if dryrun:
902 if dryrun:
903 return 0
903 return 0
904
904
905 parents = self.dirstate.parents()
905 parents = self.dirstate.parents()
906 self.destroying()
906 self.destroying()
907 transaction.rollback(self.sopener, self.sjoin('undo'), ui.warn)
907 transaction.rollback(self.sopener, self.sjoin('undo'), ui.warn)
908 if self.vfs.exists('undo.bookmarks'):
908 if self.vfs.exists('undo.bookmarks'):
909 self.vfs.rename('undo.bookmarks', 'bookmarks')
909 self.vfs.rename('undo.bookmarks', 'bookmarks')
910 if self.svfs.exists('undo.phaseroots'):
910 if self.svfs.exists('undo.phaseroots'):
911 self.svfs.rename('undo.phaseroots', 'phaseroots')
911 self.svfs.rename('undo.phaseroots', 'phaseroots')
912 self.invalidate()
912 self.invalidate()
913
913
914 parentgone = (parents[0] not in self.changelog.nodemap or
914 parentgone = (parents[0] not in self.changelog.nodemap or
915 parents[1] not in self.changelog.nodemap)
915 parents[1] not in self.changelog.nodemap)
916 if parentgone:
916 if parentgone:
917 self.vfs.rename('undo.dirstate', 'dirstate')
917 self.vfs.rename('undo.dirstate', 'dirstate')
918 try:
918 try:
919 branch = self.opener.read('undo.branch')
919 branch = self.opener.read('undo.branch')
920 self.dirstate.setbranch(encoding.tolocal(branch))
920 self.dirstate.setbranch(encoding.tolocal(branch))
921 except IOError:
921 except IOError:
922 ui.warn(_('named branch could not be reset: '
922 ui.warn(_('named branch could not be reset: '
923 'current branch is still \'%s\'\n')
923 'current branch is still \'%s\'\n')
924 % self.dirstate.branch())
924 % self.dirstate.branch())
925
925
926 self.dirstate.invalidate()
926 self.dirstate.invalidate()
927 parents = tuple([p.rev() for p in self.parents()])
927 parents = tuple([p.rev() for p in self.parents()])
928 if len(parents) > 1:
928 if len(parents) > 1:
929 ui.status(_('working directory now based on '
929 ui.status(_('working directory now based on '
930 'revisions %d and %d\n') % parents)
930 'revisions %d and %d\n') % parents)
931 else:
931 else:
932 ui.status(_('working directory now based on '
932 ui.status(_('working directory now based on '
933 'revision %d\n') % parents)
933 'revision %d\n') % parents)
934 # TODO: if we know which new heads may result from this rollback, pass
934 # TODO: if we know which new heads may result from this rollback, pass
935 # them to destroy(), which will prevent the branchhead cache from being
935 # them to destroy(), which will prevent the branchhead cache from being
936 # invalidated.
936 # invalidated.
937 self.destroyed()
937 self.destroyed()
938 return 0
938 return 0
939
939
940 def invalidatecaches(self):
940 def invalidatecaches(self):
941
941
942 if '_tagscache' in vars(self):
942 if '_tagscache' in vars(self):
943 # can't use delattr on proxy
943 # can't use delattr on proxy
944 del self.__dict__['_tagscache']
944 del self.__dict__['_tagscache']
945
945
946 self.unfiltered()._branchcaches.clear()
946 self.unfiltered()._branchcaches.clear()
947 self.invalidatevolatilesets()
947 self.invalidatevolatilesets()
948
948
949 def invalidatevolatilesets(self):
949 def invalidatevolatilesets(self):
950 self.filteredrevcache.clear()
950 self.filteredrevcache.clear()
951 obsolete.clearobscaches(self)
951 obsolete.clearobscaches(self)
952
952
953 def invalidatedirstate(self):
953 def invalidatedirstate(self):
954 '''Invalidates the dirstate, causing the next call to dirstate
954 '''Invalidates the dirstate, causing the next call to dirstate
955 to check if it was modified since the last time it was read,
955 to check if it was modified since the last time it was read,
956 rereading it if it has.
956 rereading it if it has.
957
957
958 This is different to dirstate.invalidate() that it doesn't always
958 This is different to dirstate.invalidate() that it doesn't always
959 rereads the dirstate. Use dirstate.invalidate() if you want to
959 rereads the dirstate. Use dirstate.invalidate() if you want to
960 explicitly read the dirstate again (i.e. restoring it to a previous
960 explicitly read the dirstate again (i.e. restoring it to a previous
961 known good state).'''
961 known good state).'''
962 if hasunfilteredcache(self, 'dirstate'):
962 if hasunfilteredcache(self, 'dirstate'):
963 for k in self.dirstate._filecache:
963 for k in self.dirstate._filecache:
964 try:
964 try:
965 delattr(self.dirstate, k)
965 delattr(self.dirstate, k)
966 except AttributeError:
966 except AttributeError:
967 pass
967 pass
968 delattr(self.unfiltered(), 'dirstate')
968 delattr(self.unfiltered(), 'dirstate')
969
969
970 def invalidate(self):
970 def invalidate(self):
971 unfiltered = self.unfiltered() # all file caches are stored unfiltered
971 unfiltered = self.unfiltered() # all file caches are stored unfiltered
972 for k in self._filecache:
972 for k in self._filecache:
973 # dirstate is invalidated separately in invalidatedirstate()
973 # dirstate is invalidated separately in invalidatedirstate()
974 if k == 'dirstate':
974 if k == 'dirstate':
975 continue
975 continue
976
976
977 try:
977 try:
978 delattr(unfiltered, k)
978 delattr(unfiltered, k)
979 except AttributeError:
979 except AttributeError:
980 pass
980 pass
981 self.invalidatecaches()
981 self.invalidatecaches()
982
982
983 def _lock(self, lockname, wait, releasefn, acquirefn, desc):
983 def _lock(self, lockname, wait, releasefn, acquirefn, desc):
984 try:
984 try:
985 l = lock.lock(lockname, 0, releasefn, desc=desc)
985 l = lock.lock(lockname, 0, releasefn, desc=desc)
986 except error.LockHeld, inst:
986 except error.LockHeld, inst:
987 if not wait:
987 if not wait:
988 raise
988 raise
989 self.ui.warn(_("waiting for lock on %s held by %r\n") %
989 self.ui.warn(_("waiting for lock on %s held by %r\n") %
990 (desc, inst.locker))
990 (desc, inst.locker))
991 # default to 600 seconds timeout
991 # default to 600 seconds timeout
992 l = lock.lock(lockname, int(self.ui.config("ui", "timeout", "600")),
992 l = lock.lock(lockname, int(self.ui.config("ui", "timeout", "600")),
993 releasefn, desc=desc)
993 releasefn, desc=desc)
994 if acquirefn:
994 if acquirefn:
995 acquirefn()
995 acquirefn()
996 return l
996 return l
997
997
998 def _afterlock(self, callback):
998 def _afterlock(self, callback):
999 """add a callback to the current repository lock.
999 """add a callback to the current repository lock.
1000
1000
1001 The callback will be executed on lock release."""
1001 The callback will be executed on lock release."""
1002 l = self._lockref and self._lockref()
1002 l = self._lockref and self._lockref()
1003 if l:
1003 if l:
1004 l.postrelease.append(callback)
1004 l.postrelease.append(callback)
1005 else:
1005 else:
1006 callback()
1006 callback()
1007
1007
1008 def lock(self, wait=True):
1008 def lock(self, wait=True):
1009 '''Lock the repository store (.hg/store) and return a weak reference
1009 '''Lock the repository store (.hg/store) and return a weak reference
1010 to the lock. Use this before modifying the store (e.g. committing or
1010 to the lock. Use this before modifying the store (e.g. committing or
1011 stripping). If you are opening a transaction, get a lock as well.)'''
1011 stripping). If you are opening a transaction, get a lock as well.)'''
1012 l = self._lockref and self._lockref()
1012 l = self._lockref and self._lockref()
1013 if l is not None and l.held:
1013 if l is not None and l.held:
1014 l.lock()
1014 l.lock()
1015 return l
1015 return l
1016
1016
1017 def unlock():
1017 def unlock():
1018 self.store.write()
1018 self.store.write()
1019 if hasunfilteredcache(self, '_phasecache'):
1019 if hasunfilteredcache(self, '_phasecache'):
1020 self._phasecache.write()
1020 self._phasecache.write()
1021 for k, ce in self._filecache.items():
1021 for k, ce in self._filecache.items():
1022 if k == 'dirstate' or k not in self.__dict__:
1022 if k == 'dirstate' or k not in self.__dict__:
1023 continue
1023 continue
1024 ce.refresh()
1024 ce.refresh()
1025
1025
1026 l = self._lock(self.sjoin("lock"), wait, unlock,
1026 l = self._lock(self.sjoin("lock"), wait, unlock,
1027 self.invalidate, _('repository %s') % self.origroot)
1027 self.invalidate, _('repository %s') % self.origroot)
1028 self._lockref = weakref.ref(l)
1028 self._lockref = weakref.ref(l)
1029 return l
1029 return l
1030
1030
1031 def wlock(self, wait=True):
1031 def wlock(self, wait=True):
1032 '''Lock the non-store parts of the repository (everything under
1032 '''Lock the non-store parts of the repository (everything under
1033 .hg except .hg/store) and return a weak reference to the lock.
1033 .hg except .hg/store) and return a weak reference to the lock.
1034 Use this before modifying files in .hg.'''
1034 Use this before modifying files in .hg.'''
1035 l = self._wlockref and self._wlockref()
1035 l = self._wlockref and self._wlockref()
1036 if l is not None and l.held:
1036 if l is not None and l.held:
1037 l.lock()
1037 l.lock()
1038 return l
1038 return l
1039
1039
1040 def unlock():
1040 def unlock():
1041 self.dirstate.write()
1041 self.dirstate.write()
1042 self._filecache['dirstate'].refresh()
1042 self._filecache['dirstate'].refresh()
1043
1043
1044 l = self._lock(self.join("wlock"), wait, unlock,
1044 l = self._lock(self.join("wlock"), wait, unlock,
1045 self.invalidatedirstate, _('working directory of %s') %
1045 self.invalidatedirstate, _('working directory of %s') %
1046 self.origroot)
1046 self.origroot)
1047 self._wlockref = weakref.ref(l)
1047 self._wlockref = weakref.ref(l)
1048 return l
1048 return l
1049
1049
1050 def _filecommit(self, fctx, manifest1, manifest2, linkrev, tr, changelist):
1050 def _filecommit(self, fctx, manifest1, manifest2, linkrev, tr, changelist):
1051 """
1051 """
1052 commit an individual file as part of a larger transaction
1052 commit an individual file as part of a larger transaction
1053 """
1053 """
1054
1054
1055 fname = fctx.path()
1055 fname = fctx.path()
1056 text = fctx.data()
1056 text = fctx.data()
1057 flog = self.file(fname)
1057 flog = self.file(fname)
1058 fparent1 = manifest1.get(fname, nullid)
1058 fparent1 = manifest1.get(fname, nullid)
1059 fparent2 = fparent2o = manifest2.get(fname, nullid)
1059 fparent2 = fparent2o = manifest2.get(fname, nullid)
1060
1060
1061 meta = {}
1061 meta = {}
1062 copy = fctx.renamed()
1062 copy = fctx.renamed()
1063 if copy and copy[0] != fname:
1063 if copy and copy[0] != fname:
1064 # Mark the new revision of this file as a copy of another
1064 # Mark the new revision of this file as a copy of another
1065 # file. This copy data will effectively act as a parent
1065 # file. This copy data will effectively act as a parent
1066 # of this new revision. If this is a merge, the first
1066 # of this new revision. If this is a merge, the first
1067 # parent will be the nullid (meaning "look up the copy data")
1067 # parent will be the nullid (meaning "look up the copy data")
1068 # and the second one will be the other parent. For example:
1068 # and the second one will be the other parent. For example:
1069 #
1069 #
1070 # 0 --- 1 --- 3 rev1 changes file foo
1070 # 0 --- 1 --- 3 rev1 changes file foo
1071 # \ / rev2 renames foo to bar and changes it
1071 # \ / rev2 renames foo to bar and changes it
1072 # \- 2 -/ rev3 should have bar with all changes and
1072 # \- 2 -/ rev3 should have bar with all changes and
1073 # should record that bar descends from
1073 # should record that bar descends from
1074 # bar in rev2 and foo in rev1
1074 # bar in rev2 and foo in rev1
1075 #
1075 #
1076 # this allows this merge to succeed:
1076 # this allows this merge to succeed:
1077 #
1077 #
1078 # 0 --- 1 --- 3 rev4 reverts the content change from rev2
1078 # 0 --- 1 --- 3 rev4 reverts the content change from rev2
1079 # \ / merging rev3 and rev4 should use bar@rev2
1079 # \ / merging rev3 and rev4 should use bar@rev2
1080 # \- 2 --- 4 as the merge base
1080 # \- 2 --- 4 as the merge base
1081 #
1081 #
1082
1082
1083 cfname = copy[0]
1083 cfname = copy[0]
1084 crev = manifest1.get(cfname)
1084 crev = manifest1.get(cfname)
1085 newfparent = fparent2
1085 newfparent = fparent2
1086
1086
1087 if manifest2: # branch merge
1087 if manifest2: # branch merge
1088 if fparent2 == nullid or crev is None: # copied on remote side
1088 if fparent2 == nullid or crev is None: # copied on remote side
1089 if cfname in manifest2:
1089 if cfname in manifest2:
1090 crev = manifest2[cfname]
1090 crev = manifest2[cfname]
1091 newfparent = fparent1
1091 newfparent = fparent1
1092
1092
1093 # find source in nearest ancestor if we've lost track
1093 # find source in nearest ancestor if we've lost track
1094 if not crev:
1094 if not crev:
1095 self.ui.debug(" %s: searching for copy revision for %s\n" %
1095 self.ui.debug(" %s: searching for copy revision for %s\n" %
1096 (fname, cfname))
1096 (fname, cfname))
1097 for ancestor in self[None].ancestors():
1097 for ancestor in self[None].ancestors():
1098 if cfname in ancestor:
1098 if cfname in ancestor:
1099 crev = ancestor[cfname].filenode()
1099 crev = ancestor[cfname].filenode()
1100 break
1100 break
1101
1101
1102 if crev:
1102 if crev:
1103 self.ui.debug(" %s: copy %s:%s\n" % (fname, cfname, hex(crev)))
1103 self.ui.debug(" %s: copy %s:%s\n" % (fname, cfname, hex(crev)))
1104 meta["copy"] = cfname
1104 meta["copy"] = cfname
1105 meta["copyrev"] = hex(crev)
1105 meta["copyrev"] = hex(crev)
1106 fparent1, fparent2 = nullid, newfparent
1106 fparent1, fparent2 = nullid, newfparent
1107 else:
1107 else:
1108 self.ui.warn(_("warning: can't find ancestor for '%s' "
1108 self.ui.warn(_("warning: can't find ancestor for '%s' "
1109 "copied from '%s'!\n") % (fname, cfname))
1109 "copied from '%s'!\n") % (fname, cfname))
1110
1110
1111 elif fparent2 != nullid:
1111 elif fparent2 != nullid:
1112 # is one parent an ancestor of the other?
1112 # is one parent an ancestor of the other?
1113 fparentancestor = flog.ancestor(fparent1, fparent2)
1113 fparentancestor = flog.ancestor(fparent1, fparent2)
1114 if fparentancestor == fparent1:
1114 if fparentancestor == fparent1:
1115 fparent1, fparent2 = fparent2, nullid
1115 fparent1, fparent2 = fparent2, nullid
1116 elif fparentancestor == fparent2:
1116 elif fparentancestor == fparent2:
1117 fparent2 = nullid
1117 fparent2 = nullid
1118
1118
1119 # is the file changed?
1119 # is the file changed?
1120 if fparent2 != nullid or flog.cmp(fparent1, text) or meta:
1120 if fparent2 != nullid or flog.cmp(fparent1, text) or meta:
1121 changelist.append(fname)
1121 changelist.append(fname)
1122 return flog.add(text, meta, tr, linkrev, fparent1, fparent2)
1122 return flog.add(text, meta, tr, linkrev, fparent1, fparent2)
1123
1123
1124 # are just the flags changed during merge?
1124 # are just the flags changed during merge?
1125 if fparent1 != fparent2o and manifest1.flags(fname) != fctx.flags():
1125 if fparent1 != fparent2o and manifest1.flags(fname) != fctx.flags():
1126 changelist.append(fname)
1126 changelist.append(fname)
1127
1127
1128 return fparent1
1128 return fparent1
1129
1129
1130 @unfilteredmethod
1130 @unfilteredmethod
1131 def commit(self, text="", user=None, date=None, match=None, force=False,
1131 def commit(self, text="", user=None, date=None, match=None, force=False,
1132 editor=False, extra={}):
1132 editor=False, extra={}):
1133 """Add a new revision to current repository.
1133 """Add a new revision to current repository.
1134
1134
1135 Revision information is gathered from the working directory,
1135 Revision information is gathered from the working directory,
1136 match can be used to filter the committed files. If editor is
1136 match can be used to filter the committed files. If editor is
1137 supplied, it is called to get a commit message.
1137 supplied, it is called to get a commit message.
1138 """
1138 """
1139
1139
1140 def fail(f, msg):
1140 def fail(f, msg):
1141 raise util.Abort('%s: %s' % (f, msg))
1141 raise util.Abort('%s: %s' % (f, msg))
1142
1142
1143 if not match:
1143 if not match:
1144 match = matchmod.always(self.root, '')
1144 match = matchmod.always(self.root, '')
1145
1145
1146 if not force:
1146 if not force:
1147 vdirs = []
1147 vdirs = []
1148 match.dir = vdirs.append
1148 match.dir = vdirs.append
1149 match.bad = fail
1149 match.bad = fail
1150
1150
1151 wlock = self.wlock()
1151 wlock = self.wlock()
1152 try:
1152 try:
1153 wctx = self[None]
1153 wctx = self[None]
1154 merge = len(wctx.parents()) > 1
1154 merge = len(wctx.parents()) > 1
1155
1155
1156 if (not force and merge and match and
1156 if (not force and merge and match and
1157 (match.files() or match.anypats())):
1157 (match.files() or match.anypats())):
1158 raise util.Abort(_('cannot partially commit a merge '
1158 raise util.Abort(_('cannot partially commit a merge '
1159 '(do not specify files or patterns)'))
1159 '(do not specify files or patterns)'))
1160
1160
1161 changes = self.status(match=match, clean=force)
1161 changes = self.status(match=match, clean=force)
1162 if force:
1162 if force:
1163 changes[0].extend(changes[6]) # mq may commit unchanged files
1163 changes[0].extend(changes[6]) # mq may commit unchanged files
1164
1164
1165 # check subrepos
1165 # check subrepos
1166 subs = []
1166 subs = []
1167 commitsubs = set()
1167 commitsubs = set()
1168 newstate = wctx.substate.copy()
1168 newstate = wctx.substate.copy()
1169 # only manage subrepos and .hgsubstate if .hgsub is present
1169 # only manage subrepos and .hgsubstate if .hgsub is present
1170 if '.hgsub' in wctx:
1170 if '.hgsub' in wctx:
1171 # we'll decide whether to track this ourselves, thanks
1171 # we'll decide whether to track this ourselves, thanks
1172 if '.hgsubstate' in changes[0]:
1172 if '.hgsubstate' in changes[0]:
1173 changes[0].remove('.hgsubstate')
1173 changes[0].remove('.hgsubstate')
1174 if '.hgsubstate' in changes[2]:
1174 if '.hgsubstate' in changes[2]:
1175 changes[2].remove('.hgsubstate')
1175 changes[2].remove('.hgsubstate')
1176
1176
1177 # compare current state to last committed state
1177 # compare current state to last committed state
1178 # build new substate based on last committed state
1178 # build new substate based on last committed state
1179 oldstate = wctx.p1().substate
1179 oldstate = wctx.p1().substate
1180 for s in sorted(newstate.keys()):
1180 for s in sorted(newstate.keys()):
1181 if not match(s):
1181 if not match(s):
1182 # ignore working copy, use old state if present
1182 # ignore working copy, use old state if present
1183 if s in oldstate:
1183 if s in oldstate:
1184 newstate[s] = oldstate[s]
1184 newstate[s] = oldstate[s]
1185 continue
1185 continue
1186 if not force:
1186 if not force:
1187 raise util.Abort(
1187 raise util.Abort(
1188 _("commit with new subrepo %s excluded") % s)
1188 _("commit with new subrepo %s excluded") % s)
1189 if wctx.sub(s).dirty(True):
1189 if wctx.sub(s).dirty(True):
1190 if not self.ui.configbool('ui', 'commitsubrepos'):
1190 if not self.ui.configbool('ui', 'commitsubrepos'):
1191 raise util.Abort(
1191 raise util.Abort(
1192 _("uncommitted changes in subrepo %s") % s,
1192 _("uncommitted changes in subrepo %s") % s,
1193 hint=_("use --subrepos for recursive commit"))
1193 hint=_("use --subrepos for recursive commit"))
1194 subs.append(s)
1194 subs.append(s)
1195 commitsubs.add(s)
1195 commitsubs.add(s)
1196 else:
1196 else:
1197 bs = wctx.sub(s).basestate()
1197 bs = wctx.sub(s).basestate()
1198 newstate[s] = (newstate[s][0], bs, newstate[s][2])
1198 newstate[s] = (newstate[s][0], bs, newstate[s][2])
1199 if oldstate.get(s, (None, None, None))[1] != bs:
1199 if oldstate.get(s, (None, None, None))[1] != bs:
1200 subs.append(s)
1200 subs.append(s)
1201
1201
1202 # check for removed subrepos
1202 # check for removed subrepos
1203 for p in wctx.parents():
1203 for p in wctx.parents():
1204 r = [s for s in p.substate if s not in newstate]
1204 r = [s for s in p.substate if s not in newstate]
1205 subs += [s for s in r if match(s)]
1205 subs += [s for s in r if match(s)]
1206 if subs:
1206 if subs:
1207 if (not match('.hgsub') and
1207 if (not match('.hgsub') and
1208 '.hgsub' in (wctx.modified() + wctx.added())):
1208 '.hgsub' in (wctx.modified() + wctx.added())):
1209 raise util.Abort(
1209 raise util.Abort(
1210 _("can't commit subrepos without .hgsub"))
1210 _("can't commit subrepos without .hgsub"))
1211 changes[0].insert(0, '.hgsubstate')
1211 changes[0].insert(0, '.hgsubstate')
1212
1212
1213 elif '.hgsub' in changes[2]:
1213 elif '.hgsub' in changes[2]:
1214 # clean up .hgsubstate when .hgsub is removed
1214 # clean up .hgsubstate when .hgsub is removed
1215 if ('.hgsubstate' in wctx and
1215 if ('.hgsubstate' in wctx and
1216 '.hgsubstate' not in changes[0] + changes[1] + changes[2]):
1216 '.hgsubstate' not in changes[0] + changes[1] + changes[2]):
1217 changes[2].insert(0, '.hgsubstate')
1217 changes[2].insert(0, '.hgsubstate')
1218
1218
1219 # make sure all explicit patterns are matched
1219 # make sure all explicit patterns are matched
1220 if not force and match.files():
1220 if not force and match.files():
1221 matched = set(changes[0] + changes[1] + changes[2])
1221 matched = set(changes[0] + changes[1] + changes[2])
1222
1222
1223 for f in match.files():
1223 for f in match.files():
1224 f = self.dirstate.normalize(f)
1224 f = self.dirstate.normalize(f)
1225 if f == '.' or f in matched or f in wctx.substate:
1225 if f == '.' or f in matched or f in wctx.substate:
1226 continue
1226 continue
1227 if f in changes[3]: # missing
1227 if f in changes[3]: # missing
1228 fail(f, _('file not found!'))
1228 fail(f, _('file not found!'))
1229 if f in vdirs: # visited directory
1229 if f in vdirs: # visited directory
1230 d = f + '/'
1230 d = f + '/'
1231 for mf in matched:
1231 for mf in matched:
1232 if mf.startswith(d):
1232 if mf.startswith(d):
1233 break
1233 break
1234 else:
1234 else:
1235 fail(f, _("no match under directory!"))
1235 fail(f, _("no match under directory!"))
1236 elif f not in self.dirstate:
1236 elif f not in self.dirstate:
1237 fail(f, _("file not tracked!"))
1237 fail(f, _("file not tracked!"))
1238
1238
1239 cctx = context.workingctx(self, text, user, date, extra, changes)
1239 cctx = context.workingctx(self, text, user, date, extra, changes)
1240
1240
1241 if (not force and not extra.get("close") and not merge
1241 if (not force and not extra.get("close") and not merge
1242 and not cctx.files()
1242 and not cctx.files()
1243 and wctx.branch() == wctx.p1().branch()):
1243 and wctx.branch() == wctx.p1().branch()):
1244 return None
1244 return None
1245
1245
1246 if merge and cctx.deleted():
1246 if merge and cctx.deleted():
1247 raise util.Abort(_("cannot commit merge with missing files"))
1247 raise util.Abort(_("cannot commit merge with missing files"))
1248
1248
1249 ms = mergemod.mergestate(self)
1249 ms = mergemod.mergestate(self)
1250 for f in changes[0]:
1250 for f in changes[0]:
1251 if f in ms and ms[f] == 'u':
1251 if f in ms and ms[f] == 'u':
1252 raise util.Abort(_("unresolved merge conflicts "
1252 raise util.Abort(_("unresolved merge conflicts "
1253 "(see hg help resolve)"))
1253 "(see hg help resolve)"))
1254
1254
1255 if editor:
1255 if editor:
1256 cctx._text = editor(self, cctx, subs)
1256 cctx._text = editor(self, cctx, subs)
1257 edited = (text != cctx._text)
1257 edited = (text != cctx._text)
1258
1258
1259 # commit subs and write new state
1259 # commit subs and write new state
1260 if subs:
1260 if subs:
1261 for s in sorted(commitsubs):
1261 for s in sorted(commitsubs):
1262 sub = wctx.sub(s)
1262 sub = wctx.sub(s)
1263 self.ui.status(_('committing subrepository %s\n') %
1263 self.ui.status(_('committing subrepository %s\n') %
1264 subrepo.subrelpath(sub))
1264 subrepo.subrelpath(sub))
1265 sr = sub.commit(cctx._text, user, date)
1265 sr = sub.commit(cctx._text, user, date)
1266 newstate[s] = (newstate[s][0], sr)
1266 newstate[s] = (newstate[s][0], sr)
1267 subrepo.writestate(self, newstate)
1267 subrepo.writestate(self, newstate)
1268
1268
1269 # Save commit message in case this transaction gets rolled back
1269 # Save commit message in case this transaction gets rolled back
1270 # (e.g. by a pretxncommit hook). Leave the content alone on
1270 # (e.g. by a pretxncommit hook). Leave the content alone on
1271 # the assumption that the user will use the same editor again.
1271 # the assumption that the user will use the same editor again.
1272 msgfn = self.savecommitmessage(cctx._text)
1272 msgfn = self.savecommitmessage(cctx._text)
1273
1273
1274 p1, p2 = self.dirstate.parents()
1274 p1, p2 = self.dirstate.parents()
1275 hookp1, hookp2 = hex(p1), (p2 != nullid and hex(p2) or '')
1275 hookp1, hookp2 = hex(p1), (p2 != nullid and hex(p2) or '')
1276 try:
1276 try:
1277 self.hook("precommit", throw=True, parent1=hookp1,
1277 self.hook("precommit", throw=True, parent1=hookp1,
1278 parent2=hookp2)
1278 parent2=hookp2)
1279 ret = self.commitctx(cctx, True)
1279 ret = self.commitctx(cctx, True)
1280 except: # re-raises
1280 except: # re-raises
1281 if edited:
1281 if edited:
1282 self.ui.write(
1282 self.ui.write(
1283 _('note: commit message saved in %s\n') % msgfn)
1283 _('note: commit message saved in %s\n') % msgfn)
1284 raise
1284 raise
1285
1285
1286 # update bookmarks, dirstate and mergestate
1286 # update bookmarks, dirstate and mergestate
1287 bookmarks.update(self, [p1, p2], ret)
1287 bookmarks.update(self, [p1, p2], ret)
1288 cctx.markcommitted(ret)
1288 cctx.markcommitted(ret)
1289 ms.reset()
1289 ms.reset()
1290 finally:
1290 finally:
1291 wlock.release()
1291 wlock.release()
1292
1292
1293 def commithook(node=hex(ret), parent1=hookp1, parent2=hookp2):
1293 def commithook(node=hex(ret), parent1=hookp1, parent2=hookp2):
1294 self.hook("commit", node=node, parent1=parent1, parent2=parent2)
1294 self.hook("commit", node=node, parent1=parent1, parent2=parent2)
1295 self._afterlock(commithook)
1295 self._afterlock(commithook)
1296 return ret
1296 return ret
1297
1297
1298 @unfilteredmethod
1298 @unfilteredmethod
1299 def commitctx(self, ctx, error=False):
1299 def commitctx(self, ctx, error=False):
1300 """Add a new revision to current repository.
1300 """Add a new revision to current repository.
1301 Revision information is passed via the context argument.
1301 Revision information is passed via the context argument.
1302 """
1302 """
1303
1303
1304 tr = lock = None
1304 tr = lock = None
1305 removed = list(ctx.removed())
1305 removed = list(ctx.removed())
1306 p1, p2 = ctx.p1(), ctx.p2()
1306 p1, p2 = ctx.p1(), ctx.p2()
1307 user = ctx.user()
1307 user = ctx.user()
1308
1308
1309 lock = self.lock()
1309 lock = self.lock()
1310 try:
1310 try:
1311 tr = self.transaction("commit")
1311 tr = self.transaction("commit")
1312 trp = weakref.proxy(tr)
1312 trp = weakref.proxy(tr)
1313
1313
1314 if ctx.files():
1314 if ctx.files():
1315 m1 = p1.manifest().copy()
1315 m1 = p1.manifest().copy()
1316 m2 = p2.manifest()
1316 m2 = p2.manifest()
1317
1317
1318 # check in files
1318 # check in files
1319 new = {}
1319 new = {}
1320 changed = []
1320 changed = []
1321 linkrev = len(self)
1321 linkrev = len(self)
1322 for f in sorted(ctx.modified() + ctx.added()):
1322 for f in sorted(ctx.modified() + ctx.added()):
1323 self.ui.note(f + "\n")
1323 self.ui.note(f + "\n")
1324 try:
1324 try:
1325 fctx = ctx[f]
1325 fctx = ctx[f]
1326 new[f] = self._filecommit(fctx, m1, m2, linkrev, trp,
1326 new[f] = self._filecommit(fctx, m1, m2, linkrev, trp,
1327 changed)
1327 changed)
1328 m1.set(f, fctx.flags())
1328 m1.set(f, fctx.flags())
1329 except OSError, inst:
1329 except OSError, inst:
1330 self.ui.warn(_("trouble committing %s!\n") % f)
1330 self.ui.warn(_("trouble committing %s!\n") % f)
1331 raise
1331 raise
1332 except IOError, inst:
1332 except IOError, inst:
1333 errcode = getattr(inst, 'errno', errno.ENOENT)
1333 errcode = getattr(inst, 'errno', errno.ENOENT)
1334 if error or errcode and errcode != errno.ENOENT:
1334 if error or errcode and errcode != errno.ENOENT:
1335 self.ui.warn(_("trouble committing %s!\n") % f)
1335 self.ui.warn(_("trouble committing %s!\n") % f)
1336 raise
1336 raise
1337 else:
1337 else:
1338 removed.append(f)
1338 removed.append(f)
1339
1339
1340 # update manifest
1340 # update manifest
1341 m1.update(new)
1341 m1.update(new)
1342 removed = [f for f in sorted(removed) if f in m1 or f in m2]
1342 removed = [f for f in sorted(removed) if f in m1 or f in m2]
1343 drop = [f for f in removed if f in m1]
1343 drop = [f for f in removed if f in m1]
1344 for f in drop:
1344 for f in drop:
1345 del m1[f]
1345 del m1[f]
1346 mn = self.manifest.add(m1, trp, linkrev, p1.manifestnode(),
1346 mn = self.manifest.add(m1, trp, linkrev, p1.manifestnode(),
1347 p2.manifestnode(), (new, drop))
1347 p2.manifestnode(), (new, drop))
1348 files = changed + removed
1348 files = changed + removed
1349 else:
1349 else:
1350 mn = p1.manifestnode()
1350 mn = p1.manifestnode()
1351 files = []
1351 files = []
1352
1352
1353 # update changelog
1353 # update changelog
1354 self.changelog.delayupdate()
1354 self.changelog.delayupdate()
1355 n = self.changelog.add(mn, files, ctx.description(),
1355 n = self.changelog.add(mn, files, ctx.description(),
1356 trp, p1.node(), p2.node(),
1356 trp, p1.node(), p2.node(),
1357 user, ctx.date(), ctx.extra().copy())
1357 user, ctx.date(), ctx.extra().copy())
1358 p = lambda: self.changelog.writepending() and self.root or ""
1358 p = lambda: self.changelog.writepending() and self.root or ""
1359 xp1, xp2 = p1.hex(), p2 and p2.hex() or ''
1359 xp1, xp2 = p1.hex(), p2 and p2.hex() or ''
1360 self.hook('pretxncommit', throw=True, node=hex(n), parent1=xp1,
1360 self.hook('pretxncommit', throw=True, node=hex(n), parent1=xp1,
1361 parent2=xp2, pending=p)
1361 parent2=xp2, pending=p)
1362 self.changelog.finalize(trp)
1362 self.changelog.finalize(trp)
1363 # set the new commit is proper phase
1363 # set the new commit is proper phase
1364 targetphase = phases.newcommitphase(self.ui)
1364 targetphase = phases.newcommitphase(self.ui)
1365 if targetphase:
1365 if targetphase:
1366 # retract boundary do not alter parent changeset.
1366 # retract boundary do not alter parent changeset.
1367 # if a parent have higher the resulting phase will
1367 # if a parent have higher the resulting phase will
1368 # be compliant anyway
1368 # be compliant anyway
1369 #
1369 #
1370 # if minimal phase was 0 we don't need to retract anything
1370 # if minimal phase was 0 we don't need to retract anything
1371 phases.retractboundary(self, targetphase, [n])
1371 phases.retractboundary(self, targetphase, [n])
1372 tr.close()
1372 tr.close()
1373 branchmap.updatecache(self.filtered('served'))
1373 branchmap.updatecache(self.filtered('served'))
1374 return n
1374 return n
1375 finally:
1375 finally:
1376 if tr:
1376 if tr:
1377 tr.release()
1377 tr.release()
1378 lock.release()
1378 lock.release()
1379
1379
1380 @unfilteredmethod
1380 @unfilteredmethod
1381 def destroying(self):
1381 def destroying(self):
1382 '''Inform the repository that nodes are about to be destroyed.
1382 '''Inform the repository that nodes are about to be destroyed.
1383 Intended for use by strip and rollback, so there's a common
1383 Intended for use by strip and rollback, so there's a common
1384 place for anything that has to be done before destroying history.
1384 place for anything that has to be done before destroying history.
1385
1385
1386 This is mostly useful for saving state that is in memory and waiting
1386 This is mostly useful for saving state that is in memory and waiting
1387 to be flushed when the current lock is released. Because a call to
1387 to be flushed when the current lock is released. Because a call to
1388 destroyed is imminent, the repo will be invalidated causing those
1388 destroyed is imminent, the repo will be invalidated causing those
1389 changes to stay in memory (waiting for the next unlock), or vanish
1389 changes to stay in memory (waiting for the next unlock), or vanish
1390 completely.
1390 completely.
1391 '''
1391 '''
1392 # When using the same lock to commit and strip, the phasecache is left
1392 # When using the same lock to commit and strip, the phasecache is left
1393 # dirty after committing. Then when we strip, the repo is invalidated,
1393 # dirty after committing. Then when we strip, the repo is invalidated,
1394 # causing those changes to disappear.
1394 # causing those changes to disappear.
1395 if '_phasecache' in vars(self):
1395 if '_phasecache' in vars(self):
1396 self._phasecache.write()
1396 self._phasecache.write()
1397
1397
1398 @unfilteredmethod
1398 @unfilteredmethod
1399 def destroyed(self):
1399 def destroyed(self):
1400 '''Inform the repository that nodes have been destroyed.
1400 '''Inform the repository that nodes have been destroyed.
1401 Intended for use by strip and rollback, so there's a common
1401 Intended for use by strip and rollback, so there's a common
1402 place for anything that has to be done after destroying history.
1402 place for anything that has to be done after destroying history.
1403 '''
1403 '''
1404 # When one tries to:
1404 # When one tries to:
1405 # 1) destroy nodes thus calling this method (e.g. strip)
1405 # 1) destroy nodes thus calling this method (e.g. strip)
1406 # 2) use phasecache somewhere (e.g. commit)
1406 # 2) use phasecache somewhere (e.g. commit)
1407 #
1407 #
1408 # then 2) will fail because the phasecache contains nodes that were
1408 # then 2) will fail because the phasecache contains nodes that were
1409 # removed. We can either remove phasecache from the filecache,
1409 # removed. We can either remove phasecache from the filecache,
1410 # causing it to reload next time it is accessed, or simply filter
1410 # causing it to reload next time it is accessed, or simply filter
1411 # the removed nodes now and write the updated cache.
1411 # the removed nodes now and write the updated cache.
1412 self._phasecache.filterunknown(self)
1412 self._phasecache.filterunknown(self)
1413 self._phasecache.write()
1413 self._phasecache.write()
1414
1414
1415 # update the 'served' branch cache to help read only server process
1415 # update the 'served' branch cache to help read only server process
1416 # Thanks to branchcache collaboration this is done from the nearest
1416 # Thanks to branchcache collaboration this is done from the nearest
1417 # filtered subset and it is expected to be fast.
1417 # filtered subset and it is expected to be fast.
1418 branchmap.updatecache(self.filtered('served'))
1418 branchmap.updatecache(self.filtered('served'))
1419
1419
1420 # Ensure the persistent tag cache is updated. Doing it now
1420 # Ensure the persistent tag cache is updated. Doing it now
1421 # means that the tag cache only has to worry about destroyed
1421 # means that the tag cache only has to worry about destroyed
1422 # heads immediately after a strip/rollback. That in turn
1422 # heads immediately after a strip/rollback. That in turn
1423 # guarantees that "cachetip == currenttip" (comparing both rev
1423 # guarantees that "cachetip == currenttip" (comparing both rev
1424 # and node) always means no nodes have been added or destroyed.
1424 # and node) always means no nodes have been added or destroyed.
1425
1425
1426 # XXX this is suboptimal when qrefresh'ing: we strip the current
1426 # XXX this is suboptimal when qrefresh'ing: we strip the current
1427 # head, refresh the tag cache, then immediately add a new head.
1427 # head, refresh the tag cache, then immediately add a new head.
1428 # But I think doing it this way is necessary for the "instant
1428 # But I think doing it this way is necessary for the "instant
1429 # tag cache retrieval" case to work.
1429 # tag cache retrieval" case to work.
1430 self.invalidate()
1430 self.invalidate()
1431
1431
1432 def walk(self, match, node=None):
1432 def walk(self, match, node=None):
1433 '''
1433 '''
1434 walk recursively through the directory tree or a given
1434 walk recursively through the directory tree or a given
1435 changeset, finding all files matched by the match
1435 changeset, finding all files matched by the match
1436 function
1436 function
1437 '''
1437 '''
1438 return self[node].walk(match)
1438 return self[node].walk(match)
1439
1439
1440 def status(self, node1='.', node2=None, match=None,
1440 def status(self, node1='.', node2=None, match=None,
1441 ignored=False, clean=False, unknown=False,
1441 ignored=False, clean=False, unknown=False,
1442 listsubrepos=False):
1442 listsubrepos=False):
1443 """return status of files between two nodes or node and working
1443 """return status of files between two nodes or node and working
1444 directory.
1444 directory.
1445
1445
1446 If node1 is None, use the first dirstate parent instead.
1446 If node1 is None, use the first dirstate parent instead.
1447 If node2 is None, compare node1 with working directory.
1447 If node2 is None, compare node1 with working directory.
1448 """
1448 """
1449
1449
1450 def mfmatches(ctx):
1450 def mfmatches(ctx):
1451 mf = ctx.manifest().copy()
1451 mf = ctx.manifest().copy()
1452 if match.always():
1452 if match.always():
1453 return mf
1453 return mf
1454 for fn in mf.keys():
1454 for fn in mf.keys():
1455 if not match(fn):
1455 if not match(fn):
1456 del mf[fn]
1456 del mf[fn]
1457 return mf
1457 return mf
1458
1458
1459 if isinstance(node1, context.changectx):
1459 if isinstance(node1, context.changectx):
1460 ctx1 = node1
1460 ctx1 = node1
1461 else:
1461 else:
1462 ctx1 = self[node1]
1462 ctx1 = self[node1]
1463 if isinstance(node2, context.changectx):
1463 if isinstance(node2, context.changectx):
1464 ctx2 = node2
1464 ctx2 = node2
1465 else:
1465 else:
1466 ctx2 = self[node2]
1466 ctx2 = self[node2]
1467
1467
1468 working = ctx2.rev() is None
1468 working = ctx2.rev() is None
1469 parentworking = working and ctx1 == self['.']
1469 parentworking = working and ctx1 == self['.']
1470 match = match or matchmod.always(self.root, self.getcwd())
1470 match = match or matchmod.always(self.root, self.getcwd())
1471 listignored, listclean, listunknown = ignored, clean, unknown
1471 listignored, listclean, listunknown = ignored, clean, unknown
1472
1472
1473 # load earliest manifest first for caching reasons
1473 # load earliest manifest first for caching reasons
1474 if not working and ctx2.rev() < ctx1.rev():
1474 if not working and ctx2.rev() < ctx1.rev():
1475 ctx2.manifest()
1475 ctx2.manifest()
1476
1476
1477 if not parentworking:
1477 if not parentworking:
1478 def bad(f, msg):
1478 def bad(f, msg):
1479 # 'f' may be a directory pattern from 'match.files()',
1479 # 'f' may be a directory pattern from 'match.files()',
1480 # so 'f not in ctx1' is not enough
1480 # so 'f not in ctx1' is not enough
1481 if f not in ctx1 and f not in ctx1.dirs():
1481 if f not in ctx1 and f not in ctx1.dirs():
1482 self.ui.warn('%s: %s\n' % (self.dirstate.pathto(f), msg))
1482 self.ui.warn('%s: %s\n' % (self.dirstate.pathto(f), msg))
1483 match.bad = bad
1483 match.bad = bad
1484
1484
1485 if working: # we need to scan the working dir
1485 if working: # we need to scan the working dir
1486 subrepos = []
1486 subrepos = []
1487 if '.hgsub' in self.dirstate:
1487 if '.hgsub' in self.dirstate:
1488 subrepos = sorted(ctx2.substate)
1488 subrepos = sorted(ctx2.substate)
1489 s = self.dirstate.status(match, subrepos, listignored,
1489 s = self.dirstate.status(match, subrepos, listignored,
1490 listclean, listunknown)
1490 listclean, listunknown)
1491 cmp, modified, added, removed, deleted, unknown, ignored, clean = s
1491 cmp, modified, added, removed, deleted, unknown, ignored, clean = s
1492
1492
1493 # check for any possibly clean files
1493 # check for any possibly clean files
1494 if parentworking and cmp:
1494 if parentworking and cmp:
1495 fixup = []
1495 fixup = []
1496 # do a full compare of any files that might have changed
1496 # do a full compare of any files that might have changed
1497 for f in sorted(cmp):
1497 for f in sorted(cmp):
1498 if (f not in ctx1 or ctx2.flags(f) != ctx1.flags(f)
1498 if (f not in ctx1 or ctx2.flags(f) != ctx1.flags(f)
1499 or ctx1[f].cmp(ctx2[f])):
1499 or ctx1[f].cmp(ctx2[f])):
1500 modified.append(f)
1500 modified.append(f)
1501 else:
1501 else:
1502 fixup.append(f)
1502 fixup.append(f)
1503
1503
1504 # update dirstate for files that are actually clean
1504 # update dirstate for files that are actually clean
1505 if fixup:
1505 if fixup:
1506 if listclean:
1506 if listclean:
1507 clean += fixup
1507 clean += fixup
1508
1508
1509 try:
1509 try:
1510 # updating the dirstate is optional
1510 # updating the dirstate is optional
1511 # so we don't wait on the lock
1511 # so we don't wait on the lock
1512 wlock = self.wlock(False)
1512 wlock = self.wlock(False)
1513 try:
1513 try:
1514 for f in fixup:
1514 for f in fixup:
1515 self.dirstate.normal(f)
1515 self.dirstate.normal(f)
1516 finally:
1516 finally:
1517 wlock.release()
1517 wlock.release()
1518 except error.LockError:
1518 except error.LockError:
1519 pass
1519 pass
1520
1520
1521 if not parentworking:
1521 if not parentworking:
1522 mf1 = mfmatches(ctx1)
1522 mf1 = mfmatches(ctx1)
1523 if working:
1523 if working:
1524 # we are comparing working dir against non-parent
1524 # we are comparing working dir against non-parent
1525 # generate a pseudo-manifest for the working dir
1525 # generate a pseudo-manifest for the working dir
1526 mf2 = mfmatches(self['.'])
1526 mf2 = mfmatches(self['.'])
1527 for f in cmp + modified + added:
1527 for f in cmp + modified + added:
1528 mf2[f] = None
1528 mf2[f] = None
1529 mf2.set(f, ctx2.flags(f))
1529 mf2.set(f, ctx2.flags(f))
1530 for f in removed:
1530 for f in removed:
1531 if f in mf2:
1531 if f in mf2:
1532 del mf2[f]
1532 del mf2[f]
1533 else:
1533 else:
1534 # we are comparing two revisions
1534 # we are comparing two revisions
1535 deleted, unknown, ignored = [], [], []
1535 deleted, unknown, ignored = [], [], []
1536 mf2 = mfmatches(ctx2)
1536 mf2 = mfmatches(ctx2)
1537
1537
1538 modified, added, clean = [], [], []
1538 modified, added, clean = [], [], []
1539 withflags = mf1.withflags() | mf2.withflags()
1539 withflags = mf1.withflags() | mf2.withflags()
1540 for fn, mf2node in mf2.iteritems():
1540 for fn, mf2node in mf2.iteritems():
1541 if fn in mf1:
1541 if fn in mf1:
1542 if (fn not in deleted and
1542 if (fn not in deleted and
1543 ((fn in withflags and mf1.flags(fn) != mf2.flags(fn)) or
1543 ((fn in withflags and mf1.flags(fn) != mf2.flags(fn)) or
1544 (mf1[fn] != mf2node and
1544 (mf1[fn] != mf2node and
1545 (mf2node or ctx1[fn].cmp(ctx2[fn]))))):
1545 (mf2node or ctx1[fn].cmp(ctx2[fn]))))):
1546 modified.append(fn)
1546 modified.append(fn)
1547 elif listclean:
1547 elif listclean:
1548 clean.append(fn)
1548 clean.append(fn)
1549 del mf1[fn]
1549 del mf1[fn]
1550 elif fn not in deleted:
1550 elif fn not in deleted:
1551 added.append(fn)
1551 added.append(fn)
1552 removed = mf1.keys()
1552 removed = mf1.keys()
1553
1553
1554 if working and modified and not self.dirstate._checklink:
1554 if working and modified and not self.dirstate._checklink:
1555 # Symlink placeholders may get non-symlink-like contents
1555 # Symlink placeholders may get non-symlink-like contents
1556 # via user error or dereferencing by NFS or Samba servers,
1556 # via user error or dereferencing by NFS or Samba servers,
1557 # so we filter out any placeholders that don't look like a
1557 # so we filter out any placeholders that don't look like a
1558 # symlink
1558 # symlink
1559 sane = []
1559 sane = []
1560 for f in modified:
1560 for f in modified:
1561 if ctx2.flags(f) == 'l':
1561 if ctx2.flags(f) == 'l':
1562 d = ctx2[f].data()
1562 d = ctx2[f].data()
1563 if len(d) >= 1024 or '\n' in d or util.binary(d):
1563 if len(d) >= 1024 or '\n' in d or util.binary(d):
1564 self.ui.debug('ignoring suspect symlink placeholder'
1564 self.ui.debug('ignoring suspect symlink placeholder'
1565 ' "%s"\n' % f)
1565 ' "%s"\n' % f)
1566 continue
1566 continue
1567 sane.append(f)
1567 sane.append(f)
1568 modified = sane
1568 modified = sane
1569
1569
1570 r = modified, added, removed, deleted, unknown, ignored, clean
1570 r = modified, added, removed, deleted, unknown, ignored, clean
1571
1571
1572 if listsubrepos:
1572 if listsubrepos:
1573 for subpath, sub in subrepo.itersubrepos(ctx1, ctx2):
1573 for subpath, sub in subrepo.itersubrepos(ctx1, ctx2):
1574 if working:
1574 if working:
1575 rev2 = None
1575 rev2 = None
1576 else:
1576 else:
1577 rev2 = ctx2.substate[subpath][1]
1577 rev2 = ctx2.substate[subpath][1]
1578 try:
1578 try:
1579 submatch = matchmod.narrowmatcher(subpath, match)
1579 submatch = matchmod.narrowmatcher(subpath, match)
1580 s = sub.status(rev2, match=submatch, ignored=listignored,
1580 s = sub.status(rev2, match=submatch, ignored=listignored,
1581 clean=listclean, unknown=listunknown,
1581 clean=listclean, unknown=listunknown,
1582 listsubrepos=True)
1582 listsubrepos=True)
1583 for rfiles, sfiles in zip(r, s):
1583 for rfiles, sfiles in zip(r, s):
1584 rfiles.extend("%s/%s" % (subpath, f) for f in sfiles)
1584 rfiles.extend("%s/%s" % (subpath, f) for f in sfiles)
1585 except error.LookupError:
1585 except error.LookupError:
1586 self.ui.status(_("skipping missing subrepository: %s\n")
1586 self.ui.status(_("skipping missing subrepository: %s\n")
1587 % subpath)
1587 % subpath)
1588
1588
1589 for l in r:
1589 for l in r:
1590 l.sort()
1590 l.sort()
1591 return r
1591 return r
1592
1592
1593 def heads(self, start=None):
1593 def heads(self, start=None):
1594 heads = self.changelog.heads(start)
1594 heads = self.changelog.heads(start)
1595 # sort the output in rev descending order
1595 # sort the output in rev descending order
1596 return sorted(heads, key=self.changelog.rev, reverse=True)
1596 return sorted(heads, key=self.changelog.rev, reverse=True)
1597
1597
1598 def branchheads(self, branch=None, start=None, closed=False):
1598 def branchheads(self, branch=None, start=None, closed=False):
1599 '''return a (possibly filtered) list of heads for the given branch
1599 '''return a (possibly filtered) list of heads for the given branch
1600
1600
1601 Heads are returned in topological order, from newest to oldest.
1601 Heads are returned in topological order, from newest to oldest.
1602 If branch is None, use the dirstate branch.
1602 If branch is None, use the dirstate branch.
1603 If start is not None, return only heads reachable from start.
1603 If start is not None, return only heads reachable from start.
1604 If closed is True, return heads that are marked as closed as well.
1604 If closed is True, return heads that are marked as closed as well.
1605 '''
1605 '''
1606 if branch is None:
1606 if branch is None:
1607 branch = self[None].branch()
1607 branch = self[None].branch()
1608 branches = self.branchmap()
1608 branches = self.branchmap()
1609 if branch not in branches:
1609 if branch not in branches:
1610 return []
1610 return []
1611 # the cache returns heads ordered lowest to highest
1611 # the cache returns heads ordered lowest to highest
1612 bheads = list(reversed(branches[branch]))
1612 bheads = list(reversed(branches[branch]))
1613 if start is not None:
1613 if start is not None:
1614 # filter out the heads that cannot be reached from startrev
1614 # filter out the heads that cannot be reached from startrev
1615 fbheads = set(self.changelog.nodesbetween([start], bheads)[2])
1615 fbheads = set(self.changelog.nodesbetween([start], bheads)[2])
1616 bheads = [h for h in bheads if h in fbheads]
1616 bheads = [h for h in bheads if h in fbheads]
1617 if not closed:
1617 if not closed:
1618 bheads = [h for h in bheads if not self[h].closesbranch()]
1618 bheads = [h for h in bheads if not self[h].closesbranch()]
1619 return bheads
1619 return bheads
1620
1620
1621 def branches(self, nodes):
1621 def branches(self, nodes):
1622 if not nodes:
1622 if not nodes:
1623 nodes = [self.changelog.tip()]
1623 nodes = [self.changelog.tip()]
1624 b = []
1624 b = []
1625 for n in nodes:
1625 for n in nodes:
1626 t = n
1626 t = n
1627 while True:
1627 while True:
1628 p = self.changelog.parents(n)
1628 p = self.changelog.parents(n)
1629 if p[1] != nullid or p[0] == nullid:
1629 if p[1] != nullid or p[0] == nullid:
1630 b.append((t, n, p[0], p[1]))
1630 b.append((t, n, p[0], p[1]))
1631 break
1631 break
1632 n = p[0]
1632 n = p[0]
1633 return b
1633 return b
1634
1634
1635 def between(self, pairs):
1635 def between(self, pairs):
1636 r = []
1636 r = []
1637
1637
1638 for top, bottom in pairs:
1638 for top, bottom in pairs:
1639 n, l, i = top, [], 0
1639 n, l, i = top, [], 0
1640 f = 1
1640 f = 1
1641
1641
1642 while n != bottom and n != nullid:
1642 while n != bottom and n != nullid:
1643 p = self.changelog.parents(n)[0]
1643 p = self.changelog.parents(n)[0]
1644 if i == f:
1644 if i == f:
1645 l.append(n)
1645 l.append(n)
1646 f = f * 2
1646 f = f * 2
1647 n = p
1647 n = p
1648 i += 1
1648 i += 1
1649
1649
1650 r.append(l)
1650 r.append(l)
1651
1651
1652 return r
1652 return r
1653
1653
1654 def pull(self, remote, heads=None, force=False):
1654 def pull(self, remote, heads=None, force=False):
1655 # don't open transaction for nothing or you break future useful
1655 # don't open transaction for nothing or you break future useful
1656 # rollback call
1656 # rollback call
1657 tr = None
1657 tr = None
1658 trname = 'pull\n' + util.hidepassword(remote.url())
1658 trname = 'pull\n' + util.hidepassword(remote.url())
1659 lock = self.lock()
1659 lock = self.lock()
1660 try:
1660 try:
1661 tmp = discovery.findcommonincoming(self, remote, heads=heads,
1661 tmp = discovery.findcommonincoming(self, remote, heads=heads,
1662 force=force)
1662 force=force)
1663 common, fetch, rheads = tmp
1663 common, fetch, rheads = tmp
1664 if not fetch:
1664 if not fetch:
1665 self.ui.status(_("no changes found\n"))
1665 self.ui.status(_("no changes found\n"))
1666 added = []
1666 added = []
1667 result = 0
1667 result = 0
1668 else:
1668 else:
1669 tr = self.transaction(trname)
1669 tr = self.transaction(trname)
1670 if heads is None and list(common) == [nullid]:
1670 if heads is None and list(common) == [nullid]:
1671 self.ui.status(_("requesting all changes\n"))
1671 self.ui.status(_("requesting all changes\n"))
1672 elif heads is None and remote.capable('changegroupsubset'):
1672 elif heads is None and remote.capable('changegroupsubset'):
1673 # issue1320, avoid a race if remote changed after discovery
1673 # issue1320, avoid a race if remote changed after discovery
1674 heads = rheads
1674 heads = rheads
1675
1675
1676 if remote.capable('getbundle'):
1676 if remote.capable('getbundle'):
1677 cg = remote.getbundle('pull', common=common,
1677 cg = remote.getbundle('pull', common=common,
1678 heads=heads or rheads)
1678 heads=heads or rheads)
1679 elif heads is None:
1679 elif heads is None:
1680 cg = remote.changegroup(fetch, 'pull')
1680 cg = remote.changegroup(fetch, 'pull')
1681 elif not remote.capable('changegroupsubset'):
1681 elif not remote.capable('changegroupsubset'):
1682 raise util.Abort(_("partial pull cannot be done because "
1682 raise util.Abort(_("partial pull cannot be done because "
1683 "other repository doesn't support "
1683 "other repository doesn't support "
1684 "changegroupsubset."))
1684 "changegroupsubset."))
1685 else:
1685 else:
1686 cg = remote.changegroupsubset(fetch, heads, 'pull')
1686 cg = remote.changegroupsubset(fetch, heads, 'pull')
1687 # we use unfiltered changelog here because hidden revision must
1687 # we use unfiltered changelog here because hidden revision must
1688 # be taken in account for phase synchronization. They may
1688 # be taken in account for phase synchronization. They may
1689 # becomes public and becomes visible again.
1689 # becomes public and becomes visible again.
1690 cl = self.unfiltered().changelog
1690 cl = self.unfiltered().changelog
1691 clstart = len(cl)
1691 clstart = len(cl)
1692 result = self.addchangegroup(cg, 'pull', remote.url())
1692 result = self.addchangegroup(cg, 'pull', remote.url())
1693 clend = len(cl)
1693 clend = len(cl)
1694 added = [cl.node(r) for r in xrange(clstart, clend)]
1694 added = [cl.node(r) for r in xrange(clstart, clend)]
1695
1695
1696 # compute target subset
1696 # compute target subset
1697 if heads is None:
1697 if heads is None:
1698 # We pulled every thing possible
1698 # We pulled every thing possible
1699 # sync on everything common
1699 # sync on everything common
1700 subset = common + added
1700 subset = common + added
1701 else:
1701 else:
1702 # We pulled a specific subset
1702 # We pulled a specific subset
1703 # sync on this subset
1703 # sync on this subset
1704 subset = heads
1704 subset = heads
1705
1705
1706 # Get remote phases data from remote
1706 # Get remote phases data from remote
1707 remotephases = remote.listkeys('phases')
1707 remotephases = remote.listkeys('phases')
1708 publishing = bool(remotephases.get('publishing', False))
1708 publishing = bool(remotephases.get('publishing', False))
1709 if remotephases and not publishing:
1709 if remotephases and not publishing:
1710 # remote is new and unpublishing
1710 # remote is new and unpublishing
1711 pheads, _dr = phases.analyzeremotephases(self, subset,
1711 pheads, _dr = phases.analyzeremotephases(self, subset,
1712 remotephases)
1712 remotephases)
1713 phases.advanceboundary(self, phases.public, pheads)
1713 phases.advanceboundary(self, phases.public, pheads)
1714 phases.advanceboundary(self, phases.draft, subset)
1714 phases.advanceboundary(self, phases.draft, subset)
1715 else:
1715 else:
1716 # Remote is old or publishing all common changesets
1716 # Remote is old or publishing all common changesets
1717 # should be seen as public
1717 # should be seen as public
1718 phases.advanceboundary(self, phases.public, subset)
1718 phases.advanceboundary(self, phases.public, subset)
1719
1719
1720 def gettransaction():
1720 def gettransaction():
1721 if tr is None:
1721 if tr is None:
1722 return self.transaction(trname)
1722 return self.transaction(trname)
1723 return tr
1723 return tr
1724
1724
1725 obstr = obsolete.syncpull(self, remote, gettransaction)
1725 obstr = obsolete.syncpull(self, remote, gettransaction)
1726 if obstr is not None:
1726 if obstr is not None:
1727 tr = obstr
1727 tr = obstr
1728
1728
1729 if tr is not None:
1729 if tr is not None:
1730 tr.close()
1730 tr.close()
1731 finally:
1731 finally:
1732 if tr is not None:
1732 if tr is not None:
1733 tr.release()
1733 tr.release()
1734 lock.release()
1734 lock.release()
1735
1735
1736 return result
1736 return result
1737
1737
1738 def checkpush(self, force, revs):
1738 def checkpush(self, force, revs):
1739 """Extensions can override this function if additional checks have
1739 """Extensions can override this function if additional checks have
1740 to be performed before pushing, or call it if they override push
1740 to be performed before pushing, or call it if they override push
1741 command.
1741 command.
1742 """
1742 """
1743 pass
1743 pass
1744
1744
1745 def push(self, remote, force=False, revs=None, newbranch=False):
1745 def push(self, remote, force=False, revs=None, newbranch=False):
1746 '''Push outgoing changesets (limited by revs) from the current
1746 '''Push outgoing changesets (limited by revs) from the current
1747 repository to remote. Return an integer:
1747 repository to remote. Return an integer:
1748 - None means nothing to push
1748 - None means nothing to push
1749 - 0 means HTTP error
1749 - 0 means HTTP error
1750 - 1 means we pushed and remote head count is unchanged *or*
1750 - 1 means we pushed and remote head count is unchanged *or*
1751 we have outgoing changesets but refused to push
1751 we have outgoing changesets but refused to push
1752 - other values as described by addchangegroup()
1752 - other values as described by addchangegroup()
1753 '''
1753 '''
1754 # there are two ways to push to remote repo:
1754 # there are two ways to push to remote repo:
1755 #
1755 #
1756 # addchangegroup assumes local user can lock remote
1756 # addchangegroup assumes local user can lock remote
1757 # repo (local filesystem, old ssh servers).
1757 # repo (local filesystem, old ssh servers).
1758 #
1758 #
1759 # unbundle assumes local user cannot lock remote repo (new ssh
1759 # unbundle assumes local user cannot lock remote repo (new ssh
1760 # servers, http servers).
1760 # servers, http servers).
1761
1761
1762 if not remote.canpush():
1762 if not remote.canpush():
1763 raise util.Abort(_("destination does not support push"))
1763 raise util.Abort(_("destination does not support push"))
1764 unfi = self.unfiltered()
1764 unfi = self.unfiltered()
1765 def localphasemove(nodes, phase=phases.public):
1765 def localphasemove(nodes, phase=phases.public):
1766 """move <nodes> to <phase> in the local source repo"""
1766 """move <nodes> to <phase> in the local source repo"""
1767 phases.advanceboundary(self, phase, nodes)
1767 if locallock is not None:
1768 phases.advanceboundary(self, phase, nodes)
1769 else:
1770 # repo is not locked, do not change any phases!
1771 # Informs the user that phases should have been moved when
1772 # applicable.
1773 actualmoves = [n for n in nodes if phase < self[n].phase()]
1774 phasestr = phases.phasenames[phase]
1775 if actualmoves:
1776 self.ui.status(_('cannot lock source repo, skipping local'
1777 ' %s phase update\n') % phasestr)
1768 # get local lock as we might write phase data
1778 # get local lock as we might write phase data
1769 locallock = self.lock()
1779 locallock = None
1780 try:
1781 locallock = self.lock()
1782 except IOError, err:
1783 if err.errno != errno.EACCES:
1784 raise
1785 # source repo cannot be locked.
1786 # We do not abort the push, but just disable the local phase
1787 # synchronisation.
1788 msg = 'cannot lock source repository: %s\n' % err
1789 self.ui.debug(msg)
1770 try:
1790 try:
1771 self.checkpush(force, revs)
1791 self.checkpush(force, revs)
1772 lock = None
1792 lock = None
1773 unbundle = remote.capable('unbundle')
1793 unbundle = remote.capable('unbundle')
1774 if not unbundle:
1794 if not unbundle:
1775 lock = remote.lock()
1795 lock = remote.lock()
1776 try:
1796 try:
1777 # discovery
1797 # discovery
1778 fci = discovery.findcommonincoming
1798 fci = discovery.findcommonincoming
1779 commoninc = fci(unfi, remote, force=force)
1799 commoninc = fci(unfi, remote, force=force)
1780 common, inc, remoteheads = commoninc
1800 common, inc, remoteheads = commoninc
1781 fco = discovery.findcommonoutgoing
1801 fco = discovery.findcommonoutgoing
1782 outgoing = fco(unfi, remote, onlyheads=revs,
1802 outgoing = fco(unfi, remote, onlyheads=revs,
1783 commoninc=commoninc, force=force)
1803 commoninc=commoninc, force=force)
1784
1804
1785
1805
1786 if not outgoing.missing:
1806 if not outgoing.missing:
1787 # nothing to push
1807 # nothing to push
1788 scmutil.nochangesfound(unfi.ui, unfi, outgoing.excluded)
1808 scmutil.nochangesfound(unfi.ui, unfi, outgoing.excluded)
1789 ret = None
1809 ret = None
1790 else:
1810 else:
1791 # something to push
1811 # something to push
1792 if not force:
1812 if not force:
1793 # if self.obsstore == False --> no obsolete
1813 # if self.obsstore == False --> no obsolete
1794 # then, save the iteration
1814 # then, save the iteration
1795 if unfi.obsstore:
1815 if unfi.obsstore:
1796 # this message are here for 80 char limit reason
1816 # this message are here for 80 char limit reason
1797 mso = _("push includes obsolete changeset: %s!")
1817 mso = _("push includes obsolete changeset: %s!")
1798 mst = "push includes %s changeset: %s!"
1818 mst = "push includes %s changeset: %s!"
1799 # plain versions for i18n tool to detect them
1819 # plain versions for i18n tool to detect them
1800 _("push includes unstable changeset: %s!")
1820 _("push includes unstable changeset: %s!")
1801 _("push includes bumped changeset: %s!")
1821 _("push includes bumped changeset: %s!")
1802 _("push includes divergent changeset: %s!")
1822 _("push includes divergent changeset: %s!")
1803 # If we are to push if there is at least one
1823 # If we are to push if there is at least one
1804 # obsolete or unstable changeset in missing, at
1824 # obsolete or unstable changeset in missing, at
1805 # least one of the missinghead will be obsolete or
1825 # least one of the missinghead will be obsolete or
1806 # unstable. So checking heads only is ok
1826 # unstable. So checking heads only is ok
1807 for node in outgoing.missingheads:
1827 for node in outgoing.missingheads:
1808 ctx = unfi[node]
1828 ctx = unfi[node]
1809 if ctx.obsolete():
1829 if ctx.obsolete():
1810 raise util.Abort(mso % ctx)
1830 raise util.Abort(mso % ctx)
1811 elif ctx.troubled():
1831 elif ctx.troubled():
1812 raise util.Abort(_(mst)
1832 raise util.Abort(_(mst)
1813 % (ctx.troubles()[0],
1833 % (ctx.troubles()[0],
1814 ctx))
1834 ctx))
1815 discovery.checkheads(unfi, remote, outgoing,
1835 discovery.checkheads(unfi, remote, outgoing,
1816 remoteheads, newbranch,
1836 remoteheads, newbranch,
1817 bool(inc))
1837 bool(inc))
1818
1838
1819 # create a changegroup from local
1839 # create a changegroup from local
1820 if revs is None and not outgoing.excluded:
1840 if revs is None and not outgoing.excluded:
1821 # push everything,
1841 # push everything,
1822 # use the fast path, no race possible on push
1842 # use the fast path, no race possible on push
1823 cg = self._changegroup(outgoing.missing, 'push')
1843 cg = self._changegroup(outgoing.missing, 'push')
1824 else:
1844 else:
1825 cg = self.getlocalbundle('push', outgoing)
1845 cg = self.getlocalbundle('push', outgoing)
1826
1846
1827 # apply changegroup to remote
1847 # apply changegroup to remote
1828 if unbundle:
1848 if unbundle:
1829 # local repo finds heads on server, finds out what
1849 # local repo finds heads on server, finds out what
1830 # revs it must push. once revs transferred, if server
1850 # revs it must push. once revs transferred, if server
1831 # finds it has different heads (someone else won
1851 # finds it has different heads (someone else won
1832 # commit/push race), server aborts.
1852 # commit/push race), server aborts.
1833 if force:
1853 if force:
1834 remoteheads = ['force']
1854 remoteheads = ['force']
1835 # ssh: return remote's addchangegroup()
1855 # ssh: return remote's addchangegroup()
1836 # http: return remote's addchangegroup() or 0 for error
1856 # http: return remote's addchangegroup() or 0 for error
1837 ret = remote.unbundle(cg, remoteheads, 'push')
1857 ret = remote.unbundle(cg, remoteheads, 'push')
1838 else:
1858 else:
1839 # we return an integer indicating remote head count
1859 # we return an integer indicating remote head count
1840 # change
1860 # change
1841 ret = remote.addchangegroup(cg, 'push', self.url())
1861 ret = remote.addchangegroup(cg, 'push', self.url())
1842
1862
1843 if ret:
1863 if ret:
1844 # push succeed, synchronize target of the push
1864 # push succeed, synchronize target of the push
1845 cheads = outgoing.missingheads
1865 cheads = outgoing.missingheads
1846 elif revs is None:
1866 elif revs is None:
1847 # All out push fails. synchronize all common
1867 # All out push fails. synchronize all common
1848 cheads = outgoing.commonheads
1868 cheads = outgoing.commonheads
1849 else:
1869 else:
1850 # I want cheads = heads(::missingheads and ::commonheads)
1870 # I want cheads = heads(::missingheads and ::commonheads)
1851 # (missingheads is revs with secret changeset filtered out)
1871 # (missingheads is revs with secret changeset filtered out)
1852 #
1872 #
1853 # This can be expressed as:
1873 # This can be expressed as:
1854 # cheads = ( (missingheads and ::commonheads)
1874 # cheads = ( (missingheads and ::commonheads)
1855 # + (commonheads and ::missingheads))"
1875 # + (commonheads and ::missingheads))"
1856 # )
1876 # )
1857 #
1877 #
1858 # while trying to push we already computed the following:
1878 # while trying to push we already computed the following:
1859 # common = (::commonheads)
1879 # common = (::commonheads)
1860 # missing = ((commonheads::missingheads) - commonheads)
1880 # missing = ((commonheads::missingheads) - commonheads)
1861 #
1881 #
1862 # We can pick:
1882 # We can pick:
1863 # * missingheads part of common (::commonheads)
1883 # * missingheads part of common (::commonheads)
1864 common = set(outgoing.common)
1884 common = set(outgoing.common)
1865 cheads = [node for node in revs if node in common]
1885 cheads = [node for node in revs if node in common]
1866 # and
1886 # and
1867 # * commonheads parents on missing
1887 # * commonheads parents on missing
1868 revset = unfi.set('%ln and parents(roots(%ln))',
1888 revset = unfi.set('%ln and parents(roots(%ln))',
1869 outgoing.commonheads,
1889 outgoing.commonheads,
1870 outgoing.missing)
1890 outgoing.missing)
1871 cheads.extend(c.node() for c in revset)
1891 cheads.extend(c.node() for c in revset)
1872 # even when we don't push, exchanging phase data is useful
1892 # even when we don't push, exchanging phase data is useful
1873 remotephases = remote.listkeys('phases')
1893 remotephases = remote.listkeys('phases')
1874 if (self.ui.configbool('ui', '_usedassubrepo', False)
1894 if (self.ui.configbool('ui', '_usedassubrepo', False)
1875 and remotephases # server supports phases
1895 and remotephases # server supports phases
1876 and ret is None # nothing was pushed
1896 and ret is None # nothing was pushed
1877 and remotephases.get('publishing', False)):
1897 and remotephases.get('publishing', False)):
1878 # When:
1898 # When:
1879 # - this is a subrepo push
1899 # - this is a subrepo push
1880 # - and remote support phase
1900 # - and remote support phase
1881 # - and no changeset was pushed
1901 # - and no changeset was pushed
1882 # - and remote is publishing
1902 # - and remote is publishing
1883 # We may be in issue 3871 case!
1903 # We may be in issue 3871 case!
1884 # We drop the possible phase synchronisation done by
1904 # We drop the possible phase synchronisation done by
1885 # courtesy to publish changesets possibly locally draft
1905 # courtesy to publish changesets possibly locally draft
1886 # on the remote.
1906 # on the remote.
1887 remotephases = {'publishing': 'True'}
1907 remotephases = {'publishing': 'True'}
1888 if not remotephases: # old server or public only repo
1908 if not remotephases: # old server or public only repo
1889 localphasemove(cheads)
1909 localphasemove(cheads)
1890 # don't push any phase data as there is nothing to push
1910 # don't push any phase data as there is nothing to push
1891 else:
1911 else:
1892 ana = phases.analyzeremotephases(self, cheads, remotephases)
1912 ana = phases.analyzeremotephases(self, cheads, remotephases)
1893 pheads, droots = ana
1913 pheads, droots = ana
1894 ### Apply remote phase on local
1914 ### Apply remote phase on local
1895 if remotephases.get('publishing', False):
1915 if remotephases.get('publishing', False):
1896 localphasemove(cheads)
1916 localphasemove(cheads)
1897 else: # publish = False
1917 else: # publish = False
1898 localphasemove(pheads)
1918 localphasemove(pheads)
1899 localphasemove(cheads, phases.draft)
1919 localphasemove(cheads, phases.draft)
1900 ### Apply local phase on remote
1920 ### Apply local phase on remote
1901
1921
1902 # Get the list of all revs draft on remote by public here.
1922 # Get the list of all revs draft on remote by public here.
1903 # XXX Beware that revset break if droots is not strictly
1923 # XXX Beware that revset break if droots is not strictly
1904 # XXX root we may want to ensure it is but it is costly
1924 # XXX root we may want to ensure it is but it is costly
1905 outdated = unfi.set('heads((%ln::%ln) and public())',
1925 outdated = unfi.set('heads((%ln::%ln) and public())',
1906 droots, cheads)
1926 droots, cheads)
1907 for newremotehead in outdated:
1927 for newremotehead in outdated:
1908 r = remote.pushkey('phases',
1928 r = remote.pushkey('phases',
1909 newremotehead.hex(),
1929 newremotehead.hex(),
1910 str(phases.draft),
1930 str(phases.draft),
1911 str(phases.public))
1931 str(phases.public))
1912 if not r:
1932 if not r:
1913 self.ui.warn(_('updating %s to public failed!\n')
1933 self.ui.warn(_('updating %s to public failed!\n')
1914 % newremotehead)
1934 % newremotehead)
1915 self.ui.debug('try to push obsolete markers to remote\n')
1935 self.ui.debug('try to push obsolete markers to remote\n')
1916 obsolete.syncpush(self, remote)
1936 obsolete.syncpush(self, remote)
1917 finally:
1937 finally:
1918 if lock is not None:
1938 if lock is not None:
1919 lock.release()
1939 lock.release()
1920 finally:
1940 finally:
1921 locallock.release()
1941 if locallock is not None:
1942 locallock.release()
1922
1943
1923 self.ui.debug("checking for updated bookmarks\n")
1944 self.ui.debug("checking for updated bookmarks\n")
1924 rb = remote.listkeys('bookmarks')
1945 rb = remote.listkeys('bookmarks')
1925 for k in rb.keys():
1946 for k in rb.keys():
1926 if k in unfi._bookmarks:
1947 if k in unfi._bookmarks:
1927 nr, nl = rb[k], hex(self._bookmarks[k])
1948 nr, nl = rb[k], hex(self._bookmarks[k])
1928 if nr in unfi:
1949 if nr in unfi:
1929 cr = unfi[nr]
1950 cr = unfi[nr]
1930 cl = unfi[nl]
1951 cl = unfi[nl]
1931 if bookmarks.validdest(unfi, cr, cl):
1952 if bookmarks.validdest(unfi, cr, cl):
1932 r = remote.pushkey('bookmarks', k, nr, nl)
1953 r = remote.pushkey('bookmarks', k, nr, nl)
1933 if r:
1954 if r:
1934 self.ui.status(_("updating bookmark %s\n") % k)
1955 self.ui.status(_("updating bookmark %s\n") % k)
1935 else:
1956 else:
1936 self.ui.warn(_('updating bookmark %s'
1957 self.ui.warn(_('updating bookmark %s'
1937 ' failed!\n') % k)
1958 ' failed!\n') % k)
1938
1959
1939 return ret
1960 return ret
1940
1961
1941 def changegroupinfo(self, nodes, source):
1962 def changegroupinfo(self, nodes, source):
1942 if self.ui.verbose or source == 'bundle':
1963 if self.ui.verbose or source == 'bundle':
1943 self.ui.status(_("%d changesets found\n") % len(nodes))
1964 self.ui.status(_("%d changesets found\n") % len(nodes))
1944 if self.ui.debugflag:
1965 if self.ui.debugflag:
1945 self.ui.debug("list of changesets:\n")
1966 self.ui.debug("list of changesets:\n")
1946 for node in nodes:
1967 for node in nodes:
1947 self.ui.debug("%s\n" % hex(node))
1968 self.ui.debug("%s\n" % hex(node))
1948
1969
1949 def changegroupsubset(self, bases, heads, source):
1970 def changegroupsubset(self, bases, heads, source):
1950 """Compute a changegroup consisting of all the nodes that are
1971 """Compute a changegroup consisting of all the nodes that are
1951 descendants of any of the bases and ancestors of any of the heads.
1972 descendants of any of the bases and ancestors of any of the heads.
1952 Return a chunkbuffer object whose read() method will return
1973 Return a chunkbuffer object whose read() method will return
1953 successive changegroup chunks.
1974 successive changegroup chunks.
1954
1975
1955 It is fairly complex as determining which filenodes and which
1976 It is fairly complex as determining which filenodes and which
1956 manifest nodes need to be included for the changeset to be complete
1977 manifest nodes need to be included for the changeset to be complete
1957 is non-trivial.
1978 is non-trivial.
1958
1979
1959 Another wrinkle is doing the reverse, figuring out which changeset in
1980 Another wrinkle is doing the reverse, figuring out which changeset in
1960 the changegroup a particular filenode or manifestnode belongs to.
1981 the changegroup a particular filenode or manifestnode belongs to.
1961 """
1982 """
1962 cl = self.changelog
1983 cl = self.changelog
1963 if not bases:
1984 if not bases:
1964 bases = [nullid]
1985 bases = [nullid]
1965 csets, bases, heads = cl.nodesbetween(bases, heads)
1986 csets, bases, heads = cl.nodesbetween(bases, heads)
1966 # We assume that all ancestors of bases are known
1987 # We assume that all ancestors of bases are known
1967 common = cl.ancestors([cl.rev(n) for n in bases])
1988 common = cl.ancestors([cl.rev(n) for n in bases])
1968 return self._changegroupsubset(common, csets, heads, source)
1989 return self._changegroupsubset(common, csets, heads, source)
1969
1990
1970 def getlocalbundle(self, source, outgoing):
1991 def getlocalbundle(self, source, outgoing):
1971 """Like getbundle, but taking a discovery.outgoing as an argument.
1992 """Like getbundle, but taking a discovery.outgoing as an argument.
1972
1993
1973 This is only implemented for local repos and reuses potentially
1994 This is only implemented for local repos and reuses potentially
1974 precomputed sets in outgoing."""
1995 precomputed sets in outgoing."""
1975 if not outgoing.missing:
1996 if not outgoing.missing:
1976 return None
1997 return None
1977 return self._changegroupsubset(outgoing.common,
1998 return self._changegroupsubset(outgoing.common,
1978 outgoing.missing,
1999 outgoing.missing,
1979 outgoing.missingheads,
2000 outgoing.missingheads,
1980 source)
2001 source)
1981
2002
1982 def getbundle(self, source, heads=None, common=None):
2003 def getbundle(self, source, heads=None, common=None):
1983 """Like changegroupsubset, but returns the set difference between the
2004 """Like changegroupsubset, but returns the set difference between the
1984 ancestors of heads and the ancestors common.
2005 ancestors of heads and the ancestors common.
1985
2006
1986 If heads is None, use the local heads. If common is None, use [nullid].
2007 If heads is None, use the local heads. If common is None, use [nullid].
1987
2008
1988 The nodes in common might not all be known locally due to the way the
2009 The nodes in common might not all be known locally due to the way the
1989 current discovery protocol works.
2010 current discovery protocol works.
1990 """
2011 """
1991 cl = self.changelog
2012 cl = self.changelog
1992 if common:
2013 if common:
1993 hasnode = cl.hasnode
2014 hasnode = cl.hasnode
1994 common = [n for n in common if hasnode(n)]
2015 common = [n for n in common if hasnode(n)]
1995 else:
2016 else:
1996 common = [nullid]
2017 common = [nullid]
1997 if not heads:
2018 if not heads:
1998 heads = cl.heads()
2019 heads = cl.heads()
1999 return self.getlocalbundle(source,
2020 return self.getlocalbundle(source,
2000 discovery.outgoing(cl, common, heads))
2021 discovery.outgoing(cl, common, heads))
2001
2022
2002 @unfilteredmethod
2023 @unfilteredmethod
2003 def _changegroupsubset(self, commonrevs, csets, heads, source):
2024 def _changegroupsubset(self, commonrevs, csets, heads, source):
2004
2025
2005 cl = self.changelog
2026 cl = self.changelog
2006 mf = self.manifest
2027 mf = self.manifest
2007 mfs = {} # needed manifests
2028 mfs = {} # needed manifests
2008 fnodes = {} # needed file nodes
2029 fnodes = {} # needed file nodes
2009 changedfiles = set()
2030 changedfiles = set()
2010 fstate = ['', {}]
2031 fstate = ['', {}]
2011 count = [0, 0]
2032 count = [0, 0]
2012
2033
2013 # can we go through the fast path ?
2034 # can we go through the fast path ?
2014 heads.sort()
2035 heads.sort()
2015 if heads == sorted(self.heads()):
2036 if heads == sorted(self.heads()):
2016 return self._changegroup(csets, source)
2037 return self._changegroup(csets, source)
2017
2038
2018 # slow path
2039 # slow path
2019 self.hook('preoutgoing', throw=True, source=source)
2040 self.hook('preoutgoing', throw=True, source=source)
2020 self.changegroupinfo(csets, source)
2041 self.changegroupinfo(csets, source)
2021
2042
2022 # filter any nodes that claim to be part of the known set
2043 # filter any nodes that claim to be part of the known set
2023 def prune(revlog, missing):
2044 def prune(revlog, missing):
2024 rr, rl = revlog.rev, revlog.linkrev
2045 rr, rl = revlog.rev, revlog.linkrev
2025 return [n for n in missing
2046 return [n for n in missing
2026 if rl(rr(n)) not in commonrevs]
2047 if rl(rr(n)) not in commonrevs]
2027
2048
2028 progress = self.ui.progress
2049 progress = self.ui.progress
2029 _bundling = _('bundling')
2050 _bundling = _('bundling')
2030 _changesets = _('changesets')
2051 _changesets = _('changesets')
2031 _manifests = _('manifests')
2052 _manifests = _('manifests')
2032 _files = _('files')
2053 _files = _('files')
2033
2054
2034 def lookup(revlog, x):
2055 def lookup(revlog, x):
2035 if revlog == cl:
2056 if revlog == cl:
2036 c = cl.read(x)
2057 c = cl.read(x)
2037 changedfiles.update(c[3])
2058 changedfiles.update(c[3])
2038 mfs.setdefault(c[0], x)
2059 mfs.setdefault(c[0], x)
2039 count[0] += 1
2060 count[0] += 1
2040 progress(_bundling, count[0],
2061 progress(_bundling, count[0],
2041 unit=_changesets, total=count[1])
2062 unit=_changesets, total=count[1])
2042 return x
2063 return x
2043 elif revlog == mf:
2064 elif revlog == mf:
2044 clnode = mfs[x]
2065 clnode = mfs[x]
2045 mdata = mf.readfast(x)
2066 mdata = mf.readfast(x)
2046 for f, n in mdata.iteritems():
2067 for f, n in mdata.iteritems():
2047 if f in changedfiles:
2068 if f in changedfiles:
2048 fnodes[f].setdefault(n, clnode)
2069 fnodes[f].setdefault(n, clnode)
2049 count[0] += 1
2070 count[0] += 1
2050 progress(_bundling, count[0],
2071 progress(_bundling, count[0],
2051 unit=_manifests, total=count[1])
2072 unit=_manifests, total=count[1])
2052 return clnode
2073 return clnode
2053 else:
2074 else:
2054 progress(_bundling, count[0], item=fstate[0],
2075 progress(_bundling, count[0], item=fstate[0],
2055 unit=_files, total=count[1])
2076 unit=_files, total=count[1])
2056 return fstate[1][x]
2077 return fstate[1][x]
2057
2078
2058 bundler = changegroup.bundle10(lookup)
2079 bundler = changegroup.bundle10(lookup)
2059 reorder = self.ui.config('bundle', 'reorder', 'auto')
2080 reorder = self.ui.config('bundle', 'reorder', 'auto')
2060 if reorder == 'auto':
2081 if reorder == 'auto':
2061 reorder = None
2082 reorder = None
2062 else:
2083 else:
2063 reorder = util.parsebool(reorder)
2084 reorder = util.parsebool(reorder)
2064
2085
2065 def gengroup():
2086 def gengroup():
2066 # Create a changenode group generator that will call our functions
2087 # Create a changenode group generator that will call our functions
2067 # back to lookup the owning changenode and collect information.
2088 # back to lookup the owning changenode and collect information.
2068 count[:] = [0, len(csets)]
2089 count[:] = [0, len(csets)]
2069 for chunk in cl.group(csets, bundler, reorder=reorder):
2090 for chunk in cl.group(csets, bundler, reorder=reorder):
2070 yield chunk
2091 yield chunk
2071 progress(_bundling, None)
2092 progress(_bundling, None)
2072
2093
2073 # Create a generator for the manifestnodes that calls our lookup
2094 # Create a generator for the manifestnodes that calls our lookup
2074 # and data collection functions back.
2095 # and data collection functions back.
2075 for f in changedfiles:
2096 for f in changedfiles:
2076 fnodes[f] = {}
2097 fnodes[f] = {}
2077 count[:] = [0, len(mfs)]
2098 count[:] = [0, len(mfs)]
2078 for chunk in mf.group(prune(mf, mfs), bundler, reorder=reorder):
2099 for chunk in mf.group(prune(mf, mfs), bundler, reorder=reorder):
2079 yield chunk
2100 yield chunk
2080 progress(_bundling, None)
2101 progress(_bundling, None)
2081
2102
2082 mfs.clear()
2103 mfs.clear()
2083
2104
2084 # Go through all our files in order sorted by name.
2105 # Go through all our files in order sorted by name.
2085 count[:] = [0, len(changedfiles)]
2106 count[:] = [0, len(changedfiles)]
2086 for fname in sorted(changedfiles):
2107 for fname in sorted(changedfiles):
2087 filerevlog = self.file(fname)
2108 filerevlog = self.file(fname)
2088 if not len(filerevlog):
2109 if not len(filerevlog):
2089 raise util.Abort(_("empty or missing revlog for %s")
2110 raise util.Abort(_("empty or missing revlog for %s")
2090 % fname)
2111 % fname)
2091 fstate[0] = fname
2112 fstate[0] = fname
2092 fstate[1] = fnodes.pop(fname, {})
2113 fstate[1] = fnodes.pop(fname, {})
2093
2114
2094 nodelist = prune(filerevlog, fstate[1])
2115 nodelist = prune(filerevlog, fstate[1])
2095 if nodelist:
2116 if nodelist:
2096 count[0] += 1
2117 count[0] += 1
2097 yield bundler.fileheader(fname)
2118 yield bundler.fileheader(fname)
2098 for chunk in filerevlog.group(nodelist, bundler, reorder):
2119 for chunk in filerevlog.group(nodelist, bundler, reorder):
2099 yield chunk
2120 yield chunk
2100
2121
2101 # Signal that no more groups are left.
2122 # Signal that no more groups are left.
2102 yield bundler.close()
2123 yield bundler.close()
2103 progress(_bundling, None)
2124 progress(_bundling, None)
2104
2125
2105 if csets:
2126 if csets:
2106 self.hook('outgoing', node=hex(csets[0]), source=source)
2127 self.hook('outgoing', node=hex(csets[0]), source=source)
2107
2128
2108 return changegroup.unbundle10(util.chunkbuffer(gengroup()), 'UN')
2129 return changegroup.unbundle10(util.chunkbuffer(gengroup()), 'UN')
2109
2130
2110 def changegroup(self, basenodes, source):
2131 def changegroup(self, basenodes, source):
2111 # to avoid a race we use changegroupsubset() (issue1320)
2132 # to avoid a race we use changegroupsubset() (issue1320)
2112 return self.changegroupsubset(basenodes, self.heads(), source)
2133 return self.changegroupsubset(basenodes, self.heads(), source)
2113
2134
2114 @unfilteredmethod
2135 @unfilteredmethod
2115 def _changegroup(self, nodes, source):
2136 def _changegroup(self, nodes, source):
2116 """Compute the changegroup of all nodes that we have that a recipient
2137 """Compute the changegroup of all nodes that we have that a recipient
2117 doesn't. Return a chunkbuffer object whose read() method will return
2138 doesn't. Return a chunkbuffer object whose read() method will return
2118 successive changegroup chunks.
2139 successive changegroup chunks.
2119
2140
2120 This is much easier than the previous function as we can assume that
2141 This is much easier than the previous function as we can assume that
2121 the recipient has any changenode we aren't sending them.
2142 the recipient has any changenode we aren't sending them.
2122
2143
2123 nodes is the set of nodes to send"""
2144 nodes is the set of nodes to send"""
2124
2145
2125 cl = self.changelog
2146 cl = self.changelog
2126 mf = self.manifest
2147 mf = self.manifest
2127 mfs = {}
2148 mfs = {}
2128 changedfiles = set()
2149 changedfiles = set()
2129 fstate = ['']
2150 fstate = ['']
2130 count = [0, 0]
2151 count = [0, 0]
2131
2152
2132 self.hook('preoutgoing', throw=True, source=source)
2153 self.hook('preoutgoing', throw=True, source=source)
2133 self.changegroupinfo(nodes, source)
2154 self.changegroupinfo(nodes, source)
2134
2155
2135 revset = set([cl.rev(n) for n in nodes])
2156 revset = set([cl.rev(n) for n in nodes])
2136
2157
2137 def gennodelst(log):
2158 def gennodelst(log):
2138 ln, llr = log.node, log.linkrev
2159 ln, llr = log.node, log.linkrev
2139 return [ln(r) for r in log if llr(r) in revset]
2160 return [ln(r) for r in log if llr(r) in revset]
2140
2161
2141 progress = self.ui.progress
2162 progress = self.ui.progress
2142 _bundling = _('bundling')
2163 _bundling = _('bundling')
2143 _changesets = _('changesets')
2164 _changesets = _('changesets')
2144 _manifests = _('manifests')
2165 _manifests = _('manifests')
2145 _files = _('files')
2166 _files = _('files')
2146
2167
2147 def lookup(revlog, x):
2168 def lookup(revlog, x):
2148 if revlog == cl:
2169 if revlog == cl:
2149 c = cl.read(x)
2170 c = cl.read(x)
2150 changedfiles.update(c[3])
2171 changedfiles.update(c[3])
2151 mfs.setdefault(c[0], x)
2172 mfs.setdefault(c[0], x)
2152 count[0] += 1
2173 count[0] += 1
2153 progress(_bundling, count[0],
2174 progress(_bundling, count[0],
2154 unit=_changesets, total=count[1])
2175 unit=_changesets, total=count[1])
2155 return x
2176 return x
2156 elif revlog == mf:
2177 elif revlog == mf:
2157 count[0] += 1
2178 count[0] += 1
2158 progress(_bundling, count[0],
2179 progress(_bundling, count[0],
2159 unit=_manifests, total=count[1])
2180 unit=_manifests, total=count[1])
2160 return cl.node(revlog.linkrev(revlog.rev(x)))
2181 return cl.node(revlog.linkrev(revlog.rev(x)))
2161 else:
2182 else:
2162 progress(_bundling, count[0], item=fstate[0],
2183 progress(_bundling, count[0], item=fstate[0],
2163 total=count[1], unit=_files)
2184 total=count[1], unit=_files)
2164 return cl.node(revlog.linkrev(revlog.rev(x)))
2185 return cl.node(revlog.linkrev(revlog.rev(x)))
2165
2186
2166 bundler = changegroup.bundle10(lookup)
2187 bundler = changegroup.bundle10(lookup)
2167 reorder = self.ui.config('bundle', 'reorder', 'auto')
2188 reorder = self.ui.config('bundle', 'reorder', 'auto')
2168 if reorder == 'auto':
2189 if reorder == 'auto':
2169 reorder = None
2190 reorder = None
2170 else:
2191 else:
2171 reorder = util.parsebool(reorder)
2192 reorder = util.parsebool(reorder)
2172
2193
2173 def gengroup():
2194 def gengroup():
2174 '''yield a sequence of changegroup chunks (strings)'''
2195 '''yield a sequence of changegroup chunks (strings)'''
2175 # construct a list of all changed files
2196 # construct a list of all changed files
2176
2197
2177 count[:] = [0, len(nodes)]
2198 count[:] = [0, len(nodes)]
2178 for chunk in cl.group(nodes, bundler, reorder=reorder):
2199 for chunk in cl.group(nodes, bundler, reorder=reorder):
2179 yield chunk
2200 yield chunk
2180 progress(_bundling, None)
2201 progress(_bundling, None)
2181
2202
2182 count[:] = [0, len(mfs)]
2203 count[:] = [0, len(mfs)]
2183 for chunk in mf.group(gennodelst(mf), bundler, reorder=reorder):
2204 for chunk in mf.group(gennodelst(mf), bundler, reorder=reorder):
2184 yield chunk
2205 yield chunk
2185 progress(_bundling, None)
2206 progress(_bundling, None)
2186
2207
2187 count[:] = [0, len(changedfiles)]
2208 count[:] = [0, len(changedfiles)]
2188 for fname in sorted(changedfiles):
2209 for fname in sorted(changedfiles):
2189 filerevlog = self.file(fname)
2210 filerevlog = self.file(fname)
2190 if not len(filerevlog):
2211 if not len(filerevlog):
2191 raise util.Abort(_("empty or missing revlog for %s")
2212 raise util.Abort(_("empty or missing revlog for %s")
2192 % fname)
2213 % fname)
2193 fstate[0] = fname
2214 fstate[0] = fname
2194 nodelist = gennodelst(filerevlog)
2215 nodelist = gennodelst(filerevlog)
2195 if nodelist:
2216 if nodelist:
2196 count[0] += 1
2217 count[0] += 1
2197 yield bundler.fileheader(fname)
2218 yield bundler.fileheader(fname)
2198 for chunk in filerevlog.group(nodelist, bundler, reorder):
2219 for chunk in filerevlog.group(nodelist, bundler, reorder):
2199 yield chunk
2220 yield chunk
2200 yield bundler.close()
2221 yield bundler.close()
2201 progress(_bundling, None)
2222 progress(_bundling, None)
2202
2223
2203 if nodes:
2224 if nodes:
2204 self.hook('outgoing', node=hex(nodes[0]), source=source)
2225 self.hook('outgoing', node=hex(nodes[0]), source=source)
2205
2226
2206 return changegroup.unbundle10(util.chunkbuffer(gengroup()), 'UN')
2227 return changegroup.unbundle10(util.chunkbuffer(gengroup()), 'UN')
2207
2228
2208 @unfilteredmethod
2229 @unfilteredmethod
2209 def addchangegroup(self, source, srctype, url, emptyok=False):
2230 def addchangegroup(self, source, srctype, url, emptyok=False):
2210 """Add the changegroup returned by source.read() to this repo.
2231 """Add the changegroup returned by source.read() to this repo.
2211 srctype is a string like 'push', 'pull', or 'unbundle'. url is
2232 srctype is a string like 'push', 'pull', or 'unbundle'. url is
2212 the URL of the repo where this changegroup is coming from.
2233 the URL of the repo where this changegroup is coming from.
2213
2234
2214 Return an integer summarizing the change to this repo:
2235 Return an integer summarizing the change to this repo:
2215 - nothing changed or no source: 0
2236 - nothing changed or no source: 0
2216 - more heads than before: 1+added heads (2..n)
2237 - more heads than before: 1+added heads (2..n)
2217 - fewer heads than before: -1-removed heads (-2..-n)
2238 - fewer heads than before: -1-removed heads (-2..-n)
2218 - number of heads stays the same: 1
2239 - number of heads stays the same: 1
2219 """
2240 """
2220 def csmap(x):
2241 def csmap(x):
2221 self.ui.debug("add changeset %s\n" % short(x))
2242 self.ui.debug("add changeset %s\n" % short(x))
2222 return len(cl)
2243 return len(cl)
2223
2244
2224 def revmap(x):
2245 def revmap(x):
2225 return cl.rev(x)
2246 return cl.rev(x)
2226
2247
2227 if not source:
2248 if not source:
2228 return 0
2249 return 0
2229
2250
2230 self.hook('prechangegroup', throw=True, source=srctype, url=url)
2251 self.hook('prechangegroup', throw=True, source=srctype, url=url)
2231
2252
2232 changesets = files = revisions = 0
2253 changesets = files = revisions = 0
2233 efiles = set()
2254 efiles = set()
2234
2255
2235 # write changelog data to temp files so concurrent readers will not see
2256 # write changelog data to temp files so concurrent readers will not see
2236 # inconsistent view
2257 # inconsistent view
2237 cl = self.changelog
2258 cl = self.changelog
2238 cl.delayupdate()
2259 cl.delayupdate()
2239 oldheads = cl.heads()
2260 oldheads = cl.heads()
2240
2261
2241 tr = self.transaction("\n".join([srctype, util.hidepassword(url)]))
2262 tr = self.transaction("\n".join([srctype, util.hidepassword(url)]))
2242 try:
2263 try:
2243 trp = weakref.proxy(tr)
2264 trp = weakref.proxy(tr)
2244 # pull off the changeset group
2265 # pull off the changeset group
2245 self.ui.status(_("adding changesets\n"))
2266 self.ui.status(_("adding changesets\n"))
2246 clstart = len(cl)
2267 clstart = len(cl)
2247 class prog(object):
2268 class prog(object):
2248 step = _('changesets')
2269 step = _('changesets')
2249 count = 1
2270 count = 1
2250 ui = self.ui
2271 ui = self.ui
2251 total = None
2272 total = None
2252 def __call__(self):
2273 def __call__(self):
2253 self.ui.progress(self.step, self.count, unit=_('chunks'),
2274 self.ui.progress(self.step, self.count, unit=_('chunks'),
2254 total=self.total)
2275 total=self.total)
2255 self.count += 1
2276 self.count += 1
2256 pr = prog()
2277 pr = prog()
2257 source.callback = pr
2278 source.callback = pr
2258
2279
2259 source.changelogheader()
2280 source.changelogheader()
2260 srccontent = cl.addgroup(source, csmap, trp)
2281 srccontent = cl.addgroup(source, csmap, trp)
2261 if not (srccontent or emptyok):
2282 if not (srccontent or emptyok):
2262 raise util.Abort(_("received changelog group is empty"))
2283 raise util.Abort(_("received changelog group is empty"))
2263 clend = len(cl)
2284 clend = len(cl)
2264 changesets = clend - clstart
2285 changesets = clend - clstart
2265 for c in xrange(clstart, clend):
2286 for c in xrange(clstart, clend):
2266 efiles.update(self[c].files())
2287 efiles.update(self[c].files())
2267 efiles = len(efiles)
2288 efiles = len(efiles)
2268 self.ui.progress(_('changesets'), None)
2289 self.ui.progress(_('changesets'), None)
2269
2290
2270 # pull off the manifest group
2291 # pull off the manifest group
2271 self.ui.status(_("adding manifests\n"))
2292 self.ui.status(_("adding manifests\n"))
2272 pr.step = _('manifests')
2293 pr.step = _('manifests')
2273 pr.count = 1
2294 pr.count = 1
2274 pr.total = changesets # manifests <= changesets
2295 pr.total = changesets # manifests <= changesets
2275 # no need to check for empty manifest group here:
2296 # no need to check for empty manifest group here:
2276 # if the result of the merge of 1 and 2 is the same in 3 and 4,
2297 # if the result of the merge of 1 and 2 is the same in 3 and 4,
2277 # no new manifest will be created and the manifest group will
2298 # no new manifest will be created and the manifest group will
2278 # be empty during the pull
2299 # be empty during the pull
2279 source.manifestheader()
2300 source.manifestheader()
2280 self.manifest.addgroup(source, revmap, trp)
2301 self.manifest.addgroup(source, revmap, trp)
2281 self.ui.progress(_('manifests'), None)
2302 self.ui.progress(_('manifests'), None)
2282
2303
2283 needfiles = {}
2304 needfiles = {}
2284 if self.ui.configbool('server', 'validate', default=False):
2305 if self.ui.configbool('server', 'validate', default=False):
2285 # validate incoming csets have their manifests
2306 # validate incoming csets have their manifests
2286 for cset in xrange(clstart, clend):
2307 for cset in xrange(clstart, clend):
2287 mfest = self.changelog.read(self.changelog.node(cset))[0]
2308 mfest = self.changelog.read(self.changelog.node(cset))[0]
2288 mfest = self.manifest.readdelta(mfest)
2309 mfest = self.manifest.readdelta(mfest)
2289 # store file nodes we must see
2310 # store file nodes we must see
2290 for f, n in mfest.iteritems():
2311 for f, n in mfest.iteritems():
2291 needfiles.setdefault(f, set()).add(n)
2312 needfiles.setdefault(f, set()).add(n)
2292
2313
2293 # process the files
2314 # process the files
2294 self.ui.status(_("adding file changes\n"))
2315 self.ui.status(_("adding file changes\n"))
2295 pr.step = _('files')
2316 pr.step = _('files')
2296 pr.count = 1
2317 pr.count = 1
2297 pr.total = efiles
2318 pr.total = efiles
2298 source.callback = None
2319 source.callback = None
2299
2320
2300 while True:
2321 while True:
2301 chunkdata = source.filelogheader()
2322 chunkdata = source.filelogheader()
2302 if not chunkdata:
2323 if not chunkdata:
2303 break
2324 break
2304 f = chunkdata["filename"]
2325 f = chunkdata["filename"]
2305 self.ui.debug("adding %s revisions\n" % f)
2326 self.ui.debug("adding %s revisions\n" % f)
2306 pr()
2327 pr()
2307 fl = self.file(f)
2328 fl = self.file(f)
2308 o = len(fl)
2329 o = len(fl)
2309 if not fl.addgroup(source, revmap, trp):
2330 if not fl.addgroup(source, revmap, trp):
2310 raise util.Abort(_("received file revlog group is empty"))
2331 raise util.Abort(_("received file revlog group is empty"))
2311 revisions += len(fl) - o
2332 revisions += len(fl) - o
2312 files += 1
2333 files += 1
2313 if f in needfiles:
2334 if f in needfiles:
2314 needs = needfiles[f]
2335 needs = needfiles[f]
2315 for new in xrange(o, len(fl)):
2336 for new in xrange(o, len(fl)):
2316 n = fl.node(new)
2337 n = fl.node(new)
2317 if n in needs:
2338 if n in needs:
2318 needs.remove(n)
2339 needs.remove(n)
2319 else:
2340 else:
2320 raise util.Abort(
2341 raise util.Abort(
2321 _("received spurious file revlog entry"))
2342 _("received spurious file revlog entry"))
2322 if not needs:
2343 if not needs:
2323 del needfiles[f]
2344 del needfiles[f]
2324 self.ui.progress(_('files'), None)
2345 self.ui.progress(_('files'), None)
2325
2346
2326 for f, needs in needfiles.iteritems():
2347 for f, needs in needfiles.iteritems():
2327 fl = self.file(f)
2348 fl = self.file(f)
2328 for n in needs:
2349 for n in needs:
2329 try:
2350 try:
2330 fl.rev(n)
2351 fl.rev(n)
2331 except error.LookupError:
2352 except error.LookupError:
2332 raise util.Abort(
2353 raise util.Abort(
2333 _('missing file data for %s:%s - run hg verify') %
2354 _('missing file data for %s:%s - run hg verify') %
2334 (f, hex(n)))
2355 (f, hex(n)))
2335
2356
2336 dh = 0
2357 dh = 0
2337 if oldheads:
2358 if oldheads:
2338 heads = cl.heads()
2359 heads = cl.heads()
2339 dh = len(heads) - len(oldheads)
2360 dh = len(heads) - len(oldheads)
2340 for h in heads:
2361 for h in heads:
2341 if h not in oldheads and self[h].closesbranch():
2362 if h not in oldheads and self[h].closesbranch():
2342 dh -= 1
2363 dh -= 1
2343 htext = ""
2364 htext = ""
2344 if dh:
2365 if dh:
2345 htext = _(" (%+d heads)") % dh
2366 htext = _(" (%+d heads)") % dh
2346
2367
2347 self.ui.status(_("added %d changesets"
2368 self.ui.status(_("added %d changesets"
2348 " with %d changes to %d files%s\n")
2369 " with %d changes to %d files%s\n")
2349 % (changesets, revisions, files, htext))
2370 % (changesets, revisions, files, htext))
2350 self.invalidatevolatilesets()
2371 self.invalidatevolatilesets()
2351
2372
2352 if changesets > 0:
2373 if changesets > 0:
2353 p = lambda: cl.writepending() and self.root or ""
2374 p = lambda: cl.writepending() and self.root or ""
2354 self.hook('pretxnchangegroup', throw=True,
2375 self.hook('pretxnchangegroup', throw=True,
2355 node=hex(cl.node(clstart)), source=srctype,
2376 node=hex(cl.node(clstart)), source=srctype,
2356 url=url, pending=p)
2377 url=url, pending=p)
2357
2378
2358 added = [cl.node(r) for r in xrange(clstart, clend)]
2379 added = [cl.node(r) for r in xrange(clstart, clend)]
2359 publishing = self.ui.configbool('phases', 'publish', True)
2380 publishing = self.ui.configbool('phases', 'publish', True)
2360 if srctype == 'push':
2381 if srctype == 'push':
2361 # Old server can not push the boundary themself.
2382 # Old server can not push the boundary themself.
2362 # New server won't push the boundary if changeset already
2383 # New server won't push the boundary if changeset already
2363 # existed locally as secrete
2384 # existed locally as secrete
2364 #
2385 #
2365 # We should not use added here but the list of all change in
2386 # We should not use added here but the list of all change in
2366 # the bundle
2387 # the bundle
2367 if publishing:
2388 if publishing:
2368 phases.advanceboundary(self, phases.public, srccontent)
2389 phases.advanceboundary(self, phases.public, srccontent)
2369 else:
2390 else:
2370 phases.advanceboundary(self, phases.draft, srccontent)
2391 phases.advanceboundary(self, phases.draft, srccontent)
2371 phases.retractboundary(self, phases.draft, added)
2392 phases.retractboundary(self, phases.draft, added)
2372 elif srctype != 'strip':
2393 elif srctype != 'strip':
2373 # publishing only alter behavior during push
2394 # publishing only alter behavior during push
2374 #
2395 #
2375 # strip should not touch boundary at all
2396 # strip should not touch boundary at all
2376 phases.retractboundary(self, phases.draft, added)
2397 phases.retractboundary(self, phases.draft, added)
2377
2398
2378 # make changelog see real files again
2399 # make changelog see real files again
2379 cl.finalize(trp)
2400 cl.finalize(trp)
2380
2401
2381 tr.close()
2402 tr.close()
2382
2403
2383 if changesets > 0:
2404 if changesets > 0:
2384 if srctype != 'strip':
2405 if srctype != 'strip':
2385 # During strip, branchcache is invalid but coming call to
2406 # During strip, branchcache is invalid but coming call to
2386 # `destroyed` will repair it.
2407 # `destroyed` will repair it.
2387 # In other case we can safely update cache on disk.
2408 # In other case we can safely update cache on disk.
2388 branchmap.updatecache(self.filtered('served'))
2409 branchmap.updatecache(self.filtered('served'))
2389 def runhooks():
2410 def runhooks():
2390 # forcefully update the on-disk branch cache
2411 # forcefully update the on-disk branch cache
2391 self.ui.debug("updating the branch cache\n")
2412 self.ui.debug("updating the branch cache\n")
2392 self.hook("changegroup", node=hex(cl.node(clstart)),
2413 self.hook("changegroup", node=hex(cl.node(clstart)),
2393 source=srctype, url=url)
2414 source=srctype, url=url)
2394
2415
2395 for n in added:
2416 for n in added:
2396 self.hook("incoming", node=hex(n), source=srctype,
2417 self.hook("incoming", node=hex(n), source=srctype,
2397 url=url)
2418 url=url)
2398
2419
2399 newheads = [h for h in self.heads() if h not in oldheads]
2420 newheads = [h for h in self.heads() if h not in oldheads]
2400 self.ui.log("incoming",
2421 self.ui.log("incoming",
2401 "%s incoming changes - new heads: %s\n",
2422 "%s incoming changes - new heads: %s\n",
2402 len(added),
2423 len(added),
2403 ', '.join([hex(c[:6]) for c in newheads]))
2424 ', '.join([hex(c[:6]) for c in newheads]))
2404 self._afterlock(runhooks)
2425 self._afterlock(runhooks)
2405
2426
2406 finally:
2427 finally:
2407 tr.release()
2428 tr.release()
2408 # never return 0 here:
2429 # never return 0 here:
2409 if dh < 0:
2430 if dh < 0:
2410 return dh - 1
2431 return dh - 1
2411 else:
2432 else:
2412 return dh + 1
2433 return dh + 1
2413
2434
2414 def stream_in(self, remote, requirements):
2435 def stream_in(self, remote, requirements):
2415 lock = self.lock()
2436 lock = self.lock()
2416 try:
2437 try:
2417 # Save remote branchmap. We will use it later
2438 # Save remote branchmap. We will use it later
2418 # to speed up branchcache creation
2439 # to speed up branchcache creation
2419 rbranchmap = None
2440 rbranchmap = None
2420 if remote.capable("branchmap"):
2441 if remote.capable("branchmap"):
2421 rbranchmap = remote.branchmap()
2442 rbranchmap = remote.branchmap()
2422
2443
2423 fp = remote.stream_out()
2444 fp = remote.stream_out()
2424 l = fp.readline()
2445 l = fp.readline()
2425 try:
2446 try:
2426 resp = int(l)
2447 resp = int(l)
2427 except ValueError:
2448 except ValueError:
2428 raise error.ResponseError(
2449 raise error.ResponseError(
2429 _('unexpected response from remote server:'), l)
2450 _('unexpected response from remote server:'), l)
2430 if resp == 1:
2451 if resp == 1:
2431 raise util.Abort(_('operation forbidden by server'))
2452 raise util.Abort(_('operation forbidden by server'))
2432 elif resp == 2:
2453 elif resp == 2:
2433 raise util.Abort(_('locking the remote repository failed'))
2454 raise util.Abort(_('locking the remote repository failed'))
2434 elif resp != 0:
2455 elif resp != 0:
2435 raise util.Abort(_('the server sent an unknown error code'))
2456 raise util.Abort(_('the server sent an unknown error code'))
2436 self.ui.status(_('streaming all changes\n'))
2457 self.ui.status(_('streaming all changes\n'))
2437 l = fp.readline()
2458 l = fp.readline()
2438 try:
2459 try:
2439 total_files, total_bytes = map(int, l.split(' ', 1))
2460 total_files, total_bytes = map(int, l.split(' ', 1))
2440 except (ValueError, TypeError):
2461 except (ValueError, TypeError):
2441 raise error.ResponseError(
2462 raise error.ResponseError(
2442 _('unexpected response from remote server:'), l)
2463 _('unexpected response from remote server:'), l)
2443 self.ui.status(_('%d files to transfer, %s of data\n') %
2464 self.ui.status(_('%d files to transfer, %s of data\n') %
2444 (total_files, util.bytecount(total_bytes)))
2465 (total_files, util.bytecount(total_bytes)))
2445 handled_bytes = 0
2466 handled_bytes = 0
2446 self.ui.progress(_('clone'), 0, total=total_bytes)
2467 self.ui.progress(_('clone'), 0, total=total_bytes)
2447 start = time.time()
2468 start = time.time()
2448 for i in xrange(total_files):
2469 for i in xrange(total_files):
2449 # XXX doesn't support '\n' or '\r' in filenames
2470 # XXX doesn't support '\n' or '\r' in filenames
2450 l = fp.readline()
2471 l = fp.readline()
2451 try:
2472 try:
2452 name, size = l.split('\0', 1)
2473 name, size = l.split('\0', 1)
2453 size = int(size)
2474 size = int(size)
2454 except (ValueError, TypeError):
2475 except (ValueError, TypeError):
2455 raise error.ResponseError(
2476 raise error.ResponseError(
2456 _('unexpected response from remote server:'), l)
2477 _('unexpected response from remote server:'), l)
2457 if self.ui.debugflag:
2478 if self.ui.debugflag:
2458 self.ui.debug('adding %s (%s)\n' %
2479 self.ui.debug('adding %s (%s)\n' %
2459 (name, util.bytecount(size)))
2480 (name, util.bytecount(size)))
2460 # for backwards compat, name was partially encoded
2481 # for backwards compat, name was partially encoded
2461 ofp = self.sopener(store.decodedir(name), 'w')
2482 ofp = self.sopener(store.decodedir(name), 'w')
2462 for chunk in util.filechunkiter(fp, limit=size):
2483 for chunk in util.filechunkiter(fp, limit=size):
2463 handled_bytes += len(chunk)
2484 handled_bytes += len(chunk)
2464 self.ui.progress(_('clone'), handled_bytes,
2485 self.ui.progress(_('clone'), handled_bytes,
2465 total=total_bytes)
2486 total=total_bytes)
2466 ofp.write(chunk)
2487 ofp.write(chunk)
2467 ofp.close()
2488 ofp.close()
2468 elapsed = time.time() - start
2489 elapsed = time.time() - start
2469 if elapsed <= 0:
2490 if elapsed <= 0:
2470 elapsed = 0.001
2491 elapsed = 0.001
2471 self.ui.progress(_('clone'), None)
2492 self.ui.progress(_('clone'), None)
2472 self.ui.status(_('transferred %s in %.1f seconds (%s/sec)\n') %
2493 self.ui.status(_('transferred %s in %.1f seconds (%s/sec)\n') %
2473 (util.bytecount(total_bytes), elapsed,
2494 (util.bytecount(total_bytes), elapsed,
2474 util.bytecount(total_bytes / elapsed)))
2495 util.bytecount(total_bytes / elapsed)))
2475
2496
2476 # new requirements = old non-format requirements +
2497 # new requirements = old non-format requirements +
2477 # new format-related
2498 # new format-related
2478 # requirements from the streamed-in repository
2499 # requirements from the streamed-in repository
2479 requirements.update(set(self.requirements) - self.supportedformats)
2500 requirements.update(set(self.requirements) - self.supportedformats)
2480 self._applyrequirements(requirements)
2501 self._applyrequirements(requirements)
2481 self._writerequirements()
2502 self._writerequirements()
2482
2503
2483 if rbranchmap:
2504 if rbranchmap:
2484 rbheads = []
2505 rbheads = []
2485 for bheads in rbranchmap.itervalues():
2506 for bheads in rbranchmap.itervalues():
2486 rbheads.extend(bheads)
2507 rbheads.extend(bheads)
2487
2508
2488 if rbheads:
2509 if rbheads:
2489 rtiprev = max((int(self.changelog.rev(node))
2510 rtiprev = max((int(self.changelog.rev(node))
2490 for node in rbheads))
2511 for node in rbheads))
2491 cache = branchmap.branchcache(rbranchmap,
2512 cache = branchmap.branchcache(rbranchmap,
2492 self[rtiprev].node(),
2513 self[rtiprev].node(),
2493 rtiprev)
2514 rtiprev)
2494 # Try to stick it as low as possible
2515 # Try to stick it as low as possible
2495 # filter above served are unlikely to be fetch from a clone
2516 # filter above served are unlikely to be fetch from a clone
2496 for candidate in ('base', 'immutable', 'served'):
2517 for candidate in ('base', 'immutable', 'served'):
2497 rview = self.filtered(candidate)
2518 rview = self.filtered(candidate)
2498 if cache.validfor(rview):
2519 if cache.validfor(rview):
2499 self._branchcaches[candidate] = cache
2520 self._branchcaches[candidate] = cache
2500 cache.write(rview)
2521 cache.write(rview)
2501 break
2522 break
2502 self.invalidate()
2523 self.invalidate()
2503 return len(self.heads()) + 1
2524 return len(self.heads()) + 1
2504 finally:
2525 finally:
2505 lock.release()
2526 lock.release()
2506
2527
2507 def clone(self, remote, heads=[], stream=False):
2528 def clone(self, remote, heads=[], stream=False):
2508 '''clone remote repository.
2529 '''clone remote repository.
2509
2530
2510 keyword arguments:
2531 keyword arguments:
2511 heads: list of revs to clone (forces use of pull)
2532 heads: list of revs to clone (forces use of pull)
2512 stream: use streaming clone if possible'''
2533 stream: use streaming clone if possible'''
2513
2534
2514 # now, all clients that can request uncompressed clones can
2535 # now, all clients that can request uncompressed clones can
2515 # read repo formats supported by all servers that can serve
2536 # read repo formats supported by all servers that can serve
2516 # them.
2537 # them.
2517
2538
2518 # if revlog format changes, client will have to check version
2539 # if revlog format changes, client will have to check version
2519 # and format flags on "stream" capability, and use
2540 # and format flags on "stream" capability, and use
2520 # uncompressed only if compatible.
2541 # uncompressed only if compatible.
2521
2542
2522 if not stream:
2543 if not stream:
2523 # if the server explicitly prefers to stream (for fast LANs)
2544 # if the server explicitly prefers to stream (for fast LANs)
2524 stream = remote.capable('stream-preferred')
2545 stream = remote.capable('stream-preferred')
2525
2546
2526 if stream and not heads:
2547 if stream and not heads:
2527 # 'stream' means remote revlog format is revlogv1 only
2548 # 'stream' means remote revlog format is revlogv1 only
2528 if remote.capable('stream'):
2549 if remote.capable('stream'):
2529 return self.stream_in(remote, set(('revlogv1',)))
2550 return self.stream_in(remote, set(('revlogv1',)))
2530 # otherwise, 'streamreqs' contains the remote revlog format
2551 # otherwise, 'streamreqs' contains the remote revlog format
2531 streamreqs = remote.capable('streamreqs')
2552 streamreqs = remote.capable('streamreqs')
2532 if streamreqs:
2553 if streamreqs:
2533 streamreqs = set(streamreqs.split(','))
2554 streamreqs = set(streamreqs.split(','))
2534 # if we support it, stream in and adjust our requirements
2555 # if we support it, stream in and adjust our requirements
2535 if not streamreqs - self.supportedformats:
2556 if not streamreqs - self.supportedformats:
2536 return self.stream_in(remote, streamreqs)
2557 return self.stream_in(remote, streamreqs)
2537 return self.pull(remote, heads)
2558 return self.pull(remote, heads)
2538
2559
2539 def pushkey(self, namespace, key, old, new):
2560 def pushkey(self, namespace, key, old, new):
2540 self.hook('prepushkey', throw=True, namespace=namespace, key=key,
2561 self.hook('prepushkey', throw=True, namespace=namespace, key=key,
2541 old=old, new=new)
2562 old=old, new=new)
2542 self.ui.debug('pushing key for "%s:%s"\n' % (namespace, key))
2563 self.ui.debug('pushing key for "%s:%s"\n' % (namespace, key))
2543 ret = pushkey.push(self, namespace, key, old, new)
2564 ret = pushkey.push(self, namespace, key, old, new)
2544 self.hook('pushkey', namespace=namespace, key=key, old=old, new=new,
2565 self.hook('pushkey', namespace=namespace, key=key, old=old, new=new,
2545 ret=ret)
2566 ret=ret)
2546 return ret
2567 return ret
2547
2568
2548 def listkeys(self, namespace):
2569 def listkeys(self, namespace):
2549 self.hook('prelistkeys', throw=True, namespace=namespace)
2570 self.hook('prelistkeys', throw=True, namespace=namespace)
2550 self.ui.debug('listing keys for "%s"\n' % namespace)
2571 self.ui.debug('listing keys for "%s"\n' % namespace)
2551 values = pushkey.list(self, namespace)
2572 values = pushkey.list(self, namespace)
2552 self.hook('listkeys', namespace=namespace, values=values)
2573 self.hook('listkeys', namespace=namespace, values=values)
2553 return values
2574 return values
2554
2575
2555 def debugwireargs(self, one, two, three=None, four=None, five=None):
2576 def debugwireargs(self, one, two, three=None, four=None, five=None):
2556 '''used to test argument passing over the wire'''
2577 '''used to test argument passing over the wire'''
2557 return "%s %s %s %s %s" % (one, two, three, four, five)
2578 return "%s %s %s %s %s" % (one, two, three, four, five)
2558
2579
2559 def savecommitmessage(self, text):
2580 def savecommitmessage(self, text):
2560 fp = self.opener('last-message.txt', 'wb')
2581 fp = self.opener('last-message.txt', 'wb')
2561 try:
2582 try:
2562 fp.write(text)
2583 fp.write(text)
2563 finally:
2584 finally:
2564 fp.close()
2585 fp.close()
2565 return self.pathto(fp.name[len(self.root) + 1:])
2586 return self.pathto(fp.name[len(self.root) + 1:])
2566
2587
2567 # used to avoid circular references so destructors work
2588 # used to avoid circular references so destructors work
2568 def aftertrans(files):
2589 def aftertrans(files):
2569 renamefiles = [tuple(t) for t in files]
2590 renamefiles = [tuple(t) for t in files]
2570 def a():
2591 def a():
2571 for vfs, src, dest in renamefiles:
2592 for vfs, src, dest in renamefiles:
2572 try:
2593 try:
2573 vfs.rename(src, dest)
2594 vfs.rename(src, dest)
2574 except OSError: # journal file does not yet exist
2595 except OSError: # journal file does not yet exist
2575 pass
2596 pass
2576 return a
2597 return a
2577
2598
2578 def undoname(fn):
2599 def undoname(fn):
2579 base, name = os.path.split(fn)
2600 base, name = os.path.split(fn)
2580 assert name.startswith('journal')
2601 assert name.startswith('journal')
2581 return os.path.join(base, name.replace('journal', 'undo', 1))
2602 return os.path.join(base, name.replace('journal', 'undo', 1))
2582
2603
2583 def instance(ui, path, create):
2604 def instance(ui, path, create):
2584 return localrepository(ui, util.urllocalpath(path), create)
2605 return localrepository(ui, util.urllocalpath(path), create)
2585
2606
2586 def islocal(path):
2607 def islocal(path):
2587 return True
2608 return True
@@ -1,1066 +1,1104
1 $ "$TESTDIR/hghave" killdaemons || exit 80
1 $ "$TESTDIR/hghave" killdaemons || exit 80
2
2
3 $ cat >> $HGRCPATH <<EOF
3 $ cat >> $HGRCPATH <<EOF
4 > [extensions]
4 > [extensions]
5 > graphlog=
5 > graphlog=
6 > EOF
6 > EOF
7 $ hgph() { hg log -G --template "{rev} {phase} {desc} - {node|short}\n" $*; }
7 $ hgph() { hg log -G --template "{rev} {phase} {desc} - {node|short}\n" $*; }
8
8
9 $ mkcommit() {
9 $ mkcommit() {
10 > echo "$1" > "$1"
10 > echo "$1" > "$1"
11 > hg add "$1"
11 > hg add "$1"
12 > message="$1"
12 > message="$1"
13 > shift
13 > shift
14 > hg ci -m "$message" $*
14 > hg ci -m "$message" $*
15 > }
15 > }
16
16
17 $ hg init alpha
17 $ hg init alpha
18 $ cd alpha
18 $ cd alpha
19 $ mkcommit a-A
19 $ mkcommit a-A
20 $ mkcommit a-B
20 $ mkcommit a-B
21 $ mkcommit a-C
21 $ mkcommit a-C
22 $ mkcommit a-D
22 $ mkcommit a-D
23 $ hgph
23 $ hgph
24 @ 3 draft a-D - b555f63b6063
24 @ 3 draft a-D - b555f63b6063
25 |
25 |
26 o 2 draft a-C - 54acac6f23ab
26 o 2 draft a-C - 54acac6f23ab
27 |
27 |
28 o 1 draft a-B - 548a3d25dbf0
28 o 1 draft a-B - 548a3d25dbf0
29 |
29 |
30 o 0 draft a-A - 054250a37db4
30 o 0 draft a-A - 054250a37db4
31
31
32
32
33 $ hg init ../beta
33 $ hg init ../beta
34 $ hg push -r 1 ../beta
34 $ hg push -r 1 ../beta
35 pushing to ../beta
35 pushing to ../beta
36 searching for changes
36 searching for changes
37 adding changesets
37 adding changesets
38 adding manifests
38 adding manifests
39 adding file changes
39 adding file changes
40 added 2 changesets with 2 changes to 2 files
40 added 2 changesets with 2 changes to 2 files
41 $ hgph
41 $ hgph
42 @ 3 draft a-D - b555f63b6063
42 @ 3 draft a-D - b555f63b6063
43 |
43 |
44 o 2 draft a-C - 54acac6f23ab
44 o 2 draft a-C - 54acac6f23ab
45 |
45 |
46 o 1 public a-B - 548a3d25dbf0
46 o 1 public a-B - 548a3d25dbf0
47 |
47 |
48 o 0 public a-A - 054250a37db4
48 o 0 public a-A - 054250a37db4
49
49
50
50
51 $ cd ../beta
51 $ cd ../beta
52 $ hgph
52 $ hgph
53 o 1 public a-B - 548a3d25dbf0
53 o 1 public a-B - 548a3d25dbf0
54 |
54 |
55 o 0 public a-A - 054250a37db4
55 o 0 public a-A - 054250a37db4
56
56
57 $ hg up -q
57 $ hg up -q
58 $ mkcommit b-A
58 $ mkcommit b-A
59 $ hgph
59 $ hgph
60 @ 2 draft b-A - f54f1bb90ff3
60 @ 2 draft b-A - f54f1bb90ff3
61 |
61 |
62 o 1 public a-B - 548a3d25dbf0
62 o 1 public a-B - 548a3d25dbf0
63 |
63 |
64 o 0 public a-A - 054250a37db4
64 o 0 public a-A - 054250a37db4
65
65
66 $ hg pull ../alpha
66 $ hg pull ../alpha
67 pulling from ../alpha
67 pulling from ../alpha
68 searching for changes
68 searching for changes
69 adding changesets
69 adding changesets
70 adding manifests
70 adding manifests
71 adding file changes
71 adding file changes
72 added 2 changesets with 2 changes to 2 files (+1 heads)
72 added 2 changesets with 2 changes to 2 files (+1 heads)
73 (run 'hg heads' to see heads, 'hg merge' to merge)
73 (run 'hg heads' to see heads, 'hg merge' to merge)
74 $ hgph
74 $ hgph
75 o 4 public a-D - b555f63b6063
75 o 4 public a-D - b555f63b6063
76 |
76 |
77 o 3 public a-C - 54acac6f23ab
77 o 3 public a-C - 54acac6f23ab
78 |
78 |
79 | @ 2 draft b-A - f54f1bb90ff3
79 | @ 2 draft b-A - f54f1bb90ff3
80 |/
80 |/
81 o 1 public a-B - 548a3d25dbf0
81 o 1 public a-B - 548a3d25dbf0
82 |
82 |
83 o 0 public a-A - 054250a37db4
83 o 0 public a-A - 054250a37db4
84
84
85
85
86 pull did not updated ../alpha state.
86 pull did not updated ../alpha state.
87 push from alpha to beta should update phase even if nothing is transferred
87 push from alpha to beta should update phase even if nothing is transferred
88
88
89 $ cd ../alpha
89 $ cd ../alpha
90 $ hgph # not updated by remote pull
90 $ hgph # not updated by remote pull
91 @ 3 draft a-D - b555f63b6063
91 @ 3 draft a-D - b555f63b6063
92 |
92 |
93 o 2 draft a-C - 54acac6f23ab
93 o 2 draft a-C - 54acac6f23ab
94 |
94 |
95 o 1 public a-B - 548a3d25dbf0
95 o 1 public a-B - 548a3d25dbf0
96 |
96 |
97 o 0 public a-A - 054250a37db4
97 o 0 public a-A - 054250a37db4
98
98
99 $ hg push ../beta
99 $ hg push ../beta
100 pushing to ../beta
100 pushing to ../beta
101 searching for changes
101 searching for changes
102 no changes found
102 no changes found
103 [1]
103 [1]
104 $ hgph
104 $ hgph
105 @ 3 public a-D - b555f63b6063
105 @ 3 public a-D - b555f63b6063
106 |
106 |
107 o 2 public a-C - 54acac6f23ab
107 o 2 public a-C - 54acac6f23ab
108 |
108 |
109 o 1 public a-B - 548a3d25dbf0
109 o 1 public a-B - 548a3d25dbf0
110 |
110 |
111 o 0 public a-A - 054250a37db4
111 o 0 public a-A - 054250a37db4
112
112
113
113
114 update must update phase of common changeset too
114 update must update phase of common changeset too
115
115
116 $ hg pull ../beta # getting b-A
116 $ hg pull ../beta # getting b-A
117 pulling from ../beta
117 pulling from ../beta
118 searching for changes
118 searching for changes
119 adding changesets
119 adding changesets
120 adding manifests
120 adding manifests
121 adding file changes
121 adding file changes
122 added 1 changesets with 1 changes to 1 files (+1 heads)
122 added 1 changesets with 1 changes to 1 files (+1 heads)
123 (run 'hg heads' to see heads, 'hg merge' to merge)
123 (run 'hg heads' to see heads, 'hg merge' to merge)
124
124
125 $ cd ../beta
125 $ cd ../beta
126 $ hgph # not updated by remote pull
126 $ hgph # not updated by remote pull
127 o 4 public a-D - b555f63b6063
127 o 4 public a-D - b555f63b6063
128 |
128 |
129 o 3 public a-C - 54acac6f23ab
129 o 3 public a-C - 54acac6f23ab
130 |
130 |
131 | @ 2 draft b-A - f54f1bb90ff3
131 | @ 2 draft b-A - f54f1bb90ff3
132 |/
132 |/
133 o 1 public a-B - 548a3d25dbf0
133 o 1 public a-B - 548a3d25dbf0
134 |
134 |
135 o 0 public a-A - 054250a37db4
135 o 0 public a-A - 054250a37db4
136
136
137 $ hg pull ../alpha
137 $ hg pull ../alpha
138 pulling from ../alpha
138 pulling from ../alpha
139 searching for changes
139 searching for changes
140 no changes found
140 no changes found
141 $ hgph
141 $ hgph
142 o 4 public a-D - b555f63b6063
142 o 4 public a-D - b555f63b6063
143 |
143 |
144 o 3 public a-C - 54acac6f23ab
144 o 3 public a-C - 54acac6f23ab
145 |
145 |
146 | @ 2 public b-A - f54f1bb90ff3
146 | @ 2 public b-A - f54f1bb90ff3
147 |/
147 |/
148 o 1 public a-B - 548a3d25dbf0
148 o 1 public a-B - 548a3d25dbf0
149 |
149 |
150 o 0 public a-A - 054250a37db4
150 o 0 public a-A - 054250a37db4
151
151
152
152
153 Publish configuration option
153 Publish configuration option
154 ----------------------------
154 ----------------------------
155
155
156 Pull
156 Pull
157 ````
157 ````
158
158
159 changegroup are added without phase movement
159 changegroup are added without phase movement
160
160
161 $ hg bundle -a ../base.bundle
161 $ hg bundle -a ../base.bundle
162 5 changesets found
162 5 changesets found
163 $ cd ..
163 $ cd ..
164 $ hg init mu
164 $ hg init mu
165 $ cd mu
165 $ cd mu
166 $ cat > .hg/hgrc << EOF
166 $ cat > .hg/hgrc << EOF
167 > [phases]
167 > [phases]
168 > publish=0
168 > publish=0
169 > EOF
169 > EOF
170 $ hg unbundle ../base.bundle
170 $ hg unbundle ../base.bundle
171 adding changesets
171 adding changesets
172 adding manifests
172 adding manifests
173 adding file changes
173 adding file changes
174 added 5 changesets with 5 changes to 5 files (+1 heads)
174 added 5 changesets with 5 changes to 5 files (+1 heads)
175 (run 'hg heads' to see heads, 'hg merge' to merge)
175 (run 'hg heads' to see heads, 'hg merge' to merge)
176 $ hgph
176 $ hgph
177 o 4 draft a-D - b555f63b6063
177 o 4 draft a-D - b555f63b6063
178 |
178 |
179 o 3 draft a-C - 54acac6f23ab
179 o 3 draft a-C - 54acac6f23ab
180 |
180 |
181 | o 2 draft b-A - f54f1bb90ff3
181 | o 2 draft b-A - f54f1bb90ff3
182 |/
182 |/
183 o 1 draft a-B - 548a3d25dbf0
183 o 1 draft a-B - 548a3d25dbf0
184 |
184 |
185 o 0 draft a-A - 054250a37db4
185 o 0 draft a-A - 054250a37db4
186
186
187 $ cd ..
187 $ cd ..
188
188
189 Pulling from publish=False to publish=False does not move boundary.
189 Pulling from publish=False to publish=False does not move boundary.
190
190
191 $ hg init nu
191 $ hg init nu
192 $ cd nu
192 $ cd nu
193 $ cat > .hg/hgrc << EOF
193 $ cat > .hg/hgrc << EOF
194 > [phases]
194 > [phases]
195 > publish=0
195 > publish=0
196 > EOF
196 > EOF
197 $ hg pull ../mu -r 54acac6f23ab
197 $ hg pull ../mu -r 54acac6f23ab
198 pulling from ../mu
198 pulling from ../mu
199 adding changesets
199 adding changesets
200 adding manifests
200 adding manifests
201 adding file changes
201 adding file changes
202 added 3 changesets with 3 changes to 3 files
202 added 3 changesets with 3 changes to 3 files
203 (run 'hg update' to get a working copy)
203 (run 'hg update' to get a working copy)
204 $ hgph
204 $ hgph
205 o 2 draft a-C - 54acac6f23ab
205 o 2 draft a-C - 54acac6f23ab
206 |
206 |
207 o 1 draft a-B - 548a3d25dbf0
207 o 1 draft a-B - 548a3d25dbf0
208 |
208 |
209 o 0 draft a-A - 054250a37db4
209 o 0 draft a-A - 054250a37db4
210
210
211
211
212 Even for common
212 Even for common
213
213
214 $ hg pull ../mu -r f54f1bb90ff3
214 $ hg pull ../mu -r f54f1bb90ff3
215 pulling from ../mu
215 pulling from ../mu
216 searching for changes
216 searching for changes
217 adding changesets
217 adding changesets
218 adding manifests
218 adding manifests
219 adding file changes
219 adding file changes
220 added 1 changesets with 1 changes to 1 files (+1 heads)
220 added 1 changesets with 1 changes to 1 files (+1 heads)
221 (run 'hg heads' to see heads, 'hg merge' to merge)
221 (run 'hg heads' to see heads, 'hg merge' to merge)
222 $ hgph
222 $ hgph
223 o 3 draft b-A - f54f1bb90ff3
223 o 3 draft b-A - f54f1bb90ff3
224 |
224 |
225 | o 2 draft a-C - 54acac6f23ab
225 | o 2 draft a-C - 54acac6f23ab
226 |/
226 |/
227 o 1 draft a-B - 548a3d25dbf0
227 o 1 draft a-B - 548a3d25dbf0
228 |
228 |
229 o 0 draft a-A - 054250a37db4
229 o 0 draft a-A - 054250a37db4
230
230
231
231
232
232
233 Pulling from Publish=True to Publish=False move boundary in common set.
233 Pulling from Publish=True to Publish=False move boundary in common set.
234 we are in nu
234 we are in nu
235
235
236 $ hg pull ../alpha -r b555f63b6063
236 $ hg pull ../alpha -r b555f63b6063
237 pulling from ../alpha
237 pulling from ../alpha
238 searching for changes
238 searching for changes
239 adding changesets
239 adding changesets
240 adding manifests
240 adding manifests
241 adding file changes
241 adding file changes
242 added 1 changesets with 1 changes to 1 files
242 added 1 changesets with 1 changes to 1 files
243 (run 'hg update' to get a working copy)
243 (run 'hg update' to get a working copy)
244 $ hgph # f54f1bb90ff3 stay draft, not ancestor of -r
244 $ hgph # f54f1bb90ff3 stay draft, not ancestor of -r
245 o 4 public a-D - b555f63b6063
245 o 4 public a-D - b555f63b6063
246 |
246 |
247 | o 3 draft b-A - f54f1bb90ff3
247 | o 3 draft b-A - f54f1bb90ff3
248 | |
248 | |
249 o | 2 public a-C - 54acac6f23ab
249 o | 2 public a-C - 54acac6f23ab
250 |/
250 |/
251 o 1 public a-B - 548a3d25dbf0
251 o 1 public a-B - 548a3d25dbf0
252 |
252 |
253 o 0 public a-A - 054250a37db4
253 o 0 public a-A - 054250a37db4
254
254
255
255
256 pulling from Publish=False to publish=False with some public
256 pulling from Publish=False to publish=False with some public
257
257
258 $ hg up -q f54f1bb90ff3
258 $ hg up -q f54f1bb90ff3
259 $ mkcommit n-A
259 $ mkcommit n-A
260 $ mkcommit n-B
260 $ mkcommit n-B
261 $ hgph
261 $ hgph
262 @ 6 draft n-B - 145e75495359
262 @ 6 draft n-B - 145e75495359
263 |
263 |
264 o 5 draft n-A - d6bcb4f74035
264 o 5 draft n-A - d6bcb4f74035
265 |
265 |
266 | o 4 public a-D - b555f63b6063
266 | o 4 public a-D - b555f63b6063
267 | |
267 | |
268 o | 3 draft b-A - f54f1bb90ff3
268 o | 3 draft b-A - f54f1bb90ff3
269 | |
269 | |
270 | o 2 public a-C - 54acac6f23ab
270 | o 2 public a-C - 54acac6f23ab
271 |/
271 |/
272 o 1 public a-B - 548a3d25dbf0
272 o 1 public a-B - 548a3d25dbf0
273 |
273 |
274 o 0 public a-A - 054250a37db4
274 o 0 public a-A - 054250a37db4
275
275
276 $ cd ../mu
276 $ cd ../mu
277 $ hg pull ../nu
277 $ hg pull ../nu
278 pulling from ../nu
278 pulling from ../nu
279 searching for changes
279 searching for changes
280 adding changesets
280 adding changesets
281 adding manifests
281 adding manifests
282 adding file changes
282 adding file changes
283 added 2 changesets with 2 changes to 2 files
283 added 2 changesets with 2 changes to 2 files
284 (run 'hg update' to get a working copy)
284 (run 'hg update' to get a working copy)
285 $ hgph
285 $ hgph
286 o 6 draft n-B - 145e75495359
286 o 6 draft n-B - 145e75495359
287 |
287 |
288 o 5 draft n-A - d6bcb4f74035
288 o 5 draft n-A - d6bcb4f74035
289 |
289 |
290 | o 4 public a-D - b555f63b6063
290 | o 4 public a-D - b555f63b6063
291 | |
291 | |
292 | o 3 public a-C - 54acac6f23ab
292 | o 3 public a-C - 54acac6f23ab
293 | |
293 | |
294 o | 2 draft b-A - f54f1bb90ff3
294 o | 2 draft b-A - f54f1bb90ff3
295 |/
295 |/
296 o 1 public a-B - 548a3d25dbf0
296 o 1 public a-B - 548a3d25dbf0
297 |
297 |
298 o 0 public a-A - 054250a37db4
298 o 0 public a-A - 054250a37db4
299
299
300 $ cd ..
300 $ cd ..
301
301
302 pulling into publish=True
302 pulling into publish=True
303
303
304 $ cd alpha
304 $ cd alpha
305 $ hgph
305 $ hgph
306 o 4 public b-A - f54f1bb90ff3
306 o 4 public b-A - f54f1bb90ff3
307 |
307 |
308 | @ 3 public a-D - b555f63b6063
308 | @ 3 public a-D - b555f63b6063
309 | |
309 | |
310 | o 2 public a-C - 54acac6f23ab
310 | o 2 public a-C - 54acac6f23ab
311 |/
311 |/
312 o 1 public a-B - 548a3d25dbf0
312 o 1 public a-B - 548a3d25dbf0
313 |
313 |
314 o 0 public a-A - 054250a37db4
314 o 0 public a-A - 054250a37db4
315
315
316 $ hg pull ../mu
316 $ hg pull ../mu
317 pulling from ../mu
317 pulling from ../mu
318 searching for changes
318 searching for changes
319 adding changesets
319 adding changesets
320 adding manifests
320 adding manifests
321 adding file changes
321 adding file changes
322 added 2 changesets with 2 changes to 2 files
322 added 2 changesets with 2 changes to 2 files
323 (run 'hg update' to get a working copy)
323 (run 'hg update' to get a working copy)
324 $ hgph
324 $ hgph
325 o 6 draft n-B - 145e75495359
325 o 6 draft n-B - 145e75495359
326 |
326 |
327 o 5 draft n-A - d6bcb4f74035
327 o 5 draft n-A - d6bcb4f74035
328 |
328 |
329 o 4 public b-A - f54f1bb90ff3
329 o 4 public b-A - f54f1bb90ff3
330 |
330 |
331 | @ 3 public a-D - b555f63b6063
331 | @ 3 public a-D - b555f63b6063
332 | |
332 | |
333 | o 2 public a-C - 54acac6f23ab
333 | o 2 public a-C - 54acac6f23ab
334 |/
334 |/
335 o 1 public a-B - 548a3d25dbf0
335 o 1 public a-B - 548a3d25dbf0
336 |
336 |
337 o 0 public a-A - 054250a37db4
337 o 0 public a-A - 054250a37db4
338
338
339 $ cd ..
339 $ cd ..
340
340
341 pulling back into original repo
341 pulling back into original repo
342
342
343 $ cd nu
343 $ cd nu
344 $ hg pull ../alpha
344 $ hg pull ../alpha
345 pulling from ../alpha
345 pulling from ../alpha
346 searching for changes
346 searching for changes
347 no changes found
347 no changes found
348 $ hgph
348 $ hgph
349 @ 6 public n-B - 145e75495359
349 @ 6 public n-B - 145e75495359
350 |
350 |
351 o 5 public n-A - d6bcb4f74035
351 o 5 public n-A - d6bcb4f74035
352 |
352 |
353 | o 4 public a-D - b555f63b6063
353 | o 4 public a-D - b555f63b6063
354 | |
354 | |
355 o | 3 public b-A - f54f1bb90ff3
355 o | 3 public b-A - f54f1bb90ff3
356 | |
356 | |
357 | o 2 public a-C - 54acac6f23ab
357 | o 2 public a-C - 54acac6f23ab
358 |/
358 |/
359 o 1 public a-B - 548a3d25dbf0
359 o 1 public a-B - 548a3d25dbf0
360 |
360 |
361 o 0 public a-A - 054250a37db4
361 o 0 public a-A - 054250a37db4
362
362
363
363
364 Push
364 Push
365 ````
365 ````
366
366
367 (inserted)
367 (inserted)
368
368
369 Test that phase are pushed even when they are nothing to pus
369 Test that phase are pushed even when they are nothing to pus
370 (this might be tested later bu are very convenient to not alter too much test)
370 (this might be tested later bu are very convenient to not alter too much test)
371
371
372 Push back to alpha
372 Push back to alpha
373
373
374 $ hg push ../alpha # from nu
374 $ hg push ../alpha # from nu
375 pushing to ../alpha
375 pushing to ../alpha
376 searching for changes
376 searching for changes
377 no changes found
377 no changes found
378 [1]
378 [1]
379 $ cd ..
379 $ cd ..
380 $ cd alpha
380 $ cd alpha
381 $ hgph
381 $ hgph
382 o 6 public n-B - 145e75495359
382 o 6 public n-B - 145e75495359
383 |
383 |
384 o 5 public n-A - d6bcb4f74035
384 o 5 public n-A - d6bcb4f74035
385 |
385 |
386 o 4 public b-A - f54f1bb90ff3
386 o 4 public b-A - f54f1bb90ff3
387 |
387 |
388 | @ 3 public a-D - b555f63b6063
388 | @ 3 public a-D - b555f63b6063
389 | |
389 | |
390 | o 2 public a-C - 54acac6f23ab
390 | o 2 public a-C - 54acac6f23ab
391 |/
391 |/
392 o 1 public a-B - 548a3d25dbf0
392 o 1 public a-B - 548a3d25dbf0
393 |
393 |
394 o 0 public a-A - 054250a37db4
394 o 0 public a-A - 054250a37db4
395
395
396
396
397 (end insertion)
397 (end insertion)
398
398
399
399
400 initial setup
400 initial setup
401
401
402 $ hg glog # of alpha
402 $ hg glog # of alpha
403 o changeset: 6:145e75495359
403 o changeset: 6:145e75495359
404 | tag: tip
404 | tag: tip
405 | user: test
405 | user: test
406 | date: Thu Jan 01 00:00:00 1970 +0000
406 | date: Thu Jan 01 00:00:00 1970 +0000
407 | summary: n-B
407 | summary: n-B
408 |
408 |
409 o changeset: 5:d6bcb4f74035
409 o changeset: 5:d6bcb4f74035
410 | user: test
410 | user: test
411 | date: Thu Jan 01 00:00:00 1970 +0000
411 | date: Thu Jan 01 00:00:00 1970 +0000
412 | summary: n-A
412 | summary: n-A
413 |
413 |
414 o changeset: 4:f54f1bb90ff3
414 o changeset: 4:f54f1bb90ff3
415 | parent: 1:548a3d25dbf0
415 | parent: 1:548a3d25dbf0
416 | user: test
416 | user: test
417 | date: Thu Jan 01 00:00:00 1970 +0000
417 | date: Thu Jan 01 00:00:00 1970 +0000
418 | summary: b-A
418 | summary: b-A
419 |
419 |
420 | @ changeset: 3:b555f63b6063
420 | @ changeset: 3:b555f63b6063
421 | | user: test
421 | | user: test
422 | | date: Thu Jan 01 00:00:00 1970 +0000
422 | | date: Thu Jan 01 00:00:00 1970 +0000
423 | | summary: a-D
423 | | summary: a-D
424 | |
424 | |
425 | o changeset: 2:54acac6f23ab
425 | o changeset: 2:54acac6f23ab
426 |/ user: test
426 |/ user: test
427 | date: Thu Jan 01 00:00:00 1970 +0000
427 | date: Thu Jan 01 00:00:00 1970 +0000
428 | summary: a-C
428 | summary: a-C
429 |
429 |
430 o changeset: 1:548a3d25dbf0
430 o changeset: 1:548a3d25dbf0
431 | user: test
431 | user: test
432 | date: Thu Jan 01 00:00:00 1970 +0000
432 | date: Thu Jan 01 00:00:00 1970 +0000
433 | summary: a-B
433 | summary: a-B
434 |
434 |
435 o changeset: 0:054250a37db4
435 o changeset: 0:054250a37db4
436 user: test
436 user: test
437 date: Thu Jan 01 00:00:00 1970 +0000
437 date: Thu Jan 01 00:00:00 1970 +0000
438 summary: a-A
438 summary: a-A
439
439
440 $ mkcommit a-E
440 $ mkcommit a-E
441 $ mkcommit a-F
441 $ mkcommit a-F
442 $ mkcommit a-G
442 $ mkcommit a-G
443 $ hg up d6bcb4f74035 -q
443 $ hg up d6bcb4f74035 -q
444 $ mkcommit a-H
444 $ mkcommit a-H
445 created new head
445 created new head
446 $ hgph
446 $ hgph
447 @ 10 draft a-H - 967b449fbc94
447 @ 10 draft a-H - 967b449fbc94
448 |
448 |
449 | o 9 draft a-G - 3e27b6f1eee1
449 | o 9 draft a-G - 3e27b6f1eee1
450 | |
450 | |
451 | o 8 draft a-F - b740e3e5c05d
451 | o 8 draft a-F - b740e3e5c05d
452 | |
452 | |
453 | o 7 draft a-E - e9f537e46dea
453 | o 7 draft a-E - e9f537e46dea
454 | |
454 | |
455 +---o 6 public n-B - 145e75495359
455 +---o 6 public n-B - 145e75495359
456 | |
456 | |
457 o | 5 public n-A - d6bcb4f74035
457 o | 5 public n-A - d6bcb4f74035
458 | |
458 | |
459 o | 4 public b-A - f54f1bb90ff3
459 o | 4 public b-A - f54f1bb90ff3
460 | |
460 | |
461 | o 3 public a-D - b555f63b6063
461 | o 3 public a-D - b555f63b6063
462 | |
462 | |
463 | o 2 public a-C - 54acac6f23ab
463 | o 2 public a-C - 54acac6f23ab
464 |/
464 |/
465 o 1 public a-B - 548a3d25dbf0
465 o 1 public a-B - 548a3d25dbf0
466 |
466 |
467 o 0 public a-A - 054250a37db4
467 o 0 public a-A - 054250a37db4
468
468
469
469
470 Pulling from bundle does not alter phases of changeset not present in the bundle
470 Pulling from bundle does not alter phases of changeset not present in the bundle
471
471
472 $ hg bundle --base 1 -r 6 -r 3 ../partial-bundle.hg
472 $ hg bundle --base 1 -r 6 -r 3 ../partial-bundle.hg
473 5 changesets found
473 5 changesets found
474 $ hg pull ../partial-bundle.hg
474 $ hg pull ../partial-bundle.hg
475 pulling from ../partial-bundle.hg
475 pulling from ../partial-bundle.hg
476 searching for changes
476 searching for changes
477 no changes found
477 no changes found
478 $ hgph
478 $ hgph
479 @ 10 draft a-H - 967b449fbc94
479 @ 10 draft a-H - 967b449fbc94
480 |
480 |
481 | o 9 draft a-G - 3e27b6f1eee1
481 | o 9 draft a-G - 3e27b6f1eee1
482 | |
482 | |
483 | o 8 draft a-F - b740e3e5c05d
483 | o 8 draft a-F - b740e3e5c05d
484 | |
484 | |
485 | o 7 draft a-E - e9f537e46dea
485 | o 7 draft a-E - e9f537e46dea
486 | |
486 | |
487 +---o 6 public n-B - 145e75495359
487 +---o 6 public n-B - 145e75495359
488 | |
488 | |
489 o | 5 public n-A - d6bcb4f74035
489 o | 5 public n-A - d6bcb4f74035
490 | |
490 | |
491 o | 4 public b-A - f54f1bb90ff3
491 o | 4 public b-A - f54f1bb90ff3
492 | |
492 | |
493 | o 3 public a-D - b555f63b6063
493 | o 3 public a-D - b555f63b6063
494 | |
494 | |
495 | o 2 public a-C - 54acac6f23ab
495 | o 2 public a-C - 54acac6f23ab
496 |/
496 |/
497 o 1 public a-B - 548a3d25dbf0
497 o 1 public a-B - 548a3d25dbf0
498 |
498 |
499 o 0 public a-A - 054250a37db4
499 o 0 public a-A - 054250a37db4
500
500
501
501
502 Pushing to Publish=False (unknown changeset)
502 Pushing to Publish=False (unknown changeset)
503
503
504 $ hg push ../mu -r b740e3e5c05d # a-F
504 $ hg push ../mu -r b740e3e5c05d # a-F
505 pushing to ../mu
505 pushing to ../mu
506 searching for changes
506 searching for changes
507 adding changesets
507 adding changesets
508 adding manifests
508 adding manifests
509 adding file changes
509 adding file changes
510 added 2 changesets with 2 changes to 2 files
510 added 2 changesets with 2 changes to 2 files
511 $ hgph
511 $ hgph
512 @ 10 draft a-H - 967b449fbc94
512 @ 10 draft a-H - 967b449fbc94
513 |
513 |
514 | o 9 draft a-G - 3e27b6f1eee1
514 | o 9 draft a-G - 3e27b6f1eee1
515 | |
515 | |
516 | o 8 draft a-F - b740e3e5c05d
516 | o 8 draft a-F - b740e3e5c05d
517 | |
517 | |
518 | o 7 draft a-E - e9f537e46dea
518 | o 7 draft a-E - e9f537e46dea
519 | |
519 | |
520 +---o 6 public n-B - 145e75495359
520 +---o 6 public n-B - 145e75495359
521 | |
521 | |
522 o | 5 public n-A - d6bcb4f74035
522 o | 5 public n-A - d6bcb4f74035
523 | |
523 | |
524 o | 4 public b-A - f54f1bb90ff3
524 o | 4 public b-A - f54f1bb90ff3
525 | |
525 | |
526 | o 3 public a-D - b555f63b6063
526 | o 3 public a-D - b555f63b6063
527 | |
527 | |
528 | o 2 public a-C - 54acac6f23ab
528 | o 2 public a-C - 54acac6f23ab
529 |/
529 |/
530 o 1 public a-B - 548a3d25dbf0
530 o 1 public a-B - 548a3d25dbf0
531 |
531 |
532 o 0 public a-A - 054250a37db4
532 o 0 public a-A - 054250a37db4
533
533
534
534
535 $ cd ../mu
535 $ cd ../mu
536 $ hgph # again f54f1bb90ff3, d6bcb4f74035 and 145e75495359 stay draft,
536 $ hgph # again f54f1bb90ff3, d6bcb4f74035 and 145e75495359 stay draft,
537 > # not ancestor of -r
537 > # not ancestor of -r
538 o 8 draft a-F - b740e3e5c05d
538 o 8 draft a-F - b740e3e5c05d
539 |
539 |
540 o 7 draft a-E - e9f537e46dea
540 o 7 draft a-E - e9f537e46dea
541 |
541 |
542 | o 6 draft n-B - 145e75495359
542 | o 6 draft n-B - 145e75495359
543 | |
543 | |
544 | o 5 draft n-A - d6bcb4f74035
544 | o 5 draft n-A - d6bcb4f74035
545 | |
545 | |
546 o | 4 public a-D - b555f63b6063
546 o | 4 public a-D - b555f63b6063
547 | |
547 | |
548 o | 3 public a-C - 54acac6f23ab
548 o | 3 public a-C - 54acac6f23ab
549 | |
549 | |
550 | o 2 draft b-A - f54f1bb90ff3
550 | o 2 draft b-A - f54f1bb90ff3
551 |/
551 |/
552 o 1 public a-B - 548a3d25dbf0
552 o 1 public a-B - 548a3d25dbf0
553 |
553 |
554 o 0 public a-A - 054250a37db4
554 o 0 public a-A - 054250a37db4
555
555
556
556
557 Pushing to Publish=True (unknown changeset)
557 Pushing to Publish=True (unknown changeset)
558
558
559 $ hg push ../beta -r b740e3e5c05d
559 $ hg push ../beta -r b740e3e5c05d
560 pushing to ../beta
560 pushing to ../beta
561 searching for changes
561 searching for changes
562 adding changesets
562 adding changesets
563 adding manifests
563 adding manifests
564 adding file changes
564 adding file changes
565 added 2 changesets with 2 changes to 2 files
565 added 2 changesets with 2 changes to 2 files
566 $ hgph # again f54f1bb90ff3, d6bcb4f74035 and 145e75495359 stay draft,
566 $ hgph # again f54f1bb90ff3, d6bcb4f74035 and 145e75495359 stay draft,
567 > # not ancestor of -r
567 > # not ancestor of -r
568 o 8 public a-F - b740e3e5c05d
568 o 8 public a-F - b740e3e5c05d
569 |
569 |
570 o 7 public a-E - e9f537e46dea
570 o 7 public a-E - e9f537e46dea
571 |
571 |
572 | o 6 draft n-B - 145e75495359
572 | o 6 draft n-B - 145e75495359
573 | |
573 | |
574 | o 5 draft n-A - d6bcb4f74035
574 | o 5 draft n-A - d6bcb4f74035
575 | |
575 | |
576 o | 4 public a-D - b555f63b6063
576 o | 4 public a-D - b555f63b6063
577 | |
577 | |
578 o | 3 public a-C - 54acac6f23ab
578 o | 3 public a-C - 54acac6f23ab
579 | |
579 | |
580 | o 2 draft b-A - f54f1bb90ff3
580 | o 2 draft b-A - f54f1bb90ff3
581 |/
581 |/
582 o 1 public a-B - 548a3d25dbf0
582 o 1 public a-B - 548a3d25dbf0
583 |
583 |
584 o 0 public a-A - 054250a37db4
584 o 0 public a-A - 054250a37db4
585
585
586
586
587 Pushing to Publish=True (common changeset)
587 Pushing to Publish=True (common changeset)
588
588
589 $ cd ../beta
589 $ cd ../beta
590 $ hg push ../alpha
590 $ hg push ../alpha
591 pushing to ../alpha
591 pushing to ../alpha
592 searching for changes
592 searching for changes
593 no changes found
593 no changes found
594 [1]
594 [1]
595 $ hgph
595 $ hgph
596 o 6 public a-F - b740e3e5c05d
596 o 6 public a-F - b740e3e5c05d
597 |
597 |
598 o 5 public a-E - e9f537e46dea
598 o 5 public a-E - e9f537e46dea
599 |
599 |
600 o 4 public a-D - b555f63b6063
600 o 4 public a-D - b555f63b6063
601 |
601 |
602 o 3 public a-C - 54acac6f23ab
602 o 3 public a-C - 54acac6f23ab
603 |
603 |
604 | @ 2 public b-A - f54f1bb90ff3
604 | @ 2 public b-A - f54f1bb90ff3
605 |/
605 |/
606 o 1 public a-B - 548a3d25dbf0
606 o 1 public a-B - 548a3d25dbf0
607 |
607 |
608 o 0 public a-A - 054250a37db4
608 o 0 public a-A - 054250a37db4
609
609
610 $ cd ../alpha
610 $ cd ../alpha
611 $ hgph
611 $ hgph
612 @ 10 draft a-H - 967b449fbc94
612 @ 10 draft a-H - 967b449fbc94
613 |
613 |
614 | o 9 draft a-G - 3e27b6f1eee1
614 | o 9 draft a-G - 3e27b6f1eee1
615 | |
615 | |
616 | o 8 public a-F - b740e3e5c05d
616 | o 8 public a-F - b740e3e5c05d
617 | |
617 | |
618 | o 7 public a-E - e9f537e46dea
618 | o 7 public a-E - e9f537e46dea
619 | |
619 | |
620 +---o 6 public n-B - 145e75495359
620 +---o 6 public n-B - 145e75495359
621 | |
621 | |
622 o | 5 public n-A - d6bcb4f74035
622 o | 5 public n-A - d6bcb4f74035
623 | |
623 | |
624 o | 4 public b-A - f54f1bb90ff3
624 o | 4 public b-A - f54f1bb90ff3
625 | |
625 | |
626 | o 3 public a-D - b555f63b6063
626 | o 3 public a-D - b555f63b6063
627 | |
627 | |
628 | o 2 public a-C - 54acac6f23ab
628 | o 2 public a-C - 54acac6f23ab
629 |/
629 |/
630 o 1 public a-B - 548a3d25dbf0
630 o 1 public a-B - 548a3d25dbf0
631 |
631 |
632 o 0 public a-A - 054250a37db4
632 o 0 public a-A - 054250a37db4
633
633
634
634
635 Pushing to Publish=False (common changeset that change phase + unknown one)
635 Pushing to Publish=False (common changeset that change phase + unknown one)
636
636
637 $ hg push ../mu -r 967b449fbc94 -f
637 $ hg push ../mu -r 967b449fbc94 -f
638 pushing to ../mu
638 pushing to ../mu
639 searching for changes
639 searching for changes
640 adding changesets
640 adding changesets
641 adding manifests
641 adding manifests
642 adding file changes
642 adding file changes
643 added 1 changesets with 1 changes to 1 files (+1 heads)
643 added 1 changesets with 1 changes to 1 files (+1 heads)
644 $ hgph
644 $ hgph
645 @ 10 draft a-H - 967b449fbc94
645 @ 10 draft a-H - 967b449fbc94
646 |
646 |
647 | o 9 draft a-G - 3e27b6f1eee1
647 | o 9 draft a-G - 3e27b6f1eee1
648 | |
648 | |
649 | o 8 public a-F - b740e3e5c05d
649 | o 8 public a-F - b740e3e5c05d
650 | |
650 | |
651 | o 7 public a-E - e9f537e46dea
651 | o 7 public a-E - e9f537e46dea
652 | |
652 | |
653 +---o 6 public n-B - 145e75495359
653 +---o 6 public n-B - 145e75495359
654 | |
654 | |
655 o | 5 public n-A - d6bcb4f74035
655 o | 5 public n-A - d6bcb4f74035
656 | |
656 | |
657 o | 4 public b-A - f54f1bb90ff3
657 o | 4 public b-A - f54f1bb90ff3
658 | |
658 | |
659 | o 3 public a-D - b555f63b6063
659 | o 3 public a-D - b555f63b6063
660 | |
660 | |
661 | o 2 public a-C - 54acac6f23ab
661 | o 2 public a-C - 54acac6f23ab
662 |/
662 |/
663 o 1 public a-B - 548a3d25dbf0
663 o 1 public a-B - 548a3d25dbf0
664 |
664 |
665 o 0 public a-A - 054250a37db4
665 o 0 public a-A - 054250a37db4
666
666
667 $ cd ../mu
667 $ cd ../mu
668 $ hgph # d6bcb4f74035 should have changed phase
668 $ hgph # d6bcb4f74035 should have changed phase
669 > # 145e75495359 is still draft. not ancestor of -r
669 > # 145e75495359 is still draft. not ancestor of -r
670 o 9 draft a-H - 967b449fbc94
670 o 9 draft a-H - 967b449fbc94
671 |
671 |
672 | o 8 public a-F - b740e3e5c05d
672 | o 8 public a-F - b740e3e5c05d
673 | |
673 | |
674 | o 7 public a-E - e9f537e46dea
674 | o 7 public a-E - e9f537e46dea
675 | |
675 | |
676 +---o 6 draft n-B - 145e75495359
676 +---o 6 draft n-B - 145e75495359
677 | |
677 | |
678 o | 5 public n-A - d6bcb4f74035
678 o | 5 public n-A - d6bcb4f74035
679 | |
679 | |
680 | o 4 public a-D - b555f63b6063
680 | o 4 public a-D - b555f63b6063
681 | |
681 | |
682 | o 3 public a-C - 54acac6f23ab
682 | o 3 public a-C - 54acac6f23ab
683 | |
683 | |
684 o | 2 public b-A - f54f1bb90ff3
684 o | 2 public b-A - f54f1bb90ff3
685 |/
685 |/
686 o 1 public a-B - 548a3d25dbf0
686 o 1 public a-B - 548a3d25dbf0
687 |
687 |
688 o 0 public a-A - 054250a37db4
688 o 0 public a-A - 054250a37db4
689
689
690
690
691
691
692 Pushing to Publish=True (common changeset from publish=False)
692 Pushing to Publish=True (common changeset from publish=False)
693
693
694 (in mu)
694 (in mu)
695 $ hg push ../alpha
695 $ hg push ../alpha
696 pushing to ../alpha
696 pushing to ../alpha
697 searching for changes
697 searching for changes
698 no changes found
698 no changes found
699 [1]
699 [1]
700 $ hgph
700 $ hgph
701 o 9 public a-H - 967b449fbc94
701 o 9 public a-H - 967b449fbc94
702 |
702 |
703 | o 8 public a-F - b740e3e5c05d
703 | o 8 public a-F - b740e3e5c05d
704 | |
704 | |
705 | o 7 public a-E - e9f537e46dea
705 | o 7 public a-E - e9f537e46dea
706 | |
706 | |
707 +---o 6 public n-B - 145e75495359
707 +---o 6 public n-B - 145e75495359
708 | |
708 | |
709 o | 5 public n-A - d6bcb4f74035
709 o | 5 public n-A - d6bcb4f74035
710 | |
710 | |
711 | o 4 public a-D - b555f63b6063
711 | o 4 public a-D - b555f63b6063
712 | |
712 | |
713 | o 3 public a-C - 54acac6f23ab
713 | o 3 public a-C - 54acac6f23ab
714 | |
714 | |
715 o | 2 public b-A - f54f1bb90ff3
715 o | 2 public b-A - f54f1bb90ff3
716 |/
716 |/
717 o 1 public a-B - 548a3d25dbf0
717 o 1 public a-B - 548a3d25dbf0
718 |
718 |
719 o 0 public a-A - 054250a37db4
719 o 0 public a-A - 054250a37db4
720
720
721 $ hgph -R ../alpha # a-H should have been synced to 0
721 $ hgph -R ../alpha # a-H should have been synced to 0
722 @ 10 public a-H - 967b449fbc94
722 @ 10 public a-H - 967b449fbc94
723 |
723 |
724 | o 9 draft a-G - 3e27b6f1eee1
724 | o 9 draft a-G - 3e27b6f1eee1
725 | |
725 | |
726 | o 8 public a-F - b740e3e5c05d
726 | o 8 public a-F - b740e3e5c05d
727 | |
727 | |
728 | o 7 public a-E - e9f537e46dea
728 | o 7 public a-E - e9f537e46dea
729 | |
729 | |
730 +---o 6 public n-B - 145e75495359
730 +---o 6 public n-B - 145e75495359
731 | |
731 | |
732 o | 5 public n-A - d6bcb4f74035
732 o | 5 public n-A - d6bcb4f74035
733 | |
733 | |
734 o | 4 public b-A - f54f1bb90ff3
734 o | 4 public b-A - f54f1bb90ff3
735 | |
735 | |
736 | o 3 public a-D - b555f63b6063
736 | o 3 public a-D - b555f63b6063
737 | |
737 | |
738 | o 2 public a-C - 54acac6f23ab
738 | o 2 public a-C - 54acac6f23ab
739 |/
739 |/
740 o 1 public a-B - 548a3d25dbf0
740 o 1 public a-B - 548a3d25dbf0
741 |
741 |
742 o 0 public a-A - 054250a37db4
742 o 0 public a-A - 054250a37db4
743
743
744
744
745
745
746 Discovery locally secret changeset on a remote repository:
746 Discovery locally secret changeset on a remote repository:
747
747
748 - should make it non-secret
748 - should make it non-secret
749
749
750 $ cd ../alpha
750 $ cd ../alpha
751 $ mkcommit A-secret --config phases.new-commit=2
751 $ mkcommit A-secret --config phases.new-commit=2
752 $ hgph
752 $ hgph
753 @ 11 secret A-secret - 435b5d83910c
753 @ 11 secret A-secret - 435b5d83910c
754 |
754 |
755 o 10 public a-H - 967b449fbc94
755 o 10 public a-H - 967b449fbc94
756 |
756 |
757 | o 9 draft a-G - 3e27b6f1eee1
757 | o 9 draft a-G - 3e27b6f1eee1
758 | |
758 | |
759 | o 8 public a-F - b740e3e5c05d
759 | o 8 public a-F - b740e3e5c05d
760 | |
760 | |
761 | o 7 public a-E - e9f537e46dea
761 | o 7 public a-E - e9f537e46dea
762 | |
762 | |
763 +---o 6 public n-B - 145e75495359
763 +---o 6 public n-B - 145e75495359
764 | |
764 | |
765 o | 5 public n-A - d6bcb4f74035
765 o | 5 public n-A - d6bcb4f74035
766 | |
766 | |
767 o | 4 public b-A - f54f1bb90ff3
767 o | 4 public b-A - f54f1bb90ff3
768 | |
768 | |
769 | o 3 public a-D - b555f63b6063
769 | o 3 public a-D - b555f63b6063
770 | |
770 | |
771 | o 2 public a-C - 54acac6f23ab
771 | o 2 public a-C - 54acac6f23ab
772 |/
772 |/
773 o 1 public a-B - 548a3d25dbf0
773 o 1 public a-B - 548a3d25dbf0
774 |
774 |
775 o 0 public a-A - 054250a37db4
775 o 0 public a-A - 054250a37db4
776
776
777 $ hg bundle --base 'parents(.)' -r . ../secret-bundle.hg
777 $ hg bundle --base 'parents(.)' -r . ../secret-bundle.hg
778 1 changesets found
778 1 changesets found
779 $ hg -R ../mu unbundle ../secret-bundle.hg
779 $ hg -R ../mu unbundle ../secret-bundle.hg
780 adding changesets
780 adding changesets
781 adding manifests
781 adding manifests
782 adding file changes
782 adding file changes
783 added 1 changesets with 1 changes to 1 files
783 added 1 changesets with 1 changes to 1 files
784 (run 'hg update' to get a working copy)
784 (run 'hg update' to get a working copy)
785 $ hgph -R ../mu
785 $ hgph -R ../mu
786 o 10 draft A-secret - 435b5d83910c
786 o 10 draft A-secret - 435b5d83910c
787 |
787 |
788 o 9 public a-H - 967b449fbc94
788 o 9 public a-H - 967b449fbc94
789 |
789 |
790 | o 8 public a-F - b740e3e5c05d
790 | o 8 public a-F - b740e3e5c05d
791 | |
791 | |
792 | o 7 public a-E - e9f537e46dea
792 | o 7 public a-E - e9f537e46dea
793 | |
793 | |
794 +---o 6 public n-B - 145e75495359
794 +---o 6 public n-B - 145e75495359
795 | |
795 | |
796 o | 5 public n-A - d6bcb4f74035
796 o | 5 public n-A - d6bcb4f74035
797 | |
797 | |
798 | o 4 public a-D - b555f63b6063
798 | o 4 public a-D - b555f63b6063
799 | |
799 | |
800 | o 3 public a-C - 54acac6f23ab
800 | o 3 public a-C - 54acac6f23ab
801 | |
801 | |
802 o | 2 public b-A - f54f1bb90ff3
802 o | 2 public b-A - f54f1bb90ff3
803 |/
803 |/
804 o 1 public a-B - 548a3d25dbf0
804 o 1 public a-B - 548a3d25dbf0
805 |
805 |
806 o 0 public a-A - 054250a37db4
806 o 0 public a-A - 054250a37db4
807
807
808 $ hg pull ../mu
808 $ hg pull ../mu
809 pulling from ../mu
809 pulling from ../mu
810 searching for changes
810 searching for changes
811 no changes found
811 no changes found
812 $ hgph
812 $ hgph
813 @ 11 draft A-secret - 435b5d83910c
813 @ 11 draft A-secret - 435b5d83910c
814 |
814 |
815 o 10 public a-H - 967b449fbc94
815 o 10 public a-H - 967b449fbc94
816 |
816 |
817 | o 9 draft a-G - 3e27b6f1eee1
817 | o 9 draft a-G - 3e27b6f1eee1
818 | |
818 | |
819 | o 8 public a-F - b740e3e5c05d
819 | o 8 public a-F - b740e3e5c05d
820 | |
820 | |
821 | o 7 public a-E - e9f537e46dea
821 | o 7 public a-E - e9f537e46dea
822 | |
822 | |
823 +---o 6 public n-B - 145e75495359
823 +---o 6 public n-B - 145e75495359
824 | |
824 | |
825 o | 5 public n-A - d6bcb4f74035
825 o | 5 public n-A - d6bcb4f74035
826 | |
826 | |
827 o | 4 public b-A - f54f1bb90ff3
827 o | 4 public b-A - f54f1bb90ff3
828 | |
828 | |
829 | o 3 public a-D - b555f63b6063
829 | o 3 public a-D - b555f63b6063
830 | |
830 | |
831 | o 2 public a-C - 54acac6f23ab
831 | o 2 public a-C - 54acac6f23ab
832 |/
832 |/
833 o 1 public a-B - 548a3d25dbf0
833 o 1 public a-B - 548a3d25dbf0
834 |
834 |
835 o 0 public a-A - 054250a37db4
835 o 0 public a-A - 054250a37db4
836
836
837
837
838 pushing a locally public and draft changesets remotly secret should make them
838 pushing a locally public and draft changesets remotly secret should make them
839 appear on the remote side.
839 appear on the remote side.
840
840
841
841
842 $ hg -R ../mu phase --secret --force 967b449fbc94
842 $ hg -R ../mu phase --secret --force 967b449fbc94
843 $ hg push -r 435b5d83910c ../mu
843 $ hg push -r 435b5d83910c ../mu
844 pushing to ../mu
844 pushing to ../mu
845 searching for changes
845 searching for changes
846 abort: push creates new remote head 435b5d83910c!
846 abort: push creates new remote head 435b5d83910c!
847 (did you forget to merge? use push -f to force)
847 (did you forget to merge? use push -f to force)
848 [255]
848 [255]
849 $ hg push -fr 435b5d83910c ../mu # because the push will create new visible head
849 $ hg push -fr 435b5d83910c ../mu # because the push will create new visible head
850 pushing to ../mu
850 pushing to ../mu
851 searching for changes
851 searching for changes
852 adding changesets
852 adding changesets
853 adding manifests
853 adding manifests
854 adding file changes
854 adding file changes
855 added 0 changesets with 0 changes to 2 files
855 added 0 changesets with 0 changes to 2 files
856 $ hgph -R ../mu
856 $ hgph -R ../mu
857 o 10 draft A-secret - 435b5d83910c
857 o 10 draft A-secret - 435b5d83910c
858 |
858 |
859 o 9 public a-H - 967b449fbc94
859 o 9 public a-H - 967b449fbc94
860 |
860 |
861 | o 8 public a-F - b740e3e5c05d
861 | o 8 public a-F - b740e3e5c05d
862 | |
862 | |
863 | o 7 public a-E - e9f537e46dea
863 | o 7 public a-E - e9f537e46dea
864 | |
864 | |
865 +---o 6 public n-B - 145e75495359
865 +---o 6 public n-B - 145e75495359
866 | |
866 | |
867 o | 5 public n-A - d6bcb4f74035
867 o | 5 public n-A - d6bcb4f74035
868 | |
868 | |
869 | o 4 public a-D - b555f63b6063
869 | o 4 public a-D - b555f63b6063
870 | |
870 | |
871 | o 3 public a-C - 54acac6f23ab
871 | o 3 public a-C - 54acac6f23ab
872 | |
872 | |
873 o | 2 public b-A - f54f1bb90ff3
873 o | 2 public b-A - f54f1bb90ff3
874 |/
874 |/
875 o 1 public a-B - 548a3d25dbf0
875 o 1 public a-B - 548a3d25dbf0
876 |
876 |
877 o 0 public a-A - 054250a37db4
877 o 0 public a-A - 054250a37db4
878
878
879
879
880 pull new changeset with common draft locally
880 pull new changeset with common draft locally
881
881
882 $ hg up -q 967b449fbc94 # create a new root for draft
882 $ hg up -q 967b449fbc94 # create a new root for draft
883 $ mkcommit 'alpha-more'
883 $ mkcommit 'alpha-more'
884 created new head
884 created new head
885 $ hg push -fr . ../mu
885 $ hg push -fr . ../mu
886 pushing to ../mu
886 pushing to ../mu
887 searching for changes
887 searching for changes
888 adding changesets
888 adding changesets
889 adding manifests
889 adding manifests
890 adding file changes
890 adding file changes
891 added 1 changesets with 1 changes to 1 files (+1 heads)
891 added 1 changesets with 1 changes to 1 files (+1 heads)
892 $ cd ../mu
892 $ cd ../mu
893 $ hg phase --secret --force 1c5cfd894796
893 $ hg phase --secret --force 1c5cfd894796
894 $ hg up -q 435b5d83910c
894 $ hg up -q 435b5d83910c
895 $ mkcommit 'mu-more'
895 $ mkcommit 'mu-more'
896 $ cd ../alpha
896 $ cd ../alpha
897 $ hg pull ../mu
897 $ hg pull ../mu
898 pulling from ../mu
898 pulling from ../mu
899 searching for changes
899 searching for changes
900 adding changesets
900 adding changesets
901 adding manifests
901 adding manifests
902 adding file changes
902 adding file changes
903 added 1 changesets with 1 changes to 1 files
903 added 1 changesets with 1 changes to 1 files
904 (run 'hg update' to get a working copy)
904 (run 'hg update' to get a working copy)
905 $ hgph
905 $ hgph
906 o 13 draft mu-more - 5237fb433fc8
906 o 13 draft mu-more - 5237fb433fc8
907 |
907 |
908 | @ 12 draft alpha-more - 1c5cfd894796
908 | @ 12 draft alpha-more - 1c5cfd894796
909 | |
909 | |
910 o | 11 draft A-secret - 435b5d83910c
910 o | 11 draft A-secret - 435b5d83910c
911 |/
911 |/
912 o 10 public a-H - 967b449fbc94
912 o 10 public a-H - 967b449fbc94
913 |
913 |
914 | o 9 draft a-G - 3e27b6f1eee1
914 | o 9 draft a-G - 3e27b6f1eee1
915 | |
915 | |
916 | o 8 public a-F - b740e3e5c05d
916 | o 8 public a-F - b740e3e5c05d
917 | |
917 | |
918 | o 7 public a-E - e9f537e46dea
918 | o 7 public a-E - e9f537e46dea
919 | |
919 | |
920 +---o 6 public n-B - 145e75495359
920 +---o 6 public n-B - 145e75495359
921 | |
921 | |
922 o | 5 public n-A - d6bcb4f74035
922 o | 5 public n-A - d6bcb4f74035
923 | |
923 | |
924 o | 4 public b-A - f54f1bb90ff3
924 o | 4 public b-A - f54f1bb90ff3
925 | |
925 | |
926 | o 3 public a-D - b555f63b6063
926 | o 3 public a-D - b555f63b6063
927 | |
927 | |
928 | o 2 public a-C - 54acac6f23ab
928 | o 2 public a-C - 54acac6f23ab
929 |/
929 |/
930 o 1 public a-B - 548a3d25dbf0
930 o 1 public a-B - 548a3d25dbf0
931 |
931 |
932 o 0 public a-A - 054250a37db4
932 o 0 public a-A - 054250a37db4
933
933
934
934
935 Test that test are properly ignored on remote event when existing locally
935 Test that test are properly ignored on remote event when existing locally
936
936
937 $ cd ..
937 $ cd ..
938 $ hg clone -qU -r b555f63b6063 -r f54f1bb90ff3 beta gamma
938 $ hg clone -qU -r b555f63b6063 -r f54f1bb90ff3 beta gamma
939
939
940 # pathological case are
940 # pathological case are
941 #
941 #
942 # * secret remotely
942 # * secret remotely
943 # * known locally
943 # * known locally
944 # * repo have uncommon changeset
944 # * repo have uncommon changeset
945
945
946 $ hg -R beta phase --secret --force f54f1bb90ff3
946 $ hg -R beta phase --secret --force f54f1bb90ff3
947 $ hg -R gamma phase --draft --force f54f1bb90ff3
947 $ hg -R gamma phase --draft --force f54f1bb90ff3
948
948
949 $ cd gamma
949 $ cd gamma
950 $ hg pull ../beta
950 $ hg pull ../beta
951 pulling from ../beta
951 pulling from ../beta
952 searching for changes
952 searching for changes
953 adding changesets
953 adding changesets
954 adding manifests
954 adding manifests
955 adding file changes
955 adding file changes
956 added 2 changesets with 2 changes to 2 files
956 added 2 changesets with 2 changes to 2 files
957 (run 'hg update' to get a working copy)
957 (run 'hg update' to get a working copy)
958 $ hg phase f54f1bb90ff3
958 $ hg phase f54f1bb90ff3
959 2: draft
959 2: draft
960
960
961 same over the wire
961 same over the wire
962
962
963 $ cd ../beta
963 $ cd ../beta
964 $ hg serve -p $HGPORT -d --pid-file=../beta.pid -E ../beta-error.log
964 $ hg serve -p $HGPORT -d --pid-file=../beta.pid -E ../beta-error.log
965 $ cat ../beta.pid >> $DAEMON_PIDS
965 $ cat ../beta.pid >> $DAEMON_PIDS
966 $ cd ../gamma
966 $ cd ../gamma
967
967
968 $ hg pull http://localhost:$HGPORT/
968 $ hg pull http://localhost:$HGPORT/
969 pulling from http://localhost:$HGPORT/
969 pulling from http://localhost:$HGPORT/
970 searching for changes
970 searching for changes
971 no changes found
971 no changes found
972 $ hg phase f54f1bb90ff3
972 $ hg phase f54f1bb90ff3
973 2: draft
973 2: draft
974
974
975 check that secret local on both side are not synced to public
975 check that secret local on both side are not synced to public
976
976
977 $ hg push -r b555f63b6063 http://localhost:$HGPORT/
977 $ hg push -r b555f63b6063 http://localhost:$HGPORT/
978 pushing to http://localhost:$HGPORT/
978 pushing to http://localhost:$HGPORT/
979 searching for changes
979 searching for changes
980 no changes found
980 no changes found
981 [1]
981 [1]
982 $ hg phase f54f1bb90ff3
982 $ hg phase f54f1bb90ff3
983 2: draft
983 2: draft
984
984
985 put the changeset in the draft state again
985 put the changeset in the draft state again
986 (first test after this one expect to be able to copy)
986 (first test after this one expect to be able to copy)
987
987
988 $ cd ..
988 $ cd ..
989
989
990
990
991 Test Clone behavior
991 Test Clone behavior
992
992
993 A. Clone without secret changeset
993 A. Clone without secret changeset
994
994
995 1. cloning non-publishing repository
995 1. cloning non-publishing repository
996 (Phase should be preserved)
996 (Phase should be preserved)
997
997
998 # make sure there is no secret so we can use a copy clone
998 # make sure there is no secret so we can use a copy clone
999
999
1000 $ hg -R mu phase --draft 'secret()'
1000 $ hg -R mu phase --draft 'secret()'
1001
1001
1002 $ hg clone -U mu Tau
1002 $ hg clone -U mu Tau
1003 $ hgph -R Tau
1003 $ hgph -R Tau
1004 o 12 draft mu-more - 5237fb433fc8
1004 o 12 draft mu-more - 5237fb433fc8
1005 |
1005 |
1006 | o 11 draft alpha-more - 1c5cfd894796
1006 | o 11 draft alpha-more - 1c5cfd894796
1007 | |
1007 | |
1008 o | 10 draft A-secret - 435b5d83910c
1008 o | 10 draft A-secret - 435b5d83910c
1009 |/
1009 |/
1010 o 9 public a-H - 967b449fbc94
1010 o 9 public a-H - 967b449fbc94
1011 |
1011 |
1012 | o 8 public a-F - b740e3e5c05d
1012 | o 8 public a-F - b740e3e5c05d
1013 | |
1013 | |
1014 | o 7 public a-E - e9f537e46dea
1014 | o 7 public a-E - e9f537e46dea
1015 | |
1015 | |
1016 +---o 6 public n-B - 145e75495359
1016 +---o 6 public n-B - 145e75495359
1017 | |
1017 | |
1018 o | 5 public n-A - d6bcb4f74035
1018 o | 5 public n-A - d6bcb4f74035
1019 | |
1019 | |
1020 | o 4 public a-D - b555f63b6063
1020 | o 4 public a-D - b555f63b6063
1021 | |
1021 | |
1022 | o 3 public a-C - 54acac6f23ab
1022 | o 3 public a-C - 54acac6f23ab
1023 | |
1023 | |
1024 o | 2 public b-A - f54f1bb90ff3
1024 o | 2 public b-A - f54f1bb90ff3
1025 |/
1025 |/
1026 o 1 public a-B - 548a3d25dbf0
1026 o 1 public a-B - 548a3d25dbf0
1027 |
1027 |
1028 o 0 public a-A - 054250a37db4
1028 o 0 public a-A - 054250a37db4
1029
1029
1030
1030
1031 2. cloning publishing repository
1031 2. cloning publishing repository
1032
1032
1033 (everything should be public)
1033 (everything should be public)
1034
1034
1035 $ hg clone -U alpha Upsilon
1035 $ hg clone -U alpha Upsilon
1036 $ hgph -R Upsilon
1036 $ hgph -R Upsilon
1037 o 13 public mu-more - 5237fb433fc8
1037 o 13 public mu-more - 5237fb433fc8
1038 |
1038 |
1039 | o 12 public alpha-more - 1c5cfd894796
1039 | o 12 public alpha-more - 1c5cfd894796
1040 | |
1040 | |
1041 o | 11 public A-secret - 435b5d83910c
1041 o | 11 public A-secret - 435b5d83910c
1042 |/
1042 |/
1043 o 10 public a-H - 967b449fbc94
1043 o 10 public a-H - 967b449fbc94
1044 |
1044 |
1045 | o 9 public a-G - 3e27b6f1eee1
1045 | o 9 public a-G - 3e27b6f1eee1
1046 | |
1046 | |
1047 | o 8 public a-F - b740e3e5c05d
1047 | o 8 public a-F - b740e3e5c05d
1048 | |
1048 | |
1049 | o 7 public a-E - e9f537e46dea
1049 | o 7 public a-E - e9f537e46dea
1050 | |
1050 | |
1051 +---o 6 public n-B - 145e75495359
1051 +---o 6 public n-B - 145e75495359
1052 | |
1052 | |
1053 o | 5 public n-A - d6bcb4f74035
1053 o | 5 public n-A - d6bcb4f74035
1054 | |
1054 | |
1055 o | 4 public b-A - f54f1bb90ff3
1055 o | 4 public b-A - f54f1bb90ff3
1056 | |
1056 | |
1057 | o 3 public a-D - b555f63b6063
1057 | o 3 public a-D - b555f63b6063
1058 | |
1058 | |
1059 | o 2 public a-C - 54acac6f23ab
1059 | o 2 public a-C - 54acac6f23ab
1060 |/
1060 |/
1061 o 1 public a-B - 548a3d25dbf0
1061 o 1 public a-B - 548a3d25dbf0
1062 |
1062 |
1063 o 0 public a-A - 054250a37db4
1063 o 0 public a-A - 054250a37db4
1064
1064
1065
1066 Pushing From an unlockable repo
1067 --------------------------------
1068 (issue3684)
1069
1070 Unability to lock the source repo should not prevent the push. It will prevent
1071 the retrieval of remote phase during push. For example, pushing to a publishing
1072 server won't turn changeset public.
1073
1074 1. Test that push is not prevented
1075
1076 $ hg init Phi
1077 $ cd Upsilon
1078 $ chmod -R -w .hg
1079 $ hg push ../Phi
1080 pushing to ../Phi
1081 searching for changes
1082 adding changesets
1083 adding manifests
1084 adding file changes
1085 added 14 changesets with 14 changes to 14 files (+3 heads)
1086 $ chmod -R +w .hg
1087
1088 2. Test that failed phases movement are reported
1089
1090 $ hg phase --force --draft 3
1091 $ chmod -R -w .hg
1092 $ hg push ../Phi
1093 pushing to ../Phi
1094 searching for changes
1095 no changes found
1096 cannot lock source repo, skipping local public phase update
1097 [1]
1098 $ chmod -R +w .hg
1099 $ hgph Upsilon
1100
1101 $ cd ..
1102
1065 $ "$TESTDIR/killdaemons.py" $DAEMON_PIDS
1103 $ "$TESTDIR/killdaemons.py" $DAEMON_PIDS
1066
1104
General Comments 0
You need to be logged in to leave comments. Login now