##// END OF EJS Templates
rename: properly report removed and added file as modified (issue4458)...
Pierre-Yves David -
r23402:2963d5c9 stable
parent child Browse files
Show More
@@ -1,1731 +1,1733 b''
1 # context.py - changeset and file context objects for mercurial
1 # context.py - changeset and file context objects for mercurial
2 #
2 #
3 # Copyright 2006, 2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2006, 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
7
8 from node import nullid, nullrev, short, hex, bin
8 from node import nullid, nullrev, short, hex, bin
9 from i18n import _
9 from i18n import _
10 import mdiff, error, util, scmutil, subrepo, patch, encoding, phases
10 import mdiff, error, util, scmutil, subrepo, patch, encoding, phases
11 import match as matchmod
11 import match as matchmod
12 import os, errno, stat
12 import os, errno, stat
13 import obsolete as obsmod
13 import obsolete as obsmod
14 import repoview
14 import repoview
15 import fileset
15 import fileset
16 import revlog
16 import revlog
17
17
18 propertycache = util.propertycache
18 propertycache = util.propertycache
19
19
20 class basectx(object):
20 class basectx(object):
21 """A basectx object represents the common logic for its children:
21 """A basectx object represents the common logic for its children:
22 changectx: read-only context that is already present in the repo,
22 changectx: read-only context that is already present in the repo,
23 workingctx: a context that represents the working directory and can
23 workingctx: a context that represents the working directory and can
24 be committed,
24 be committed,
25 memctx: a context that represents changes in-memory and can also
25 memctx: a context that represents changes in-memory and can also
26 be committed."""
26 be committed."""
27 def __new__(cls, repo, changeid='', *args, **kwargs):
27 def __new__(cls, repo, changeid='', *args, **kwargs):
28 if isinstance(changeid, basectx):
28 if isinstance(changeid, basectx):
29 return changeid
29 return changeid
30
30
31 o = super(basectx, cls).__new__(cls)
31 o = super(basectx, cls).__new__(cls)
32
32
33 o._repo = repo
33 o._repo = repo
34 o._rev = nullrev
34 o._rev = nullrev
35 o._node = nullid
35 o._node = nullid
36
36
37 return o
37 return o
38
38
39 def __str__(self):
39 def __str__(self):
40 return short(self.node())
40 return short(self.node())
41
41
42 def __int__(self):
42 def __int__(self):
43 return self.rev()
43 return self.rev()
44
44
45 def __repr__(self):
45 def __repr__(self):
46 return "<%s %s>" % (type(self).__name__, str(self))
46 return "<%s %s>" % (type(self).__name__, str(self))
47
47
48 def __eq__(self, other):
48 def __eq__(self, other):
49 try:
49 try:
50 return type(self) == type(other) and self._rev == other._rev
50 return type(self) == type(other) and self._rev == other._rev
51 except AttributeError:
51 except AttributeError:
52 return False
52 return False
53
53
54 def __ne__(self, other):
54 def __ne__(self, other):
55 return not (self == other)
55 return not (self == other)
56
56
57 def __contains__(self, key):
57 def __contains__(self, key):
58 return key in self._manifest
58 return key in self._manifest
59
59
60 def __getitem__(self, key):
60 def __getitem__(self, key):
61 return self.filectx(key)
61 return self.filectx(key)
62
62
63 def __iter__(self):
63 def __iter__(self):
64 for f in sorted(self._manifest):
64 for f in sorted(self._manifest):
65 yield f
65 yield f
66
66
67 def _manifestmatches(self, match, s):
67 def _manifestmatches(self, match, s):
68 """generate a new manifest filtered by the match argument
68 """generate a new manifest filtered by the match argument
69
69
70 This method is for internal use only and mainly exists to provide an
70 This method is for internal use only and mainly exists to provide an
71 object oriented way for other contexts to customize the manifest
71 object oriented way for other contexts to customize the manifest
72 generation.
72 generation.
73 """
73 """
74 if match.always():
74 if match.always():
75 return self.manifest().copy()
75 return self.manifest().copy()
76
76
77 files = match.files()
77 files = match.files()
78 if (match.matchfn == match.exact or
78 if (match.matchfn == match.exact or
79 (not match.anypats() and util.all(fn in self for fn in files))):
79 (not match.anypats() and util.all(fn in self for fn in files))):
80 return self.manifest().intersectfiles(files)
80 return self.manifest().intersectfiles(files)
81
81
82 mf = self.manifest().copy()
82 mf = self.manifest().copy()
83 for fn in mf.keys():
83 for fn in mf.keys():
84 if not match(fn):
84 if not match(fn):
85 del mf[fn]
85 del mf[fn]
86 return mf
86 return mf
87
87
88 def _matchstatus(self, other, s, match, listignored, listclean,
88 def _matchstatus(self, other, s, match, listignored, listclean,
89 listunknown):
89 listunknown):
90 """return match.always if match is none
90 """return match.always if match is none
91
91
92 This internal method provides a way for child objects to override the
92 This internal method provides a way for child objects to override the
93 match operator.
93 match operator.
94 """
94 """
95 return match or matchmod.always(self._repo.root, self._repo.getcwd())
95 return match or matchmod.always(self._repo.root, self._repo.getcwd())
96
96
97 def _prestatus(self, other, s, match, listignored, listclean, listunknown):
97 def _prestatus(self, other, s, match, listignored, listclean, listunknown):
98 """provide a hook to allow child objects to preprocess status results
98 """provide a hook to allow child objects to preprocess status results
99
99
100 For example, this allows other contexts, such as workingctx, to query
100 For example, this allows other contexts, such as workingctx, to query
101 the dirstate before comparing the manifests.
101 the dirstate before comparing the manifests.
102 """
102 """
103 # load earliest manifest first for caching reasons
103 # load earliest manifest first for caching reasons
104 if self.rev() < other.rev():
104 if self.rev() < other.rev():
105 self.manifest()
105 self.manifest()
106 return s
106 return s
107
107
108 def _poststatus(self, other, s, match, listignored, listclean, listunknown):
108 def _poststatus(self, other, s, match, listignored, listclean, listunknown):
109 """provide a hook to allow child objects to postprocess status results
109 """provide a hook to allow child objects to postprocess status results
110
110
111 For example, this allows other contexts, such as workingctx, to filter
111 For example, this allows other contexts, such as workingctx, to filter
112 suspect symlinks in the case of FAT32 and NTFS filesytems.
112 suspect symlinks in the case of FAT32 and NTFS filesytems.
113 """
113 """
114 return s
114 return s
115
115
116 def _buildstatus(self, other, s, match, listignored, listclean,
116 def _buildstatus(self, other, s, match, listignored, listclean,
117 listunknown):
117 listunknown):
118 """build a status with respect to another context"""
118 """build a status with respect to another context"""
119 mf1 = other._manifestmatches(match, s)
119 mf1 = other._manifestmatches(match, s)
120 mf2 = self._manifestmatches(match, s)
120 mf2 = self._manifestmatches(match, s)
121
121
122 modified, added, clean = [], [], []
122 modified, added, clean = [], [], []
123 deleted, unknown, ignored = s[3], s[4], s[5]
123 deleted, unknown, ignored = s[3], s[4], s[5]
124 deletedset = set(deleted)
124 deletedset = set(deleted)
125 withflags = mf1.withflags() | mf2.withflags()
125 withflags = mf1.withflags() | mf2.withflags()
126 for fn, mf2node in mf2.iteritems():
126 for fn, mf2node in mf2.iteritems():
127 if fn in mf1:
127 if fn in mf1:
128 if (fn not in deletedset and
128 if (fn not in deletedset and
129 ((fn in withflags and mf1.flags(fn) != mf2.flags(fn)) or
129 ((fn in withflags and mf1.flags(fn) != mf2.flags(fn)) or
130 (mf1[fn] != mf2node and
130 (mf1[fn] != mf2node and
131 (mf2node or self[fn].cmp(other[fn]))))):
131 (mf2node or self[fn].cmp(other[fn]))))):
132 modified.append(fn)
132 modified.append(fn)
133 elif listclean:
133 elif listclean:
134 clean.append(fn)
134 clean.append(fn)
135 del mf1[fn]
135 del mf1[fn]
136 elif fn not in deletedset:
136 elif fn not in deletedset:
137 added.append(fn)
137 added.append(fn)
138 removed = mf1.keys()
138 removed = mf1.keys()
139 if removed:
139 if removed:
140 # need to filter files if they are already reported as removed
140 # need to filter files if they are already reported as removed
141 unknown = [fn for fn in unknown if fn not in mf1]
141 unknown = [fn for fn in unknown if fn not in mf1]
142 ignored = [fn for fn in ignored if fn not in mf1]
142 ignored = [fn for fn in ignored if fn not in mf1]
143
143
144 return [modified, added, removed, deleted, unknown, ignored, clean]
144 return [modified, added, removed, deleted, unknown, ignored, clean]
145
145
146 @propertycache
146 @propertycache
147 def substate(self):
147 def substate(self):
148 return subrepo.state(self, self._repo.ui)
148 return subrepo.state(self, self._repo.ui)
149
149
150 def subrev(self, subpath):
150 def subrev(self, subpath):
151 return self.substate[subpath][1]
151 return self.substate[subpath][1]
152
152
153 def rev(self):
153 def rev(self):
154 return self._rev
154 return self._rev
155 def node(self):
155 def node(self):
156 return self._node
156 return self._node
157 def hex(self):
157 def hex(self):
158 return hex(self.node())
158 return hex(self.node())
159 def manifest(self):
159 def manifest(self):
160 return self._manifest
160 return self._manifest
161 def phasestr(self):
161 def phasestr(self):
162 return phases.phasenames[self.phase()]
162 return phases.phasenames[self.phase()]
163 def mutable(self):
163 def mutable(self):
164 return self.phase() > phases.public
164 return self.phase() > phases.public
165
165
166 def getfileset(self, expr):
166 def getfileset(self, expr):
167 return fileset.getfileset(self, expr)
167 return fileset.getfileset(self, expr)
168
168
169 def obsolete(self):
169 def obsolete(self):
170 """True if the changeset is obsolete"""
170 """True if the changeset is obsolete"""
171 return self.rev() in obsmod.getrevs(self._repo, 'obsolete')
171 return self.rev() in obsmod.getrevs(self._repo, 'obsolete')
172
172
173 def extinct(self):
173 def extinct(self):
174 """True if the changeset is extinct"""
174 """True if the changeset is extinct"""
175 return self.rev() in obsmod.getrevs(self._repo, 'extinct')
175 return self.rev() in obsmod.getrevs(self._repo, 'extinct')
176
176
177 def unstable(self):
177 def unstable(self):
178 """True if the changeset is not obsolete but it's ancestor are"""
178 """True if the changeset is not obsolete but it's ancestor are"""
179 return self.rev() in obsmod.getrevs(self._repo, 'unstable')
179 return self.rev() in obsmod.getrevs(self._repo, 'unstable')
180
180
181 def bumped(self):
181 def bumped(self):
182 """True if the changeset try to be a successor of a public changeset
182 """True if the changeset try to be a successor of a public changeset
183
183
184 Only non-public and non-obsolete changesets may be bumped.
184 Only non-public and non-obsolete changesets may be bumped.
185 """
185 """
186 return self.rev() in obsmod.getrevs(self._repo, 'bumped')
186 return self.rev() in obsmod.getrevs(self._repo, 'bumped')
187
187
188 def divergent(self):
188 def divergent(self):
189 """Is a successors of a changeset with multiple possible successors set
189 """Is a successors of a changeset with multiple possible successors set
190
190
191 Only non-public and non-obsolete changesets may be divergent.
191 Only non-public and non-obsolete changesets may be divergent.
192 """
192 """
193 return self.rev() in obsmod.getrevs(self._repo, 'divergent')
193 return self.rev() in obsmod.getrevs(self._repo, 'divergent')
194
194
195 def troubled(self):
195 def troubled(self):
196 """True if the changeset is either unstable, bumped or divergent"""
196 """True if the changeset is either unstable, bumped or divergent"""
197 return self.unstable() or self.bumped() or self.divergent()
197 return self.unstable() or self.bumped() or self.divergent()
198
198
199 def troubles(self):
199 def troubles(self):
200 """return the list of troubles affecting this changesets.
200 """return the list of troubles affecting this changesets.
201
201
202 Troubles are returned as strings. possible values are:
202 Troubles are returned as strings. possible values are:
203 - unstable,
203 - unstable,
204 - bumped,
204 - bumped,
205 - divergent.
205 - divergent.
206 """
206 """
207 troubles = []
207 troubles = []
208 if self.unstable():
208 if self.unstable():
209 troubles.append('unstable')
209 troubles.append('unstable')
210 if self.bumped():
210 if self.bumped():
211 troubles.append('bumped')
211 troubles.append('bumped')
212 if self.divergent():
212 if self.divergent():
213 troubles.append('divergent')
213 troubles.append('divergent')
214 return troubles
214 return troubles
215
215
216 def parents(self):
216 def parents(self):
217 """return contexts for each parent changeset"""
217 """return contexts for each parent changeset"""
218 return self._parents
218 return self._parents
219
219
220 def p1(self):
220 def p1(self):
221 return self._parents[0]
221 return self._parents[0]
222
222
223 def p2(self):
223 def p2(self):
224 if len(self._parents) == 2:
224 if len(self._parents) == 2:
225 return self._parents[1]
225 return self._parents[1]
226 return changectx(self._repo, -1)
226 return changectx(self._repo, -1)
227
227
228 def _fileinfo(self, path):
228 def _fileinfo(self, path):
229 if '_manifest' in self.__dict__:
229 if '_manifest' in self.__dict__:
230 try:
230 try:
231 return self._manifest[path], self._manifest.flags(path)
231 return self._manifest[path], self._manifest.flags(path)
232 except KeyError:
232 except KeyError:
233 raise error.ManifestLookupError(self._node, path,
233 raise error.ManifestLookupError(self._node, path,
234 _('not found in manifest'))
234 _('not found in manifest'))
235 if '_manifestdelta' in self.__dict__ or path in self.files():
235 if '_manifestdelta' in self.__dict__ or path in self.files():
236 if path in self._manifestdelta:
236 if path in self._manifestdelta:
237 return (self._manifestdelta[path],
237 return (self._manifestdelta[path],
238 self._manifestdelta.flags(path))
238 self._manifestdelta.flags(path))
239 node, flag = self._repo.manifest.find(self._changeset[0], path)
239 node, flag = self._repo.manifest.find(self._changeset[0], path)
240 if not node:
240 if not node:
241 raise error.ManifestLookupError(self._node, path,
241 raise error.ManifestLookupError(self._node, path,
242 _('not found in manifest'))
242 _('not found in manifest'))
243
243
244 return node, flag
244 return node, flag
245
245
246 def filenode(self, path):
246 def filenode(self, path):
247 return self._fileinfo(path)[0]
247 return self._fileinfo(path)[0]
248
248
249 def flags(self, path):
249 def flags(self, path):
250 try:
250 try:
251 return self._fileinfo(path)[1]
251 return self._fileinfo(path)[1]
252 except error.LookupError:
252 except error.LookupError:
253 return ''
253 return ''
254
254
255 def sub(self, path):
255 def sub(self, path):
256 return subrepo.subrepo(self, path)
256 return subrepo.subrepo(self, path)
257
257
258 def match(self, pats=[], include=None, exclude=None, default='glob'):
258 def match(self, pats=[], include=None, exclude=None, default='glob'):
259 r = self._repo
259 r = self._repo
260 return matchmod.match(r.root, r.getcwd(), pats,
260 return matchmod.match(r.root, r.getcwd(), pats,
261 include, exclude, default,
261 include, exclude, default,
262 auditor=r.auditor, ctx=self)
262 auditor=r.auditor, ctx=self)
263
263
264 def diff(self, ctx2=None, match=None, **opts):
264 def diff(self, ctx2=None, match=None, **opts):
265 """Returns a diff generator for the given contexts and matcher"""
265 """Returns a diff generator for the given contexts and matcher"""
266 if ctx2 is None:
266 if ctx2 is None:
267 ctx2 = self.p1()
267 ctx2 = self.p1()
268 if ctx2 is not None:
268 if ctx2 is not None:
269 ctx2 = self._repo[ctx2]
269 ctx2 = self._repo[ctx2]
270 diffopts = patch.diffopts(self._repo.ui, opts)
270 diffopts = patch.diffopts(self._repo.ui, opts)
271 return patch.diff(self._repo, ctx2, self, match=match, opts=diffopts)
271 return patch.diff(self._repo, ctx2, self, match=match, opts=diffopts)
272
272
273 @propertycache
273 @propertycache
274 def _dirs(self):
274 def _dirs(self):
275 return scmutil.dirs(self._manifest)
275 return scmutil.dirs(self._manifest)
276
276
277 def dirs(self):
277 def dirs(self):
278 return self._dirs
278 return self._dirs
279
279
280 def dirty(self, missing=False, merge=True, branch=True):
280 def dirty(self, missing=False, merge=True, branch=True):
281 return False
281 return False
282
282
283 def status(self, other=None, match=None, listignored=False,
283 def status(self, other=None, match=None, listignored=False,
284 listclean=False, listunknown=False, listsubrepos=False):
284 listclean=False, listunknown=False, listsubrepos=False):
285 """return status of files between two nodes or node and working
285 """return status of files between two nodes or node and working
286 directory.
286 directory.
287
287
288 If other is None, compare this node with working directory.
288 If other is None, compare this node with working directory.
289
289
290 returns (modified, added, removed, deleted, unknown, ignored, clean)
290 returns (modified, added, removed, deleted, unknown, ignored, clean)
291 """
291 """
292
292
293 ctx1 = self
293 ctx1 = self
294 ctx2 = self._repo[other]
294 ctx2 = self._repo[other]
295
295
296 # This next code block is, admittedly, fragile logic that tests for
296 # This next code block is, admittedly, fragile logic that tests for
297 # reversing the contexts and wouldn't need to exist if it weren't for
297 # reversing the contexts and wouldn't need to exist if it weren't for
298 # the fast (and common) code path of comparing the working directory
298 # the fast (and common) code path of comparing the working directory
299 # with its first parent.
299 # with its first parent.
300 #
300 #
301 # What we're aiming for here is the ability to call:
301 # What we're aiming for here is the ability to call:
302 #
302 #
303 # workingctx.status(parentctx)
303 # workingctx.status(parentctx)
304 #
304 #
305 # If we always built the manifest for each context and compared those,
305 # If we always built the manifest for each context and compared those,
306 # then we'd be done. But the special case of the above call means we
306 # then we'd be done. But the special case of the above call means we
307 # just copy the manifest of the parent.
307 # just copy the manifest of the parent.
308 reversed = False
308 reversed = False
309 if (not isinstance(ctx1, changectx)
309 if (not isinstance(ctx1, changectx)
310 and isinstance(ctx2, changectx)):
310 and isinstance(ctx2, changectx)):
311 reversed = True
311 reversed = True
312 ctx1, ctx2 = ctx2, ctx1
312 ctx1, ctx2 = ctx2, ctx1
313
313
314 r = [[], [], [], [], [], [], []]
314 r = [[], [], [], [], [], [], []]
315 match = ctx2._matchstatus(ctx1, r, match, listignored, listclean,
315 match = ctx2._matchstatus(ctx1, r, match, listignored, listclean,
316 listunknown)
316 listunknown)
317 r = ctx2._prestatus(ctx1, r, match, listignored, listclean, listunknown)
317 r = ctx2._prestatus(ctx1, r, match, listignored, listclean, listunknown)
318 r = ctx2._buildstatus(ctx1, r, match, listignored, listclean,
318 r = ctx2._buildstatus(ctx1, r, match, listignored, listclean,
319 listunknown)
319 listunknown)
320 r = ctx2._poststatus(ctx1, r, match, listignored, listclean,
320 r = ctx2._poststatus(ctx1, r, match, listignored, listclean,
321 listunknown)
321 listunknown)
322
322
323 if reversed:
323 if reversed:
324 # reverse added and removed
324 # reverse added and removed
325 r[1], r[2] = r[2], r[1]
325 r[1], r[2] = r[2], r[1]
326
326
327 if listsubrepos:
327 if listsubrepos:
328 for subpath, sub in scmutil.itersubrepos(ctx1, ctx2):
328 for subpath, sub in scmutil.itersubrepos(ctx1, ctx2):
329 rev2 = ctx2.subrev(subpath)
329 rev2 = ctx2.subrev(subpath)
330 try:
330 try:
331 submatch = matchmod.narrowmatcher(subpath, match)
331 submatch = matchmod.narrowmatcher(subpath, match)
332 s = sub.status(rev2, match=submatch, ignored=listignored,
332 s = sub.status(rev2, match=submatch, ignored=listignored,
333 clean=listclean, unknown=listunknown,
333 clean=listclean, unknown=listunknown,
334 listsubrepos=True)
334 listsubrepos=True)
335 for rfiles, sfiles in zip(r, s):
335 for rfiles, sfiles in zip(r, s):
336 rfiles.extend("%s/%s" % (subpath, f) for f in sfiles)
336 rfiles.extend("%s/%s" % (subpath, f) for f in sfiles)
337 except error.LookupError:
337 except error.LookupError:
338 self._repo.ui.status(_("skipping missing "
338 self._repo.ui.status(_("skipping missing "
339 "subrepository: %s\n") % subpath)
339 "subrepository: %s\n") % subpath)
340
340
341 for l in r:
341 for l in r:
342 l.sort()
342 l.sort()
343
343
344 # we return a tuple to signify that this list isn't changing
344 # we return a tuple to signify that this list isn't changing
345 return scmutil.status(*r)
345 return scmutil.status(*r)
346
346
347
347
348 def makememctx(repo, parents, text, user, date, branch, files, store,
348 def makememctx(repo, parents, text, user, date, branch, files, store,
349 editor=None):
349 editor=None):
350 def getfilectx(repo, memctx, path):
350 def getfilectx(repo, memctx, path):
351 data, mode, copied = store.getfile(path)
351 data, mode, copied = store.getfile(path)
352 if data is None:
352 if data is None:
353 return None
353 return None
354 islink, isexec = mode
354 islink, isexec = mode
355 return memfilectx(repo, path, data, islink=islink, isexec=isexec,
355 return memfilectx(repo, path, data, islink=islink, isexec=isexec,
356 copied=copied, memctx=memctx)
356 copied=copied, memctx=memctx)
357 extra = {}
357 extra = {}
358 if branch:
358 if branch:
359 extra['branch'] = encoding.fromlocal(branch)
359 extra['branch'] = encoding.fromlocal(branch)
360 ctx = memctx(repo, parents, text, files, getfilectx, user,
360 ctx = memctx(repo, parents, text, files, getfilectx, user,
361 date, extra, editor)
361 date, extra, editor)
362 return ctx
362 return ctx
363
363
364 class changectx(basectx):
364 class changectx(basectx):
365 """A changecontext object makes access to data related to a particular
365 """A changecontext object makes access to data related to a particular
366 changeset convenient. It represents a read-only context already present in
366 changeset convenient. It represents a read-only context already present in
367 the repo."""
367 the repo."""
368 def __init__(self, repo, changeid=''):
368 def __init__(self, repo, changeid=''):
369 """changeid is a revision number, node, or tag"""
369 """changeid is a revision number, node, or tag"""
370
370
371 # since basectx.__new__ already took care of copying the object, we
371 # since basectx.__new__ already took care of copying the object, we
372 # don't need to do anything in __init__, so we just exit here
372 # don't need to do anything in __init__, so we just exit here
373 if isinstance(changeid, basectx):
373 if isinstance(changeid, basectx):
374 return
374 return
375
375
376 if changeid == '':
376 if changeid == '':
377 changeid = '.'
377 changeid = '.'
378 self._repo = repo
378 self._repo = repo
379
379
380 try:
380 try:
381 if isinstance(changeid, int):
381 if isinstance(changeid, int):
382 self._node = repo.changelog.node(changeid)
382 self._node = repo.changelog.node(changeid)
383 self._rev = changeid
383 self._rev = changeid
384 return
384 return
385 if isinstance(changeid, long):
385 if isinstance(changeid, long):
386 changeid = str(changeid)
386 changeid = str(changeid)
387 if changeid == '.':
387 if changeid == '.':
388 self._node = repo.dirstate.p1()
388 self._node = repo.dirstate.p1()
389 self._rev = repo.changelog.rev(self._node)
389 self._rev = repo.changelog.rev(self._node)
390 return
390 return
391 if changeid == 'null':
391 if changeid == 'null':
392 self._node = nullid
392 self._node = nullid
393 self._rev = nullrev
393 self._rev = nullrev
394 return
394 return
395 if changeid == 'tip':
395 if changeid == 'tip':
396 self._node = repo.changelog.tip()
396 self._node = repo.changelog.tip()
397 self._rev = repo.changelog.rev(self._node)
397 self._rev = repo.changelog.rev(self._node)
398 return
398 return
399 if len(changeid) == 20:
399 if len(changeid) == 20:
400 try:
400 try:
401 self._node = changeid
401 self._node = changeid
402 self._rev = repo.changelog.rev(changeid)
402 self._rev = repo.changelog.rev(changeid)
403 return
403 return
404 except error.FilteredRepoLookupError:
404 except error.FilteredRepoLookupError:
405 raise
405 raise
406 except LookupError:
406 except LookupError:
407 pass
407 pass
408
408
409 try:
409 try:
410 r = int(changeid)
410 r = int(changeid)
411 if str(r) != changeid:
411 if str(r) != changeid:
412 raise ValueError
412 raise ValueError
413 l = len(repo.changelog)
413 l = len(repo.changelog)
414 if r < 0:
414 if r < 0:
415 r += l
415 r += l
416 if r < 0 or r >= l:
416 if r < 0 or r >= l:
417 raise ValueError
417 raise ValueError
418 self._rev = r
418 self._rev = r
419 self._node = repo.changelog.node(r)
419 self._node = repo.changelog.node(r)
420 return
420 return
421 except error.FilteredIndexError:
421 except error.FilteredIndexError:
422 raise
422 raise
423 except (ValueError, OverflowError, IndexError):
423 except (ValueError, OverflowError, IndexError):
424 pass
424 pass
425
425
426 if len(changeid) == 40:
426 if len(changeid) == 40:
427 try:
427 try:
428 self._node = bin(changeid)
428 self._node = bin(changeid)
429 self._rev = repo.changelog.rev(self._node)
429 self._rev = repo.changelog.rev(self._node)
430 return
430 return
431 except error.FilteredLookupError:
431 except error.FilteredLookupError:
432 raise
432 raise
433 except (TypeError, LookupError):
433 except (TypeError, LookupError):
434 pass
434 pass
435
435
436 if changeid in repo._bookmarks:
436 if changeid in repo._bookmarks:
437 self._node = repo._bookmarks[changeid]
437 self._node = repo._bookmarks[changeid]
438 self._rev = repo.changelog.rev(self._node)
438 self._rev = repo.changelog.rev(self._node)
439 return
439 return
440 if changeid in repo._tagscache.tags:
440 if changeid in repo._tagscache.tags:
441 self._node = repo._tagscache.tags[changeid]
441 self._node = repo._tagscache.tags[changeid]
442 self._rev = repo.changelog.rev(self._node)
442 self._rev = repo.changelog.rev(self._node)
443 return
443 return
444 try:
444 try:
445 self._node = repo.branchtip(changeid)
445 self._node = repo.branchtip(changeid)
446 self._rev = repo.changelog.rev(self._node)
446 self._rev = repo.changelog.rev(self._node)
447 return
447 return
448 except error.FilteredRepoLookupError:
448 except error.FilteredRepoLookupError:
449 raise
449 raise
450 except error.RepoLookupError:
450 except error.RepoLookupError:
451 pass
451 pass
452
452
453 self._node = repo.unfiltered().changelog._partialmatch(changeid)
453 self._node = repo.unfiltered().changelog._partialmatch(changeid)
454 if self._node is not None:
454 if self._node is not None:
455 self._rev = repo.changelog.rev(self._node)
455 self._rev = repo.changelog.rev(self._node)
456 return
456 return
457
457
458 # lookup failed
458 # lookup failed
459 # check if it might have come from damaged dirstate
459 # check if it might have come from damaged dirstate
460 #
460 #
461 # XXX we could avoid the unfiltered if we had a recognizable
461 # XXX we could avoid the unfiltered if we had a recognizable
462 # exception for filtered changeset access
462 # exception for filtered changeset access
463 if changeid in repo.unfiltered().dirstate.parents():
463 if changeid in repo.unfiltered().dirstate.parents():
464 msg = _("working directory has unknown parent '%s'!")
464 msg = _("working directory has unknown parent '%s'!")
465 raise error.Abort(msg % short(changeid))
465 raise error.Abort(msg % short(changeid))
466 try:
466 try:
467 if len(changeid) == 20:
467 if len(changeid) == 20:
468 changeid = hex(changeid)
468 changeid = hex(changeid)
469 except TypeError:
469 except TypeError:
470 pass
470 pass
471 except (error.FilteredIndexError, error.FilteredLookupError,
471 except (error.FilteredIndexError, error.FilteredLookupError,
472 error.FilteredRepoLookupError):
472 error.FilteredRepoLookupError):
473 if repo.filtername == 'visible':
473 if repo.filtername == 'visible':
474 msg = _("hidden revision '%s'") % changeid
474 msg = _("hidden revision '%s'") % changeid
475 hint = _('use --hidden to access hidden revisions')
475 hint = _('use --hidden to access hidden revisions')
476 raise error.FilteredRepoLookupError(msg, hint=hint)
476 raise error.FilteredRepoLookupError(msg, hint=hint)
477 msg = _("filtered revision '%s' (not in '%s' subset)")
477 msg = _("filtered revision '%s' (not in '%s' subset)")
478 msg %= (changeid, repo.filtername)
478 msg %= (changeid, repo.filtername)
479 raise error.FilteredRepoLookupError(msg)
479 raise error.FilteredRepoLookupError(msg)
480 except IndexError:
480 except IndexError:
481 pass
481 pass
482 raise error.RepoLookupError(
482 raise error.RepoLookupError(
483 _("unknown revision '%s'") % changeid)
483 _("unknown revision '%s'") % changeid)
484
484
485 def __hash__(self):
485 def __hash__(self):
486 try:
486 try:
487 return hash(self._rev)
487 return hash(self._rev)
488 except AttributeError:
488 except AttributeError:
489 return id(self)
489 return id(self)
490
490
491 def __nonzero__(self):
491 def __nonzero__(self):
492 return self._rev != nullrev
492 return self._rev != nullrev
493
493
494 @propertycache
494 @propertycache
495 def _changeset(self):
495 def _changeset(self):
496 return self._repo.changelog.read(self.rev())
496 return self._repo.changelog.read(self.rev())
497
497
498 @propertycache
498 @propertycache
499 def _manifest(self):
499 def _manifest(self):
500 return self._repo.manifest.read(self._changeset[0])
500 return self._repo.manifest.read(self._changeset[0])
501
501
502 @propertycache
502 @propertycache
503 def _manifestdelta(self):
503 def _manifestdelta(self):
504 return self._repo.manifest.readdelta(self._changeset[0])
504 return self._repo.manifest.readdelta(self._changeset[0])
505
505
506 @propertycache
506 @propertycache
507 def _parents(self):
507 def _parents(self):
508 p = self._repo.changelog.parentrevs(self._rev)
508 p = self._repo.changelog.parentrevs(self._rev)
509 if p[1] == nullrev:
509 if p[1] == nullrev:
510 p = p[:-1]
510 p = p[:-1]
511 return [changectx(self._repo, x) for x in p]
511 return [changectx(self._repo, x) for x in p]
512
512
513 def changeset(self):
513 def changeset(self):
514 return self._changeset
514 return self._changeset
515 def manifestnode(self):
515 def manifestnode(self):
516 return self._changeset[0]
516 return self._changeset[0]
517
517
518 def user(self):
518 def user(self):
519 return self._changeset[1]
519 return self._changeset[1]
520 def date(self):
520 def date(self):
521 return self._changeset[2]
521 return self._changeset[2]
522 def files(self):
522 def files(self):
523 return self._changeset[3]
523 return self._changeset[3]
524 def description(self):
524 def description(self):
525 return self._changeset[4]
525 return self._changeset[4]
526 def branch(self):
526 def branch(self):
527 return encoding.tolocal(self._changeset[5].get("branch"))
527 return encoding.tolocal(self._changeset[5].get("branch"))
528 def closesbranch(self):
528 def closesbranch(self):
529 return 'close' in self._changeset[5]
529 return 'close' in self._changeset[5]
530 def extra(self):
530 def extra(self):
531 return self._changeset[5]
531 return self._changeset[5]
532 def tags(self):
532 def tags(self):
533 return self._repo.nodetags(self._node)
533 return self._repo.nodetags(self._node)
534 def bookmarks(self):
534 def bookmarks(self):
535 return self._repo.nodebookmarks(self._node)
535 return self._repo.nodebookmarks(self._node)
536 def phase(self):
536 def phase(self):
537 return self._repo._phasecache.phase(self._repo, self._rev)
537 return self._repo._phasecache.phase(self._repo, self._rev)
538 def hidden(self):
538 def hidden(self):
539 return self._rev in repoview.filterrevs(self._repo, 'visible')
539 return self._rev in repoview.filterrevs(self._repo, 'visible')
540
540
541 def children(self):
541 def children(self):
542 """return contexts for each child changeset"""
542 """return contexts for each child changeset"""
543 c = self._repo.changelog.children(self._node)
543 c = self._repo.changelog.children(self._node)
544 return [changectx(self._repo, x) for x in c]
544 return [changectx(self._repo, x) for x in c]
545
545
546 def ancestors(self):
546 def ancestors(self):
547 for a in self._repo.changelog.ancestors([self._rev]):
547 for a in self._repo.changelog.ancestors([self._rev]):
548 yield changectx(self._repo, a)
548 yield changectx(self._repo, a)
549
549
550 def descendants(self):
550 def descendants(self):
551 for d in self._repo.changelog.descendants([self._rev]):
551 for d in self._repo.changelog.descendants([self._rev]):
552 yield changectx(self._repo, d)
552 yield changectx(self._repo, d)
553
553
554 def filectx(self, path, fileid=None, filelog=None):
554 def filectx(self, path, fileid=None, filelog=None):
555 """get a file context from this changeset"""
555 """get a file context from this changeset"""
556 if fileid is None:
556 if fileid is None:
557 fileid = self.filenode(path)
557 fileid = self.filenode(path)
558 return filectx(self._repo, path, fileid=fileid,
558 return filectx(self._repo, path, fileid=fileid,
559 changectx=self, filelog=filelog)
559 changectx=self, filelog=filelog)
560
560
561 def ancestor(self, c2, warn=False):
561 def ancestor(self, c2, warn=False):
562 """return the "best" ancestor context of self and c2
562 """return the "best" ancestor context of self and c2
563
563
564 If there are multiple candidates, it will show a message and check
564 If there are multiple candidates, it will show a message and check
565 merge.preferancestor configuration before falling back to the
565 merge.preferancestor configuration before falling back to the
566 revlog ancestor."""
566 revlog ancestor."""
567 # deal with workingctxs
567 # deal with workingctxs
568 n2 = c2._node
568 n2 = c2._node
569 if n2 is None:
569 if n2 is None:
570 n2 = c2._parents[0]._node
570 n2 = c2._parents[0]._node
571 cahs = self._repo.changelog.commonancestorsheads(self._node, n2)
571 cahs = self._repo.changelog.commonancestorsheads(self._node, n2)
572 if not cahs:
572 if not cahs:
573 anc = nullid
573 anc = nullid
574 elif len(cahs) == 1:
574 elif len(cahs) == 1:
575 anc = cahs[0]
575 anc = cahs[0]
576 else:
576 else:
577 for r in self._repo.ui.configlist('merge', 'preferancestor'):
577 for r in self._repo.ui.configlist('merge', 'preferancestor'):
578 try:
578 try:
579 ctx = changectx(self._repo, r)
579 ctx = changectx(self._repo, r)
580 except error.RepoLookupError:
580 except error.RepoLookupError:
581 continue
581 continue
582 anc = ctx.node()
582 anc = ctx.node()
583 if anc in cahs:
583 if anc in cahs:
584 break
584 break
585 else:
585 else:
586 anc = self._repo.changelog.ancestor(self._node, n2)
586 anc = self._repo.changelog.ancestor(self._node, n2)
587 if warn:
587 if warn:
588 self._repo.ui.status(
588 self._repo.ui.status(
589 (_("note: using %s as ancestor of %s and %s\n") %
589 (_("note: using %s as ancestor of %s and %s\n") %
590 (short(anc), short(self._node), short(n2))) +
590 (short(anc), short(self._node), short(n2))) +
591 ''.join(_(" alternatively, use --config "
591 ''.join(_(" alternatively, use --config "
592 "merge.preferancestor=%s\n") %
592 "merge.preferancestor=%s\n") %
593 short(n) for n in sorted(cahs) if n != anc))
593 short(n) for n in sorted(cahs) if n != anc))
594 return changectx(self._repo, anc)
594 return changectx(self._repo, anc)
595
595
596 def descendant(self, other):
596 def descendant(self, other):
597 """True if other is descendant of this changeset"""
597 """True if other is descendant of this changeset"""
598 return self._repo.changelog.descendant(self._rev, other._rev)
598 return self._repo.changelog.descendant(self._rev, other._rev)
599
599
600 def walk(self, match):
600 def walk(self, match):
601 fset = set(match.files())
601 fset = set(match.files())
602 # for dirstate.walk, files=['.'] means "walk the whole tree".
602 # for dirstate.walk, files=['.'] means "walk the whole tree".
603 # follow that here, too
603 # follow that here, too
604 fset.discard('.')
604 fset.discard('.')
605
605
606 # avoid the entire walk if we're only looking for specific files
606 # avoid the entire walk if we're only looking for specific files
607 if fset and not match.anypats():
607 if fset and not match.anypats():
608 if util.all([fn in self for fn in fset]):
608 if util.all([fn in self for fn in fset]):
609 for fn in sorted(fset):
609 for fn in sorted(fset):
610 if match(fn):
610 if match(fn):
611 yield fn
611 yield fn
612 raise StopIteration
612 raise StopIteration
613
613
614 for fn in self:
614 for fn in self:
615 if fn in fset:
615 if fn in fset:
616 # specified pattern is the exact name
616 # specified pattern is the exact name
617 fset.remove(fn)
617 fset.remove(fn)
618 if match(fn):
618 if match(fn):
619 yield fn
619 yield fn
620 for fn in sorted(fset):
620 for fn in sorted(fset):
621 if fn in self._dirs:
621 if fn in self._dirs:
622 # specified pattern is a directory
622 # specified pattern is a directory
623 continue
623 continue
624 match.bad(fn, _('no such file in rev %s') % self)
624 match.bad(fn, _('no such file in rev %s') % self)
625
625
626 def matches(self, match):
626 def matches(self, match):
627 return self.walk(match)
627 return self.walk(match)
628
628
629 class basefilectx(object):
629 class basefilectx(object):
630 """A filecontext object represents the common logic for its children:
630 """A filecontext object represents the common logic for its children:
631 filectx: read-only access to a filerevision that is already present
631 filectx: read-only access to a filerevision that is already present
632 in the repo,
632 in the repo,
633 workingfilectx: a filecontext that represents files from the working
633 workingfilectx: a filecontext that represents files from the working
634 directory,
634 directory,
635 memfilectx: a filecontext that represents files in-memory."""
635 memfilectx: a filecontext that represents files in-memory."""
636 def __new__(cls, repo, path, *args, **kwargs):
636 def __new__(cls, repo, path, *args, **kwargs):
637 return super(basefilectx, cls).__new__(cls)
637 return super(basefilectx, cls).__new__(cls)
638
638
639 @propertycache
639 @propertycache
640 def _filelog(self):
640 def _filelog(self):
641 return self._repo.file(self._path)
641 return self._repo.file(self._path)
642
642
643 @propertycache
643 @propertycache
644 def _changeid(self):
644 def _changeid(self):
645 if '_changeid' in self.__dict__:
645 if '_changeid' in self.__dict__:
646 return self._changeid
646 return self._changeid
647 elif '_changectx' in self.__dict__:
647 elif '_changectx' in self.__dict__:
648 return self._changectx.rev()
648 return self._changectx.rev()
649 else:
649 else:
650 return self._filelog.linkrev(self._filerev)
650 return self._filelog.linkrev(self._filerev)
651
651
652 @propertycache
652 @propertycache
653 def _filenode(self):
653 def _filenode(self):
654 if '_fileid' in self.__dict__:
654 if '_fileid' in self.__dict__:
655 return self._filelog.lookup(self._fileid)
655 return self._filelog.lookup(self._fileid)
656 else:
656 else:
657 return self._changectx.filenode(self._path)
657 return self._changectx.filenode(self._path)
658
658
659 @propertycache
659 @propertycache
660 def _filerev(self):
660 def _filerev(self):
661 return self._filelog.rev(self._filenode)
661 return self._filelog.rev(self._filenode)
662
662
663 @propertycache
663 @propertycache
664 def _repopath(self):
664 def _repopath(self):
665 return self._path
665 return self._path
666
666
667 def __nonzero__(self):
667 def __nonzero__(self):
668 try:
668 try:
669 self._filenode
669 self._filenode
670 return True
670 return True
671 except error.LookupError:
671 except error.LookupError:
672 # file is missing
672 # file is missing
673 return False
673 return False
674
674
675 def __str__(self):
675 def __str__(self):
676 return "%s@%s" % (self.path(), self._changectx)
676 return "%s@%s" % (self.path(), self._changectx)
677
677
678 def __repr__(self):
678 def __repr__(self):
679 return "<%s %s>" % (type(self).__name__, str(self))
679 return "<%s %s>" % (type(self).__name__, str(self))
680
680
681 def __hash__(self):
681 def __hash__(self):
682 try:
682 try:
683 return hash((self._path, self._filenode))
683 return hash((self._path, self._filenode))
684 except AttributeError:
684 except AttributeError:
685 return id(self)
685 return id(self)
686
686
687 def __eq__(self, other):
687 def __eq__(self, other):
688 try:
688 try:
689 return (type(self) == type(other) and self._path == other._path
689 return (type(self) == type(other) and self._path == other._path
690 and self._filenode == other._filenode)
690 and self._filenode == other._filenode)
691 except AttributeError:
691 except AttributeError:
692 return False
692 return False
693
693
694 def __ne__(self, other):
694 def __ne__(self, other):
695 return not (self == other)
695 return not (self == other)
696
696
697 def filerev(self):
697 def filerev(self):
698 return self._filerev
698 return self._filerev
699 def filenode(self):
699 def filenode(self):
700 return self._filenode
700 return self._filenode
701 def flags(self):
701 def flags(self):
702 return self._changectx.flags(self._path)
702 return self._changectx.flags(self._path)
703 def filelog(self):
703 def filelog(self):
704 return self._filelog
704 return self._filelog
705 def rev(self):
705 def rev(self):
706 return self._changeid
706 return self._changeid
707 def linkrev(self):
707 def linkrev(self):
708 return self._filelog.linkrev(self._filerev)
708 return self._filelog.linkrev(self._filerev)
709 def node(self):
709 def node(self):
710 return self._changectx.node()
710 return self._changectx.node()
711 def hex(self):
711 def hex(self):
712 return self._changectx.hex()
712 return self._changectx.hex()
713 def user(self):
713 def user(self):
714 return self._changectx.user()
714 return self._changectx.user()
715 def date(self):
715 def date(self):
716 return self._changectx.date()
716 return self._changectx.date()
717 def files(self):
717 def files(self):
718 return self._changectx.files()
718 return self._changectx.files()
719 def description(self):
719 def description(self):
720 return self._changectx.description()
720 return self._changectx.description()
721 def branch(self):
721 def branch(self):
722 return self._changectx.branch()
722 return self._changectx.branch()
723 def extra(self):
723 def extra(self):
724 return self._changectx.extra()
724 return self._changectx.extra()
725 def phase(self):
725 def phase(self):
726 return self._changectx.phase()
726 return self._changectx.phase()
727 def phasestr(self):
727 def phasestr(self):
728 return self._changectx.phasestr()
728 return self._changectx.phasestr()
729 def manifest(self):
729 def manifest(self):
730 return self._changectx.manifest()
730 return self._changectx.manifest()
731 def changectx(self):
731 def changectx(self):
732 return self._changectx
732 return self._changectx
733
733
734 def path(self):
734 def path(self):
735 return self._path
735 return self._path
736
736
737 def isbinary(self):
737 def isbinary(self):
738 try:
738 try:
739 return util.binary(self.data())
739 return util.binary(self.data())
740 except IOError:
740 except IOError:
741 return False
741 return False
742 def isexec(self):
742 def isexec(self):
743 return 'x' in self.flags()
743 return 'x' in self.flags()
744 def islink(self):
744 def islink(self):
745 return 'l' in self.flags()
745 return 'l' in self.flags()
746
746
747 def cmp(self, fctx):
747 def cmp(self, fctx):
748 """compare with other file context
748 """compare with other file context
749
749
750 returns True if different than fctx.
750 returns True if different than fctx.
751 """
751 """
752 if (fctx._filerev is None
752 if (fctx._filerev is None
753 and (self._repo._encodefilterpats
753 and (self._repo._encodefilterpats
754 # if file data starts with '\1\n', empty metadata block is
754 # if file data starts with '\1\n', empty metadata block is
755 # prepended, which adds 4 bytes to filelog.size().
755 # prepended, which adds 4 bytes to filelog.size().
756 or self.size() - 4 == fctx.size())
756 or self.size() - 4 == fctx.size())
757 or self.size() == fctx.size()):
757 or self.size() == fctx.size()):
758 return self._filelog.cmp(self._filenode, fctx.data())
758 return self._filelog.cmp(self._filenode, fctx.data())
759
759
760 return True
760 return True
761
761
762 def parents(self):
762 def parents(self):
763 _path = self._path
763 _path = self._path
764 fl = self._filelog
764 fl = self._filelog
765 pl = [(_path, n, fl) for n in self._filelog.parents(self._filenode)]
765 pl = [(_path, n, fl) for n in self._filelog.parents(self._filenode)]
766
766
767 r = self._filelog.renamed(self._filenode)
767 r = self._filelog.renamed(self._filenode)
768 if r:
768 if r:
769 pl[0] = (r[0], r[1], None)
769 pl[0] = (r[0], r[1], None)
770
770
771 return [filectx(self._repo, p, fileid=n, filelog=l)
771 return [filectx(self._repo, p, fileid=n, filelog=l)
772 for p, n, l in pl if n != nullid]
772 for p, n, l in pl if n != nullid]
773
773
774 def p1(self):
774 def p1(self):
775 return self.parents()[0]
775 return self.parents()[0]
776
776
777 def p2(self):
777 def p2(self):
778 p = self.parents()
778 p = self.parents()
779 if len(p) == 2:
779 if len(p) == 2:
780 return p[1]
780 return p[1]
781 return filectx(self._repo, self._path, fileid=-1, filelog=self._filelog)
781 return filectx(self._repo, self._path, fileid=-1, filelog=self._filelog)
782
782
783 def annotate(self, follow=False, linenumber=None, diffopts=None):
783 def annotate(self, follow=False, linenumber=None, diffopts=None):
784 '''returns a list of tuples of (ctx, line) for each line
784 '''returns a list of tuples of (ctx, line) for each line
785 in the file, where ctx is the filectx of the node where
785 in the file, where ctx is the filectx of the node where
786 that line was last changed.
786 that line was last changed.
787 This returns tuples of ((ctx, linenumber), line) for each line,
787 This returns tuples of ((ctx, linenumber), line) for each line,
788 if "linenumber" parameter is NOT "None".
788 if "linenumber" parameter is NOT "None".
789 In such tuples, linenumber means one at the first appearance
789 In such tuples, linenumber means one at the first appearance
790 in the managed file.
790 in the managed file.
791 To reduce annotation cost,
791 To reduce annotation cost,
792 this returns fixed value(False is used) as linenumber,
792 this returns fixed value(False is used) as linenumber,
793 if "linenumber" parameter is "False".'''
793 if "linenumber" parameter is "False".'''
794
794
795 if linenumber is None:
795 if linenumber is None:
796 def decorate(text, rev):
796 def decorate(text, rev):
797 return ([rev] * len(text.splitlines()), text)
797 return ([rev] * len(text.splitlines()), text)
798 elif linenumber:
798 elif linenumber:
799 def decorate(text, rev):
799 def decorate(text, rev):
800 size = len(text.splitlines())
800 size = len(text.splitlines())
801 return ([(rev, i) for i in xrange(1, size + 1)], text)
801 return ([(rev, i) for i in xrange(1, size + 1)], text)
802 else:
802 else:
803 def decorate(text, rev):
803 def decorate(text, rev):
804 return ([(rev, False)] * len(text.splitlines()), text)
804 return ([(rev, False)] * len(text.splitlines()), text)
805
805
806 def pair(parent, child):
806 def pair(parent, child):
807 blocks = mdiff.allblocks(parent[1], child[1], opts=diffopts,
807 blocks = mdiff.allblocks(parent[1], child[1], opts=diffopts,
808 refine=True)
808 refine=True)
809 for (a1, a2, b1, b2), t in blocks:
809 for (a1, a2, b1, b2), t in blocks:
810 # Changed blocks ('!') or blocks made only of blank lines ('~')
810 # Changed blocks ('!') or blocks made only of blank lines ('~')
811 # belong to the child.
811 # belong to the child.
812 if t == '=':
812 if t == '=':
813 child[0][b1:b2] = parent[0][a1:a2]
813 child[0][b1:b2] = parent[0][a1:a2]
814 return child
814 return child
815
815
816 getlog = util.lrucachefunc(lambda x: self._repo.file(x))
816 getlog = util.lrucachefunc(lambda x: self._repo.file(x))
817
817
818 def parents(f):
818 def parents(f):
819 pl = f.parents()
819 pl = f.parents()
820
820
821 # Don't return renamed parents if we aren't following.
821 # Don't return renamed parents if we aren't following.
822 if not follow:
822 if not follow:
823 pl = [p for p in pl if p.path() == f.path()]
823 pl = [p for p in pl if p.path() == f.path()]
824
824
825 # renamed filectx won't have a filelog yet, so set it
825 # renamed filectx won't have a filelog yet, so set it
826 # from the cache to save time
826 # from the cache to save time
827 for p in pl:
827 for p in pl:
828 if not '_filelog' in p.__dict__:
828 if not '_filelog' in p.__dict__:
829 p._filelog = getlog(p.path())
829 p._filelog = getlog(p.path())
830
830
831 return pl
831 return pl
832
832
833 # use linkrev to find the first changeset where self appeared
833 # use linkrev to find the first changeset where self appeared
834 if self.rev() != self.linkrev():
834 if self.rev() != self.linkrev():
835 base = self.filectx(self.filenode())
835 base = self.filectx(self.filenode())
836 else:
836 else:
837 base = self
837 base = self
838
838
839 # This algorithm would prefer to be recursive, but Python is a
839 # This algorithm would prefer to be recursive, but Python is a
840 # bit recursion-hostile. Instead we do an iterative
840 # bit recursion-hostile. Instead we do an iterative
841 # depth-first search.
841 # depth-first search.
842
842
843 visit = [base]
843 visit = [base]
844 hist = {}
844 hist = {}
845 pcache = {}
845 pcache = {}
846 needed = {base: 1}
846 needed = {base: 1}
847 while visit:
847 while visit:
848 f = visit[-1]
848 f = visit[-1]
849 pcached = f in pcache
849 pcached = f in pcache
850 if not pcached:
850 if not pcached:
851 pcache[f] = parents(f)
851 pcache[f] = parents(f)
852
852
853 ready = True
853 ready = True
854 pl = pcache[f]
854 pl = pcache[f]
855 for p in pl:
855 for p in pl:
856 if p not in hist:
856 if p not in hist:
857 ready = False
857 ready = False
858 visit.append(p)
858 visit.append(p)
859 if not pcached:
859 if not pcached:
860 needed[p] = needed.get(p, 0) + 1
860 needed[p] = needed.get(p, 0) + 1
861 if ready:
861 if ready:
862 visit.pop()
862 visit.pop()
863 reusable = f in hist
863 reusable = f in hist
864 if reusable:
864 if reusable:
865 curr = hist[f]
865 curr = hist[f]
866 else:
866 else:
867 curr = decorate(f.data(), f)
867 curr = decorate(f.data(), f)
868 for p in pl:
868 for p in pl:
869 if not reusable:
869 if not reusable:
870 curr = pair(hist[p], curr)
870 curr = pair(hist[p], curr)
871 if needed[p] == 1:
871 if needed[p] == 1:
872 del hist[p]
872 del hist[p]
873 del needed[p]
873 del needed[p]
874 else:
874 else:
875 needed[p] -= 1
875 needed[p] -= 1
876
876
877 hist[f] = curr
877 hist[f] = curr
878 pcache[f] = []
878 pcache[f] = []
879
879
880 return zip(hist[base][0], hist[base][1].splitlines(True))
880 return zip(hist[base][0], hist[base][1].splitlines(True))
881
881
882 def ancestors(self, followfirst=False):
882 def ancestors(self, followfirst=False):
883 visit = {}
883 visit = {}
884 c = self
884 c = self
885 cut = followfirst and 1 or None
885 cut = followfirst and 1 or None
886 while True:
886 while True:
887 for parent in c.parents()[:cut]:
887 for parent in c.parents()[:cut]:
888 visit[(parent.rev(), parent.node())] = parent
888 visit[(parent.rev(), parent.node())] = parent
889 if not visit:
889 if not visit:
890 break
890 break
891 c = visit.pop(max(visit))
891 c = visit.pop(max(visit))
892 yield c
892 yield c
893
893
894 class filectx(basefilectx):
894 class filectx(basefilectx):
895 """A filecontext object makes access to data related to a particular
895 """A filecontext object makes access to data related to a particular
896 filerevision convenient."""
896 filerevision convenient."""
897 def __init__(self, repo, path, changeid=None, fileid=None,
897 def __init__(self, repo, path, changeid=None, fileid=None,
898 filelog=None, changectx=None):
898 filelog=None, changectx=None):
899 """changeid can be a changeset revision, node, or tag.
899 """changeid can be a changeset revision, node, or tag.
900 fileid can be a file revision or node."""
900 fileid can be a file revision or node."""
901 self._repo = repo
901 self._repo = repo
902 self._path = path
902 self._path = path
903
903
904 assert (changeid is not None
904 assert (changeid is not None
905 or fileid is not None
905 or fileid is not None
906 or changectx is not None), \
906 or changectx is not None), \
907 ("bad args: changeid=%r, fileid=%r, changectx=%r"
907 ("bad args: changeid=%r, fileid=%r, changectx=%r"
908 % (changeid, fileid, changectx))
908 % (changeid, fileid, changectx))
909
909
910 if filelog is not None:
910 if filelog is not None:
911 self._filelog = filelog
911 self._filelog = filelog
912
912
913 if changeid is not None:
913 if changeid is not None:
914 self._changeid = changeid
914 self._changeid = changeid
915 if changectx is not None:
915 if changectx is not None:
916 self._changectx = changectx
916 self._changectx = changectx
917 if fileid is not None:
917 if fileid is not None:
918 self._fileid = fileid
918 self._fileid = fileid
919
919
920 @propertycache
920 @propertycache
921 def _changectx(self):
921 def _changectx(self):
922 try:
922 try:
923 return changectx(self._repo, self._changeid)
923 return changectx(self._repo, self._changeid)
924 except error.RepoLookupError:
924 except error.RepoLookupError:
925 # Linkrev may point to any revision in the repository. When the
925 # Linkrev may point to any revision in the repository. When the
926 # repository is filtered this may lead to `filectx` trying to build
926 # repository is filtered this may lead to `filectx` trying to build
927 # `changectx` for filtered revision. In such case we fallback to
927 # `changectx` for filtered revision. In such case we fallback to
928 # creating `changectx` on the unfiltered version of the reposition.
928 # creating `changectx` on the unfiltered version of the reposition.
929 # This fallback should not be an issue because `changectx` from
929 # This fallback should not be an issue because `changectx` from
930 # `filectx` are not used in complex operations that care about
930 # `filectx` are not used in complex operations that care about
931 # filtering.
931 # filtering.
932 #
932 #
933 # This fallback is a cheap and dirty fix that prevent several
933 # This fallback is a cheap and dirty fix that prevent several
934 # crashes. It does not ensure the behavior is correct. However the
934 # crashes. It does not ensure the behavior is correct. However the
935 # behavior was not correct before filtering either and "incorrect
935 # behavior was not correct before filtering either and "incorrect
936 # behavior" is seen as better as "crash"
936 # behavior" is seen as better as "crash"
937 #
937 #
938 # Linkrevs have several serious troubles with filtering that are
938 # Linkrevs have several serious troubles with filtering that are
939 # complicated to solve. Proper handling of the issue here should be
939 # complicated to solve. Proper handling of the issue here should be
940 # considered when solving linkrev issue are on the table.
940 # considered when solving linkrev issue are on the table.
941 return changectx(self._repo.unfiltered(), self._changeid)
941 return changectx(self._repo.unfiltered(), self._changeid)
942
942
943 def filectx(self, fileid):
943 def filectx(self, fileid):
944 '''opens an arbitrary revision of the file without
944 '''opens an arbitrary revision of the file without
945 opening a new filelog'''
945 opening a new filelog'''
946 return filectx(self._repo, self._path, fileid=fileid,
946 return filectx(self._repo, self._path, fileid=fileid,
947 filelog=self._filelog)
947 filelog=self._filelog)
948
948
949 def data(self):
949 def data(self):
950 try:
950 try:
951 return self._filelog.read(self._filenode)
951 return self._filelog.read(self._filenode)
952 except error.CensoredNodeError:
952 except error.CensoredNodeError:
953 if self._repo.ui.config("censor", "policy", "abort") == "ignore":
953 if self._repo.ui.config("censor", "policy", "abort") == "ignore":
954 return ""
954 return ""
955 raise util.Abort(_("censored node: %s") % short(self._filenode),
955 raise util.Abort(_("censored node: %s") % short(self._filenode),
956 hint=_("set censor.policy to ignore errors"))
956 hint=_("set censor.policy to ignore errors"))
957
957
958 def size(self):
958 def size(self):
959 return self._filelog.size(self._filerev)
959 return self._filelog.size(self._filerev)
960
960
961 def renamed(self):
961 def renamed(self):
962 """check if file was actually renamed in this changeset revision
962 """check if file was actually renamed in this changeset revision
963
963
964 If rename logged in file revision, we report copy for changeset only
964 If rename logged in file revision, we report copy for changeset only
965 if file revisions linkrev points back to the changeset in question
965 if file revisions linkrev points back to the changeset in question
966 or both changeset parents contain different file revisions.
966 or both changeset parents contain different file revisions.
967 """
967 """
968
968
969 renamed = self._filelog.renamed(self._filenode)
969 renamed = self._filelog.renamed(self._filenode)
970 if not renamed:
970 if not renamed:
971 return renamed
971 return renamed
972
972
973 if self.rev() == self.linkrev():
973 if self.rev() == self.linkrev():
974 return renamed
974 return renamed
975
975
976 name = self.path()
976 name = self.path()
977 fnode = self._filenode
977 fnode = self._filenode
978 for p in self._changectx.parents():
978 for p in self._changectx.parents():
979 try:
979 try:
980 if fnode == p.filenode(name):
980 if fnode == p.filenode(name):
981 return None
981 return None
982 except error.LookupError:
982 except error.LookupError:
983 pass
983 pass
984 return renamed
984 return renamed
985
985
986 def children(self):
986 def children(self):
987 # hard for renames
987 # hard for renames
988 c = self._filelog.children(self._filenode)
988 c = self._filelog.children(self._filenode)
989 return [filectx(self._repo, self._path, fileid=x,
989 return [filectx(self._repo, self._path, fileid=x,
990 filelog=self._filelog) for x in c]
990 filelog=self._filelog) for x in c]
991
991
992 class committablectx(basectx):
992 class committablectx(basectx):
993 """A committablectx object provides common functionality for a context that
993 """A committablectx object provides common functionality for a context that
994 wants the ability to commit, e.g. workingctx or memctx."""
994 wants the ability to commit, e.g. workingctx or memctx."""
995 def __init__(self, repo, text="", user=None, date=None, extra=None,
995 def __init__(self, repo, text="", user=None, date=None, extra=None,
996 changes=None):
996 changes=None):
997 self._repo = repo
997 self._repo = repo
998 self._rev = None
998 self._rev = None
999 self._node = None
999 self._node = None
1000 self._text = text
1000 self._text = text
1001 if date:
1001 if date:
1002 self._date = util.parsedate(date)
1002 self._date = util.parsedate(date)
1003 if user:
1003 if user:
1004 self._user = user
1004 self._user = user
1005 if changes:
1005 if changes:
1006 self._status = changes
1006 self._status = changes
1007
1007
1008 self._extra = {}
1008 self._extra = {}
1009 if extra:
1009 if extra:
1010 self._extra = extra.copy()
1010 self._extra = extra.copy()
1011 if 'branch' not in self._extra:
1011 if 'branch' not in self._extra:
1012 try:
1012 try:
1013 branch = encoding.fromlocal(self._repo.dirstate.branch())
1013 branch = encoding.fromlocal(self._repo.dirstate.branch())
1014 except UnicodeDecodeError:
1014 except UnicodeDecodeError:
1015 raise util.Abort(_('branch name not in UTF-8!'))
1015 raise util.Abort(_('branch name not in UTF-8!'))
1016 self._extra['branch'] = branch
1016 self._extra['branch'] = branch
1017 if self._extra['branch'] == '':
1017 if self._extra['branch'] == '':
1018 self._extra['branch'] = 'default'
1018 self._extra['branch'] = 'default'
1019
1019
1020 def __str__(self):
1020 def __str__(self):
1021 return str(self._parents[0]) + "+"
1021 return str(self._parents[0]) + "+"
1022
1022
1023 def __nonzero__(self):
1023 def __nonzero__(self):
1024 return True
1024 return True
1025
1025
1026 def _buildflagfunc(self):
1026 def _buildflagfunc(self):
1027 # Create a fallback function for getting file flags when the
1027 # Create a fallback function for getting file flags when the
1028 # filesystem doesn't support them
1028 # filesystem doesn't support them
1029
1029
1030 copiesget = self._repo.dirstate.copies().get
1030 copiesget = self._repo.dirstate.copies().get
1031
1031
1032 if len(self._parents) < 2:
1032 if len(self._parents) < 2:
1033 # when we have one parent, it's easy: copy from parent
1033 # when we have one parent, it's easy: copy from parent
1034 man = self._parents[0].manifest()
1034 man = self._parents[0].manifest()
1035 def func(f):
1035 def func(f):
1036 f = copiesget(f, f)
1036 f = copiesget(f, f)
1037 return man.flags(f)
1037 return man.flags(f)
1038 else:
1038 else:
1039 # merges are tricky: we try to reconstruct the unstored
1039 # merges are tricky: we try to reconstruct the unstored
1040 # result from the merge (issue1802)
1040 # result from the merge (issue1802)
1041 p1, p2 = self._parents
1041 p1, p2 = self._parents
1042 pa = p1.ancestor(p2)
1042 pa = p1.ancestor(p2)
1043 m1, m2, ma = p1.manifest(), p2.manifest(), pa.manifest()
1043 m1, m2, ma = p1.manifest(), p2.manifest(), pa.manifest()
1044
1044
1045 def func(f):
1045 def func(f):
1046 f = copiesget(f, f) # may be wrong for merges with copies
1046 f = copiesget(f, f) # may be wrong for merges with copies
1047 fl1, fl2, fla = m1.flags(f), m2.flags(f), ma.flags(f)
1047 fl1, fl2, fla = m1.flags(f), m2.flags(f), ma.flags(f)
1048 if fl1 == fl2:
1048 if fl1 == fl2:
1049 return fl1
1049 return fl1
1050 if fl1 == fla:
1050 if fl1 == fla:
1051 return fl2
1051 return fl2
1052 if fl2 == fla:
1052 if fl2 == fla:
1053 return fl1
1053 return fl1
1054 return '' # punt for conflicts
1054 return '' # punt for conflicts
1055
1055
1056 return func
1056 return func
1057
1057
1058 @propertycache
1058 @propertycache
1059 def _flagfunc(self):
1059 def _flagfunc(self):
1060 return self._repo.dirstate.flagfunc(self._buildflagfunc)
1060 return self._repo.dirstate.flagfunc(self._buildflagfunc)
1061
1061
1062 @propertycache
1062 @propertycache
1063 def _manifest(self):
1063 def _manifest(self):
1064 """generate a manifest corresponding to the values in self._status"""
1064 """generate a manifest corresponding to the values in self._status"""
1065
1065
1066 man1 = self._parents[0].manifest()
1066 man1 = self._parents[0].manifest()
1067 man = man1.copy()
1067 man = man1.copy()
1068 if len(self._parents) > 1:
1068 if len(self._parents) > 1:
1069 man2 = self.p2().manifest()
1069 man2 = self.p2().manifest()
1070 def getman(f):
1070 def getman(f):
1071 if f in man1:
1071 if f in man1:
1072 return man1
1072 return man1
1073 return man2
1073 return man2
1074 else:
1074 else:
1075 getman = lambda f: man1
1075 getman = lambda f: man1
1076
1076
1077 copied = self._repo.dirstate.copies()
1077 copied = self._repo.dirstate.copies()
1078 ff = self._flagfunc
1078 ff = self._flagfunc
1079 for i, l in (("a", self._status.added), ("m", self._status.modified)):
1079 for i, l in (("a", self._status.added), ("m", self._status.modified)):
1080 for f in l:
1080 for f in l:
1081 orig = copied.get(f, f)
1081 orig = copied.get(f, f)
1082 man[f] = getman(orig).get(orig, nullid) + i
1082 man[f] = getman(orig).get(orig, nullid) + i
1083 try:
1083 try:
1084 man.setflag(f, ff(f))
1084 man.setflag(f, ff(f))
1085 except OSError:
1085 except OSError:
1086 pass
1086 pass
1087
1087
1088 for f in self._status.deleted + self._status.removed:
1088 for f in self._status.deleted + self._status.removed:
1089 if f in man:
1089 if f in man:
1090 del man[f]
1090 del man[f]
1091
1091
1092 return man
1092 return man
1093
1093
1094 @propertycache
1094 @propertycache
1095 def _status(self):
1095 def _status(self):
1096 return self._repo.status()
1096 return self._repo.status()
1097
1097
1098 @propertycache
1098 @propertycache
1099 def _user(self):
1099 def _user(self):
1100 return self._repo.ui.username()
1100 return self._repo.ui.username()
1101
1101
1102 @propertycache
1102 @propertycache
1103 def _date(self):
1103 def _date(self):
1104 return util.makedate()
1104 return util.makedate()
1105
1105
1106 def subrev(self, subpath):
1106 def subrev(self, subpath):
1107 return None
1107 return None
1108
1108
1109 def user(self):
1109 def user(self):
1110 return self._user or self._repo.ui.username()
1110 return self._user or self._repo.ui.username()
1111 def date(self):
1111 def date(self):
1112 return self._date
1112 return self._date
1113 def description(self):
1113 def description(self):
1114 return self._text
1114 return self._text
1115 def files(self):
1115 def files(self):
1116 return sorted(self._status.modified + self._status.added +
1116 return sorted(self._status.modified + self._status.added +
1117 self._status.removed)
1117 self._status.removed)
1118
1118
1119 def modified(self):
1119 def modified(self):
1120 return self._status.modified
1120 return self._status.modified
1121 def added(self):
1121 def added(self):
1122 return self._status.added
1122 return self._status.added
1123 def removed(self):
1123 def removed(self):
1124 return self._status.removed
1124 return self._status.removed
1125 def deleted(self):
1125 def deleted(self):
1126 return self._status.deleted
1126 return self._status.deleted
1127 def unknown(self):
1127 def unknown(self):
1128 return self._status.unknown
1128 return self._status.unknown
1129 def ignored(self):
1129 def ignored(self):
1130 return self._status.ignored
1130 return self._status.ignored
1131 def clean(self):
1131 def clean(self):
1132 return self._status.clean
1132 return self._status.clean
1133 def branch(self):
1133 def branch(self):
1134 return encoding.tolocal(self._extra['branch'])
1134 return encoding.tolocal(self._extra['branch'])
1135 def closesbranch(self):
1135 def closesbranch(self):
1136 return 'close' in self._extra
1136 return 'close' in self._extra
1137 def extra(self):
1137 def extra(self):
1138 return self._extra
1138 return self._extra
1139
1139
1140 def tags(self):
1140 def tags(self):
1141 t = []
1141 t = []
1142 for p in self.parents():
1142 for p in self.parents():
1143 t.extend(p.tags())
1143 t.extend(p.tags())
1144 return t
1144 return t
1145
1145
1146 def bookmarks(self):
1146 def bookmarks(self):
1147 b = []
1147 b = []
1148 for p in self.parents():
1148 for p in self.parents():
1149 b.extend(p.bookmarks())
1149 b.extend(p.bookmarks())
1150 return b
1150 return b
1151
1151
1152 def phase(self):
1152 def phase(self):
1153 phase = phases.draft # default phase to draft
1153 phase = phases.draft # default phase to draft
1154 for p in self.parents():
1154 for p in self.parents():
1155 phase = max(phase, p.phase())
1155 phase = max(phase, p.phase())
1156 return phase
1156 return phase
1157
1157
1158 def hidden(self):
1158 def hidden(self):
1159 return False
1159 return False
1160
1160
1161 def children(self):
1161 def children(self):
1162 return []
1162 return []
1163
1163
1164 def flags(self, path):
1164 def flags(self, path):
1165 if '_manifest' in self.__dict__:
1165 if '_manifest' in self.__dict__:
1166 try:
1166 try:
1167 return self._manifest.flags(path)
1167 return self._manifest.flags(path)
1168 except KeyError:
1168 except KeyError:
1169 return ''
1169 return ''
1170
1170
1171 try:
1171 try:
1172 return self._flagfunc(path)
1172 return self._flagfunc(path)
1173 except OSError:
1173 except OSError:
1174 return ''
1174 return ''
1175
1175
1176 def ancestor(self, c2):
1176 def ancestor(self, c2):
1177 """return the "best" ancestor context of self and c2"""
1177 """return the "best" ancestor context of self and c2"""
1178 return self._parents[0].ancestor(c2) # punt on two parents for now
1178 return self._parents[0].ancestor(c2) # punt on two parents for now
1179
1179
1180 def walk(self, match):
1180 def walk(self, match):
1181 return sorted(self._repo.dirstate.walk(match, sorted(self.substate),
1181 return sorted(self._repo.dirstate.walk(match, sorted(self.substate),
1182 True, False))
1182 True, False))
1183
1183
1184 def matches(self, match):
1184 def matches(self, match):
1185 return sorted(self._repo.dirstate.matches(match))
1185 return sorted(self._repo.dirstate.matches(match))
1186
1186
1187 def ancestors(self):
1187 def ancestors(self):
1188 for a in self._repo.changelog.ancestors(
1188 for a in self._repo.changelog.ancestors(
1189 [p.rev() for p in self._parents]):
1189 [p.rev() for p in self._parents]):
1190 yield changectx(self._repo, a)
1190 yield changectx(self._repo, a)
1191
1191
1192 def markcommitted(self, node):
1192 def markcommitted(self, node):
1193 """Perform post-commit cleanup necessary after committing this ctx
1193 """Perform post-commit cleanup necessary after committing this ctx
1194
1194
1195 Specifically, this updates backing stores this working context
1195 Specifically, this updates backing stores this working context
1196 wraps to reflect the fact that the changes reflected by this
1196 wraps to reflect the fact that the changes reflected by this
1197 workingctx have been committed. For example, it marks
1197 workingctx have been committed. For example, it marks
1198 modified and added files as normal in the dirstate.
1198 modified and added files as normal in the dirstate.
1199
1199
1200 """
1200 """
1201
1201
1202 self._repo.dirstate.beginparentchange()
1202 self._repo.dirstate.beginparentchange()
1203 for f in self.modified() + self.added():
1203 for f in self.modified() + self.added():
1204 self._repo.dirstate.normal(f)
1204 self._repo.dirstate.normal(f)
1205 for f in self.removed():
1205 for f in self.removed():
1206 self._repo.dirstate.drop(f)
1206 self._repo.dirstate.drop(f)
1207 self._repo.dirstate.setparents(node)
1207 self._repo.dirstate.setparents(node)
1208 self._repo.dirstate.endparentchange()
1208 self._repo.dirstate.endparentchange()
1209
1209
1210 def dirs(self):
1210 def dirs(self):
1211 return self._repo.dirstate.dirs()
1211 return self._repo.dirstate.dirs()
1212
1212
1213 class workingctx(committablectx):
1213 class workingctx(committablectx):
1214 """A workingctx object makes access to data related to
1214 """A workingctx object makes access to data related to
1215 the current working directory convenient.
1215 the current working directory convenient.
1216 date - any valid date string or (unixtime, offset), or None.
1216 date - any valid date string or (unixtime, offset), or None.
1217 user - username string, or None.
1217 user - username string, or None.
1218 extra - a dictionary of extra values, or None.
1218 extra - a dictionary of extra values, or None.
1219 changes - a list of file lists as returned by localrepo.status()
1219 changes - a list of file lists as returned by localrepo.status()
1220 or None to use the repository status.
1220 or None to use the repository status.
1221 """
1221 """
1222 def __init__(self, repo, text="", user=None, date=None, extra=None,
1222 def __init__(self, repo, text="", user=None, date=None, extra=None,
1223 changes=None):
1223 changes=None):
1224 super(workingctx, self).__init__(repo, text, user, date, extra, changes)
1224 super(workingctx, self).__init__(repo, text, user, date, extra, changes)
1225
1225
1226 def __iter__(self):
1226 def __iter__(self):
1227 d = self._repo.dirstate
1227 d = self._repo.dirstate
1228 for f in d:
1228 for f in d:
1229 if d[f] != 'r':
1229 if d[f] != 'r':
1230 yield f
1230 yield f
1231
1231
1232 def __contains__(self, key):
1232 def __contains__(self, key):
1233 return self._repo.dirstate[key] not in "?r"
1233 return self._repo.dirstate[key] not in "?r"
1234
1234
1235 @propertycache
1235 @propertycache
1236 def _parents(self):
1236 def _parents(self):
1237 p = self._repo.dirstate.parents()
1237 p = self._repo.dirstate.parents()
1238 if p[1] == nullid:
1238 if p[1] == nullid:
1239 p = p[:-1]
1239 p = p[:-1]
1240 return [changectx(self._repo, x) for x in p]
1240 return [changectx(self._repo, x) for x in p]
1241
1241
1242 def filectx(self, path, filelog=None):
1242 def filectx(self, path, filelog=None):
1243 """get a file context from the working directory"""
1243 """get a file context from the working directory"""
1244 return workingfilectx(self._repo, path, workingctx=self,
1244 return workingfilectx(self._repo, path, workingctx=self,
1245 filelog=filelog)
1245 filelog=filelog)
1246
1246
1247 def dirty(self, missing=False, merge=True, branch=True):
1247 def dirty(self, missing=False, merge=True, branch=True):
1248 "check whether a working directory is modified"
1248 "check whether a working directory is modified"
1249 # check subrepos first
1249 # check subrepos first
1250 for s in sorted(self.substate):
1250 for s in sorted(self.substate):
1251 if self.sub(s).dirty():
1251 if self.sub(s).dirty():
1252 return True
1252 return True
1253 # check current working dir
1253 # check current working dir
1254 return ((merge and self.p2()) or
1254 return ((merge and self.p2()) or
1255 (branch and self.branch() != self.p1().branch()) or
1255 (branch and self.branch() != self.p1().branch()) or
1256 self.modified() or self.added() or self.removed() or
1256 self.modified() or self.added() or self.removed() or
1257 (missing and self.deleted()))
1257 (missing and self.deleted()))
1258
1258
1259 def add(self, list, prefix=""):
1259 def add(self, list, prefix=""):
1260 join = lambda f: os.path.join(prefix, f)
1260 join = lambda f: os.path.join(prefix, f)
1261 wlock = self._repo.wlock()
1261 wlock = self._repo.wlock()
1262 ui, ds = self._repo.ui, self._repo.dirstate
1262 ui, ds = self._repo.ui, self._repo.dirstate
1263 try:
1263 try:
1264 rejected = []
1264 rejected = []
1265 lstat = self._repo.wvfs.lstat
1265 lstat = self._repo.wvfs.lstat
1266 for f in list:
1266 for f in list:
1267 scmutil.checkportable(ui, join(f))
1267 scmutil.checkportable(ui, join(f))
1268 try:
1268 try:
1269 st = lstat(f)
1269 st = lstat(f)
1270 except OSError:
1270 except OSError:
1271 ui.warn(_("%s does not exist!\n") % join(f))
1271 ui.warn(_("%s does not exist!\n") % join(f))
1272 rejected.append(f)
1272 rejected.append(f)
1273 continue
1273 continue
1274 if st.st_size > 10000000:
1274 if st.st_size > 10000000:
1275 ui.warn(_("%s: up to %d MB of RAM may be required "
1275 ui.warn(_("%s: up to %d MB of RAM may be required "
1276 "to manage this file\n"
1276 "to manage this file\n"
1277 "(use 'hg revert %s' to cancel the "
1277 "(use 'hg revert %s' to cancel the "
1278 "pending addition)\n")
1278 "pending addition)\n")
1279 % (f, 3 * st.st_size // 1000000, join(f)))
1279 % (f, 3 * st.st_size // 1000000, join(f)))
1280 if not (stat.S_ISREG(st.st_mode) or stat.S_ISLNK(st.st_mode)):
1280 if not (stat.S_ISREG(st.st_mode) or stat.S_ISLNK(st.st_mode)):
1281 ui.warn(_("%s not added: only files and symlinks "
1281 ui.warn(_("%s not added: only files and symlinks "
1282 "supported currently\n") % join(f))
1282 "supported currently\n") % join(f))
1283 rejected.append(f)
1283 rejected.append(f)
1284 elif ds[f] in 'amn':
1284 elif ds[f] in 'amn':
1285 ui.warn(_("%s already tracked!\n") % join(f))
1285 ui.warn(_("%s already tracked!\n") % join(f))
1286 elif ds[f] == 'r':
1286 elif ds[f] == 'r':
1287 ds.normallookup(f)
1287 ds.normallookup(f)
1288 else:
1288 else:
1289 ds.add(f)
1289 ds.add(f)
1290 return rejected
1290 return rejected
1291 finally:
1291 finally:
1292 wlock.release()
1292 wlock.release()
1293
1293
1294 def forget(self, files, prefix=""):
1294 def forget(self, files, prefix=""):
1295 join = lambda f: os.path.join(prefix, f)
1295 join = lambda f: os.path.join(prefix, f)
1296 wlock = self._repo.wlock()
1296 wlock = self._repo.wlock()
1297 try:
1297 try:
1298 rejected = []
1298 rejected = []
1299 for f in files:
1299 for f in files:
1300 if f not in self._repo.dirstate:
1300 if f not in self._repo.dirstate:
1301 self._repo.ui.warn(_("%s not tracked!\n") % join(f))
1301 self._repo.ui.warn(_("%s not tracked!\n") % join(f))
1302 rejected.append(f)
1302 rejected.append(f)
1303 elif self._repo.dirstate[f] != 'a':
1303 elif self._repo.dirstate[f] != 'a':
1304 self._repo.dirstate.remove(f)
1304 self._repo.dirstate.remove(f)
1305 else:
1305 else:
1306 self._repo.dirstate.drop(f)
1306 self._repo.dirstate.drop(f)
1307 return rejected
1307 return rejected
1308 finally:
1308 finally:
1309 wlock.release()
1309 wlock.release()
1310
1310
1311 def undelete(self, list):
1311 def undelete(self, list):
1312 pctxs = self.parents()
1312 pctxs = self.parents()
1313 wlock = self._repo.wlock()
1313 wlock = self._repo.wlock()
1314 try:
1314 try:
1315 for f in list:
1315 for f in list:
1316 if self._repo.dirstate[f] != 'r':
1316 if self._repo.dirstate[f] != 'r':
1317 self._repo.ui.warn(_("%s not removed!\n") % f)
1317 self._repo.ui.warn(_("%s not removed!\n") % f)
1318 else:
1318 else:
1319 fctx = f in pctxs[0] and pctxs[0][f] or pctxs[1][f]
1319 fctx = f in pctxs[0] and pctxs[0][f] or pctxs[1][f]
1320 t = fctx.data()
1320 t = fctx.data()
1321 self._repo.wwrite(f, t, fctx.flags())
1321 self._repo.wwrite(f, t, fctx.flags())
1322 self._repo.dirstate.normal(f)
1322 self._repo.dirstate.normal(f)
1323 finally:
1323 finally:
1324 wlock.release()
1324 wlock.release()
1325
1325
1326 def copy(self, source, dest):
1326 def copy(self, source, dest):
1327 try:
1327 try:
1328 st = self._repo.wvfs.lstat(dest)
1328 st = self._repo.wvfs.lstat(dest)
1329 except OSError, err:
1329 except OSError, err:
1330 if err.errno != errno.ENOENT:
1330 if err.errno != errno.ENOENT:
1331 raise
1331 raise
1332 self._repo.ui.warn(_("%s does not exist!\n") % dest)
1332 self._repo.ui.warn(_("%s does not exist!\n") % dest)
1333 return
1333 return
1334 if not (stat.S_ISREG(st.st_mode) or stat.S_ISLNK(st.st_mode)):
1334 if not (stat.S_ISREG(st.st_mode) or stat.S_ISLNK(st.st_mode)):
1335 self._repo.ui.warn(_("copy failed: %s is not a file or a "
1335 self._repo.ui.warn(_("copy failed: %s is not a file or a "
1336 "symbolic link\n") % dest)
1336 "symbolic link\n") % dest)
1337 else:
1337 else:
1338 wlock = self._repo.wlock()
1338 wlock = self._repo.wlock()
1339 try:
1339 try:
1340 if self._repo.dirstate[dest] in '?r':
1340 if self._repo.dirstate[dest] in '?':
1341 self._repo.dirstate.add(dest)
1341 self._repo.dirstate.add(dest)
1342 elif self._repo.dirstate[dest] in 'r':
1343 self._repo.dirstate.normallookup(dest)
1342 self._repo.dirstate.copy(source, dest)
1344 self._repo.dirstate.copy(source, dest)
1343 finally:
1345 finally:
1344 wlock.release()
1346 wlock.release()
1345
1347
1346 def _filtersuspectsymlink(self, files):
1348 def _filtersuspectsymlink(self, files):
1347 if not files or self._repo.dirstate._checklink:
1349 if not files or self._repo.dirstate._checklink:
1348 return files
1350 return files
1349
1351
1350 # Symlink placeholders may get non-symlink-like contents
1352 # Symlink placeholders may get non-symlink-like contents
1351 # via user error or dereferencing by NFS or Samba servers,
1353 # via user error or dereferencing by NFS or Samba servers,
1352 # so we filter out any placeholders that don't look like a
1354 # so we filter out any placeholders that don't look like a
1353 # symlink
1355 # symlink
1354 sane = []
1356 sane = []
1355 for f in files:
1357 for f in files:
1356 if self.flags(f) == 'l':
1358 if self.flags(f) == 'l':
1357 d = self[f].data()
1359 d = self[f].data()
1358 if d == '' or len(d) >= 1024 or '\n' in d or util.binary(d):
1360 if d == '' or len(d) >= 1024 or '\n' in d or util.binary(d):
1359 self._repo.ui.debug('ignoring suspect symlink placeholder'
1361 self._repo.ui.debug('ignoring suspect symlink placeholder'
1360 ' "%s"\n' % f)
1362 ' "%s"\n' % f)
1361 continue
1363 continue
1362 sane.append(f)
1364 sane.append(f)
1363 return sane
1365 return sane
1364
1366
1365 def _checklookup(self, files):
1367 def _checklookup(self, files):
1366 # check for any possibly clean files
1368 # check for any possibly clean files
1367 if not files:
1369 if not files:
1368 return [], []
1370 return [], []
1369
1371
1370 modified = []
1372 modified = []
1371 fixup = []
1373 fixup = []
1372 pctx = self._parents[0]
1374 pctx = self._parents[0]
1373 # do a full compare of any files that might have changed
1375 # do a full compare of any files that might have changed
1374 for f in sorted(files):
1376 for f in sorted(files):
1375 if (f not in pctx or self.flags(f) != pctx.flags(f)
1377 if (f not in pctx or self.flags(f) != pctx.flags(f)
1376 or pctx[f].cmp(self[f])):
1378 or pctx[f].cmp(self[f])):
1377 modified.append(f)
1379 modified.append(f)
1378 else:
1380 else:
1379 fixup.append(f)
1381 fixup.append(f)
1380
1382
1381 # update dirstate for files that are actually clean
1383 # update dirstate for files that are actually clean
1382 if fixup:
1384 if fixup:
1383 try:
1385 try:
1384 # updating the dirstate is optional
1386 # updating the dirstate is optional
1385 # so we don't wait on the lock
1387 # so we don't wait on the lock
1386 # wlock can invalidate the dirstate, so cache normal _after_
1388 # wlock can invalidate the dirstate, so cache normal _after_
1387 # taking the lock
1389 # taking the lock
1388 wlock = self._repo.wlock(False)
1390 wlock = self._repo.wlock(False)
1389 normal = self._repo.dirstate.normal
1391 normal = self._repo.dirstate.normal
1390 try:
1392 try:
1391 for f in fixup:
1393 for f in fixup:
1392 normal(f)
1394 normal(f)
1393 finally:
1395 finally:
1394 wlock.release()
1396 wlock.release()
1395 except error.LockError:
1397 except error.LockError:
1396 pass
1398 pass
1397 return modified, fixup
1399 return modified, fixup
1398
1400
1399 def _manifestmatches(self, match, s):
1401 def _manifestmatches(self, match, s):
1400 """Slow path for workingctx
1402 """Slow path for workingctx
1401
1403
1402 The fast path is when we compare the working directory to its parent
1404 The fast path is when we compare the working directory to its parent
1403 which means this function is comparing with a non-parent; therefore we
1405 which means this function is comparing with a non-parent; therefore we
1404 need to build a manifest and return what matches.
1406 need to build a manifest and return what matches.
1405 """
1407 """
1406 mf = self._repo['.']._manifestmatches(match, s)
1408 mf = self._repo['.']._manifestmatches(match, s)
1407 modified, added, removed = s[0:3]
1409 modified, added, removed = s[0:3]
1408 for f in modified + added:
1410 for f in modified + added:
1409 mf[f] = None
1411 mf[f] = None
1410 mf.setflag(f, self.flags(f))
1412 mf.setflag(f, self.flags(f))
1411 for f in removed:
1413 for f in removed:
1412 if f in mf:
1414 if f in mf:
1413 del mf[f]
1415 del mf[f]
1414 return mf
1416 return mf
1415
1417
1416 def _prestatus(self, other, s, match, listignored, listclean, listunknown):
1418 def _prestatus(self, other, s, match, listignored, listclean, listunknown):
1417 """override the parent hook with a dirstate query
1419 """override the parent hook with a dirstate query
1418
1420
1419 We use this prestatus hook to populate the status with information from
1421 We use this prestatus hook to populate the status with information from
1420 the dirstate.
1422 the dirstate.
1421 """
1423 """
1422 # doesn't need to call super; if that changes, be aware that super
1424 # doesn't need to call super; if that changes, be aware that super
1423 # calls self.manifest which would slow down the common case of calling
1425 # calls self.manifest which would slow down the common case of calling
1424 # status against a workingctx's parent
1426 # status against a workingctx's parent
1425 return self._dirstatestatus(match, listignored, listclean, listunknown)
1427 return self._dirstatestatus(match, listignored, listclean, listunknown)
1426
1428
1427 def _poststatus(self, other, s, match, listignored, listclean, listunknown):
1429 def _poststatus(self, other, s, match, listignored, listclean, listunknown):
1428 """override the parent hook with a filter for suspect symlinks
1430 """override the parent hook with a filter for suspect symlinks
1429
1431
1430 We use this poststatus hook to filter out symlinks that might have
1432 We use this poststatus hook to filter out symlinks that might have
1431 accidentally ended up with the entire contents of the file they are
1433 accidentally ended up with the entire contents of the file they are
1432 susposed to be linking to.
1434 susposed to be linking to.
1433 """
1435 """
1434 s[0] = self._filtersuspectsymlink(s[0])
1436 s[0] = self._filtersuspectsymlink(s[0])
1435 self._status = scmutil.status(*s)
1437 self._status = scmutil.status(*s)
1436 return s
1438 return s
1437
1439
1438 def _dirstatestatus(self, match=None, ignored=False, clean=False,
1440 def _dirstatestatus(self, match=None, ignored=False, clean=False,
1439 unknown=False):
1441 unknown=False):
1440 '''Gets the status from the dirstate -- internal use only.'''
1442 '''Gets the status from the dirstate -- internal use only.'''
1441 listignored, listclean, listunknown = ignored, clean, unknown
1443 listignored, listclean, listunknown = ignored, clean, unknown
1442 match = match or matchmod.always(self._repo.root, self._repo.getcwd())
1444 match = match or matchmod.always(self._repo.root, self._repo.getcwd())
1443 subrepos = []
1445 subrepos = []
1444 if '.hgsub' in self:
1446 if '.hgsub' in self:
1445 subrepos = sorted(self.substate)
1447 subrepos = sorted(self.substate)
1446 cmp, s = self._repo.dirstate.status(match, subrepos, listignored,
1448 cmp, s = self._repo.dirstate.status(match, subrepos, listignored,
1447 listclean, listunknown)
1449 listclean, listunknown)
1448 modified, added, removed, deleted, unknown, ignored, clean = s
1450 modified, added, removed, deleted, unknown, ignored, clean = s
1449
1451
1450 # check for any possibly clean files
1452 # check for any possibly clean files
1451 if cmp:
1453 if cmp:
1452 modified2, fixup = self._checklookup(cmp)
1454 modified2, fixup = self._checklookup(cmp)
1453 modified += modified2
1455 modified += modified2
1454
1456
1455 # update dirstate for files that are actually clean
1457 # update dirstate for files that are actually clean
1456 if fixup and listclean:
1458 if fixup and listclean:
1457 clean += fixup
1459 clean += fixup
1458
1460
1459 return [modified, added, removed, deleted, unknown, ignored, clean]
1461 return [modified, added, removed, deleted, unknown, ignored, clean]
1460
1462
1461 def _buildstatus(self, other, s, match, listignored, listclean,
1463 def _buildstatus(self, other, s, match, listignored, listclean,
1462 listunknown):
1464 listunknown):
1463 """build a status with respect to another context
1465 """build a status with respect to another context
1464
1466
1465 This includes logic for maintaining the fast path of status when
1467 This includes logic for maintaining the fast path of status when
1466 comparing the working directory against its parent, which is to skip
1468 comparing the working directory against its parent, which is to skip
1467 building a new manifest if self (working directory) is not comparing
1469 building a new manifest if self (working directory) is not comparing
1468 against its parent (repo['.']).
1470 against its parent (repo['.']).
1469 """
1471 """
1470 if other != self._repo['.']:
1472 if other != self._repo['.']:
1471 s = super(workingctx, self)._buildstatus(other, s, match,
1473 s = super(workingctx, self)._buildstatus(other, s, match,
1472 listignored, listclean,
1474 listignored, listclean,
1473 listunknown)
1475 listunknown)
1474 return s
1476 return s
1475
1477
1476 def _matchstatus(self, other, s, match, listignored, listclean,
1478 def _matchstatus(self, other, s, match, listignored, listclean,
1477 listunknown):
1479 listunknown):
1478 """override the match method with a filter for directory patterns
1480 """override the match method with a filter for directory patterns
1479
1481
1480 We use inheritance to customize the match.bad method only in cases of
1482 We use inheritance to customize the match.bad method only in cases of
1481 workingctx since it belongs only to the working directory when
1483 workingctx since it belongs only to the working directory when
1482 comparing against the parent changeset.
1484 comparing against the parent changeset.
1483
1485
1484 If we aren't comparing against the working directory's parent, then we
1486 If we aren't comparing against the working directory's parent, then we
1485 just use the default match object sent to us.
1487 just use the default match object sent to us.
1486 """
1488 """
1487 superself = super(workingctx, self)
1489 superself = super(workingctx, self)
1488 match = superself._matchstatus(other, s, match, listignored, listclean,
1490 match = superself._matchstatus(other, s, match, listignored, listclean,
1489 listunknown)
1491 listunknown)
1490 if other != self._repo['.']:
1492 if other != self._repo['.']:
1491 def bad(f, msg):
1493 def bad(f, msg):
1492 # 'f' may be a directory pattern from 'match.files()',
1494 # 'f' may be a directory pattern from 'match.files()',
1493 # so 'f not in ctx1' is not enough
1495 # so 'f not in ctx1' is not enough
1494 if f not in other and f not in other.dirs():
1496 if f not in other and f not in other.dirs():
1495 self._repo.ui.warn('%s: %s\n' %
1497 self._repo.ui.warn('%s: %s\n' %
1496 (self._repo.dirstate.pathto(f), msg))
1498 (self._repo.dirstate.pathto(f), msg))
1497 match.bad = bad
1499 match.bad = bad
1498 return match
1500 return match
1499
1501
1500 def status(self, other='.', match=None, listignored=False,
1502 def status(self, other='.', match=None, listignored=False,
1501 listclean=False, listunknown=False, listsubrepos=False):
1503 listclean=False, listunknown=False, listsubrepos=False):
1502 # yet to be determined: what to do if 'other' is a 'workingctx' or a
1504 # yet to be determined: what to do if 'other' is a 'workingctx' or a
1503 # 'memctx'?
1505 # 'memctx'?
1504 return super(workingctx, self).status(other, match, listignored,
1506 return super(workingctx, self).status(other, match, listignored,
1505 listclean, listunknown,
1507 listclean, listunknown,
1506 listsubrepos)
1508 listsubrepos)
1507
1509
1508 class committablefilectx(basefilectx):
1510 class committablefilectx(basefilectx):
1509 """A committablefilectx provides common functionality for a file context
1511 """A committablefilectx provides common functionality for a file context
1510 that wants the ability to commit, e.g. workingfilectx or memfilectx."""
1512 that wants the ability to commit, e.g. workingfilectx or memfilectx."""
1511 def __init__(self, repo, path, filelog=None, ctx=None):
1513 def __init__(self, repo, path, filelog=None, ctx=None):
1512 self._repo = repo
1514 self._repo = repo
1513 self._path = path
1515 self._path = path
1514 self._changeid = None
1516 self._changeid = None
1515 self._filerev = self._filenode = None
1517 self._filerev = self._filenode = None
1516
1518
1517 if filelog is not None:
1519 if filelog is not None:
1518 self._filelog = filelog
1520 self._filelog = filelog
1519 if ctx:
1521 if ctx:
1520 self._changectx = ctx
1522 self._changectx = ctx
1521
1523
1522 def __nonzero__(self):
1524 def __nonzero__(self):
1523 return True
1525 return True
1524
1526
1525 def parents(self):
1527 def parents(self):
1526 '''return parent filectxs, following copies if necessary'''
1528 '''return parent filectxs, following copies if necessary'''
1527 def filenode(ctx, path):
1529 def filenode(ctx, path):
1528 return ctx._manifest.get(path, nullid)
1530 return ctx._manifest.get(path, nullid)
1529
1531
1530 path = self._path
1532 path = self._path
1531 fl = self._filelog
1533 fl = self._filelog
1532 pcl = self._changectx._parents
1534 pcl = self._changectx._parents
1533 renamed = self.renamed()
1535 renamed = self.renamed()
1534
1536
1535 if renamed:
1537 if renamed:
1536 pl = [renamed + (None,)]
1538 pl = [renamed + (None,)]
1537 else:
1539 else:
1538 pl = [(path, filenode(pcl[0], path), fl)]
1540 pl = [(path, filenode(pcl[0], path), fl)]
1539
1541
1540 for pc in pcl[1:]:
1542 for pc in pcl[1:]:
1541 pl.append((path, filenode(pc, path), fl))
1543 pl.append((path, filenode(pc, path), fl))
1542
1544
1543 return [filectx(self._repo, p, fileid=n, filelog=l)
1545 return [filectx(self._repo, p, fileid=n, filelog=l)
1544 for p, n, l in pl if n != nullid]
1546 for p, n, l in pl if n != nullid]
1545
1547
1546 def children(self):
1548 def children(self):
1547 return []
1549 return []
1548
1550
1549 class workingfilectx(committablefilectx):
1551 class workingfilectx(committablefilectx):
1550 """A workingfilectx object makes access to data related to a particular
1552 """A workingfilectx object makes access to data related to a particular
1551 file in the working directory convenient."""
1553 file in the working directory convenient."""
1552 def __init__(self, repo, path, filelog=None, workingctx=None):
1554 def __init__(self, repo, path, filelog=None, workingctx=None):
1553 super(workingfilectx, self).__init__(repo, path, filelog, workingctx)
1555 super(workingfilectx, self).__init__(repo, path, filelog, workingctx)
1554
1556
1555 @propertycache
1557 @propertycache
1556 def _changectx(self):
1558 def _changectx(self):
1557 return workingctx(self._repo)
1559 return workingctx(self._repo)
1558
1560
1559 def data(self):
1561 def data(self):
1560 return self._repo.wread(self._path)
1562 return self._repo.wread(self._path)
1561 def renamed(self):
1563 def renamed(self):
1562 rp = self._repo.dirstate.copied(self._path)
1564 rp = self._repo.dirstate.copied(self._path)
1563 if not rp:
1565 if not rp:
1564 return None
1566 return None
1565 return rp, self._changectx._parents[0]._manifest.get(rp, nullid)
1567 return rp, self._changectx._parents[0]._manifest.get(rp, nullid)
1566
1568
1567 def size(self):
1569 def size(self):
1568 return self._repo.wvfs.lstat(self._path).st_size
1570 return self._repo.wvfs.lstat(self._path).st_size
1569 def date(self):
1571 def date(self):
1570 t, tz = self._changectx.date()
1572 t, tz = self._changectx.date()
1571 try:
1573 try:
1572 return (int(self._repo.wvfs.lstat(self._path).st_mtime), tz)
1574 return (int(self._repo.wvfs.lstat(self._path).st_mtime), tz)
1573 except OSError, err:
1575 except OSError, err:
1574 if err.errno != errno.ENOENT:
1576 if err.errno != errno.ENOENT:
1575 raise
1577 raise
1576 return (t, tz)
1578 return (t, tz)
1577
1579
1578 def cmp(self, fctx):
1580 def cmp(self, fctx):
1579 """compare with other file context
1581 """compare with other file context
1580
1582
1581 returns True if different than fctx.
1583 returns True if different than fctx.
1582 """
1584 """
1583 # fctx should be a filectx (not a workingfilectx)
1585 # fctx should be a filectx (not a workingfilectx)
1584 # invert comparison to reuse the same code path
1586 # invert comparison to reuse the same code path
1585 return fctx.cmp(self)
1587 return fctx.cmp(self)
1586
1588
1587 def remove(self, ignoremissing=False):
1589 def remove(self, ignoremissing=False):
1588 """wraps unlink for a repo's working directory"""
1590 """wraps unlink for a repo's working directory"""
1589 util.unlinkpath(self._repo.wjoin(self._path), ignoremissing)
1591 util.unlinkpath(self._repo.wjoin(self._path), ignoremissing)
1590
1592
1591 def write(self, data, flags):
1593 def write(self, data, flags):
1592 """wraps repo.wwrite"""
1594 """wraps repo.wwrite"""
1593 self._repo.wwrite(self._path, data, flags)
1595 self._repo.wwrite(self._path, data, flags)
1594
1596
1595 class memctx(committablectx):
1597 class memctx(committablectx):
1596 """Use memctx to perform in-memory commits via localrepo.commitctx().
1598 """Use memctx to perform in-memory commits via localrepo.commitctx().
1597
1599
1598 Revision information is supplied at initialization time while
1600 Revision information is supplied at initialization time while
1599 related files data and is made available through a callback
1601 related files data and is made available through a callback
1600 mechanism. 'repo' is the current localrepo, 'parents' is a
1602 mechanism. 'repo' is the current localrepo, 'parents' is a
1601 sequence of two parent revisions identifiers (pass None for every
1603 sequence of two parent revisions identifiers (pass None for every
1602 missing parent), 'text' is the commit message and 'files' lists
1604 missing parent), 'text' is the commit message and 'files' lists
1603 names of files touched by the revision (normalized and relative to
1605 names of files touched by the revision (normalized and relative to
1604 repository root).
1606 repository root).
1605
1607
1606 filectxfn(repo, memctx, path) is a callable receiving the
1608 filectxfn(repo, memctx, path) is a callable receiving the
1607 repository, the current memctx object and the normalized path of
1609 repository, the current memctx object and the normalized path of
1608 requested file, relative to repository root. It is fired by the
1610 requested file, relative to repository root. It is fired by the
1609 commit function for every file in 'files', but calls order is
1611 commit function for every file in 'files', but calls order is
1610 undefined. If the file is available in the revision being
1612 undefined. If the file is available in the revision being
1611 committed (updated or added), filectxfn returns a memfilectx
1613 committed (updated or added), filectxfn returns a memfilectx
1612 object. If the file was removed, filectxfn raises an
1614 object. If the file was removed, filectxfn raises an
1613 IOError. Moved files are represented by marking the source file
1615 IOError. Moved files are represented by marking the source file
1614 removed and the new file added with copy information (see
1616 removed and the new file added with copy information (see
1615 memfilectx).
1617 memfilectx).
1616
1618
1617 user receives the committer name and defaults to current
1619 user receives the committer name and defaults to current
1618 repository username, date is the commit date in any format
1620 repository username, date is the commit date in any format
1619 supported by util.parsedate() and defaults to current date, extra
1621 supported by util.parsedate() and defaults to current date, extra
1620 is a dictionary of metadata or is left empty.
1622 is a dictionary of metadata or is left empty.
1621 """
1623 """
1622
1624
1623 # Mercurial <= 3.1 expects the filectxfn to raise IOError for missing files.
1625 # Mercurial <= 3.1 expects the filectxfn to raise IOError for missing files.
1624 # Extensions that need to retain compatibility across Mercurial 3.1 can use
1626 # Extensions that need to retain compatibility across Mercurial 3.1 can use
1625 # this field to determine what to do in filectxfn.
1627 # this field to determine what to do in filectxfn.
1626 _returnnoneformissingfiles = True
1628 _returnnoneformissingfiles = True
1627
1629
1628 def __init__(self, repo, parents, text, files, filectxfn, user=None,
1630 def __init__(self, repo, parents, text, files, filectxfn, user=None,
1629 date=None, extra=None, editor=False):
1631 date=None, extra=None, editor=False):
1630 super(memctx, self).__init__(repo, text, user, date, extra)
1632 super(memctx, self).__init__(repo, text, user, date, extra)
1631 self._rev = None
1633 self._rev = None
1632 self._node = None
1634 self._node = None
1633 parents = [(p or nullid) for p in parents]
1635 parents = [(p or nullid) for p in parents]
1634 p1, p2 = parents
1636 p1, p2 = parents
1635 self._parents = [changectx(self._repo, p) for p in (p1, p2)]
1637 self._parents = [changectx(self._repo, p) for p in (p1, p2)]
1636 files = sorted(set(files))
1638 files = sorted(set(files))
1637 self._status = scmutil.status(files, [], [], [], [], [], [])
1639 self._status = scmutil.status(files, [], [], [], [], [], [])
1638 self._filectxfn = filectxfn
1640 self._filectxfn = filectxfn
1639 self.substate = {}
1641 self.substate = {}
1640
1642
1641 # if store is not callable, wrap it in a function
1643 # if store is not callable, wrap it in a function
1642 if not callable(filectxfn):
1644 if not callable(filectxfn):
1643 def getfilectx(repo, memctx, path):
1645 def getfilectx(repo, memctx, path):
1644 fctx = filectxfn[path]
1646 fctx = filectxfn[path]
1645 # this is weird but apparently we only keep track of one parent
1647 # this is weird but apparently we only keep track of one parent
1646 # (why not only store that instead of a tuple?)
1648 # (why not only store that instead of a tuple?)
1647 copied = fctx.renamed()
1649 copied = fctx.renamed()
1648 if copied:
1650 if copied:
1649 copied = copied[0]
1651 copied = copied[0]
1650 return memfilectx(repo, path, fctx.data(),
1652 return memfilectx(repo, path, fctx.data(),
1651 islink=fctx.islink(), isexec=fctx.isexec(),
1653 islink=fctx.islink(), isexec=fctx.isexec(),
1652 copied=copied, memctx=memctx)
1654 copied=copied, memctx=memctx)
1653 self._filectxfn = getfilectx
1655 self._filectxfn = getfilectx
1654
1656
1655 self._extra = extra and extra.copy() or {}
1657 self._extra = extra and extra.copy() or {}
1656 if self._extra.get('branch', '') == '':
1658 if self._extra.get('branch', '') == '':
1657 self._extra['branch'] = 'default'
1659 self._extra['branch'] = 'default'
1658
1660
1659 if editor:
1661 if editor:
1660 self._text = editor(self._repo, self, [])
1662 self._text = editor(self._repo, self, [])
1661 self._repo.savecommitmessage(self._text)
1663 self._repo.savecommitmessage(self._text)
1662
1664
1663 def filectx(self, path, filelog=None):
1665 def filectx(self, path, filelog=None):
1664 """get a file context from the working directory
1666 """get a file context from the working directory
1665
1667
1666 Returns None if file doesn't exist and should be removed."""
1668 Returns None if file doesn't exist and should be removed."""
1667 return self._filectxfn(self._repo, self, path)
1669 return self._filectxfn(self._repo, self, path)
1668
1670
1669 def commit(self):
1671 def commit(self):
1670 """commit context to the repo"""
1672 """commit context to the repo"""
1671 return self._repo.commitctx(self)
1673 return self._repo.commitctx(self)
1672
1674
1673 @propertycache
1675 @propertycache
1674 def _manifest(self):
1676 def _manifest(self):
1675 """generate a manifest based on the return values of filectxfn"""
1677 """generate a manifest based on the return values of filectxfn"""
1676
1678
1677 # keep this simple for now; just worry about p1
1679 # keep this simple for now; just worry about p1
1678 pctx = self._parents[0]
1680 pctx = self._parents[0]
1679 man = pctx.manifest().copy()
1681 man = pctx.manifest().copy()
1680
1682
1681 for f, fnode in man.iteritems():
1683 for f, fnode in man.iteritems():
1682 p1node = nullid
1684 p1node = nullid
1683 p2node = nullid
1685 p2node = nullid
1684 p = pctx[f].parents() # if file isn't in pctx, check p2?
1686 p = pctx[f].parents() # if file isn't in pctx, check p2?
1685 if len(p) > 0:
1687 if len(p) > 0:
1686 p1node = p[0].node()
1688 p1node = p[0].node()
1687 if len(p) > 1:
1689 if len(p) > 1:
1688 p2node = p[1].node()
1690 p2node = p[1].node()
1689 man[f] = revlog.hash(self[f].data(), p1node, p2node)
1691 man[f] = revlog.hash(self[f].data(), p1node, p2node)
1690
1692
1691 return man
1693 return man
1692
1694
1693
1695
1694 class memfilectx(committablefilectx):
1696 class memfilectx(committablefilectx):
1695 """memfilectx represents an in-memory file to commit.
1697 """memfilectx represents an in-memory file to commit.
1696
1698
1697 See memctx and commitablefilectx for more details.
1699 See memctx and commitablefilectx for more details.
1698 """
1700 """
1699 def __init__(self, repo, path, data, islink=False,
1701 def __init__(self, repo, path, data, islink=False,
1700 isexec=False, copied=None, memctx=None):
1702 isexec=False, copied=None, memctx=None):
1701 """
1703 """
1702 path is the normalized file path relative to repository root.
1704 path is the normalized file path relative to repository root.
1703 data is the file content as a string.
1705 data is the file content as a string.
1704 islink is True if the file is a symbolic link.
1706 islink is True if the file is a symbolic link.
1705 isexec is True if the file is executable.
1707 isexec is True if the file is executable.
1706 copied is the source file path if current file was copied in the
1708 copied is the source file path if current file was copied in the
1707 revision being committed, or None."""
1709 revision being committed, or None."""
1708 super(memfilectx, self).__init__(repo, path, None, memctx)
1710 super(memfilectx, self).__init__(repo, path, None, memctx)
1709 self._data = data
1711 self._data = data
1710 self._flags = (islink and 'l' or '') + (isexec and 'x' or '')
1712 self._flags = (islink and 'l' or '') + (isexec and 'x' or '')
1711 self._copied = None
1713 self._copied = None
1712 if copied:
1714 if copied:
1713 self._copied = (copied, nullid)
1715 self._copied = (copied, nullid)
1714
1716
1715 def data(self):
1717 def data(self):
1716 return self._data
1718 return self._data
1717 def size(self):
1719 def size(self):
1718 return len(self.data())
1720 return len(self.data())
1719 def flags(self):
1721 def flags(self):
1720 return self._flags
1722 return self._flags
1721 def renamed(self):
1723 def renamed(self):
1722 return self._copied
1724 return self._copied
1723
1725
1724 def remove(self, ignoremissing=False):
1726 def remove(self, ignoremissing=False):
1725 """wraps unlink for a repo's working directory"""
1727 """wraps unlink for a repo's working directory"""
1726 # need to figure out what to do here
1728 # need to figure out what to do here
1727 del self._changectx[self._path]
1729 del self._changectx[self._path]
1728
1730
1729 def write(self, data, flags):
1731 def write(self, data, flags):
1730 """wraps repo.wwrite"""
1732 """wraps repo.wwrite"""
1731 self._data = data
1733 self._data = data
@@ -1,125 +1,125 b''
1
1
2 $ echo "[extensions]" >> $HGRCPATH
2 $ echo "[extensions]" >> $HGRCPATH
3 $ echo "mq=" >> $HGRCPATH
3 $ echo "mq=" >> $HGRCPATH
4
4
5 $ hg init a
5 $ hg init a
6 $ cd a
6 $ cd a
7
7
8 $ echo 'base' > base
8 $ echo 'base' > base
9 $ hg ci -Ambase
9 $ hg ci -Ambase
10 adding base
10 adding base
11
11
12 $ hg qnew -mmqbase mqbase
12 $ hg qnew -mmqbase mqbase
13
13
14 $ hg qrename mqbase renamed
14 $ hg qrename mqbase renamed
15 $ mkdir .hg/patches/foo
15 $ mkdir .hg/patches/foo
16 $ hg qrename renamed foo
16 $ hg qrename renamed foo
17
17
18 $ hg qseries
18 $ hg qseries
19 foo/renamed
19 foo/renamed
20
20
21 $ ls .hg/patches/foo
21 $ ls .hg/patches/foo
22 renamed
22 renamed
23
23
24 $ mkdir .hg/patches/bar
24 $ mkdir .hg/patches/bar
25 $ hg qrename foo/renamed bar
25 $ hg qrename foo/renamed bar
26
26
27 $ hg qseries
27 $ hg qseries
28 bar/renamed
28 bar/renamed
29
29
30 $ ls .hg/patches/bar
30 $ ls .hg/patches/bar
31 renamed
31 renamed
32
32
33 $ hg qrename bar/renamed baz
33 $ hg qrename bar/renamed baz
34
34
35 $ hg qseries
35 $ hg qseries
36 baz
36 baz
37
37
38 $ ls .hg/patches/baz
38 $ ls .hg/patches/baz
39 .hg/patches/baz
39 .hg/patches/baz
40
40
41 $ hg qrename baz new/dir
41 $ hg qrename baz new/dir
42
42
43 $ hg qseries
43 $ hg qseries
44 new/dir
44 new/dir
45
45
46 $ ls .hg/patches/new/dir
46 $ ls .hg/patches/new/dir
47 .hg/patches/new/dir
47 .hg/patches/new/dir
48
48
49 $ cd ..
49 $ cd ..
50
50
51 Test patch being renamed before committed:
51 Test patch being renamed before committed:
52
52
53 $ hg init b
53 $ hg init b
54 $ cd b
54 $ cd b
55 $ hg qinit -c
55 $ hg qinit -c
56 $ hg qnew x
56 $ hg qnew x
57 $ hg qrename y
57 $ hg qrename y
58 $ hg qcommit -m rename
58 $ hg qcommit -m rename
59
59
60 $ cd ..
60 $ cd ..
61
61
62 Test overlapping renames (issue2388)
62 Test overlapping renames (issue2388)
63
63
64 $ hg init c
64 $ hg init c
65 $ cd c
65 $ cd c
66 $ hg qinit -c
66 $ hg qinit -c
67 $ echo a > a
67 $ echo a > a
68 $ hg add
68 $ hg add
69 adding a
69 adding a
70 $ hg qnew patcha
70 $ hg qnew patcha
71 $ echo b > b
71 $ echo b > b
72 $ hg add
72 $ hg add
73 adding b
73 adding b
74 $ hg qnew patchb
74 $ hg qnew patchb
75 $ hg ci --mq -m c1
75 $ hg ci --mq -m c1
76 $ hg qrename patchb patchc
76 $ hg qrename patchb patchc
77 $ hg qrename patcha patchb
77 $ hg qrename patcha patchb
78 $ hg st --mq
78 $ hg st --mq
79 M patchb
79 M series
80 M series
80 A patchb
81 A patchc
81 A patchc
82 R patcha
82 R patcha
83 $ cd ..
83 $ cd ..
84
84
85 Test renames with mq repo (issue2097)
85 Test renames with mq repo (issue2097)
86
86
87 $ hg init issue2097
87 $ hg init issue2097
88 $ cd issue2097
88 $ cd issue2097
89 $ hg qnew p0
89 $ hg qnew p0
90 $ (cd .hg/patches && hg init)
90 $ (cd .hg/patches && hg init)
91 $ hg qren p0 p1
91 $ hg qren p0 p1
92 $ hg debugstate --mq
92 $ hg debugstate --mq
93 $ hg ci --mq -mq0
93 $ hg ci --mq -mq0
94 nothing changed
94 nothing changed
95 [1]
95 [1]
96 $ cd ..
96 $ cd ..
97
97
98 Test renaming to a folded patch (issue3058)
98 Test renaming to a folded patch (issue3058)
99
99
100 $ hg init issue3058
100 $ hg init issue3058
101 $ cd issue3058
101 $ cd issue3058
102 $ hg init --mq
102 $ hg init --mq
103 $ echo a > a
103 $ echo a > a
104 $ hg add a
104 $ hg add a
105 $ hg qnew adda
105 $ hg qnew adda
106 $ echo b >> a
106 $ echo b >> a
107 $ hg qnew addb
107 $ hg qnew addb
108 $ hg qpop
108 $ hg qpop
109 popping addb
109 popping addb
110 now at: adda
110 now at: adda
111 $ hg ci --mq -m "save mq"
111 $ hg ci --mq -m "save mq"
112 $ hg qfold addb
112 $ hg qfold addb
113 $ hg qmv addb
113 $ hg qmv addb
114 $ cat .hg/patches/addb
114 $ cat .hg/patches/addb
115 # HG changeset patch
115 # HG changeset patch
116 # Parent 0000000000000000000000000000000000000000
116 # Parent 0000000000000000000000000000000000000000
117
117
118 diff -r 000000000000 a
118 diff -r 000000000000 a
119 --- /dev/null * (glob)
119 --- /dev/null * (glob)
120 +++ b/a * (glob)
120 +++ b/a * (glob)
121 @@ -0,0 +1,2 @@
121 @@ -0,0 +1,2 @@
122 +a
122 +a
123 +b
123 +b
124 $ cd ..
124 $ cd ..
125
125
@@ -1,638 +1,647 b''
1 $ hg init
1 $ hg init
2 $ mkdir d1 d1/d11 d2
2 $ mkdir d1 d1/d11 d2
3 $ echo d1/a > d1/a
3 $ echo d1/a > d1/a
4 $ echo d1/ba > d1/ba
4 $ echo d1/ba > d1/ba
5 $ echo d1/a1 > d1/d11/a1
5 $ echo d1/a1 > d1/d11/a1
6 $ echo d1/b > d1/b
6 $ echo d1/b > d1/b
7 $ echo d2/b > d2/b
7 $ echo d2/b > d2/b
8 $ hg add d1/a d1/b d1/ba d1/d11/a1 d2/b
8 $ hg add d1/a d1/b d1/ba d1/d11/a1 d2/b
9 $ hg commit -m "1"
9 $ hg commit -m "1"
10
10
11 rename a single file
11 rename a single file
12
12
13 $ hg rename d1/d11/a1 d2/c
13 $ hg rename d1/d11/a1 d2/c
14 $ hg --config ui.portablefilenames=abort rename d1/a d1/con.xml
14 $ hg --config ui.portablefilenames=abort rename d1/a d1/con.xml
15 abort: filename contains 'con', which is reserved on Windows: 'd1/con.xml'
15 abort: filename contains 'con', which is reserved on Windows: 'd1/con.xml'
16 [255]
16 [255]
17 $ hg sum
17 $ hg sum
18 parent: 0:9b4b6e7b2c26 tip
18 parent: 0:9b4b6e7b2c26 tip
19 1
19 1
20 branch: default
20 branch: default
21 commit: 1 renamed
21 commit: 1 renamed
22 update: (current)
22 update: (current)
23 $ hg status -C
23 $ hg status -C
24 A d2/c
24 A d2/c
25 d1/d11/a1
25 d1/d11/a1
26 R d1/d11/a1
26 R d1/d11/a1
27 $ hg update -C
27 $ hg update -C
28 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
28 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
29 $ rm d2/c
29 $ rm d2/c
30
30
31 rename a single file using absolute paths
31 rename a single file using absolute paths
32
32
33 $ hg rename `pwd`/d1/d11/a1 `pwd`/d2/c
33 $ hg rename `pwd`/d1/d11/a1 `pwd`/d2/c
34 $ hg status -C
34 $ hg status -C
35 A d2/c
35 A d2/c
36 d1/d11/a1
36 d1/d11/a1
37 R d1/d11/a1
37 R d1/d11/a1
38 $ hg update -C
38 $ hg update -C
39 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
39 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
40 $ rm d2/c
40 $ rm d2/c
41
41
42 rename --after a single file
42 rename --after a single file
43
43
44 $ mv d1/d11/a1 d2/c
44 $ mv d1/d11/a1 d2/c
45 $ hg rename --after d1/d11/a1 d2/c
45 $ hg rename --after d1/d11/a1 d2/c
46 $ hg status -C
46 $ hg status -C
47 A d2/c
47 A d2/c
48 d1/d11/a1
48 d1/d11/a1
49 R d1/d11/a1
49 R d1/d11/a1
50 $ hg update -C
50 $ hg update -C
51 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
51 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
52 $ rm d2/c
52 $ rm d2/c
53
53
54 rename --after a single file when src and tgt already tracked
54 rename --after a single file when src and tgt already tracked
55
55
56 $ mv d1/d11/a1 d2/c
56 $ mv d1/d11/a1 d2/c
57 $ hg addrem -s 0
57 $ hg addrem -s 0
58 removing d1/d11/a1
58 removing d1/d11/a1
59 adding d2/c
59 adding d2/c
60 $ hg rename --after d1/d11/a1 d2/c
60 $ hg rename --after d1/d11/a1 d2/c
61 $ hg status -C
61 $ hg status -C
62 A d2/c
62 A d2/c
63 d1/d11/a1
63 d1/d11/a1
64 R d1/d11/a1
64 R d1/d11/a1
65 $ hg update -C
65 $ hg update -C
66 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
66 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
67 $ rm d2/c
67 $ rm d2/c
68
68
69 rename --after a single file to a nonexistent target filename
69 rename --after a single file to a nonexistent target filename
70
70
71 $ hg rename --after d1/a dummy
71 $ hg rename --after d1/a dummy
72 d1/a: not recording move - dummy does not exist (glob)
72 d1/a: not recording move - dummy does not exist (glob)
73
73
74 move a single file to an existing directory
74 move a single file to an existing directory
75
75
76 $ hg rename d1/d11/a1 d2
76 $ hg rename d1/d11/a1 d2
77 $ hg status -C
77 $ hg status -C
78 A d2/a1
78 A d2/a1
79 d1/d11/a1
79 d1/d11/a1
80 R d1/d11/a1
80 R d1/d11/a1
81 $ hg update -C
81 $ hg update -C
82 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
82 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
83 $ rm d2/a1
83 $ rm d2/a1
84
84
85 move --after a single file to an existing directory
85 move --after a single file to an existing directory
86
86
87 $ mv d1/d11/a1 d2
87 $ mv d1/d11/a1 d2
88 $ hg rename --after d1/d11/a1 d2
88 $ hg rename --after d1/d11/a1 d2
89 $ hg status -C
89 $ hg status -C
90 A d2/a1
90 A d2/a1
91 d1/d11/a1
91 d1/d11/a1
92 R d1/d11/a1
92 R d1/d11/a1
93 $ hg update -C
93 $ hg update -C
94 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
94 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
95 $ rm d2/a1
95 $ rm d2/a1
96
96
97 rename a file using a relative path
97 rename a file using a relative path
98
98
99 $ (cd d1/d11; hg rename ../../d2/b e)
99 $ (cd d1/d11; hg rename ../../d2/b e)
100 $ hg status -C
100 $ hg status -C
101 A d1/d11/e
101 A d1/d11/e
102 d2/b
102 d2/b
103 R d2/b
103 R d2/b
104 $ hg update -C
104 $ hg update -C
105 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
105 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
106 $ rm d1/d11/e
106 $ rm d1/d11/e
107
107
108 rename --after a file using a relative path
108 rename --after a file using a relative path
109
109
110 $ (cd d1/d11; mv ../../d2/b e; hg rename --after ../../d2/b e)
110 $ (cd d1/d11; mv ../../d2/b e; hg rename --after ../../d2/b e)
111 $ hg status -C
111 $ hg status -C
112 A d1/d11/e
112 A d1/d11/e
113 d2/b
113 d2/b
114 R d2/b
114 R d2/b
115 $ hg update -C
115 $ hg update -C
116 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
116 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
117 $ rm d1/d11/e
117 $ rm d1/d11/e
118
118
119 rename directory d1 as d3
119 rename directory d1 as d3
120
120
121 $ hg rename d1/ d3
121 $ hg rename d1/ d3
122 moving d1/a to d3/a (glob)
122 moving d1/a to d3/a (glob)
123 moving d1/b to d3/b (glob)
123 moving d1/b to d3/b (glob)
124 moving d1/ba to d3/ba (glob)
124 moving d1/ba to d3/ba (glob)
125 moving d1/d11/a1 to d3/d11/a1 (glob)
125 moving d1/d11/a1 to d3/d11/a1 (glob)
126 $ hg status -C
126 $ hg status -C
127 A d3/a
127 A d3/a
128 d1/a
128 d1/a
129 A d3/b
129 A d3/b
130 d1/b
130 d1/b
131 A d3/ba
131 A d3/ba
132 d1/ba
132 d1/ba
133 A d3/d11/a1
133 A d3/d11/a1
134 d1/d11/a1
134 d1/d11/a1
135 R d1/a
135 R d1/a
136 R d1/b
136 R d1/b
137 R d1/ba
137 R d1/ba
138 R d1/d11/a1
138 R d1/d11/a1
139 $ hg update -C
139 $ hg update -C
140 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
140 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
141 $ rm -rf d3
141 $ rm -rf d3
142
142
143 rename --after directory d1 as d3
143 rename --after directory d1 as d3
144
144
145 $ mv d1 d3
145 $ mv d1 d3
146 $ hg rename --after d1 d3
146 $ hg rename --after d1 d3
147 moving d1/a to d3/a (glob)
147 moving d1/a to d3/a (glob)
148 moving d1/b to d3/b (glob)
148 moving d1/b to d3/b (glob)
149 moving d1/ba to d3/ba (glob)
149 moving d1/ba to d3/ba (glob)
150 moving d1/d11/a1 to d3/d11/a1 (glob)
150 moving d1/d11/a1 to d3/d11/a1 (glob)
151 $ hg status -C
151 $ hg status -C
152 A d3/a
152 A d3/a
153 d1/a
153 d1/a
154 A d3/b
154 A d3/b
155 d1/b
155 d1/b
156 A d3/ba
156 A d3/ba
157 d1/ba
157 d1/ba
158 A d3/d11/a1
158 A d3/d11/a1
159 d1/d11/a1
159 d1/d11/a1
160 R d1/a
160 R d1/a
161 R d1/b
161 R d1/b
162 R d1/ba
162 R d1/ba
163 R d1/d11/a1
163 R d1/d11/a1
164 $ hg update -C
164 $ hg update -C
165 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
165 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
166 $ rm -rf d3
166 $ rm -rf d3
167
167
168 move a directory using a relative path
168 move a directory using a relative path
169
169
170 $ (cd d2; mkdir d3; hg rename ../d1/d11 d3)
170 $ (cd d2; mkdir d3; hg rename ../d1/d11 d3)
171 moving ../d1/d11/a1 to d3/d11/a1 (glob)
171 moving ../d1/d11/a1 to d3/d11/a1 (glob)
172 $ hg status -C
172 $ hg status -C
173 A d2/d3/d11/a1
173 A d2/d3/d11/a1
174 d1/d11/a1
174 d1/d11/a1
175 R d1/d11/a1
175 R d1/d11/a1
176 $ hg update -C
176 $ hg update -C
177 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
177 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
178 $ rm -rf d2/d3
178 $ rm -rf d2/d3
179
179
180 move --after a directory using a relative path
180 move --after a directory using a relative path
181
181
182 $ (cd d2; mkdir d3; mv ../d1/d11 d3; hg rename --after ../d1/d11 d3)
182 $ (cd d2; mkdir d3; mv ../d1/d11 d3; hg rename --after ../d1/d11 d3)
183 moving ../d1/d11/a1 to d3/d11/a1 (glob)
183 moving ../d1/d11/a1 to d3/d11/a1 (glob)
184 $ hg status -C
184 $ hg status -C
185 A d2/d3/d11/a1
185 A d2/d3/d11/a1
186 d1/d11/a1
186 d1/d11/a1
187 R d1/d11/a1
187 R d1/d11/a1
188 $ hg update -C
188 $ hg update -C
189 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
189 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
190 $ rm -rf d2/d3
190 $ rm -rf d2/d3
191
191
192 move directory d1/d11 to an existing directory d2 (removes empty d1)
192 move directory d1/d11 to an existing directory d2 (removes empty d1)
193
193
194 $ hg rename d1/d11/ d2
194 $ hg rename d1/d11/ d2
195 moving d1/d11/a1 to d2/d11/a1 (glob)
195 moving d1/d11/a1 to d2/d11/a1 (glob)
196 $ hg status -C
196 $ hg status -C
197 A d2/d11/a1
197 A d2/d11/a1
198 d1/d11/a1
198 d1/d11/a1
199 R d1/d11/a1
199 R d1/d11/a1
200 $ hg update -C
200 $ hg update -C
201 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
201 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
202 $ rm -rf d2/d11
202 $ rm -rf d2/d11
203
203
204 move directories d1 and d2 to a new directory d3
204 move directories d1 and d2 to a new directory d3
205
205
206 $ mkdir d3
206 $ mkdir d3
207 $ hg rename d1 d2 d3
207 $ hg rename d1 d2 d3
208 moving d1/a to d3/d1/a (glob)
208 moving d1/a to d3/d1/a (glob)
209 moving d1/b to d3/d1/b (glob)
209 moving d1/b to d3/d1/b (glob)
210 moving d1/ba to d3/d1/ba (glob)
210 moving d1/ba to d3/d1/ba (glob)
211 moving d1/d11/a1 to d3/d1/d11/a1 (glob)
211 moving d1/d11/a1 to d3/d1/d11/a1 (glob)
212 moving d2/b to d3/d2/b (glob)
212 moving d2/b to d3/d2/b (glob)
213 $ hg status -C
213 $ hg status -C
214 A d3/d1/a
214 A d3/d1/a
215 d1/a
215 d1/a
216 A d3/d1/b
216 A d3/d1/b
217 d1/b
217 d1/b
218 A d3/d1/ba
218 A d3/d1/ba
219 d1/ba
219 d1/ba
220 A d3/d1/d11/a1
220 A d3/d1/d11/a1
221 d1/d11/a1
221 d1/d11/a1
222 A d3/d2/b
222 A d3/d2/b
223 d2/b
223 d2/b
224 R d1/a
224 R d1/a
225 R d1/b
225 R d1/b
226 R d1/ba
226 R d1/ba
227 R d1/d11/a1
227 R d1/d11/a1
228 R d2/b
228 R d2/b
229 $ hg update -C
229 $ hg update -C
230 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
230 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
231 $ rm -rf d3
231 $ rm -rf d3
232
232
233 move --after directories d1 and d2 to a new directory d3
233 move --after directories d1 and d2 to a new directory d3
234
234
235 $ mkdir d3
235 $ mkdir d3
236 $ mv d1 d2 d3
236 $ mv d1 d2 d3
237 $ hg rename --after d1 d2 d3
237 $ hg rename --after d1 d2 d3
238 moving d1/a to d3/d1/a (glob)
238 moving d1/a to d3/d1/a (glob)
239 moving d1/b to d3/d1/b (glob)
239 moving d1/b to d3/d1/b (glob)
240 moving d1/ba to d3/d1/ba (glob)
240 moving d1/ba to d3/d1/ba (glob)
241 moving d1/d11/a1 to d3/d1/d11/a1 (glob)
241 moving d1/d11/a1 to d3/d1/d11/a1 (glob)
242 moving d2/b to d3/d2/b (glob)
242 moving d2/b to d3/d2/b (glob)
243 $ hg status -C
243 $ hg status -C
244 A d3/d1/a
244 A d3/d1/a
245 d1/a
245 d1/a
246 A d3/d1/b
246 A d3/d1/b
247 d1/b
247 d1/b
248 A d3/d1/ba
248 A d3/d1/ba
249 d1/ba
249 d1/ba
250 A d3/d1/d11/a1
250 A d3/d1/d11/a1
251 d1/d11/a1
251 d1/d11/a1
252 A d3/d2/b
252 A d3/d2/b
253 d2/b
253 d2/b
254 R d1/a
254 R d1/a
255 R d1/b
255 R d1/b
256 R d1/ba
256 R d1/ba
257 R d1/d11/a1
257 R d1/d11/a1
258 R d2/b
258 R d2/b
259 $ hg update -C
259 $ hg update -C
260 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
260 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
261 $ rm -rf d3
261 $ rm -rf d3
262
262
263 move everything under directory d1 to existing directory d2, do not
263 move everything under directory d1 to existing directory d2, do not
264 overwrite existing files (d2/b)
264 overwrite existing files (d2/b)
265
265
266 $ hg rename d1/* d2
266 $ hg rename d1/* d2
267 d2/b: not overwriting - file exists
267 d2/b: not overwriting - file exists
268 moving d1/d11/a1 to d2/d11/a1 (glob)
268 moving d1/d11/a1 to d2/d11/a1 (glob)
269 $ hg status -C
269 $ hg status -C
270 A d2/a
270 A d2/a
271 d1/a
271 d1/a
272 A d2/ba
272 A d2/ba
273 d1/ba
273 d1/ba
274 A d2/d11/a1
274 A d2/d11/a1
275 d1/d11/a1
275 d1/d11/a1
276 R d1/a
276 R d1/a
277 R d1/ba
277 R d1/ba
278 R d1/d11/a1
278 R d1/d11/a1
279 $ diff -u d1/b d2/b
279 $ diff -u d1/b d2/b
280 --- d1/b * (glob)
280 --- d1/b * (glob)
281 +++ d2/b * (glob)
281 +++ d2/b * (glob)
282 @@ * (glob)
282 @@ * (glob)
283 -d1/b
283 -d1/b
284 +d2/b
284 +d2/b
285 [1]
285 [1]
286 $ hg update -C
286 $ hg update -C
287 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
287 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
288 $ rm d2/a d2/ba d2/d11/a1
288 $ rm d2/a d2/ba d2/d11/a1
289
289
290 attempt to move one file into a non-existent directory
290 attempt to move one file into a non-existent directory
291
291
292 $ hg rename d1/a dx/
292 $ hg rename d1/a dx/
293 abort: destination dx/ is not a directory
293 abort: destination dx/ is not a directory
294 [255]
294 [255]
295 $ hg status -C
295 $ hg status -C
296 $ hg update -C
296 $ hg update -C
297 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
297 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
298
298
299 attempt to move potentially more than one file into a non-existent directory
299 attempt to move potentially more than one file into a non-existent directory
300
300
301 $ hg rename 'glob:d1/**' dx
301 $ hg rename 'glob:d1/**' dx
302 abort: with multiple sources, destination must be an existing directory
302 abort: with multiple sources, destination must be an existing directory
303 [255]
303 [255]
304
304
305 move every file under d1 to d2/d21 (glob)
305 move every file under d1 to d2/d21 (glob)
306
306
307 $ mkdir d2/d21
307 $ mkdir d2/d21
308 $ hg rename 'glob:d1/**' d2/d21
308 $ hg rename 'glob:d1/**' d2/d21
309 moving d1/a to d2/d21/a (glob)
309 moving d1/a to d2/d21/a (glob)
310 moving d1/b to d2/d21/b (glob)
310 moving d1/b to d2/d21/b (glob)
311 moving d1/ba to d2/d21/ba (glob)
311 moving d1/ba to d2/d21/ba (glob)
312 moving d1/d11/a1 to d2/d21/a1 (glob)
312 moving d1/d11/a1 to d2/d21/a1 (glob)
313 $ hg status -C
313 $ hg status -C
314 A d2/d21/a
314 A d2/d21/a
315 d1/a
315 d1/a
316 A d2/d21/a1
316 A d2/d21/a1
317 d1/d11/a1
317 d1/d11/a1
318 A d2/d21/b
318 A d2/d21/b
319 d1/b
319 d1/b
320 A d2/d21/ba
320 A d2/d21/ba
321 d1/ba
321 d1/ba
322 R d1/a
322 R d1/a
323 R d1/b
323 R d1/b
324 R d1/ba
324 R d1/ba
325 R d1/d11/a1
325 R d1/d11/a1
326 $ hg update -C
326 $ hg update -C
327 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
327 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
328 $ rm -rf d2/d21
328 $ rm -rf d2/d21
329
329
330 move --after some files under d1 to d2/d21 (glob)
330 move --after some files under d1 to d2/d21 (glob)
331
331
332 $ mkdir d2/d21
332 $ mkdir d2/d21
333 $ mv d1/a d1/d11/a1 d2/d21
333 $ mv d1/a d1/d11/a1 d2/d21
334 $ hg rename --after 'glob:d1/**' d2/d21
334 $ hg rename --after 'glob:d1/**' d2/d21
335 moving d1/a to d2/d21/a (glob)
335 moving d1/a to d2/d21/a (glob)
336 d1/b: not recording move - d2/d21/b does not exist (glob)
336 d1/b: not recording move - d2/d21/b does not exist (glob)
337 d1/ba: not recording move - d2/d21/ba does not exist (glob)
337 d1/ba: not recording move - d2/d21/ba does not exist (glob)
338 moving d1/d11/a1 to d2/d21/a1 (glob)
338 moving d1/d11/a1 to d2/d21/a1 (glob)
339 $ hg status -C
339 $ hg status -C
340 A d2/d21/a
340 A d2/d21/a
341 d1/a
341 d1/a
342 A d2/d21/a1
342 A d2/d21/a1
343 d1/d11/a1
343 d1/d11/a1
344 R d1/a
344 R d1/a
345 R d1/d11/a1
345 R d1/d11/a1
346 $ hg update -C
346 $ hg update -C
347 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
347 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
348 $ rm -rf d2/d21
348 $ rm -rf d2/d21
349
349
350 move every file under d1 starting with an 'a' to d2/d21 (regexp)
350 move every file under d1 starting with an 'a' to d2/d21 (regexp)
351
351
352 $ mkdir d2/d21
352 $ mkdir d2/d21
353 $ hg rename 're:d1/([^a][^/]*/)*a.*' d2/d21
353 $ hg rename 're:d1/([^a][^/]*/)*a.*' d2/d21
354 moving d1/a to d2/d21/a (glob)
354 moving d1/a to d2/d21/a (glob)
355 moving d1/d11/a1 to d2/d21/a1 (glob)
355 moving d1/d11/a1 to d2/d21/a1 (glob)
356 $ hg status -C
356 $ hg status -C
357 A d2/d21/a
357 A d2/d21/a
358 d1/a
358 d1/a
359 A d2/d21/a1
359 A d2/d21/a1
360 d1/d11/a1
360 d1/d11/a1
361 R d1/a
361 R d1/a
362 R d1/d11/a1
362 R d1/d11/a1
363 $ hg update -C
363 $ hg update -C
364 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
364 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
365 $ rm -rf d2/d21
365 $ rm -rf d2/d21
366
366
367 attempt to overwrite an existing file
367 attempt to overwrite an existing file
368
368
369 $ echo "ca" > d1/ca
369 $ echo "ca" > d1/ca
370 $ hg rename d1/ba d1/ca
370 $ hg rename d1/ba d1/ca
371 d1/ca: not overwriting - file exists
371 d1/ca: not overwriting - file exists
372 $ hg status -C
372 $ hg status -C
373 ? d1/ca
373 ? d1/ca
374 $ hg update -C
374 $ hg update -C
375 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
375 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
376
376
377 forced overwrite of an existing file
377 forced overwrite of an existing file
378
378
379 $ echo "ca" > d1/ca
379 $ echo "ca" > d1/ca
380 $ hg rename --force d1/ba d1/ca
380 $ hg rename --force d1/ba d1/ca
381 $ hg status -C
381 $ hg status -C
382 A d1/ca
382 A d1/ca
383 d1/ba
383 d1/ba
384 R d1/ba
384 R d1/ba
385 $ hg update -C
385 $ hg update -C
386 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
386 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
387 $ rm d1/ca
387 $ rm d1/ca
388
388
389 attempt to overwrite an existing broken symlink
389 attempt to overwrite an existing broken symlink
390
390
391 #if symlink
391 #if symlink
392 $ ln -s ba d1/ca
392 $ ln -s ba d1/ca
393 $ hg rename --traceback d1/ba d1/ca
393 $ hg rename --traceback d1/ba d1/ca
394 d1/ca: not overwriting - file exists
394 d1/ca: not overwriting - file exists
395 $ hg status -C
395 $ hg status -C
396 ? d1/ca
396 ? d1/ca
397 $ hg update -C
397 $ hg update -C
398 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
398 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
399 $ rm d1/ca
399 $ rm d1/ca
400
400
401 replace a symlink with a file
401 replace a symlink with a file
402
402
403 $ ln -s ba d1/ca
403 $ ln -s ba d1/ca
404 $ hg rename --force d1/ba d1/ca
404 $ hg rename --force d1/ba d1/ca
405 $ hg status -C
405 $ hg status -C
406 A d1/ca
406 A d1/ca
407 d1/ba
407 d1/ba
408 R d1/ba
408 R d1/ba
409 $ hg update -C
409 $ hg update -C
410 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
410 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
411 $ rm d1/ca
411 $ rm d1/ca
412 #endif
412 #endif
413
413
414 do not copy more than one source file to the same destination file
414 do not copy more than one source file to the same destination file
415
415
416 $ mkdir d3
416 $ mkdir d3
417 $ hg rename d1/* d2/* d3
417 $ hg rename d1/* d2/* d3
418 moving d1/d11/a1 to d3/d11/a1 (glob)
418 moving d1/d11/a1 to d3/d11/a1 (glob)
419 d3/b: not overwriting - d2/b collides with d1/b
419 d3/b: not overwriting - d2/b collides with d1/b
420 $ hg status -C
420 $ hg status -C
421 A d3/a
421 A d3/a
422 d1/a
422 d1/a
423 A d3/b
423 A d3/b
424 d1/b
424 d1/b
425 A d3/ba
425 A d3/ba
426 d1/ba
426 d1/ba
427 A d3/d11/a1
427 A d3/d11/a1
428 d1/d11/a1
428 d1/d11/a1
429 R d1/a
429 R d1/a
430 R d1/b
430 R d1/b
431 R d1/ba
431 R d1/ba
432 R d1/d11/a1
432 R d1/d11/a1
433 $ hg update -C
433 $ hg update -C
434 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
434 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
435 $ rm -rf d3
435 $ rm -rf d3
436
436
437 move a whole subtree with "hg rename ."
437 move a whole subtree with "hg rename ."
438
438
439 $ mkdir d3
439 $ mkdir d3
440 $ (cd d1; hg rename . ../d3)
440 $ (cd d1; hg rename . ../d3)
441 moving a to ../d3/d1/a
441 moving a to ../d3/d1/a
442 moving b to ../d3/d1/b
442 moving b to ../d3/d1/b
443 moving ba to ../d3/d1/ba
443 moving ba to ../d3/d1/ba
444 moving d11/a1 to ../d3/d1/d11/a1 (glob)
444 moving d11/a1 to ../d3/d1/d11/a1 (glob)
445 $ hg status -C
445 $ hg status -C
446 A d3/d1/a
446 A d3/d1/a
447 d1/a
447 d1/a
448 A d3/d1/b
448 A d3/d1/b
449 d1/b
449 d1/b
450 A d3/d1/ba
450 A d3/d1/ba
451 d1/ba
451 d1/ba
452 A d3/d1/d11/a1
452 A d3/d1/d11/a1
453 d1/d11/a1
453 d1/d11/a1
454 R d1/a
454 R d1/a
455 R d1/b
455 R d1/b
456 R d1/ba
456 R d1/ba
457 R d1/d11/a1
457 R d1/d11/a1
458 $ hg update -C
458 $ hg update -C
459 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
459 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
460 $ rm -rf d3
460 $ rm -rf d3
461
461
462 move a whole subtree with "hg rename --after ."
462 move a whole subtree with "hg rename --after ."
463
463
464 $ mkdir d3
464 $ mkdir d3
465 $ mv d1/* d3
465 $ mv d1/* d3
466 $ (cd d1; hg rename --after . ../d3)
466 $ (cd d1; hg rename --after . ../d3)
467 moving a to ../d3/a
467 moving a to ../d3/a
468 moving b to ../d3/b
468 moving b to ../d3/b
469 moving ba to ../d3/ba
469 moving ba to ../d3/ba
470 moving d11/a1 to ../d3/d11/a1 (glob)
470 moving d11/a1 to ../d3/d11/a1 (glob)
471 $ hg status -C
471 $ hg status -C
472 A d3/a
472 A d3/a
473 d1/a
473 d1/a
474 A d3/b
474 A d3/b
475 d1/b
475 d1/b
476 A d3/ba
476 A d3/ba
477 d1/ba
477 d1/ba
478 A d3/d11/a1
478 A d3/d11/a1
479 d1/d11/a1
479 d1/d11/a1
480 R d1/a
480 R d1/a
481 R d1/b
481 R d1/b
482 R d1/ba
482 R d1/ba
483 R d1/d11/a1
483 R d1/d11/a1
484 $ hg update -C
484 $ hg update -C
485 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
485 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
486 $ rm -rf d3
486 $ rm -rf d3
487
487
488 move the parent tree with "hg rename .."
488 move the parent tree with "hg rename .."
489
489
490 $ (cd d1/d11; hg rename .. ../../d3)
490 $ (cd d1/d11; hg rename .. ../../d3)
491 moving ../a to ../../d3/a (glob)
491 moving ../a to ../../d3/a (glob)
492 moving ../b to ../../d3/b (glob)
492 moving ../b to ../../d3/b (glob)
493 moving ../ba to ../../d3/ba (glob)
493 moving ../ba to ../../d3/ba (glob)
494 moving a1 to ../../d3/d11/a1
494 moving a1 to ../../d3/d11/a1
495 $ hg status -C
495 $ hg status -C
496 A d3/a
496 A d3/a
497 d1/a
497 d1/a
498 A d3/b
498 A d3/b
499 d1/b
499 d1/b
500 A d3/ba
500 A d3/ba
501 d1/ba
501 d1/ba
502 A d3/d11/a1
502 A d3/d11/a1
503 d1/d11/a1
503 d1/d11/a1
504 R d1/a
504 R d1/a
505 R d1/b
505 R d1/b
506 R d1/ba
506 R d1/ba
507 R d1/d11/a1
507 R d1/d11/a1
508 $ hg update -C
508 $ hg update -C
509 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
509 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
510 $ rm -rf d3
510 $ rm -rf d3
511
511
512 skip removed files
512 skip removed files
513
513
514 $ hg remove d1/b
514 $ hg remove d1/b
515 $ hg rename d1 d3
515 $ hg rename d1 d3
516 moving d1/a to d3/a (glob)
516 moving d1/a to d3/a (glob)
517 moving d1/ba to d3/ba (glob)
517 moving d1/ba to d3/ba (glob)
518 moving d1/d11/a1 to d3/d11/a1 (glob)
518 moving d1/d11/a1 to d3/d11/a1 (glob)
519 $ hg status -C
519 $ hg status -C
520 A d3/a
520 A d3/a
521 d1/a
521 d1/a
522 A d3/ba
522 A d3/ba
523 d1/ba
523 d1/ba
524 A d3/d11/a1
524 A d3/d11/a1
525 d1/d11/a1
525 d1/d11/a1
526 R d1/a
526 R d1/a
527 R d1/b
527 R d1/b
528 R d1/ba
528 R d1/ba
529 R d1/d11/a1
529 R d1/d11/a1
530 $ hg update -C
530 $ hg update -C
531 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
531 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
532 $ rm -rf d3
532 $ rm -rf d3
533
533
534 transitive rename
534 transitive rename
535
535
536 $ hg rename d1/b d1/bb
536 $ hg rename d1/b d1/bb
537 $ hg rename d1/bb d1/bc
537 $ hg rename d1/bb d1/bc
538 $ hg status -C
538 $ hg status -C
539 A d1/bc
539 A d1/bc
540 d1/b
540 d1/b
541 R d1/b
541 R d1/b
542 $ hg update -C
542 $ hg update -C
543 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
543 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
544 $ rm d1/bc
544 $ rm d1/bc
545
545
546 transitive rename --after
546 transitive rename --after
547
547
548 $ hg rename d1/b d1/bb
548 $ hg rename d1/b d1/bb
549 $ mv d1/bb d1/bc
549 $ mv d1/bb d1/bc
550 $ hg rename --after d1/bb d1/bc
550 $ hg rename --after d1/bb d1/bc
551 $ hg status -C
551 $ hg status -C
552 A d1/bc
552 A d1/bc
553 d1/b
553 d1/b
554 R d1/b
554 R d1/b
555 $ hg update -C
555 $ hg update -C
556 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
556 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
557 $ rm d1/bc
557 $ rm d1/bc
558
558
559 $ echo "# idempotent renames (d1/b -> d1/bb followed by d1/bb -> d1/b)"
559 $ echo "# idempotent renames (d1/b -> d1/bb followed by d1/bb -> d1/b)"
560 # idempotent renames (d1/b -> d1/bb followed by d1/bb -> d1/b)
560 # idempotent renames (d1/b -> d1/bb followed by d1/bb -> d1/b)
561 $ hg rename d1/b d1/bb
561 $ hg rename d1/b d1/bb
562 $ echo "some stuff added to d1/bb" >> d1/bb
562 $ echo "some stuff added to d1/bb" >> d1/bb
563 $ hg rename d1/bb d1/b
563 $ hg rename d1/bb d1/b
564 $ hg status -C
564 $ hg status -C
565 M d1/b
565 M d1/b
566 $ hg update -C
566 $ hg update -C
567 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
567 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
568
568
569 overwriting with renames (issue1959)
569 overwriting with renames (issue1959)
570
570
571 $ hg rename d1/a d1/c
571 $ hg rename d1/a d1/c
572 $ hg rename d1/b d1/a
572 $ hg rename d1/b d1/a
573 $ hg status -C
573 $ hg status -C
574 A d1/a
574 M d1/a
575 d1/b
575 d1/b
576 A d1/c
576 A d1/c
577 d1/a
577 d1/a
578 R d1/b
578 R d1/b
579 $ hg diff --git
579 $ hg diff --git
580 diff --git a/d1/b b/d1/a
580 diff --git a/d1/a b/d1/a
581 rename from d1/b
581 --- a/d1/a
582 rename to d1/a
582 +++ b/d1/a
583 @@ -1,1 +1,1 @@
584 -d1/a
585 +d1/b
586 diff --git a/d1/b b/d1/b
587 deleted file mode 100644
588 --- a/d1/b
589 +++ /dev/null
590 @@ -1,1 +0,0 @@
591 -d1/b
583 diff --git a/d1/a b/d1/c
592 diff --git a/d1/a b/d1/c
584 copy from d1/a
593 copy from d1/a
585 copy to d1/c
594 copy to d1/c
586 $ hg update -C
595 $ hg update -C
587 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
596 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
588 $ rm d1/c # The file was marked as added, so 'hg update' action was 'forget'
597 $ rm d1/c # The file was marked as added, so 'hg update' action was 'forget'
589
598
590 check illegal path components
599 check illegal path components
591
600
592 $ hg rename d1/d11/a1 .hg/foo
601 $ hg rename d1/d11/a1 .hg/foo
593 abort: path contains illegal component: .hg/foo (glob)
602 abort: path contains illegal component: .hg/foo (glob)
594 [255]
603 [255]
595 $ hg status -C
604 $ hg status -C
596 $ hg rename d1/d11/a1 ../foo
605 $ hg rename d1/d11/a1 ../foo
597 abort: ../foo not under root '$TESTTMP'
606 abort: ../foo not under root '$TESTTMP'
598 [255]
607 [255]
599 $ hg status -C
608 $ hg status -C
600
609
601 $ mv d1/d11/a1 .hg/foo
610 $ mv d1/d11/a1 .hg/foo
602 $ hg rename --after d1/d11/a1 .hg/foo
611 $ hg rename --after d1/d11/a1 .hg/foo
603 abort: path contains illegal component: .hg/foo (glob)
612 abort: path contains illegal component: .hg/foo (glob)
604 [255]
613 [255]
605 $ hg status -C
614 $ hg status -C
606 ! d1/d11/a1
615 ! d1/d11/a1
607 $ hg update -C
616 $ hg update -C
608 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
617 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
609 $ rm .hg/foo
618 $ rm .hg/foo
610
619
611 $ hg rename d1/d11/a1 .hg
620 $ hg rename d1/d11/a1 .hg
612 abort: path contains illegal component: .hg/a1 (glob)
621 abort: path contains illegal component: .hg/a1 (glob)
613 [255]
622 [255]
614 $ hg status -C
623 $ hg status -C
615 $ hg rename d1/d11/a1 ..
624 $ hg rename d1/d11/a1 ..
616 abort: ../a1 not under root '$TESTTMP' (glob)
625 abort: ../a1 not under root '$TESTTMP' (glob)
617 [255]
626 [255]
618 $ hg status -C
627 $ hg status -C
619
628
620 $ mv d1/d11/a1 .hg
629 $ mv d1/d11/a1 .hg
621 $ hg rename --after d1/d11/a1 .hg
630 $ hg rename --after d1/d11/a1 .hg
622 abort: path contains illegal component: .hg/a1 (glob)
631 abort: path contains illegal component: .hg/a1 (glob)
623 [255]
632 [255]
624 $ hg status -C
633 $ hg status -C
625 ! d1/d11/a1
634 ! d1/d11/a1
626 $ hg update -C
635 $ hg update -C
627 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
636 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
628 $ rm .hg/a1
637 $ rm .hg/a1
629
638
630 $ (cd d1/d11; hg rename ../../d2/b ../../.hg/foo)
639 $ (cd d1/d11; hg rename ../../d2/b ../../.hg/foo)
631 abort: path contains illegal component: .hg/foo (glob)
640 abort: path contains illegal component: .hg/foo (glob)
632 [255]
641 [255]
633 $ hg status -C
642 $ hg status -C
634 $ (cd d1/d11; hg rename ../../d2/b ../../../foo)
643 $ (cd d1/d11; hg rename ../../d2/b ../../../foo)
635 abort: ../../../foo not under root '$TESTTMP'
644 abort: ../../../foo not under root '$TESTTMP'
636 [255]
645 [255]
637 $ hg status -C
646 $ hg status -C
638
647
@@ -1,392 +1,438 b''
1 $ hg init repo1
1 $ hg init repo1
2 $ cd repo1
2 $ cd repo1
3 $ mkdir a b a/1 b/1 b/2
3 $ mkdir a b a/1 b/1 b/2
4 $ touch in_root a/in_a b/in_b a/1/in_a_1 b/1/in_b_1 b/2/in_b_2
4 $ touch in_root a/in_a b/in_b a/1/in_a_1 b/1/in_b_1 b/2/in_b_2
5
5
6 hg status in repo root:
6 hg status in repo root:
7
7
8 $ hg status
8 $ hg status
9 ? a/1/in_a_1
9 ? a/1/in_a_1
10 ? a/in_a
10 ? a/in_a
11 ? b/1/in_b_1
11 ? b/1/in_b_1
12 ? b/2/in_b_2
12 ? b/2/in_b_2
13 ? b/in_b
13 ? b/in_b
14 ? in_root
14 ? in_root
15
15
16 hg status . in repo root:
16 hg status . in repo root:
17
17
18 $ hg status .
18 $ hg status .
19 ? a/1/in_a_1
19 ? a/1/in_a_1
20 ? a/in_a
20 ? a/in_a
21 ? b/1/in_b_1
21 ? b/1/in_b_1
22 ? b/2/in_b_2
22 ? b/2/in_b_2
23 ? b/in_b
23 ? b/in_b
24 ? in_root
24 ? in_root
25
25
26 $ hg status --cwd a
26 $ hg status --cwd a
27 ? a/1/in_a_1
27 ? a/1/in_a_1
28 ? a/in_a
28 ? a/in_a
29 ? b/1/in_b_1
29 ? b/1/in_b_1
30 ? b/2/in_b_2
30 ? b/2/in_b_2
31 ? b/in_b
31 ? b/in_b
32 ? in_root
32 ? in_root
33 $ hg status --cwd a .
33 $ hg status --cwd a .
34 ? 1/in_a_1
34 ? 1/in_a_1
35 ? in_a
35 ? in_a
36 $ hg status --cwd a ..
36 $ hg status --cwd a ..
37 ? 1/in_a_1
37 ? 1/in_a_1
38 ? in_a
38 ? in_a
39 ? ../b/1/in_b_1
39 ? ../b/1/in_b_1
40 ? ../b/2/in_b_2
40 ? ../b/2/in_b_2
41 ? ../b/in_b
41 ? ../b/in_b
42 ? ../in_root
42 ? ../in_root
43
43
44 $ hg status --cwd b
44 $ hg status --cwd b
45 ? a/1/in_a_1
45 ? a/1/in_a_1
46 ? a/in_a
46 ? a/in_a
47 ? b/1/in_b_1
47 ? b/1/in_b_1
48 ? b/2/in_b_2
48 ? b/2/in_b_2
49 ? b/in_b
49 ? b/in_b
50 ? in_root
50 ? in_root
51 $ hg status --cwd b .
51 $ hg status --cwd b .
52 ? 1/in_b_1
52 ? 1/in_b_1
53 ? 2/in_b_2
53 ? 2/in_b_2
54 ? in_b
54 ? in_b
55 $ hg status --cwd b ..
55 $ hg status --cwd b ..
56 ? ../a/1/in_a_1
56 ? ../a/1/in_a_1
57 ? ../a/in_a
57 ? ../a/in_a
58 ? 1/in_b_1
58 ? 1/in_b_1
59 ? 2/in_b_2
59 ? 2/in_b_2
60 ? in_b
60 ? in_b
61 ? ../in_root
61 ? ../in_root
62
62
63 $ hg status --cwd a/1
63 $ hg status --cwd a/1
64 ? a/1/in_a_1
64 ? a/1/in_a_1
65 ? a/in_a
65 ? a/in_a
66 ? b/1/in_b_1
66 ? b/1/in_b_1
67 ? b/2/in_b_2
67 ? b/2/in_b_2
68 ? b/in_b
68 ? b/in_b
69 ? in_root
69 ? in_root
70 $ hg status --cwd a/1 .
70 $ hg status --cwd a/1 .
71 ? in_a_1
71 ? in_a_1
72 $ hg status --cwd a/1 ..
72 $ hg status --cwd a/1 ..
73 ? in_a_1
73 ? in_a_1
74 ? ../in_a
74 ? ../in_a
75
75
76 $ hg status --cwd b/1
76 $ hg status --cwd b/1
77 ? a/1/in_a_1
77 ? a/1/in_a_1
78 ? a/in_a
78 ? a/in_a
79 ? b/1/in_b_1
79 ? b/1/in_b_1
80 ? b/2/in_b_2
80 ? b/2/in_b_2
81 ? b/in_b
81 ? b/in_b
82 ? in_root
82 ? in_root
83 $ hg status --cwd b/1 .
83 $ hg status --cwd b/1 .
84 ? in_b_1
84 ? in_b_1
85 $ hg status --cwd b/1 ..
85 $ hg status --cwd b/1 ..
86 ? in_b_1
86 ? in_b_1
87 ? ../2/in_b_2
87 ? ../2/in_b_2
88 ? ../in_b
88 ? ../in_b
89
89
90 $ hg status --cwd b/2
90 $ hg status --cwd b/2
91 ? a/1/in_a_1
91 ? a/1/in_a_1
92 ? a/in_a
92 ? a/in_a
93 ? b/1/in_b_1
93 ? b/1/in_b_1
94 ? b/2/in_b_2
94 ? b/2/in_b_2
95 ? b/in_b
95 ? b/in_b
96 ? in_root
96 ? in_root
97 $ hg status --cwd b/2 .
97 $ hg status --cwd b/2 .
98 ? in_b_2
98 ? in_b_2
99 $ hg status --cwd b/2 ..
99 $ hg status --cwd b/2 ..
100 ? ../1/in_b_1
100 ? ../1/in_b_1
101 ? in_b_2
101 ? in_b_2
102 ? ../in_b
102 ? ../in_b
103
103
104 combining patterns with root and patterns without a root works
104 combining patterns with root and patterns without a root works
105
105
106 $ hg st a/in_a re:.*b$
106 $ hg st a/in_a re:.*b$
107 ? a/in_a
107 ? a/in_a
108 ? b/in_b
108 ? b/in_b
109
109
110 $ cd ..
110 $ cd ..
111
111
112 $ hg init repo2
112 $ hg init repo2
113 $ cd repo2
113 $ cd repo2
114 $ touch modified removed deleted ignored
114 $ touch modified removed deleted ignored
115 $ echo "^ignored$" > .hgignore
115 $ echo "^ignored$" > .hgignore
116 $ hg ci -A -m 'initial checkin'
116 $ hg ci -A -m 'initial checkin'
117 adding .hgignore
117 adding .hgignore
118 adding deleted
118 adding deleted
119 adding modified
119 adding modified
120 adding removed
120 adding removed
121 $ touch modified added unknown ignored
121 $ touch modified added unknown ignored
122 $ hg add added
122 $ hg add added
123 $ hg remove removed
123 $ hg remove removed
124 $ rm deleted
124 $ rm deleted
125
125
126 hg status:
126 hg status:
127
127
128 $ hg status
128 $ hg status
129 A added
129 A added
130 R removed
130 R removed
131 ! deleted
131 ! deleted
132 ? unknown
132 ? unknown
133
133
134 hg status modified added removed deleted unknown never-existed ignored:
134 hg status modified added removed deleted unknown never-existed ignored:
135
135
136 $ hg status modified added removed deleted unknown never-existed ignored
136 $ hg status modified added removed deleted unknown never-existed ignored
137 never-existed: * (glob)
137 never-existed: * (glob)
138 A added
138 A added
139 R removed
139 R removed
140 ! deleted
140 ! deleted
141 ? unknown
141 ? unknown
142
142
143 $ hg copy modified copied
143 $ hg copy modified copied
144
144
145 hg status -C:
145 hg status -C:
146
146
147 $ hg status -C
147 $ hg status -C
148 A added
148 A added
149 A copied
149 A copied
150 modified
150 modified
151 R removed
151 R removed
152 ! deleted
152 ! deleted
153 ? unknown
153 ? unknown
154
154
155 hg status -A:
155 hg status -A:
156
156
157 $ hg status -A
157 $ hg status -A
158 A added
158 A added
159 A copied
159 A copied
160 modified
160 modified
161 R removed
161 R removed
162 ! deleted
162 ! deleted
163 ? unknown
163 ? unknown
164 I ignored
164 I ignored
165 C .hgignore
165 C .hgignore
166 C modified
166 C modified
167
167
168 $ hg status -A -Tjson
168 $ hg status -A -Tjson
169 [
169 [
170 {
170 {
171 "path": "added",
171 "path": "added",
172 "status": "A"
172 "status": "A"
173 },
173 },
174 {
174 {
175 "copy": "modified",
175 "copy": "modified",
176 "path": "copied",
176 "path": "copied",
177 "status": "A"
177 "status": "A"
178 },
178 },
179 {
179 {
180 "path": "removed",
180 "path": "removed",
181 "status": "R"
181 "status": "R"
182 },
182 },
183 {
183 {
184 "path": "deleted",
184 "path": "deleted",
185 "status": "!"
185 "status": "!"
186 },
186 },
187 {
187 {
188 "path": "unknown",
188 "path": "unknown",
189 "status": "?"
189 "status": "?"
190 },
190 },
191 {
191 {
192 "path": "ignored",
192 "path": "ignored",
193 "status": "I"
193 "status": "I"
194 },
194 },
195 {
195 {
196 "path": ".hgignore",
196 "path": ".hgignore",
197 "status": "C"
197 "status": "C"
198 },
198 },
199 {
199 {
200 "path": "modified",
200 "path": "modified",
201 "status": "C"
201 "status": "C"
202 }
202 }
203 ]
203 ]
204
204
205 $ hg status -A -Tpickle > pickle
205 $ hg status -A -Tpickle > pickle
206 >>> import pickle
206 >>> import pickle
207 >>> print sorted((x['status'], x['path']) for x in pickle.load(open("pickle")))
207 >>> print sorted((x['status'], x['path']) for x in pickle.load(open("pickle")))
208 [('!', 'deleted'), ('?', 'pickle'), ('?', 'unknown'), ('A', 'added'), ('A', 'copied'), ('C', '.hgignore'), ('C', 'modified'), ('I', 'ignored'), ('R', 'removed')]
208 [('!', 'deleted'), ('?', 'pickle'), ('?', 'unknown'), ('A', 'added'), ('A', 'copied'), ('C', '.hgignore'), ('C', 'modified'), ('I', 'ignored'), ('R', 'removed')]
209 $ rm pickle
209 $ rm pickle
210
210
211 $ echo "^ignoreddir$" > .hgignore
211 $ echo "^ignoreddir$" > .hgignore
212 $ mkdir ignoreddir
212 $ mkdir ignoreddir
213 $ touch ignoreddir/file
213 $ touch ignoreddir/file
214
214
215 hg status ignoreddir/file:
215 hg status ignoreddir/file:
216
216
217 $ hg status ignoreddir/file
217 $ hg status ignoreddir/file
218
218
219 hg status -i ignoreddir/file:
219 hg status -i ignoreddir/file:
220
220
221 $ hg status -i ignoreddir/file
221 $ hg status -i ignoreddir/file
222 I ignoreddir/file
222 I ignoreddir/file
223 $ cd ..
223 $ cd ..
224
224
225 Check 'status -q' and some combinations
225 Check 'status -q' and some combinations
226
226
227 $ hg init repo3
227 $ hg init repo3
228 $ cd repo3
228 $ cd repo3
229 $ touch modified removed deleted ignored
229 $ touch modified removed deleted ignored
230 $ echo "^ignored$" > .hgignore
230 $ echo "^ignored$" > .hgignore
231 $ hg commit -A -m 'initial checkin'
231 $ hg commit -A -m 'initial checkin'
232 adding .hgignore
232 adding .hgignore
233 adding deleted
233 adding deleted
234 adding modified
234 adding modified
235 adding removed
235 adding removed
236 $ touch added unknown ignored
236 $ touch added unknown ignored
237 $ hg add added
237 $ hg add added
238 $ echo "test" >> modified
238 $ echo "test" >> modified
239 $ hg remove removed
239 $ hg remove removed
240 $ rm deleted
240 $ rm deleted
241 $ hg copy modified copied
241 $ hg copy modified copied
242
242
243 Run status with 2 different flags.
243 Run status with 2 different flags.
244 Check if result is the same or different.
244 Check if result is the same or different.
245 If result is not as expected, raise error
245 If result is not as expected, raise error
246
246
247 $ assert() {
247 $ assert() {
248 > hg status $1 > ../a
248 > hg status $1 > ../a
249 > hg status $2 > ../b
249 > hg status $2 > ../b
250 > if diff ../a ../b > /dev/null; then
250 > if diff ../a ../b > /dev/null; then
251 > out=0
251 > out=0
252 > else
252 > else
253 > out=1
253 > out=1
254 > fi
254 > fi
255 > if [ $3 -eq 0 ]; then
255 > if [ $3 -eq 0 ]; then
256 > df="same"
256 > df="same"
257 > else
257 > else
258 > df="different"
258 > df="different"
259 > fi
259 > fi
260 > if [ $out -ne $3 ]; then
260 > if [ $out -ne $3 ]; then
261 > echo "Error on $1 and $2, should be $df."
261 > echo "Error on $1 and $2, should be $df."
262 > fi
262 > fi
263 > }
263 > }
264
264
265 Assert flag1 flag2 [0-same | 1-different]
265 Assert flag1 flag2 [0-same | 1-different]
266
266
267 $ assert "-q" "-mard" 0
267 $ assert "-q" "-mard" 0
268 $ assert "-A" "-marduicC" 0
268 $ assert "-A" "-marduicC" 0
269 $ assert "-qA" "-mardcC" 0
269 $ assert "-qA" "-mardcC" 0
270 $ assert "-qAui" "-A" 0
270 $ assert "-qAui" "-A" 0
271 $ assert "-qAu" "-marducC" 0
271 $ assert "-qAu" "-marducC" 0
272 $ assert "-qAi" "-mardicC" 0
272 $ assert "-qAi" "-mardicC" 0
273 $ assert "-qu" "-u" 0
273 $ assert "-qu" "-u" 0
274 $ assert "-q" "-u" 1
274 $ assert "-q" "-u" 1
275 $ assert "-m" "-a" 1
275 $ assert "-m" "-a" 1
276 $ assert "-r" "-d" 1
276 $ assert "-r" "-d" 1
277 $ cd ..
277 $ cd ..
278
278
279 $ hg init repo4
279 $ hg init repo4
280 $ cd repo4
280 $ cd repo4
281 $ touch modified removed deleted
281 $ touch modified removed deleted
282 $ hg ci -q -A -m 'initial checkin'
282 $ hg ci -q -A -m 'initial checkin'
283 $ touch added unknown
283 $ touch added unknown
284 $ hg add added
284 $ hg add added
285 $ hg remove removed
285 $ hg remove removed
286 $ rm deleted
286 $ rm deleted
287 $ echo x > modified
287 $ echo x > modified
288 $ hg copy modified copied
288 $ hg copy modified copied
289 $ hg ci -m 'test checkin' -d "1000001 0"
289 $ hg ci -m 'test checkin' -d "1000001 0"
290 $ rm *
290 $ rm *
291 $ touch unrelated
291 $ touch unrelated
292 $ hg ci -q -A -m 'unrelated checkin' -d "1000002 0"
292 $ hg ci -q -A -m 'unrelated checkin' -d "1000002 0"
293
293
294 hg status --change 1:
294 hg status --change 1:
295
295
296 $ hg status --change 1
296 $ hg status --change 1
297 M modified
297 M modified
298 A added
298 A added
299 A copied
299 A copied
300 R removed
300 R removed
301
301
302 hg status --change 1 unrelated:
302 hg status --change 1 unrelated:
303
303
304 $ hg status --change 1 unrelated
304 $ hg status --change 1 unrelated
305
305
306 hg status -C --change 1 added modified copied removed deleted:
306 hg status -C --change 1 added modified copied removed deleted:
307
307
308 $ hg status -C --change 1 added modified copied removed deleted
308 $ hg status -C --change 1 added modified copied removed deleted
309 M modified
309 M modified
310 A added
310 A added
311 A copied
311 A copied
312 modified
312 modified
313 R removed
313 R removed
314
314
315 hg status -A --change 1 and revset:
315 hg status -A --change 1 and revset:
316
316
317 $ hg status -A --change '1|1'
317 $ hg status -A --change '1|1'
318 M modified
318 M modified
319 A added
319 A added
320 A copied
320 A copied
321 modified
321 modified
322 R removed
322 R removed
323 C deleted
323 C deleted
324
324
325 $ cd ..
325 $ cd ..
326
326
327 hg status of binary file starting with '\1\n', a separator for metadata:
327 hg status of binary file starting with '\1\n', a separator for metadata:
328
328
329 $ hg init repo5
329 $ hg init repo5
330 $ cd repo5
330 $ cd repo5
331 >>> open("010a", "wb").write("\1\nfoo")
331 >>> open("010a", "wb").write("\1\nfoo")
332 $ hg ci -q -A -m 'initial checkin'
332 $ hg ci -q -A -m 'initial checkin'
333 $ hg status -A
333 $ hg status -A
334 C 010a
334 C 010a
335
335
336 >>> open("010a", "wb").write("\1\nbar")
336 >>> open("010a", "wb").write("\1\nbar")
337 $ hg status -A
337 $ hg status -A
338 M 010a
338 M 010a
339 $ hg ci -q -m 'modify 010a'
339 $ hg ci -q -m 'modify 010a'
340 $ hg status -A --rev 0:1
340 $ hg status -A --rev 0:1
341 M 010a
341 M 010a
342
342
343 $ touch empty
343 $ touch empty
344 $ hg ci -q -A -m 'add another file'
344 $ hg ci -q -A -m 'add another file'
345 $ hg status -A --rev 1:2 010a
345 $ hg status -A --rev 1:2 010a
346 C 010a
346 C 010a
347
347
348 $ cd ..
348 $ cd ..
349
349
350 test "hg status" with "directory pattern" which matches against files
350 test "hg status" with "directory pattern" which matches against files
351 only known on target revision.
351 only known on target revision.
352
352
353 $ hg init repo6
353 $ hg init repo6
354 $ cd repo6
354 $ cd repo6
355
355
356 $ echo a > a.txt
356 $ echo a > a.txt
357 $ hg add a.txt
357 $ hg add a.txt
358 $ hg commit -m '#0'
358 $ hg commit -m '#0'
359 $ mkdir -p 1/2/3/4/5
359 $ mkdir -p 1/2/3/4/5
360 $ echo b > 1/2/3/4/5/b.txt
360 $ echo b > 1/2/3/4/5/b.txt
361 $ hg add 1/2/3/4/5/b.txt
361 $ hg add 1/2/3/4/5/b.txt
362 $ hg commit -m '#1'
362 $ hg commit -m '#1'
363
363
364 $ hg update -C 0 > /dev/null
364 $ hg update -C 0 > /dev/null
365 $ hg status -A
365 $ hg status -A
366 C a.txt
366 C a.txt
367
367
368 the directory matching against specified pattern should be removed,
368 the directory matching against specified pattern should be removed,
369 because directory existence prevents 'dirstate.walk()' from showing
369 because directory existence prevents 'dirstate.walk()' from showing
370 warning message about such pattern.
370 warning message about such pattern.
371
371
372 $ test ! -d 1
372 $ test ! -d 1
373 $ hg status -A --rev 1 1/2/3/4/5/b.txt
373 $ hg status -A --rev 1 1/2/3/4/5/b.txt
374 R 1/2/3/4/5/b.txt
374 R 1/2/3/4/5/b.txt
375 $ hg status -A --rev 1 1/2/3/4/5
375 $ hg status -A --rev 1 1/2/3/4/5
376 R 1/2/3/4/5/b.txt
376 R 1/2/3/4/5/b.txt
377 $ hg status -A --rev 1 1/2/3
377 $ hg status -A --rev 1 1/2/3
378 R 1/2/3/4/5/b.txt
378 R 1/2/3/4/5/b.txt
379 $ hg status -A --rev 1 1
379 $ hg status -A --rev 1 1
380 R 1/2/3/4/5/b.txt
380 R 1/2/3/4/5/b.txt
381
381
382 $ hg status --config ui.formatdebug=True --rev 1 1
382 $ hg status --config ui.formatdebug=True --rev 1 1
383 status = [
383 status = [
384 {*'path': '1/2/3/4/5/b.txt'*}, (glob)
384 {*'path': '1/2/3/4/5/b.txt'*}, (glob)
385 ]
385 ]
386
386
387 #if windows
387 #if windows
388 $ hg --config ui.slash=false status -A --rev 1 1
388 $ hg --config ui.slash=false status -A --rev 1 1
389 R 1\2\3\4\5\b.txt
389 R 1\2\3\4\5\b.txt
390 #endif
390 #endif
391
391
392 $ cd ..
392 $ cd ..
393
394 Status after move overwriting a file (issue4458)
395 =================================================
396
397
398 $ hg init issue4458
399 $ cd issue4458
400 $ echo a > a
401 $ echo b > b
402 $ hg commit -Am base
403 adding a
404 adding b
405
406
407 with --force
408
409 $ hg mv b --force a
410 $ hg st --copies
411 M a
412 b
413 R b
414 $ hg revert --all
415 reverting a
416 undeleting b
417 $ rm *.orig
418
419 without force
420
421 $ hg rm a
422 $ hg st --copies
423 R a
424 $ hg mv b a
425 $ hg st --copies
426 M a
427 b
428 R b
429
430 Other "bug" highlight, the revision status does not report the copy information.
431 This is buggy behavior.
432
433 $ hg commit -m 'blah'
434 $ hg st --copies --change .
435 M a
436 R b
437
438 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now