##// END OF EJS Templates
scmutil: generalize message to make it more i18n-friendly
Wagner Bruna -
r17850:71c1513f stable
parent child Browse files
Show More
@@ -1,962 +1,961 b''
1 # scmutil.py - Mercurial core utility functions
1 # scmutil.py - Mercurial core utility functions
2 #
2 #
3 # Copyright Matt Mackall <mpm@selenic.com>
3 # Copyright 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 i18n import _
8 from i18n import _
9 import util, error, osutil, revset, similar, encoding, phases
9 import util, error, osutil, revset, similar, encoding, phases
10 import match as matchmod
10 import match as matchmod
11 import os, errno, re, stat, sys, glob
11 import os, errno, re, stat, sys, glob
12
12
13 def nochangesfound(ui, repo, excluded=None):
13 def nochangesfound(ui, repo, excluded=None):
14 '''Report no changes for push/pull, excluded is None or a list of
14 '''Report no changes for push/pull, excluded is None or a list of
15 nodes excluded from the push/pull.
15 nodes excluded from the push/pull.
16 '''
16 '''
17 secretlist = []
17 secretlist = []
18 if excluded:
18 if excluded:
19 for n in excluded:
19 for n in excluded:
20 ctx = repo[n]
20 ctx = repo[n]
21 if ctx.phase() >= phases.secret and not ctx.extinct():
21 if ctx.phase() >= phases.secret and not ctx.extinct():
22 secretlist.append(n)
22 secretlist.append(n)
23
23
24 if secretlist:
24 if secretlist:
25 ui.status(_("no changes found (ignored %d secret changesets)\n")
25 ui.status(_("no changes found (ignored %d secret changesets)\n")
26 % len(secretlist))
26 % len(secretlist))
27 else:
27 else:
28 ui.status(_("no changes found\n"))
28 ui.status(_("no changes found\n"))
29
29
30 def checknewlabel(repo, lbl, kind):
30 def checknewlabel(repo, lbl, kind):
31 if lbl in ['tip', '.', 'null']:
31 if lbl in ['tip', '.', 'null']:
32 raise util.Abort(_("the name '%s' is reserved") % lbl)
32 raise util.Abort(_("the name '%s' is reserved") % lbl)
33 for c in (':', '\0', '\n', '\r'):
33 for c in (':', '\0', '\n', '\r'):
34 if c in lbl:
34 if c in lbl:
35 raise util.Abort(_("%r cannot be used in a %s name") %
35 raise util.Abort(_("%r cannot be used in a name") % c)
36 (c, kind))
37
36
38 def checkfilename(f):
37 def checkfilename(f):
39 '''Check that the filename f is an acceptable filename for a tracked file'''
38 '''Check that the filename f is an acceptable filename for a tracked file'''
40 if '\r' in f or '\n' in f:
39 if '\r' in f or '\n' in f:
41 raise util.Abort(_("'\\n' and '\\r' disallowed in filenames: %r") % f)
40 raise util.Abort(_("'\\n' and '\\r' disallowed in filenames: %r") % f)
42
41
43 def checkportable(ui, f):
42 def checkportable(ui, f):
44 '''Check if filename f is portable and warn or abort depending on config'''
43 '''Check if filename f is portable and warn or abort depending on config'''
45 checkfilename(f)
44 checkfilename(f)
46 abort, warn = checkportabilityalert(ui)
45 abort, warn = checkportabilityalert(ui)
47 if abort or warn:
46 if abort or warn:
48 msg = util.checkwinfilename(f)
47 msg = util.checkwinfilename(f)
49 if msg:
48 if msg:
50 msg = "%s: %r" % (msg, f)
49 msg = "%s: %r" % (msg, f)
51 if abort:
50 if abort:
52 raise util.Abort(msg)
51 raise util.Abort(msg)
53 ui.warn(_("warning: %s\n") % msg)
52 ui.warn(_("warning: %s\n") % msg)
54
53
55 def checkportabilityalert(ui):
54 def checkportabilityalert(ui):
56 '''check if the user's config requests nothing, a warning, or abort for
55 '''check if the user's config requests nothing, a warning, or abort for
57 non-portable filenames'''
56 non-portable filenames'''
58 val = ui.config('ui', 'portablefilenames', 'warn')
57 val = ui.config('ui', 'portablefilenames', 'warn')
59 lval = val.lower()
58 lval = val.lower()
60 bval = util.parsebool(val)
59 bval = util.parsebool(val)
61 abort = os.name == 'nt' or lval == 'abort'
60 abort = os.name == 'nt' or lval == 'abort'
62 warn = bval or lval == 'warn'
61 warn = bval or lval == 'warn'
63 if bval is None and not (warn or abort or lval == 'ignore'):
62 if bval is None and not (warn or abort or lval == 'ignore'):
64 raise error.ConfigError(
63 raise error.ConfigError(
65 _("ui.portablefilenames value is invalid ('%s')") % val)
64 _("ui.portablefilenames value is invalid ('%s')") % val)
66 return abort, warn
65 return abort, warn
67
66
68 class casecollisionauditor(object):
67 class casecollisionauditor(object):
69 def __init__(self, ui, abort, dirstate):
68 def __init__(self, ui, abort, dirstate):
70 self._ui = ui
69 self._ui = ui
71 self._abort = abort
70 self._abort = abort
72 allfiles = '\0'.join(dirstate._map)
71 allfiles = '\0'.join(dirstate._map)
73 self._loweredfiles = set(encoding.lower(allfiles).split('\0'))
72 self._loweredfiles = set(encoding.lower(allfiles).split('\0'))
74 self._dirstate = dirstate
73 self._dirstate = dirstate
75 # The purpose of _newfiles is so that we don't complain about
74 # The purpose of _newfiles is so that we don't complain about
76 # case collisions if someone were to call this object with the
75 # case collisions if someone were to call this object with the
77 # same filename twice.
76 # same filename twice.
78 self._newfiles = set()
77 self._newfiles = set()
79
78
80 def __call__(self, f):
79 def __call__(self, f):
81 fl = encoding.lower(f)
80 fl = encoding.lower(f)
82 if (fl in self._loweredfiles and f not in self._dirstate and
81 if (fl in self._loweredfiles and f not in self._dirstate and
83 f not in self._newfiles):
82 f not in self._newfiles):
84 msg = _('possible case-folding collision for %s') % f
83 msg = _('possible case-folding collision for %s') % f
85 if self._abort:
84 if self._abort:
86 raise util.Abort(msg)
85 raise util.Abort(msg)
87 self._ui.warn(_("warning: %s\n") % msg)
86 self._ui.warn(_("warning: %s\n") % msg)
88 self._loweredfiles.add(fl)
87 self._loweredfiles.add(fl)
89 self._newfiles.add(f)
88 self._newfiles.add(f)
90
89
91 class pathauditor(object):
90 class pathauditor(object):
92 '''ensure that a filesystem path contains no banned components.
91 '''ensure that a filesystem path contains no banned components.
93 the following properties of a path are checked:
92 the following properties of a path are checked:
94
93
95 - ends with a directory separator
94 - ends with a directory separator
96 - under top-level .hg
95 - under top-level .hg
97 - starts at the root of a windows drive
96 - starts at the root of a windows drive
98 - contains ".."
97 - contains ".."
99 - traverses a symlink (e.g. a/symlink_here/b)
98 - traverses a symlink (e.g. a/symlink_here/b)
100 - inside a nested repository (a callback can be used to approve
99 - inside a nested repository (a callback can be used to approve
101 some nested repositories, e.g., subrepositories)
100 some nested repositories, e.g., subrepositories)
102 '''
101 '''
103
102
104 def __init__(self, root, callback=None):
103 def __init__(self, root, callback=None):
105 self.audited = set()
104 self.audited = set()
106 self.auditeddir = set()
105 self.auditeddir = set()
107 self.root = root
106 self.root = root
108 self.callback = callback
107 self.callback = callback
109 if os.path.lexists(root) and not util.checkcase(root):
108 if os.path.lexists(root) and not util.checkcase(root):
110 self.normcase = util.normcase
109 self.normcase = util.normcase
111 else:
110 else:
112 self.normcase = lambda x: x
111 self.normcase = lambda x: x
113
112
114 def __call__(self, path):
113 def __call__(self, path):
115 '''Check the relative path.
114 '''Check the relative path.
116 path may contain a pattern (e.g. foodir/**.txt)'''
115 path may contain a pattern (e.g. foodir/**.txt)'''
117
116
118 path = util.localpath(path)
117 path = util.localpath(path)
119 normpath = self.normcase(path)
118 normpath = self.normcase(path)
120 if normpath in self.audited:
119 if normpath in self.audited:
121 return
120 return
122 # AIX ignores "/" at end of path, others raise EISDIR.
121 # AIX ignores "/" at end of path, others raise EISDIR.
123 if util.endswithsep(path):
122 if util.endswithsep(path):
124 raise util.Abort(_("path ends in directory separator: %s") % path)
123 raise util.Abort(_("path ends in directory separator: %s") % path)
125 parts = util.splitpath(path)
124 parts = util.splitpath(path)
126 if (os.path.splitdrive(path)[0]
125 if (os.path.splitdrive(path)[0]
127 or parts[0].lower() in ('.hg', '.hg.', '')
126 or parts[0].lower() in ('.hg', '.hg.', '')
128 or os.pardir in parts):
127 or os.pardir in parts):
129 raise util.Abort(_("path contains illegal component: %s") % path)
128 raise util.Abort(_("path contains illegal component: %s") % path)
130 if '.hg' in path.lower():
129 if '.hg' in path.lower():
131 lparts = [p.lower() for p in parts]
130 lparts = [p.lower() for p in parts]
132 for p in '.hg', '.hg.':
131 for p in '.hg', '.hg.':
133 if p in lparts[1:]:
132 if p in lparts[1:]:
134 pos = lparts.index(p)
133 pos = lparts.index(p)
135 base = os.path.join(*parts[:pos])
134 base = os.path.join(*parts[:pos])
136 raise util.Abort(_("path '%s' is inside nested repo %r")
135 raise util.Abort(_("path '%s' is inside nested repo %r")
137 % (path, base))
136 % (path, base))
138
137
139 normparts = util.splitpath(normpath)
138 normparts = util.splitpath(normpath)
140 assert len(parts) == len(normparts)
139 assert len(parts) == len(normparts)
141
140
142 parts.pop()
141 parts.pop()
143 normparts.pop()
142 normparts.pop()
144 prefixes = []
143 prefixes = []
145 while parts:
144 while parts:
146 prefix = os.sep.join(parts)
145 prefix = os.sep.join(parts)
147 normprefix = os.sep.join(normparts)
146 normprefix = os.sep.join(normparts)
148 if normprefix in self.auditeddir:
147 if normprefix in self.auditeddir:
149 break
148 break
150 curpath = os.path.join(self.root, prefix)
149 curpath = os.path.join(self.root, prefix)
151 try:
150 try:
152 st = os.lstat(curpath)
151 st = os.lstat(curpath)
153 except OSError, err:
152 except OSError, err:
154 # EINVAL can be raised as invalid path syntax under win32.
153 # EINVAL can be raised as invalid path syntax under win32.
155 # They must be ignored for patterns can be checked too.
154 # They must be ignored for patterns can be checked too.
156 if err.errno not in (errno.ENOENT, errno.ENOTDIR, errno.EINVAL):
155 if err.errno not in (errno.ENOENT, errno.ENOTDIR, errno.EINVAL):
157 raise
156 raise
158 else:
157 else:
159 if stat.S_ISLNK(st.st_mode):
158 if stat.S_ISLNK(st.st_mode):
160 raise util.Abort(
159 raise util.Abort(
161 _('path %r traverses symbolic link %r')
160 _('path %r traverses symbolic link %r')
162 % (path, prefix))
161 % (path, prefix))
163 elif (stat.S_ISDIR(st.st_mode) and
162 elif (stat.S_ISDIR(st.st_mode) and
164 os.path.isdir(os.path.join(curpath, '.hg'))):
163 os.path.isdir(os.path.join(curpath, '.hg'))):
165 if not self.callback or not self.callback(curpath):
164 if not self.callback or not self.callback(curpath):
166 raise util.Abort(_("path '%s' is inside nested "
165 raise util.Abort(_("path '%s' is inside nested "
167 "repo %r")
166 "repo %r")
168 % (path, prefix))
167 % (path, prefix))
169 prefixes.append(normprefix)
168 prefixes.append(normprefix)
170 parts.pop()
169 parts.pop()
171 normparts.pop()
170 normparts.pop()
172
171
173 self.audited.add(normpath)
172 self.audited.add(normpath)
174 # only add prefixes to the cache after checking everything: we don't
173 # only add prefixes to the cache after checking everything: we don't
175 # want to add "foo/bar/baz" before checking if there's a "foo/.hg"
174 # want to add "foo/bar/baz" before checking if there's a "foo/.hg"
176 self.auditeddir.update(prefixes)
175 self.auditeddir.update(prefixes)
177
176
178 class abstractvfs(object):
177 class abstractvfs(object):
179 """Abstract base class; cannot be instantiated"""
178 """Abstract base class; cannot be instantiated"""
180
179
181 def __init__(self, *args, **kwargs):
180 def __init__(self, *args, **kwargs):
182 '''Prevent instantiation; don't call this from subclasses.'''
181 '''Prevent instantiation; don't call this from subclasses.'''
183 raise NotImplementedError('attempted instantiating ' + str(type(self)))
182 raise NotImplementedError('attempted instantiating ' + str(type(self)))
184
183
185 def tryread(self, path):
184 def tryread(self, path):
186 '''gracefully return an empty string for missing files'''
185 '''gracefully return an empty string for missing files'''
187 try:
186 try:
188 return self.read(path)
187 return self.read(path)
189 except IOError, inst:
188 except IOError, inst:
190 if inst.errno != errno.ENOENT:
189 if inst.errno != errno.ENOENT:
191 raise
190 raise
192 return ""
191 return ""
193
192
194 def read(self, path):
193 def read(self, path):
195 fp = self(path, 'rb')
194 fp = self(path, 'rb')
196 try:
195 try:
197 return fp.read()
196 return fp.read()
198 finally:
197 finally:
199 fp.close()
198 fp.close()
200
199
201 def write(self, path, data):
200 def write(self, path, data):
202 fp = self(path, 'wb')
201 fp = self(path, 'wb')
203 try:
202 try:
204 return fp.write(data)
203 return fp.write(data)
205 finally:
204 finally:
206 fp.close()
205 fp.close()
207
206
208 def append(self, path, data):
207 def append(self, path, data):
209 fp = self(path, 'ab')
208 fp = self(path, 'ab')
210 try:
209 try:
211 return fp.write(data)
210 return fp.write(data)
212 finally:
211 finally:
213 fp.close()
212 fp.close()
214
213
215 def exists(self, path=None):
214 def exists(self, path=None):
216 return os.path.exists(self.join(path))
215 return os.path.exists(self.join(path))
217
216
218 def isdir(self, path=None):
217 def isdir(self, path=None):
219 return os.path.isdir(self.join(path))
218 return os.path.isdir(self.join(path))
220
219
221 def makedir(self, path=None, notindexed=True):
220 def makedir(self, path=None, notindexed=True):
222 return util.makedir(self.join(path), notindexed)
221 return util.makedir(self.join(path), notindexed)
223
222
224 def makedirs(self, path=None, mode=None):
223 def makedirs(self, path=None, mode=None):
225 return util.makedirs(self.join(path), mode)
224 return util.makedirs(self.join(path), mode)
226
225
227 def mkdir(self, path=None):
226 def mkdir(self, path=None):
228 return os.mkdir(self.join(path))
227 return os.mkdir(self.join(path))
229
228
230 def readdir(self, path=None, stat=None, skip=None):
229 def readdir(self, path=None, stat=None, skip=None):
231 return osutil.listdir(self.join(path), stat, skip)
230 return osutil.listdir(self.join(path), stat, skip)
232
231
233 def stat(self, path=None):
232 def stat(self, path=None):
234 return os.stat(self.join(path))
233 return os.stat(self.join(path))
235
234
236 class vfs(abstractvfs):
235 class vfs(abstractvfs):
237 '''Operate files relative to a base directory
236 '''Operate files relative to a base directory
238
237
239 This class is used to hide the details of COW semantics and
238 This class is used to hide the details of COW semantics and
240 remote file access from higher level code.
239 remote file access from higher level code.
241 '''
240 '''
242 def __init__(self, base, audit=True, expand=False):
241 def __init__(self, base, audit=True, expand=False):
243 if expand:
242 if expand:
244 base = os.path.realpath(util.expandpath(base))
243 base = os.path.realpath(util.expandpath(base))
245 self.base = base
244 self.base = base
246 self._setmustaudit(audit)
245 self._setmustaudit(audit)
247 self.createmode = None
246 self.createmode = None
248 self._trustnlink = None
247 self._trustnlink = None
249
248
250 def _getmustaudit(self):
249 def _getmustaudit(self):
251 return self._audit
250 return self._audit
252
251
253 def _setmustaudit(self, onoff):
252 def _setmustaudit(self, onoff):
254 self._audit = onoff
253 self._audit = onoff
255 if onoff:
254 if onoff:
256 self.auditor = pathauditor(self.base)
255 self.auditor = pathauditor(self.base)
257 else:
256 else:
258 self.auditor = util.always
257 self.auditor = util.always
259
258
260 mustaudit = property(_getmustaudit, _setmustaudit)
259 mustaudit = property(_getmustaudit, _setmustaudit)
261
260
262 @util.propertycache
261 @util.propertycache
263 def _cansymlink(self):
262 def _cansymlink(self):
264 return util.checklink(self.base)
263 return util.checklink(self.base)
265
264
266 def _fixfilemode(self, name):
265 def _fixfilemode(self, name):
267 if self.createmode is None:
266 if self.createmode is None:
268 return
267 return
269 os.chmod(name, self.createmode & 0666)
268 os.chmod(name, self.createmode & 0666)
270
269
271 def __call__(self, path, mode="r", text=False, atomictemp=False):
270 def __call__(self, path, mode="r", text=False, atomictemp=False):
272 if self._audit:
271 if self._audit:
273 r = util.checkosfilename(path)
272 r = util.checkosfilename(path)
274 if r:
273 if r:
275 raise util.Abort("%s: %r" % (r, path))
274 raise util.Abort("%s: %r" % (r, path))
276 self.auditor(path)
275 self.auditor(path)
277 f = self.join(path)
276 f = self.join(path)
278
277
279 if not text and "b" not in mode:
278 if not text and "b" not in mode:
280 mode += "b" # for that other OS
279 mode += "b" # for that other OS
281
280
282 nlink = -1
281 nlink = -1
283 dirname, basename = util.split(f)
282 dirname, basename = util.split(f)
284 # If basename is empty, then the path is malformed because it points
283 # If basename is empty, then the path is malformed because it points
285 # to a directory. Let the posixfile() call below raise IOError.
284 # to a directory. Let the posixfile() call below raise IOError.
286 if basename and mode not in ('r', 'rb'):
285 if basename and mode not in ('r', 'rb'):
287 if atomictemp:
286 if atomictemp:
288 if not os.path.isdir(dirname):
287 if not os.path.isdir(dirname):
289 util.makedirs(dirname, self.createmode)
288 util.makedirs(dirname, self.createmode)
290 return util.atomictempfile(f, mode, self.createmode)
289 return util.atomictempfile(f, mode, self.createmode)
291 try:
290 try:
292 if 'w' in mode:
291 if 'w' in mode:
293 util.unlink(f)
292 util.unlink(f)
294 nlink = 0
293 nlink = 0
295 else:
294 else:
296 # nlinks() may behave differently for files on Windows
295 # nlinks() may behave differently for files on Windows
297 # shares if the file is open.
296 # shares if the file is open.
298 fd = util.posixfile(f)
297 fd = util.posixfile(f)
299 nlink = util.nlinks(f)
298 nlink = util.nlinks(f)
300 if nlink < 1:
299 if nlink < 1:
301 nlink = 2 # force mktempcopy (issue1922)
300 nlink = 2 # force mktempcopy (issue1922)
302 fd.close()
301 fd.close()
303 except (OSError, IOError), e:
302 except (OSError, IOError), e:
304 if e.errno != errno.ENOENT:
303 if e.errno != errno.ENOENT:
305 raise
304 raise
306 nlink = 0
305 nlink = 0
307 if not os.path.isdir(dirname):
306 if not os.path.isdir(dirname):
308 util.makedirs(dirname, self.createmode)
307 util.makedirs(dirname, self.createmode)
309 if nlink > 0:
308 if nlink > 0:
310 if self._trustnlink is None:
309 if self._trustnlink is None:
311 self._trustnlink = nlink > 1 or util.checknlink(f)
310 self._trustnlink = nlink > 1 or util.checknlink(f)
312 if nlink > 1 or not self._trustnlink:
311 if nlink > 1 or not self._trustnlink:
313 util.rename(util.mktempcopy(f), f)
312 util.rename(util.mktempcopy(f), f)
314 fp = util.posixfile(f, mode)
313 fp = util.posixfile(f, mode)
315 if nlink == 0:
314 if nlink == 0:
316 self._fixfilemode(f)
315 self._fixfilemode(f)
317 return fp
316 return fp
318
317
319 def symlink(self, src, dst):
318 def symlink(self, src, dst):
320 self.auditor(dst)
319 self.auditor(dst)
321 linkname = self.join(dst)
320 linkname = self.join(dst)
322 try:
321 try:
323 os.unlink(linkname)
322 os.unlink(linkname)
324 except OSError:
323 except OSError:
325 pass
324 pass
326
325
327 dirname = os.path.dirname(linkname)
326 dirname = os.path.dirname(linkname)
328 if not os.path.exists(dirname):
327 if not os.path.exists(dirname):
329 util.makedirs(dirname, self.createmode)
328 util.makedirs(dirname, self.createmode)
330
329
331 if self._cansymlink:
330 if self._cansymlink:
332 try:
331 try:
333 os.symlink(src, linkname)
332 os.symlink(src, linkname)
334 except OSError, err:
333 except OSError, err:
335 raise OSError(err.errno, _('could not symlink to %r: %s') %
334 raise OSError(err.errno, _('could not symlink to %r: %s') %
336 (src, err.strerror), linkname)
335 (src, err.strerror), linkname)
337 else:
336 else:
338 self.write(dst, src)
337 self.write(dst, src)
339
338
340 def audit(self, path):
339 def audit(self, path):
341 self.auditor(path)
340 self.auditor(path)
342
341
343 def join(self, path):
342 def join(self, path):
344 if path:
343 if path:
345 return os.path.join(self.base, path)
344 return os.path.join(self.base, path)
346 else:
345 else:
347 return self.base
346 return self.base
348
347
349 opener = vfs
348 opener = vfs
350
349
351 class auditvfs(object):
350 class auditvfs(object):
352 def __init__(self, vfs):
351 def __init__(self, vfs):
353 self.vfs = vfs
352 self.vfs = vfs
354
353
355 def _getmustaudit(self):
354 def _getmustaudit(self):
356 return self.vfs.mustaudit
355 return self.vfs.mustaudit
357
356
358 def _setmustaudit(self, onoff):
357 def _setmustaudit(self, onoff):
359 self.vfs.mustaudit = onoff
358 self.vfs.mustaudit = onoff
360
359
361 mustaudit = property(_getmustaudit, _setmustaudit)
360 mustaudit = property(_getmustaudit, _setmustaudit)
362
361
363 class filtervfs(abstractvfs, auditvfs):
362 class filtervfs(abstractvfs, auditvfs):
364 '''Wrapper vfs for filtering filenames with a function.'''
363 '''Wrapper vfs for filtering filenames with a function.'''
365
364
366 def __init__(self, vfs, filter):
365 def __init__(self, vfs, filter):
367 auditvfs.__init__(self, vfs)
366 auditvfs.__init__(self, vfs)
368 self._filter = filter
367 self._filter = filter
369
368
370 def __call__(self, path, *args, **kwargs):
369 def __call__(self, path, *args, **kwargs):
371 return self.vfs(self._filter(path), *args, **kwargs)
370 return self.vfs(self._filter(path), *args, **kwargs)
372
371
373 def join(self, path):
372 def join(self, path):
374 if path:
373 if path:
375 return self.vfs.join(self._filter(path))
374 return self.vfs.join(self._filter(path))
376 else:
375 else:
377 return self.vfs.join(path)
376 return self.vfs.join(path)
378
377
379 filteropener = filtervfs
378 filteropener = filtervfs
380
379
381 def canonpath(root, cwd, myname, auditor=None):
380 def canonpath(root, cwd, myname, auditor=None):
382 '''return the canonical path of myname, given cwd and root'''
381 '''return the canonical path of myname, given cwd and root'''
383 if util.endswithsep(root):
382 if util.endswithsep(root):
384 rootsep = root
383 rootsep = root
385 else:
384 else:
386 rootsep = root + os.sep
385 rootsep = root + os.sep
387 name = myname
386 name = myname
388 if not os.path.isabs(name):
387 if not os.path.isabs(name):
389 name = os.path.join(root, cwd, name)
388 name = os.path.join(root, cwd, name)
390 name = os.path.normpath(name)
389 name = os.path.normpath(name)
391 if auditor is None:
390 if auditor is None:
392 auditor = pathauditor(root)
391 auditor = pathauditor(root)
393 if name != rootsep and name.startswith(rootsep):
392 if name != rootsep and name.startswith(rootsep):
394 name = name[len(rootsep):]
393 name = name[len(rootsep):]
395 auditor(name)
394 auditor(name)
396 return util.pconvert(name)
395 return util.pconvert(name)
397 elif name == root:
396 elif name == root:
398 return ''
397 return ''
399 else:
398 else:
400 # Determine whether `name' is in the hierarchy at or beneath `root',
399 # Determine whether `name' is in the hierarchy at or beneath `root',
401 # by iterating name=dirname(name) until that causes no change (can't
400 # by iterating name=dirname(name) until that causes no change (can't
402 # check name == '/', because that doesn't work on windows). The list
401 # check name == '/', because that doesn't work on windows). The list
403 # `rel' holds the reversed list of components making up the relative
402 # `rel' holds the reversed list of components making up the relative
404 # file name we want.
403 # file name we want.
405 rel = []
404 rel = []
406 while True:
405 while True:
407 try:
406 try:
408 s = util.samefile(name, root)
407 s = util.samefile(name, root)
409 except OSError:
408 except OSError:
410 s = False
409 s = False
411 if s:
410 if s:
412 if not rel:
411 if not rel:
413 # name was actually the same as root (maybe a symlink)
412 # name was actually the same as root (maybe a symlink)
414 return ''
413 return ''
415 rel.reverse()
414 rel.reverse()
416 name = os.path.join(*rel)
415 name = os.path.join(*rel)
417 auditor(name)
416 auditor(name)
418 return util.pconvert(name)
417 return util.pconvert(name)
419 dirname, basename = util.split(name)
418 dirname, basename = util.split(name)
420 rel.append(basename)
419 rel.append(basename)
421 if dirname == name:
420 if dirname == name:
422 break
421 break
423 name = dirname
422 name = dirname
424
423
425 raise util.Abort('%s not under root' % myname)
424 raise util.Abort('%s not under root' % myname)
426
425
427 def walkrepos(path, followsym=False, seen_dirs=None, recurse=False):
426 def walkrepos(path, followsym=False, seen_dirs=None, recurse=False):
428 '''yield every hg repository under path, always recursively.
427 '''yield every hg repository under path, always recursively.
429 The recurse flag will only control recursion into repo working dirs'''
428 The recurse flag will only control recursion into repo working dirs'''
430 def errhandler(err):
429 def errhandler(err):
431 if err.filename == path:
430 if err.filename == path:
432 raise err
431 raise err
433 samestat = getattr(os.path, 'samestat', None)
432 samestat = getattr(os.path, 'samestat', None)
434 if followsym and samestat is not None:
433 if followsym and samestat is not None:
435 def adddir(dirlst, dirname):
434 def adddir(dirlst, dirname):
436 match = False
435 match = False
437 dirstat = os.stat(dirname)
436 dirstat = os.stat(dirname)
438 for lstdirstat in dirlst:
437 for lstdirstat in dirlst:
439 if samestat(dirstat, lstdirstat):
438 if samestat(dirstat, lstdirstat):
440 match = True
439 match = True
441 break
440 break
442 if not match:
441 if not match:
443 dirlst.append(dirstat)
442 dirlst.append(dirstat)
444 return not match
443 return not match
445 else:
444 else:
446 followsym = False
445 followsym = False
447
446
448 if (seen_dirs is None) and followsym:
447 if (seen_dirs is None) and followsym:
449 seen_dirs = []
448 seen_dirs = []
450 adddir(seen_dirs, path)
449 adddir(seen_dirs, path)
451 for root, dirs, files in os.walk(path, topdown=True, onerror=errhandler):
450 for root, dirs, files in os.walk(path, topdown=True, onerror=errhandler):
452 dirs.sort()
451 dirs.sort()
453 if '.hg' in dirs:
452 if '.hg' in dirs:
454 yield root # found a repository
453 yield root # found a repository
455 qroot = os.path.join(root, '.hg', 'patches')
454 qroot = os.path.join(root, '.hg', 'patches')
456 if os.path.isdir(os.path.join(qroot, '.hg')):
455 if os.path.isdir(os.path.join(qroot, '.hg')):
457 yield qroot # we have a patch queue repo here
456 yield qroot # we have a patch queue repo here
458 if recurse:
457 if recurse:
459 # avoid recursing inside the .hg directory
458 # avoid recursing inside the .hg directory
460 dirs.remove('.hg')
459 dirs.remove('.hg')
461 else:
460 else:
462 dirs[:] = [] # don't descend further
461 dirs[:] = [] # don't descend further
463 elif followsym:
462 elif followsym:
464 newdirs = []
463 newdirs = []
465 for d in dirs:
464 for d in dirs:
466 fname = os.path.join(root, d)
465 fname = os.path.join(root, d)
467 if adddir(seen_dirs, fname):
466 if adddir(seen_dirs, fname):
468 if os.path.islink(fname):
467 if os.path.islink(fname):
469 for hgname in walkrepos(fname, True, seen_dirs):
468 for hgname in walkrepos(fname, True, seen_dirs):
470 yield hgname
469 yield hgname
471 else:
470 else:
472 newdirs.append(d)
471 newdirs.append(d)
473 dirs[:] = newdirs
472 dirs[:] = newdirs
474
473
475 def osrcpath():
474 def osrcpath():
476 '''return default os-specific hgrc search path'''
475 '''return default os-specific hgrc search path'''
477 path = systemrcpath()
476 path = systemrcpath()
478 path.extend(userrcpath())
477 path.extend(userrcpath())
479 path = [os.path.normpath(f) for f in path]
478 path = [os.path.normpath(f) for f in path]
480 return path
479 return path
481
480
482 _rcpath = None
481 _rcpath = None
483
482
484 def rcpath():
483 def rcpath():
485 '''return hgrc search path. if env var HGRCPATH is set, use it.
484 '''return hgrc search path. if env var HGRCPATH is set, use it.
486 for each item in path, if directory, use files ending in .rc,
485 for each item in path, if directory, use files ending in .rc,
487 else use item.
486 else use item.
488 make HGRCPATH empty to only look in .hg/hgrc of current repo.
487 make HGRCPATH empty to only look in .hg/hgrc of current repo.
489 if no HGRCPATH, use default os-specific path.'''
488 if no HGRCPATH, use default os-specific path.'''
490 global _rcpath
489 global _rcpath
491 if _rcpath is None:
490 if _rcpath is None:
492 if 'HGRCPATH' in os.environ:
491 if 'HGRCPATH' in os.environ:
493 _rcpath = []
492 _rcpath = []
494 for p in os.environ['HGRCPATH'].split(os.pathsep):
493 for p in os.environ['HGRCPATH'].split(os.pathsep):
495 if not p:
494 if not p:
496 continue
495 continue
497 p = util.expandpath(p)
496 p = util.expandpath(p)
498 if os.path.isdir(p):
497 if os.path.isdir(p):
499 for f, kind in osutil.listdir(p):
498 for f, kind in osutil.listdir(p):
500 if f.endswith('.rc'):
499 if f.endswith('.rc'):
501 _rcpath.append(os.path.join(p, f))
500 _rcpath.append(os.path.join(p, f))
502 else:
501 else:
503 _rcpath.append(p)
502 _rcpath.append(p)
504 else:
503 else:
505 _rcpath = osrcpath()
504 _rcpath = osrcpath()
506 return _rcpath
505 return _rcpath
507
506
508 if os.name != 'nt':
507 if os.name != 'nt':
509
508
510 def rcfiles(path):
509 def rcfiles(path):
511 rcs = [os.path.join(path, 'hgrc')]
510 rcs = [os.path.join(path, 'hgrc')]
512 rcdir = os.path.join(path, 'hgrc.d')
511 rcdir = os.path.join(path, 'hgrc.d')
513 try:
512 try:
514 rcs.extend([os.path.join(rcdir, f)
513 rcs.extend([os.path.join(rcdir, f)
515 for f, kind in osutil.listdir(rcdir)
514 for f, kind in osutil.listdir(rcdir)
516 if f.endswith(".rc")])
515 if f.endswith(".rc")])
517 except OSError:
516 except OSError:
518 pass
517 pass
519 return rcs
518 return rcs
520
519
521 def systemrcpath():
520 def systemrcpath():
522 path = []
521 path = []
523 if sys.platform == 'plan9':
522 if sys.platform == 'plan9':
524 root = 'lib/mercurial'
523 root = 'lib/mercurial'
525 else:
524 else:
526 root = 'etc/mercurial'
525 root = 'etc/mercurial'
527 # old mod_python does not set sys.argv
526 # old mod_python does not set sys.argv
528 if len(getattr(sys, 'argv', [])) > 0:
527 if len(getattr(sys, 'argv', [])) > 0:
529 p = os.path.dirname(os.path.dirname(sys.argv[0]))
528 p = os.path.dirname(os.path.dirname(sys.argv[0]))
530 path.extend(rcfiles(os.path.join(p, root)))
529 path.extend(rcfiles(os.path.join(p, root)))
531 path.extend(rcfiles('/' + root))
530 path.extend(rcfiles('/' + root))
532 return path
531 return path
533
532
534 def userrcpath():
533 def userrcpath():
535 if sys.platform == 'plan9':
534 if sys.platform == 'plan9':
536 return [os.environ['home'] + '/lib/hgrc']
535 return [os.environ['home'] + '/lib/hgrc']
537 else:
536 else:
538 return [os.path.expanduser('~/.hgrc')]
537 return [os.path.expanduser('~/.hgrc')]
539
538
540 else:
539 else:
541
540
542 import _winreg
541 import _winreg
543
542
544 def systemrcpath():
543 def systemrcpath():
545 '''return default os-specific hgrc search path'''
544 '''return default os-specific hgrc search path'''
546 rcpath = []
545 rcpath = []
547 filename = util.executablepath()
546 filename = util.executablepath()
548 # Use mercurial.ini found in directory with hg.exe
547 # Use mercurial.ini found in directory with hg.exe
549 progrc = os.path.join(os.path.dirname(filename), 'mercurial.ini')
548 progrc = os.path.join(os.path.dirname(filename), 'mercurial.ini')
550 if os.path.isfile(progrc):
549 if os.path.isfile(progrc):
551 rcpath.append(progrc)
550 rcpath.append(progrc)
552 return rcpath
551 return rcpath
553 # Use hgrc.d found in directory with hg.exe
552 # Use hgrc.d found in directory with hg.exe
554 progrcd = os.path.join(os.path.dirname(filename), 'hgrc.d')
553 progrcd = os.path.join(os.path.dirname(filename), 'hgrc.d')
555 if os.path.isdir(progrcd):
554 if os.path.isdir(progrcd):
556 for f, kind in osutil.listdir(progrcd):
555 for f, kind in osutil.listdir(progrcd):
557 if f.endswith('.rc'):
556 if f.endswith('.rc'):
558 rcpath.append(os.path.join(progrcd, f))
557 rcpath.append(os.path.join(progrcd, f))
559 return rcpath
558 return rcpath
560 # else look for a system rcpath in the registry
559 # else look for a system rcpath in the registry
561 value = util.lookupreg('SOFTWARE\\Mercurial', None,
560 value = util.lookupreg('SOFTWARE\\Mercurial', None,
562 _winreg.HKEY_LOCAL_MACHINE)
561 _winreg.HKEY_LOCAL_MACHINE)
563 if not isinstance(value, str) or not value:
562 if not isinstance(value, str) or not value:
564 return rcpath
563 return rcpath
565 value = util.localpath(value)
564 value = util.localpath(value)
566 for p in value.split(os.pathsep):
565 for p in value.split(os.pathsep):
567 if p.lower().endswith('mercurial.ini'):
566 if p.lower().endswith('mercurial.ini'):
568 rcpath.append(p)
567 rcpath.append(p)
569 elif os.path.isdir(p):
568 elif os.path.isdir(p):
570 for f, kind in osutil.listdir(p):
569 for f, kind in osutil.listdir(p):
571 if f.endswith('.rc'):
570 if f.endswith('.rc'):
572 rcpath.append(os.path.join(p, f))
571 rcpath.append(os.path.join(p, f))
573 return rcpath
572 return rcpath
574
573
575 def userrcpath():
574 def userrcpath():
576 '''return os-specific hgrc search path to the user dir'''
575 '''return os-specific hgrc search path to the user dir'''
577 home = os.path.expanduser('~')
576 home = os.path.expanduser('~')
578 path = [os.path.join(home, 'mercurial.ini'),
577 path = [os.path.join(home, 'mercurial.ini'),
579 os.path.join(home, '.hgrc')]
578 os.path.join(home, '.hgrc')]
580 userprofile = os.environ.get('USERPROFILE')
579 userprofile = os.environ.get('USERPROFILE')
581 if userprofile:
580 if userprofile:
582 path.append(os.path.join(userprofile, 'mercurial.ini'))
581 path.append(os.path.join(userprofile, 'mercurial.ini'))
583 path.append(os.path.join(userprofile, '.hgrc'))
582 path.append(os.path.join(userprofile, '.hgrc'))
584 return path
583 return path
585
584
586 def revsingle(repo, revspec, default='.'):
585 def revsingle(repo, revspec, default='.'):
587 if not revspec:
586 if not revspec:
588 return repo[default]
587 return repo[default]
589
588
590 l = revrange(repo, [revspec])
589 l = revrange(repo, [revspec])
591 if len(l) < 1:
590 if len(l) < 1:
592 raise util.Abort(_('empty revision set'))
591 raise util.Abort(_('empty revision set'))
593 return repo[l[-1]]
592 return repo[l[-1]]
594
593
595 def revpair(repo, revs):
594 def revpair(repo, revs):
596 if not revs:
595 if not revs:
597 return repo.dirstate.p1(), None
596 return repo.dirstate.p1(), None
598
597
599 l = revrange(repo, revs)
598 l = revrange(repo, revs)
600
599
601 if len(l) == 0:
600 if len(l) == 0:
602 if revs:
601 if revs:
603 raise util.Abort(_('empty revision range'))
602 raise util.Abort(_('empty revision range'))
604 return repo.dirstate.p1(), None
603 return repo.dirstate.p1(), None
605
604
606 if len(l) == 1 and len(revs) == 1 and _revrangesep not in revs[0]:
605 if len(l) == 1 and len(revs) == 1 and _revrangesep not in revs[0]:
607 return repo.lookup(l[0]), None
606 return repo.lookup(l[0]), None
608
607
609 return repo.lookup(l[0]), repo.lookup(l[-1])
608 return repo.lookup(l[0]), repo.lookup(l[-1])
610
609
611 _revrangesep = ':'
610 _revrangesep = ':'
612
611
613 def revrange(repo, revs):
612 def revrange(repo, revs):
614 """Yield revision as strings from a list of revision specifications."""
613 """Yield revision as strings from a list of revision specifications."""
615
614
616 def revfix(repo, val, defval):
615 def revfix(repo, val, defval):
617 if not val and val != 0 and defval is not None:
616 if not val and val != 0 and defval is not None:
618 return defval
617 return defval
619 return repo[val].rev()
618 return repo[val].rev()
620
619
621 seen, l = set(), []
620 seen, l = set(), []
622 for spec in revs:
621 for spec in revs:
623 if l and not seen:
622 if l and not seen:
624 seen = set(l)
623 seen = set(l)
625 # attempt to parse old-style ranges first to deal with
624 # attempt to parse old-style ranges first to deal with
626 # things like old-tag which contain query metacharacters
625 # things like old-tag which contain query metacharacters
627 try:
626 try:
628 if isinstance(spec, int):
627 if isinstance(spec, int):
629 seen.add(spec)
628 seen.add(spec)
630 l.append(spec)
629 l.append(spec)
631 continue
630 continue
632
631
633 if _revrangesep in spec:
632 if _revrangesep in spec:
634 start, end = spec.split(_revrangesep, 1)
633 start, end = spec.split(_revrangesep, 1)
635 start = revfix(repo, start, 0)
634 start = revfix(repo, start, 0)
636 end = revfix(repo, end, len(repo) - 1)
635 end = revfix(repo, end, len(repo) - 1)
637 step = start > end and -1 or 1
636 step = start > end and -1 or 1
638 if not seen and not l:
637 if not seen and not l:
639 # by far the most common case: revs = ["-1:0"]
638 # by far the most common case: revs = ["-1:0"]
640 l = range(start, end + step, step)
639 l = range(start, end + step, step)
641 # defer syncing seen until next iteration
640 # defer syncing seen until next iteration
642 continue
641 continue
643 newrevs = set(xrange(start, end + step, step))
642 newrevs = set(xrange(start, end + step, step))
644 if seen:
643 if seen:
645 newrevs.difference_update(seen)
644 newrevs.difference_update(seen)
646 seen.update(newrevs)
645 seen.update(newrevs)
647 else:
646 else:
648 seen = newrevs
647 seen = newrevs
649 l.extend(sorted(newrevs, reverse=start > end))
648 l.extend(sorted(newrevs, reverse=start > end))
650 continue
649 continue
651 elif spec and spec in repo: # single unquoted rev
650 elif spec and spec in repo: # single unquoted rev
652 rev = revfix(repo, spec, None)
651 rev = revfix(repo, spec, None)
653 if rev in seen:
652 if rev in seen:
654 continue
653 continue
655 seen.add(rev)
654 seen.add(rev)
656 l.append(rev)
655 l.append(rev)
657 continue
656 continue
658 except error.RepoLookupError:
657 except error.RepoLookupError:
659 pass
658 pass
660
659
661 # fall through to new-style queries if old-style fails
660 # fall through to new-style queries if old-style fails
662 m = revset.match(repo.ui, spec)
661 m = revset.match(repo.ui, spec)
663 dl = [r for r in m(repo, list(repo)) if r not in seen]
662 dl = [r for r in m(repo, list(repo)) if r not in seen]
664 l.extend(dl)
663 l.extend(dl)
665 seen.update(dl)
664 seen.update(dl)
666
665
667 return l
666 return l
668
667
669 def expandpats(pats):
668 def expandpats(pats):
670 if not util.expandglobs:
669 if not util.expandglobs:
671 return list(pats)
670 return list(pats)
672 ret = []
671 ret = []
673 for p in pats:
672 for p in pats:
674 kind, name = matchmod._patsplit(p, None)
673 kind, name = matchmod._patsplit(p, None)
675 if kind is None:
674 if kind is None:
676 try:
675 try:
677 globbed = glob.glob(name)
676 globbed = glob.glob(name)
678 except re.error:
677 except re.error:
679 globbed = [name]
678 globbed = [name]
680 if globbed:
679 if globbed:
681 ret.extend(globbed)
680 ret.extend(globbed)
682 continue
681 continue
683 ret.append(p)
682 ret.append(p)
684 return ret
683 return ret
685
684
686 def matchandpats(ctx, pats=[], opts={}, globbed=False, default='relpath'):
685 def matchandpats(ctx, pats=[], opts={}, globbed=False, default='relpath'):
687 if pats == ("",):
686 if pats == ("",):
688 pats = []
687 pats = []
689 if not globbed and default == 'relpath':
688 if not globbed and default == 'relpath':
690 pats = expandpats(pats or [])
689 pats = expandpats(pats or [])
691
690
692 m = ctx.match(pats, opts.get('include'), opts.get('exclude'),
691 m = ctx.match(pats, opts.get('include'), opts.get('exclude'),
693 default)
692 default)
694 def badfn(f, msg):
693 def badfn(f, msg):
695 ctx._repo.ui.warn("%s: %s\n" % (m.rel(f), msg))
694 ctx._repo.ui.warn("%s: %s\n" % (m.rel(f), msg))
696 m.bad = badfn
695 m.bad = badfn
697 return m, pats
696 return m, pats
698
697
699 def match(ctx, pats=[], opts={}, globbed=False, default='relpath'):
698 def match(ctx, pats=[], opts={}, globbed=False, default='relpath'):
700 return matchandpats(ctx, pats, opts, globbed, default)[0]
699 return matchandpats(ctx, pats, opts, globbed, default)[0]
701
700
702 def matchall(repo):
701 def matchall(repo):
703 return matchmod.always(repo.root, repo.getcwd())
702 return matchmod.always(repo.root, repo.getcwd())
704
703
705 def matchfiles(repo, files):
704 def matchfiles(repo, files):
706 return matchmod.exact(repo.root, repo.getcwd(), files)
705 return matchmod.exact(repo.root, repo.getcwd(), files)
707
706
708 def addremove(repo, pats=[], opts={}, dry_run=None, similarity=None):
707 def addremove(repo, pats=[], opts={}, dry_run=None, similarity=None):
709 if dry_run is None:
708 if dry_run is None:
710 dry_run = opts.get('dry_run')
709 dry_run = opts.get('dry_run')
711 if similarity is None:
710 if similarity is None:
712 similarity = float(opts.get('similarity') or 0)
711 similarity = float(opts.get('similarity') or 0)
713 # we'd use status here, except handling of symlinks and ignore is tricky
712 # we'd use status here, except handling of symlinks and ignore is tricky
714 added, unknown, deleted, removed = [], [], [], []
713 added, unknown, deleted, removed = [], [], [], []
715 audit_path = pathauditor(repo.root)
714 audit_path = pathauditor(repo.root)
716 m = match(repo[None], pats, opts)
715 m = match(repo[None], pats, opts)
717 rejected = []
716 rejected = []
718 m.bad = lambda x, y: rejected.append(x)
717 m.bad = lambda x, y: rejected.append(x)
719
718
720 for abs in repo.walk(m):
719 for abs in repo.walk(m):
721 target = repo.wjoin(abs)
720 target = repo.wjoin(abs)
722 good = True
721 good = True
723 try:
722 try:
724 audit_path(abs)
723 audit_path(abs)
725 except (OSError, util.Abort):
724 except (OSError, util.Abort):
726 good = False
725 good = False
727 rel = m.rel(abs)
726 rel = m.rel(abs)
728 exact = m.exact(abs)
727 exact = m.exact(abs)
729 if good and abs not in repo.dirstate:
728 if good and abs not in repo.dirstate:
730 unknown.append(abs)
729 unknown.append(abs)
731 if repo.ui.verbose or not exact:
730 if repo.ui.verbose or not exact:
732 repo.ui.status(_('adding %s\n') % ((pats and rel) or abs))
731 repo.ui.status(_('adding %s\n') % ((pats and rel) or abs))
733 elif (repo.dirstate[abs] != 'r' and
732 elif (repo.dirstate[abs] != 'r' and
734 (not good or not os.path.lexists(target) or
733 (not good or not os.path.lexists(target) or
735 (os.path.isdir(target) and not os.path.islink(target)))):
734 (os.path.isdir(target) and not os.path.islink(target)))):
736 deleted.append(abs)
735 deleted.append(abs)
737 if repo.ui.verbose or not exact:
736 if repo.ui.verbose or not exact:
738 repo.ui.status(_('removing %s\n') % ((pats and rel) or abs))
737 repo.ui.status(_('removing %s\n') % ((pats and rel) or abs))
739 # for finding renames
738 # for finding renames
740 elif repo.dirstate[abs] == 'r':
739 elif repo.dirstate[abs] == 'r':
741 removed.append(abs)
740 removed.append(abs)
742 elif repo.dirstate[abs] == 'a':
741 elif repo.dirstate[abs] == 'a':
743 added.append(abs)
742 added.append(abs)
744 copies = {}
743 copies = {}
745 if similarity > 0:
744 if similarity > 0:
746 for old, new, score in similar.findrenames(repo,
745 for old, new, score in similar.findrenames(repo,
747 added + unknown, removed + deleted, similarity):
746 added + unknown, removed + deleted, similarity):
748 if repo.ui.verbose or not m.exact(old) or not m.exact(new):
747 if repo.ui.verbose or not m.exact(old) or not m.exact(new):
749 repo.ui.status(_('recording removal of %s as rename to %s '
748 repo.ui.status(_('recording removal of %s as rename to %s '
750 '(%d%% similar)\n') %
749 '(%d%% similar)\n') %
751 (m.rel(old), m.rel(new), score * 100))
750 (m.rel(old), m.rel(new), score * 100))
752 copies[new] = old
751 copies[new] = old
753
752
754 if not dry_run:
753 if not dry_run:
755 wctx = repo[None]
754 wctx = repo[None]
756 wlock = repo.wlock()
755 wlock = repo.wlock()
757 try:
756 try:
758 wctx.forget(deleted)
757 wctx.forget(deleted)
759 wctx.add(unknown)
758 wctx.add(unknown)
760 for new, old in copies.iteritems():
759 for new, old in copies.iteritems():
761 wctx.copy(old, new)
760 wctx.copy(old, new)
762 finally:
761 finally:
763 wlock.release()
762 wlock.release()
764
763
765 for f in rejected:
764 for f in rejected:
766 if f in m.files():
765 if f in m.files():
767 return 1
766 return 1
768 return 0
767 return 0
769
768
770 def updatedir(ui, repo, patches, similarity=0):
769 def updatedir(ui, repo, patches, similarity=0):
771 '''Update dirstate after patch application according to metadata'''
770 '''Update dirstate after patch application according to metadata'''
772 if not patches:
771 if not patches:
773 return []
772 return []
774 copies = []
773 copies = []
775 removes = set()
774 removes = set()
776 cfiles = patches.keys()
775 cfiles = patches.keys()
777 cwd = repo.getcwd()
776 cwd = repo.getcwd()
778 if cwd:
777 if cwd:
779 cfiles = [util.pathto(repo.root, cwd, f) for f in patches.keys()]
778 cfiles = [util.pathto(repo.root, cwd, f) for f in patches.keys()]
780 for f in patches:
779 for f in patches:
781 gp = patches[f]
780 gp = patches[f]
782 if not gp:
781 if not gp:
783 continue
782 continue
784 if gp.op == 'RENAME':
783 if gp.op == 'RENAME':
785 copies.append((gp.oldpath, gp.path))
784 copies.append((gp.oldpath, gp.path))
786 removes.add(gp.oldpath)
785 removes.add(gp.oldpath)
787 elif gp.op == 'COPY':
786 elif gp.op == 'COPY':
788 copies.append((gp.oldpath, gp.path))
787 copies.append((gp.oldpath, gp.path))
789 elif gp.op == 'DELETE':
788 elif gp.op == 'DELETE':
790 removes.add(gp.path)
789 removes.add(gp.path)
791
790
792 wctx = repo[None]
791 wctx = repo[None]
793 for src, dst in copies:
792 for src, dst in copies:
794 dirstatecopy(ui, repo, wctx, src, dst, cwd=cwd)
793 dirstatecopy(ui, repo, wctx, src, dst, cwd=cwd)
795 if (not similarity) and removes:
794 if (not similarity) and removes:
796 wctx.remove(sorted(removes), True)
795 wctx.remove(sorted(removes), True)
797
796
798 for f in patches:
797 for f in patches:
799 gp = patches[f]
798 gp = patches[f]
800 if gp and gp.mode:
799 if gp and gp.mode:
801 islink, isexec = gp.mode
800 islink, isexec = gp.mode
802 dst = repo.wjoin(gp.path)
801 dst = repo.wjoin(gp.path)
803 # patch won't create empty files
802 # patch won't create empty files
804 if gp.op == 'ADD' and not os.path.lexists(dst):
803 if gp.op == 'ADD' and not os.path.lexists(dst):
805 flags = (isexec and 'x' or '') + (islink and 'l' or '')
804 flags = (isexec and 'x' or '') + (islink and 'l' or '')
806 repo.wwrite(gp.path, '', flags)
805 repo.wwrite(gp.path, '', flags)
807 util.setflags(dst, islink, isexec)
806 util.setflags(dst, islink, isexec)
808 addremove(repo, cfiles, similarity=similarity)
807 addremove(repo, cfiles, similarity=similarity)
809 files = patches.keys()
808 files = patches.keys()
810 files.extend([r for r in removes if r not in files])
809 files.extend([r for r in removes if r not in files])
811 return sorted(files)
810 return sorted(files)
812
811
813 def dirstatecopy(ui, repo, wctx, src, dst, dryrun=False, cwd=None):
812 def dirstatecopy(ui, repo, wctx, src, dst, dryrun=False, cwd=None):
814 """Update the dirstate to reflect the intent of copying src to dst. For
813 """Update the dirstate to reflect the intent of copying src to dst. For
815 different reasons it might not end with dst being marked as copied from src.
814 different reasons it might not end with dst being marked as copied from src.
816 """
815 """
817 origsrc = repo.dirstate.copied(src) or src
816 origsrc = repo.dirstate.copied(src) or src
818 if dst == origsrc: # copying back a copy?
817 if dst == origsrc: # copying back a copy?
819 if repo.dirstate[dst] not in 'mn' and not dryrun:
818 if repo.dirstate[dst] not in 'mn' and not dryrun:
820 repo.dirstate.normallookup(dst)
819 repo.dirstate.normallookup(dst)
821 else:
820 else:
822 if repo.dirstate[origsrc] == 'a' and origsrc == src:
821 if repo.dirstate[origsrc] == 'a' and origsrc == src:
823 if not ui.quiet:
822 if not ui.quiet:
824 ui.warn(_("%s has not been committed yet, so no copy "
823 ui.warn(_("%s has not been committed yet, so no copy "
825 "data will be stored for %s.\n")
824 "data will be stored for %s.\n")
826 % (repo.pathto(origsrc, cwd), repo.pathto(dst, cwd)))
825 % (repo.pathto(origsrc, cwd), repo.pathto(dst, cwd)))
827 if repo.dirstate[dst] in '?r' and not dryrun:
826 if repo.dirstate[dst] in '?r' and not dryrun:
828 wctx.add([dst])
827 wctx.add([dst])
829 elif not dryrun:
828 elif not dryrun:
830 wctx.copy(origsrc, dst)
829 wctx.copy(origsrc, dst)
831
830
832 def readrequires(opener, supported):
831 def readrequires(opener, supported):
833 '''Reads and parses .hg/requires and checks if all entries found
832 '''Reads and parses .hg/requires and checks if all entries found
834 are in the list of supported features.'''
833 are in the list of supported features.'''
835 requirements = set(opener.read("requires").splitlines())
834 requirements = set(opener.read("requires").splitlines())
836 missings = []
835 missings = []
837 for r in requirements:
836 for r in requirements:
838 if r not in supported:
837 if r not in supported:
839 if not r or not r[0].isalnum():
838 if not r or not r[0].isalnum():
840 raise error.RequirementError(_(".hg/requires file is corrupt"))
839 raise error.RequirementError(_(".hg/requires file is corrupt"))
841 missings.append(r)
840 missings.append(r)
842 missings.sort()
841 missings.sort()
843 if missings:
842 if missings:
844 raise error.RequirementError(
843 raise error.RequirementError(
845 _("unknown repository format: requires features '%s' (upgrade "
844 _("unknown repository format: requires features '%s' (upgrade "
846 "Mercurial)") % "', '".join(missings))
845 "Mercurial)") % "', '".join(missings))
847 return requirements
846 return requirements
848
847
849 class filecacheentry(object):
848 class filecacheentry(object):
850 def __init__(self, path):
849 def __init__(self, path):
851 self.path = path
850 self.path = path
852 self.cachestat = filecacheentry.stat(self.path)
851 self.cachestat = filecacheentry.stat(self.path)
853
852
854 if self.cachestat:
853 if self.cachestat:
855 self._cacheable = self.cachestat.cacheable()
854 self._cacheable = self.cachestat.cacheable()
856 else:
855 else:
857 # None means we don't know yet
856 # None means we don't know yet
858 self._cacheable = None
857 self._cacheable = None
859
858
860 def refresh(self):
859 def refresh(self):
861 if self.cacheable():
860 if self.cacheable():
862 self.cachestat = filecacheentry.stat(self.path)
861 self.cachestat = filecacheentry.stat(self.path)
863
862
864 def cacheable(self):
863 def cacheable(self):
865 if self._cacheable is not None:
864 if self._cacheable is not None:
866 return self._cacheable
865 return self._cacheable
867
866
868 # we don't know yet, assume it is for now
867 # we don't know yet, assume it is for now
869 return True
868 return True
870
869
871 def changed(self):
870 def changed(self):
872 # no point in going further if we can't cache it
871 # no point in going further if we can't cache it
873 if not self.cacheable():
872 if not self.cacheable():
874 return True
873 return True
875
874
876 newstat = filecacheentry.stat(self.path)
875 newstat = filecacheentry.stat(self.path)
877
876
878 # we may not know if it's cacheable yet, check again now
877 # we may not know if it's cacheable yet, check again now
879 if newstat and self._cacheable is None:
878 if newstat and self._cacheable is None:
880 self._cacheable = newstat.cacheable()
879 self._cacheable = newstat.cacheable()
881
880
882 # check again
881 # check again
883 if not self._cacheable:
882 if not self._cacheable:
884 return True
883 return True
885
884
886 if self.cachestat != newstat:
885 if self.cachestat != newstat:
887 self.cachestat = newstat
886 self.cachestat = newstat
888 return True
887 return True
889 else:
888 else:
890 return False
889 return False
891
890
892 @staticmethod
891 @staticmethod
893 def stat(path):
892 def stat(path):
894 try:
893 try:
895 return util.cachestat(path)
894 return util.cachestat(path)
896 except OSError, e:
895 except OSError, e:
897 if e.errno != errno.ENOENT:
896 if e.errno != errno.ENOENT:
898 raise
897 raise
899
898
900 class filecache(object):
899 class filecache(object):
901 '''A property like decorator that tracks a file under .hg/ for updates.
900 '''A property like decorator that tracks a file under .hg/ for updates.
902
901
903 Records stat info when called in _filecache.
902 Records stat info when called in _filecache.
904
903
905 On subsequent calls, compares old stat info with new info, and recreates
904 On subsequent calls, compares old stat info with new info, and recreates
906 the object when needed, updating the new stat info in _filecache.
905 the object when needed, updating the new stat info in _filecache.
907
906
908 Mercurial either atomic renames or appends for files under .hg,
907 Mercurial either atomic renames or appends for files under .hg,
909 so to ensure the cache is reliable we need the filesystem to be able
908 so to ensure the cache is reliable we need the filesystem to be able
910 to tell us if a file has been replaced. If it can't, we fallback to
909 to tell us if a file has been replaced. If it can't, we fallback to
911 recreating the object on every call (essentially the same behaviour as
910 recreating the object on every call (essentially the same behaviour as
912 propertycache).'''
911 propertycache).'''
913 def __init__(self, path):
912 def __init__(self, path):
914 self.path = path
913 self.path = path
915
914
916 def join(self, obj, fname):
915 def join(self, obj, fname):
917 """Used to compute the runtime path of the cached file.
916 """Used to compute the runtime path of the cached file.
918
917
919 Users should subclass filecache and provide their own version of this
918 Users should subclass filecache and provide their own version of this
920 function to call the appropriate join function on 'obj' (an instance
919 function to call the appropriate join function on 'obj' (an instance
921 of the class that its member function was decorated).
920 of the class that its member function was decorated).
922 """
921 """
923 return obj.join(fname)
922 return obj.join(fname)
924
923
925 def __call__(self, func):
924 def __call__(self, func):
926 self.func = func
925 self.func = func
927 self.name = func.__name__
926 self.name = func.__name__
928 return self
927 return self
929
928
930 def __get__(self, obj, type=None):
929 def __get__(self, obj, type=None):
931 # do we need to check if the file changed?
930 # do we need to check if the file changed?
932 if self.name in obj.__dict__:
931 if self.name in obj.__dict__:
933 return obj.__dict__[self.name]
932 return obj.__dict__[self.name]
934
933
935 entry = obj._filecache.get(self.name)
934 entry = obj._filecache.get(self.name)
936
935
937 if entry:
936 if entry:
938 if entry.changed():
937 if entry.changed():
939 entry.obj = self.func(obj)
938 entry.obj = self.func(obj)
940 else:
939 else:
941 path = self.join(obj, self.path)
940 path = self.join(obj, self.path)
942
941
943 # We stat -before- creating the object so our cache doesn't lie if
942 # We stat -before- creating the object so our cache doesn't lie if
944 # a writer modified between the time we read and stat
943 # a writer modified between the time we read and stat
945 entry = filecacheentry(path)
944 entry = filecacheentry(path)
946 entry.obj = self.func(obj)
945 entry.obj = self.func(obj)
947
946
948 obj._filecache[self.name] = entry
947 obj._filecache[self.name] = entry
949
948
950 obj.__dict__[self.name] = entry.obj
949 obj.__dict__[self.name] = entry.obj
951 return entry.obj
950 return entry.obj
952
951
953 def __set__(self, obj, value):
952 def __set__(self, obj, value):
954 if self.name in obj._filecache:
953 if self.name in obj._filecache:
955 obj._filecache[self.name].obj = value # update cached copy
954 obj._filecache[self.name].obj = value # update cached copy
956 obj.__dict__[self.name] = value # update copy returned by obj.x
955 obj.__dict__[self.name] = value # update copy returned by obj.x
957
956
958 def __delete__(self, obj):
957 def __delete__(self, obj):
959 try:
958 try:
960 del obj.__dict__[self.name]
959 del obj.__dict__[self.name]
961 except KeyError:
960 except KeyError:
962 raise AttributeError, self.name
961 raise AttributeError, self.name
@@ -1,540 +1,540 b''
1 $ hg init
1 $ hg init
2
2
3 no bookmarks
3 no bookmarks
4
4
5 $ hg bookmarks
5 $ hg bookmarks
6 no bookmarks set
6 no bookmarks set
7
7
8 bookmark rev -1
8 bookmark rev -1
9
9
10 $ hg bookmark X
10 $ hg bookmark X
11
11
12 list bookmarks
12 list bookmarks
13
13
14 $ hg bookmarks
14 $ hg bookmarks
15 * X -1:000000000000
15 * X -1:000000000000
16
16
17 list bookmarks with color
17 list bookmarks with color
18
18
19 $ hg --config extensions.color= --config color.mode=ansi \
19 $ hg --config extensions.color= --config color.mode=ansi \
20 > bookmarks --color=always
20 > bookmarks --color=always
21 \x1b[0;32m * X -1:000000000000\x1b[0m (esc)
21 \x1b[0;32m * X -1:000000000000\x1b[0m (esc)
22
22
23 $ echo a > a
23 $ echo a > a
24 $ hg add a
24 $ hg add a
25 $ hg commit -m 0
25 $ hg commit -m 0
26
26
27 bookmark X moved to rev 0
27 bookmark X moved to rev 0
28
28
29 $ hg bookmarks
29 $ hg bookmarks
30 * X 0:f7b1eb17ad24
30 * X 0:f7b1eb17ad24
31
31
32 look up bookmark
32 look up bookmark
33
33
34 $ hg log -r X
34 $ hg log -r X
35 changeset: 0:f7b1eb17ad24
35 changeset: 0:f7b1eb17ad24
36 bookmark: X
36 bookmark: X
37 tag: tip
37 tag: tip
38 user: test
38 user: test
39 date: Thu Jan 01 00:00:00 1970 +0000
39 date: Thu Jan 01 00:00:00 1970 +0000
40 summary: 0
40 summary: 0
41
41
42
42
43 second bookmark for rev 0
43 second bookmark for rev 0
44
44
45 $ hg bookmark X2
45 $ hg bookmark X2
46
46
47 bookmark rev -1 again
47 bookmark rev -1 again
48
48
49 $ hg bookmark -r null Y
49 $ hg bookmark -r null Y
50
50
51 list bookmarks
51 list bookmarks
52
52
53 $ hg bookmarks
53 $ hg bookmarks
54 X 0:f7b1eb17ad24
54 X 0:f7b1eb17ad24
55 * X2 0:f7b1eb17ad24
55 * X2 0:f7b1eb17ad24
56 Y -1:000000000000
56 Y -1:000000000000
57
57
58 $ echo b > b
58 $ echo b > b
59 $ hg add b
59 $ hg add b
60 $ hg commit -m 1
60 $ hg commit -m 1
61
61
62 bookmarks revset
62 bookmarks revset
63
63
64 $ hg log -r 'bookmark()'
64 $ hg log -r 'bookmark()'
65 changeset: 0:f7b1eb17ad24
65 changeset: 0:f7b1eb17ad24
66 bookmark: X
66 bookmark: X
67 user: test
67 user: test
68 date: Thu Jan 01 00:00:00 1970 +0000
68 date: Thu Jan 01 00:00:00 1970 +0000
69 summary: 0
69 summary: 0
70
70
71 changeset: 1:925d80f479bb
71 changeset: 1:925d80f479bb
72 bookmark: X2
72 bookmark: X2
73 tag: tip
73 tag: tip
74 user: test
74 user: test
75 date: Thu Jan 01 00:00:00 1970 +0000
75 date: Thu Jan 01 00:00:00 1970 +0000
76 summary: 1
76 summary: 1
77
77
78 $ hg log -r 'bookmark(Y)'
78 $ hg log -r 'bookmark(Y)'
79 $ hg log -r 'bookmark(X2)'
79 $ hg log -r 'bookmark(X2)'
80 changeset: 1:925d80f479bb
80 changeset: 1:925d80f479bb
81 bookmark: X2
81 bookmark: X2
82 tag: tip
82 tag: tip
83 user: test
83 user: test
84 date: Thu Jan 01 00:00:00 1970 +0000
84 date: Thu Jan 01 00:00:00 1970 +0000
85 summary: 1
85 summary: 1
86
86
87 $ hg log -r 'bookmark("re:X")'
87 $ hg log -r 'bookmark("re:X")'
88 changeset: 0:f7b1eb17ad24
88 changeset: 0:f7b1eb17ad24
89 bookmark: X
89 bookmark: X
90 user: test
90 user: test
91 date: Thu Jan 01 00:00:00 1970 +0000
91 date: Thu Jan 01 00:00:00 1970 +0000
92 summary: 0
92 summary: 0
93
93
94 changeset: 1:925d80f479bb
94 changeset: 1:925d80f479bb
95 bookmark: X2
95 bookmark: X2
96 tag: tip
96 tag: tip
97 user: test
97 user: test
98 date: Thu Jan 01 00:00:00 1970 +0000
98 date: Thu Jan 01 00:00:00 1970 +0000
99 summary: 1
99 summary: 1
100
100
101 $ hg log -r 'bookmark(unknown)'
101 $ hg log -r 'bookmark(unknown)'
102 abort: bookmark 'unknown' does not exist
102 abort: bookmark 'unknown' does not exist
103 [255]
103 [255]
104
104
105 $ hg help revsets | grep 'bookmark('
105 $ hg help revsets | grep 'bookmark('
106 "bookmark([name])"
106 "bookmark([name])"
107
107
108 bookmarks X and X2 moved to rev 1, Y at rev -1
108 bookmarks X and X2 moved to rev 1, Y at rev -1
109
109
110 $ hg bookmarks
110 $ hg bookmarks
111 X 0:f7b1eb17ad24
111 X 0:f7b1eb17ad24
112 * X2 1:925d80f479bb
112 * X2 1:925d80f479bb
113 Y -1:000000000000
113 Y -1:000000000000
114
114
115 bookmark rev 0 again
115 bookmark rev 0 again
116
116
117 $ hg bookmark -r 0 Z
117 $ hg bookmark -r 0 Z
118
118
119 $ hg update X
119 $ hg update X
120 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
120 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
121 $ echo c > c
121 $ echo c > c
122 $ hg add c
122 $ hg add c
123 $ hg commit -m 2
123 $ hg commit -m 2
124 created new head
124 created new head
125
125
126 bookmarks X moved to rev 2, Y at rev -1, Z at rev 0
126 bookmarks X moved to rev 2, Y at rev -1, Z at rev 0
127
127
128 $ hg bookmarks
128 $ hg bookmarks
129 * X 2:db815d6d32e6
129 * X 2:db815d6d32e6
130 X2 1:925d80f479bb
130 X2 1:925d80f479bb
131 Y -1:000000000000
131 Y -1:000000000000
132 Z 0:f7b1eb17ad24
132 Z 0:f7b1eb17ad24
133
133
134 rename nonexistent bookmark
134 rename nonexistent bookmark
135
135
136 $ hg bookmark -m A B
136 $ hg bookmark -m A B
137 abort: bookmark 'A' does not exist
137 abort: bookmark 'A' does not exist
138 [255]
138 [255]
139
139
140 rename to existent bookmark
140 rename to existent bookmark
141
141
142 $ hg bookmark -m X Y
142 $ hg bookmark -m X Y
143 abort: bookmark 'Y' already exists (use -f to force)
143 abort: bookmark 'Y' already exists (use -f to force)
144 [255]
144 [255]
145
145
146 force rename to existent bookmark
146 force rename to existent bookmark
147
147
148 $ hg bookmark -f -m X Y
148 $ hg bookmark -f -m X Y
149
149
150 list bookmarks
150 list bookmarks
151
151
152 $ hg bookmark
152 $ hg bookmark
153 X2 1:925d80f479bb
153 X2 1:925d80f479bb
154 * Y 2:db815d6d32e6
154 * Y 2:db815d6d32e6
155 Z 0:f7b1eb17ad24
155 Z 0:f7b1eb17ad24
156
156
157 bookmarks from a revset
157 bookmarks from a revset
158 $ hg bookmark -r '.^1' REVSET
158 $ hg bookmark -r '.^1' REVSET
159 $ hg bookmark -r ':tip' TIP
159 $ hg bookmark -r ':tip' TIP
160 $ hg bookmarks
160 $ hg bookmarks
161 REVSET 0:f7b1eb17ad24
161 REVSET 0:f7b1eb17ad24
162 * TIP 2:db815d6d32e6
162 * TIP 2:db815d6d32e6
163 X2 1:925d80f479bb
163 X2 1:925d80f479bb
164 Y 2:db815d6d32e6
164 Y 2:db815d6d32e6
165 Z 0:f7b1eb17ad24
165 Z 0:f7b1eb17ad24
166
166
167 $ hg bookmark -d REVSET
167 $ hg bookmark -d REVSET
168 $ hg bookmark -d TIP
168 $ hg bookmark -d TIP
169
169
170 rename without new name
170 rename without new name
171
171
172 $ hg bookmark -m Y
172 $ hg bookmark -m Y
173 abort: new bookmark name required
173 abort: new bookmark name required
174 [255]
174 [255]
175
175
176 delete without name
176 delete without name
177
177
178 $ hg bookmark -d
178 $ hg bookmark -d
179 abort: bookmark name required
179 abort: bookmark name required
180 [255]
180 [255]
181
181
182 delete nonexistent bookmark
182 delete nonexistent bookmark
183
183
184 $ hg bookmark -d A
184 $ hg bookmark -d A
185 abort: bookmark 'A' does not exist
185 abort: bookmark 'A' does not exist
186 [255]
186 [255]
187
187
188 bookmark name with spaces should be stripped
188 bookmark name with spaces should be stripped
189
189
190 $ hg bookmark ' x y '
190 $ hg bookmark ' x y '
191
191
192 list bookmarks
192 list bookmarks
193
193
194 $ hg bookmarks
194 $ hg bookmarks
195 X2 1:925d80f479bb
195 X2 1:925d80f479bb
196 Y 2:db815d6d32e6
196 Y 2:db815d6d32e6
197 Z 0:f7b1eb17ad24
197 Z 0:f7b1eb17ad24
198 * x y 2:db815d6d32e6
198 * x y 2:db815d6d32e6
199
199
200 look up stripped bookmark name
200 look up stripped bookmark name
201
201
202 $ hg log -r '"x y"'
202 $ hg log -r '"x y"'
203 changeset: 2:db815d6d32e6
203 changeset: 2:db815d6d32e6
204 bookmark: Y
204 bookmark: Y
205 bookmark: x y
205 bookmark: x y
206 tag: tip
206 tag: tip
207 parent: 0:f7b1eb17ad24
207 parent: 0:f7b1eb17ad24
208 user: test
208 user: test
209 date: Thu Jan 01 00:00:00 1970 +0000
209 date: Thu Jan 01 00:00:00 1970 +0000
210 summary: 2
210 summary: 2
211
211
212
212
213 reject bookmark name with newline
213 reject bookmark name with newline
214
214
215 $ hg bookmark '
215 $ hg bookmark '
216 > '
216 > '
217 abort: bookmark names cannot consist entirely of whitespace
217 abort: bookmark names cannot consist entirely of whitespace
218 [255]
218 [255]
219
219
220 $ hg bookmark -m Z '
220 $ hg bookmark -m Z '
221 > '
221 > '
222 abort: bookmark names cannot consist entirely of whitespace
222 abort: bookmark names cannot consist entirely of whitespace
223 [255]
223 [255]
224
224
225 bookmark with reserved name
225 bookmark with reserved name
226
226
227 $ hg bookmark tip
227 $ hg bookmark tip
228 abort: the name 'tip' is reserved
228 abort: the name 'tip' is reserved
229 [255]
229 [255]
230
230
231 $ hg bookmark .
231 $ hg bookmark .
232 abort: the name '.' is reserved
232 abort: the name '.' is reserved
233 [255]
233 [255]
234
234
235 $ hg bookmark null
235 $ hg bookmark null
236 abort: the name 'null' is reserved
236 abort: the name 'null' is reserved
237 [255]
237 [255]
238
238
239
239
240 bookmark with existing name
240 bookmark with existing name
241
241
242 $ hg bookmark Z
242 $ hg bookmark Z
243 abort: bookmark 'Z' already exists (use -f to force)
243 abort: bookmark 'Z' already exists (use -f to force)
244 [255]
244 [255]
245
245
246 $ hg bookmark -m Y Z
246 $ hg bookmark -m Y Z
247 abort: bookmark 'Z' already exists (use -f to force)
247 abort: bookmark 'Z' already exists (use -f to force)
248 [255]
248 [255]
249
249
250 bookmark with name of branch
250 bookmark with name of branch
251
251
252 $ hg bookmark default
252 $ hg bookmark default
253 abort: a bookmark cannot have the name of an existing branch
253 abort: a bookmark cannot have the name of an existing branch
254 [255]
254 [255]
255
255
256 $ hg bookmark -m Y default
256 $ hg bookmark -m Y default
257 abort: a bookmark cannot have the name of an existing branch
257 abort: a bookmark cannot have the name of an existing branch
258 [255]
258 [255]
259
259
260 incompatible options
260 incompatible options
261
261
262 $ hg bookmark -m Y -d Z
262 $ hg bookmark -m Y -d Z
263 abort: --delete and --rename are incompatible
263 abort: --delete and --rename are incompatible
264 [255]
264 [255]
265
265
266 $ hg bookmark -r 1 -d Z
266 $ hg bookmark -r 1 -d Z
267 abort: --rev is incompatible with --delete
267 abort: --rev is incompatible with --delete
268 [255]
268 [255]
269
269
270 $ hg bookmark -r 1 -m Z Y
270 $ hg bookmark -r 1 -m Z Y
271 abort: --rev is incompatible with --rename
271 abort: --rev is incompatible with --rename
272 [255]
272 [255]
273
273
274 force bookmark with existing name
274 force bookmark with existing name
275
275
276 $ hg bookmark -f Z
276 $ hg bookmark -f Z
277
277
278 list bookmarks
278 list bookmarks
279
279
280 $ hg bookmark
280 $ hg bookmark
281 X2 1:925d80f479bb
281 X2 1:925d80f479bb
282 Y 2:db815d6d32e6
282 Y 2:db815d6d32e6
283 * Z 2:db815d6d32e6
283 * Z 2:db815d6d32e6
284 x y 2:db815d6d32e6
284 x y 2:db815d6d32e6
285
285
286 revision but no bookmark name
286 revision but no bookmark name
287
287
288 $ hg bookmark -r .
288 $ hg bookmark -r .
289 abort: bookmark name required
289 abort: bookmark name required
290 [255]
290 [255]
291
291
292 bookmark name with whitespace only
292 bookmark name with whitespace only
293
293
294 $ hg bookmark ' '
294 $ hg bookmark ' '
295 abort: bookmark names cannot consist entirely of whitespace
295 abort: bookmark names cannot consist entirely of whitespace
296 [255]
296 [255]
297
297
298 $ hg bookmark -m Y ' '
298 $ hg bookmark -m Y ' '
299 abort: bookmark names cannot consist entirely of whitespace
299 abort: bookmark names cannot consist entirely of whitespace
300 [255]
300 [255]
301
301
302 invalid bookmark
302 invalid bookmark
303
303
304 $ hg bookmark 'foo:bar'
304 $ hg bookmark 'foo:bar'
305 abort: ':' cannot be used in a bookmark name
305 abort: ':' cannot be used in a name
306 [255]
306 [255]
307
307
308 $ hg bookmark 'foo
308 $ hg bookmark 'foo
309 > bar'
309 > bar'
310 abort: '\n' cannot be used in a bookmark name
310 abort: '\n' cannot be used in a name
311 [255]
311 [255]
312
312
313 the bookmark extension should be ignored now that it is part of core
313 the bookmark extension should be ignored now that it is part of core
314
314
315 $ echo "[extensions]" >> $HGRCPATH
315 $ echo "[extensions]" >> $HGRCPATH
316 $ echo "bookmarks=" >> $HGRCPATH
316 $ echo "bookmarks=" >> $HGRCPATH
317 $ hg bookmarks
317 $ hg bookmarks
318 X2 1:925d80f479bb
318 X2 1:925d80f479bb
319 Y 2:db815d6d32e6
319 Y 2:db815d6d32e6
320 * Z 2:db815d6d32e6
320 * Z 2:db815d6d32e6
321 x y 2:db815d6d32e6
321 x y 2:db815d6d32e6
322
322
323 test summary
323 test summary
324
324
325 $ hg summary
325 $ hg summary
326 parent: 2:db815d6d32e6 tip
326 parent: 2:db815d6d32e6 tip
327 2
327 2
328 branch: default
328 branch: default
329 bookmarks: *Z Y x y
329 bookmarks: *Z Y x y
330 commit: (clean)
330 commit: (clean)
331 update: 1 new changesets, 2 branch heads (merge)
331 update: 1 new changesets, 2 branch heads (merge)
332
332
333 test id
333 test id
334
334
335 $ hg id
335 $ hg id
336 db815d6d32e6 tip Y/Z/x y
336 db815d6d32e6 tip Y/Z/x y
337
337
338 test rollback
338 test rollback
339
339
340 $ echo foo > f1
340 $ echo foo > f1
341 $ hg ci -Amr
341 $ hg ci -Amr
342 adding f1
342 adding f1
343 $ hg bookmark -f Y -r 1
343 $ hg bookmark -f Y -r 1
344 $ hg bookmark -f Z -r 1
344 $ hg bookmark -f Z -r 1
345 $ hg rollback
345 $ hg rollback
346 repository tip rolled back to revision 2 (undo commit)
346 repository tip rolled back to revision 2 (undo commit)
347 working directory now based on revision 2
347 working directory now based on revision 2
348 $ hg bookmarks
348 $ hg bookmarks
349 X2 1:925d80f479bb
349 X2 1:925d80f479bb
350 Y 2:db815d6d32e6
350 Y 2:db815d6d32e6
351 * Z 2:db815d6d32e6
351 * Z 2:db815d6d32e6
352 x y 2:db815d6d32e6
352 x y 2:db815d6d32e6
353
353
354 test clone
354 test clone
355
355
356 $ hg bookmark -r 2 -i @
356 $ hg bookmark -r 2 -i @
357 $ hg bookmark -r 2 -i a@
357 $ hg bookmark -r 2 -i a@
358 $ hg bookmarks
358 $ hg bookmarks
359 @ 2:db815d6d32e6
359 @ 2:db815d6d32e6
360 X2 1:925d80f479bb
360 X2 1:925d80f479bb
361 Y 2:db815d6d32e6
361 Y 2:db815d6d32e6
362 * Z 2:db815d6d32e6
362 * Z 2:db815d6d32e6
363 a@ 2:db815d6d32e6
363 a@ 2:db815d6d32e6
364 x y 2:db815d6d32e6
364 x y 2:db815d6d32e6
365 $ hg clone . cloned-bookmarks
365 $ hg clone . cloned-bookmarks
366 updating to branch default
366 updating to branch default
367 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
367 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
368 $ hg -R cloned-bookmarks bookmarks
368 $ hg -R cloned-bookmarks bookmarks
369 @ 2:db815d6d32e6
369 @ 2:db815d6d32e6
370 X2 1:925d80f479bb
370 X2 1:925d80f479bb
371 Y 2:db815d6d32e6
371 Y 2:db815d6d32e6
372 Z 2:db815d6d32e6
372 Z 2:db815d6d32e6
373 a@ 2:db815d6d32e6
373 a@ 2:db815d6d32e6
374 x y 2:db815d6d32e6
374 x y 2:db815d6d32e6
375
375
376 test clone with pull protocol
376 test clone with pull protocol
377
377
378 $ hg clone --pull . cloned-bookmarks-pull
378 $ hg clone --pull . cloned-bookmarks-pull
379 requesting all changes
379 requesting all changes
380 adding changesets
380 adding changesets
381 adding manifests
381 adding manifests
382 adding file changes
382 adding file changes
383 added 3 changesets with 3 changes to 3 files (+1 heads)
383 added 3 changesets with 3 changes to 3 files (+1 heads)
384 updating to branch default
384 updating to branch default
385 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
385 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
386 $ hg -R cloned-bookmarks-pull bookmarks
386 $ hg -R cloned-bookmarks-pull bookmarks
387 @ 2:db815d6d32e6
387 @ 2:db815d6d32e6
388 X2 1:925d80f479bb
388 X2 1:925d80f479bb
389 Y 2:db815d6d32e6
389 Y 2:db815d6d32e6
390 Z 2:db815d6d32e6
390 Z 2:db815d6d32e6
391 a@ 2:db815d6d32e6
391 a@ 2:db815d6d32e6
392 x y 2:db815d6d32e6
392 x y 2:db815d6d32e6
393
393
394 $ hg bookmark -d @
394 $ hg bookmark -d @
395 $ hg bookmark -d a@
395 $ hg bookmark -d a@
396
396
397 test clone with a specific revision
397 test clone with a specific revision
398
398
399 $ hg clone -r 925d80 . cloned-bookmarks-rev
399 $ hg clone -r 925d80 . cloned-bookmarks-rev
400 adding changesets
400 adding changesets
401 adding manifests
401 adding manifests
402 adding file changes
402 adding file changes
403 added 2 changesets with 2 changes to 2 files
403 added 2 changesets with 2 changes to 2 files
404 updating to branch default
404 updating to branch default
405 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
405 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
406 $ hg -R cloned-bookmarks-rev bookmarks
406 $ hg -R cloned-bookmarks-rev bookmarks
407 X2 1:925d80f479bb
407 X2 1:925d80f479bb
408
408
409 test clone with update to a bookmark
409 test clone with update to a bookmark
410
410
411 $ hg clone -u Z . cloned-bookmarks-update
411 $ hg clone -u Z . cloned-bookmarks-update
412 updating to branch default
412 updating to branch default
413 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
413 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
414 $ hg -R cloned-bookmarks-update bookmarks
414 $ hg -R cloned-bookmarks-update bookmarks
415 X2 1:925d80f479bb
415 X2 1:925d80f479bb
416 Y 2:db815d6d32e6
416 Y 2:db815d6d32e6
417 * Z 2:db815d6d32e6
417 * Z 2:db815d6d32e6
418 x y 2:db815d6d32e6
418 x y 2:db815d6d32e6
419
419
420 create bundle with two heads
420 create bundle with two heads
421
421
422 $ hg clone . tobundle
422 $ hg clone . tobundle
423 updating to branch default
423 updating to branch default
424 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
424 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
425 $ echo x > tobundle/x
425 $ echo x > tobundle/x
426 $ hg -R tobundle add tobundle/x
426 $ hg -R tobundle add tobundle/x
427 $ hg -R tobundle commit -m'x'
427 $ hg -R tobundle commit -m'x'
428 $ hg -R tobundle update -r -2
428 $ hg -R tobundle update -r -2
429 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
429 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
430 $ echo y > tobundle/y
430 $ echo y > tobundle/y
431 $ hg -R tobundle branch test
431 $ hg -R tobundle branch test
432 marked working directory as branch test
432 marked working directory as branch test
433 (branches are permanent and global, did you want a bookmark?)
433 (branches are permanent and global, did you want a bookmark?)
434 $ hg -R tobundle add tobundle/y
434 $ hg -R tobundle add tobundle/y
435 $ hg -R tobundle commit -m'y'
435 $ hg -R tobundle commit -m'y'
436 $ hg -R tobundle bundle tobundle.hg
436 $ hg -R tobundle bundle tobundle.hg
437 searching for changes
437 searching for changes
438 2 changesets found
438 2 changesets found
439 $ hg unbundle tobundle.hg
439 $ hg unbundle tobundle.hg
440 adding changesets
440 adding changesets
441 adding manifests
441 adding manifests
442 adding file changes
442 adding file changes
443 added 2 changesets with 2 changes to 2 files (+1 heads)
443 added 2 changesets with 2 changes to 2 files (+1 heads)
444 (run 'hg heads' to see heads, 'hg merge' to merge)
444 (run 'hg heads' to see heads, 'hg merge' to merge)
445 $ hg update
445 $ hg update
446 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
446 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
447 $ hg bookmarks
447 $ hg bookmarks
448 X2 1:925d80f479bb
448 X2 1:925d80f479bb
449 Y 2:db815d6d32e6
449 Y 2:db815d6d32e6
450 * Z 3:125c9a1d6df6
450 * Z 3:125c9a1d6df6
451 x y 2:db815d6d32e6
451 x y 2:db815d6d32e6
452
452
453 test wrongly formated bookmark
453 test wrongly formated bookmark
454
454
455 $ echo '' >> .hg/bookmarks
455 $ echo '' >> .hg/bookmarks
456 $ hg bookmarks
456 $ hg bookmarks
457 X2 1:925d80f479bb
457 X2 1:925d80f479bb
458 Y 2:db815d6d32e6
458 Y 2:db815d6d32e6
459 * Z 3:125c9a1d6df6
459 * Z 3:125c9a1d6df6
460 x y 2:db815d6d32e6
460 x y 2:db815d6d32e6
461 $ echo "Ican'thasformatedlines" >> .hg/bookmarks
461 $ echo "Ican'thasformatedlines" >> .hg/bookmarks
462 $ hg bookmarks
462 $ hg bookmarks
463 malformed line in .hg/bookmarks: "Ican'thasformatedlines"
463 malformed line in .hg/bookmarks: "Ican'thasformatedlines"
464 X2 1:925d80f479bb
464 X2 1:925d80f479bb
465 Y 2:db815d6d32e6
465 Y 2:db815d6d32e6
466 * Z 3:125c9a1d6df6
466 * Z 3:125c9a1d6df6
467 x y 2:db815d6d32e6
467 x y 2:db815d6d32e6
468
468
469 test missing revisions
469 test missing revisions
470
470
471 $ echo "925d80f479bc z" > .hg/bookmarks
471 $ echo "925d80f479bc z" > .hg/bookmarks
472 $ hg book
472 $ hg book
473 no bookmarks set
473 no bookmarks set
474
474
475 test stripping a non-checked-out but bookmarked revision
475 test stripping a non-checked-out but bookmarked revision
476
476
477 $ hg --config extensions.graphlog= log --graph
477 $ hg --config extensions.graphlog= log --graph
478 o changeset: 4:9ba5f110a0b3
478 o changeset: 4:9ba5f110a0b3
479 | branch: test
479 | branch: test
480 | tag: tip
480 | tag: tip
481 | parent: 2:db815d6d32e6
481 | parent: 2:db815d6d32e6
482 | user: test
482 | user: test
483 | date: Thu Jan 01 00:00:00 1970 +0000
483 | date: Thu Jan 01 00:00:00 1970 +0000
484 | summary: y
484 | summary: y
485 |
485 |
486 | @ changeset: 3:125c9a1d6df6
486 | @ changeset: 3:125c9a1d6df6
487 |/ user: test
487 |/ user: test
488 | date: Thu Jan 01 00:00:00 1970 +0000
488 | date: Thu Jan 01 00:00:00 1970 +0000
489 | summary: x
489 | summary: x
490 |
490 |
491 o changeset: 2:db815d6d32e6
491 o changeset: 2:db815d6d32e6
492 | parent: 0:f7b1eb17ad24
492 | parent: 0:f7b1eb17ad24
493 | user: test
493 | user: test
494 | date: Thu Jan 01 00:00:00 1970 +0000
494 | date: Thu Jan 01 00:00:00 1970 +0000
495 | summary: 2
495 | summary: 2
496 |
496 |
497 | o changeset: 1:925d80f479bb
497 | o changeset: 1:925d80f479bb
498 |/ user: test
498 |/ user: test
499 | date: Thu Jan 01 00:00:00 1970 +0000
499 | date: Thu Jan 01 00:00:00 1970 +0000
500 | summary: 1
500 | summary: 1
501 |
501 |
502 o changeset: 0:f7b1eb17ad24
502 o changeset: 0:f7b1eb17ad24
503 user: test
503 user: test
504 date: Thu Jan 01 00:00:00 1970 +0000
504 date: Thu Jan 01 00:00:00 1970 +0000
505 summary: 0
505 summary: 0
506
506
507 $ hg book should-end-on-two
507 $ hg book should-end-on-two
508 $ hg co --clean 4
508 $ hg co --clean 4
509 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
509 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
510 $ hg book four
510 $ hg book four
511 $ hg --config extensions.mq= strip 3
511 $ hg --config extensions.mq= strip 3
512 saved backup bundle to * (glob)
512 saved backup bundle to * (glob)
513 should-end-on-two should end up pointing to revision 2, as that's the
513 should-end-on-two should end up pointing to revision 2, as that's the
514 tipmost surviving ancestor of the stripped revision.
514 tipmost surviving ancestor of the stripped revision.
515 $ hg --config extensions.graphlog= log --graph
515 $ hg --config extensions.graphlog= log --graph
516 @ changeset: 3:9ba5f110a0b3
516 @ changeset: 3:9ba5f110a0b3
517 | branch: test
517 | branch: test
518 | bookmark: four
518 | bookmark: four
519 | tag: tip
519 | tag: tip
520 | user: test
520 | user: test
521 | date: Thu Jan 01 00:00:00 1970 +0000
521 | date: Thu Jan 01 00:00:00 1970 +0000
522 | summary: y
522 | summary: y
523 |
523 |
524 o changeset: 2:db815d6d32e6
524 o changeset: 2:db815d6d32e6
525 | bookmark: should-end-on-two
525 | bookmark: should-end-on-two
526 | parent: 0:f7b1eb17ad24
526 | parent: 0:f7b1eb17ad24
527 | user: test
527 | user: test
528 | date: Thu Jan 01 00:00:00 1970 +0000
528 | date: Thu Jan 01 00:00:00 1970 +0000
529 | summary: 2
529 | summary: 2
530 |
530 |
531 | o changeset: 1:925d80f479bb
531 | o changeset: 1:925d80f479bb
532 |/ user: test
532 |/ user: test
533 | date: Thu Jan 01 00:00:00 1970 +0000
533 | date: Thu Jan 01 00:00:00 1970 +0000
534 | summary: 1
534 | summary: 1
535 |
535 |
536 o changeset: 0:f7b1eb17ad24
536 o changeset: 0:f7b1eb17ad24
537 user: test
537 user: test
538 date: Thu Jan 01 00:00:00 1970 +0000
538 date: Thu Jan 01 00:00:00 1970 +0000
539 summary: 0
539 summary: 0
540
540
@@ -1,425 +1,425 b''
1 $ hg init a
1 $ hg init a
2 $ cd a
2 $ cd a
3 $ echo 'root' >root
3 $ echo 'root' >root
4 $ hg add root
4 $ hg add root
5 $ hg commit -d '0 0' -m "Adding root node"
5 $ hg commit -d '0 0' -m "Adding root node"
6
6
7 $ echo 'a' >a
7 $ echo 'a' >a
8 $ hg add a
8 $ hg add a
9 $ hg branch a
9 $ hg branch a
10 marked working directory as branch a
10 marked working directory as branch a
11 (branches are permanent and global, did you want a bookmark?)
11 (branches are permanent and global, did you want a bookmark?)
12 $ hg commit -d '1 0' -m "Adding a branch"
12 $ hg commit -d '1 0' -m "Adding a branch"
13
13
14 $ hg branch q
14 $ hg branch q
15 marked working directory as branch q
15 marked working directory as branch q
16 (branches are permanent and global, did you want a bookmark?)
16 (branches are permanent and global, did you want a bookmark?)
17 $ echo 'aa' >a
17 $ echo 'aa' >a
18 $ hg branch -C
18 $ hg branch -C
19 reset working directory to branch a
19 reset working directory to branch a
20 $ hg commit -d '2 0' -m "Adding to a branch"
20 $ hg commit -d '2 0' -m "Adding to a branch"
21
21
22 $ hg update -C 0
22 $ hg update -C 0
23 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
23 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
24 $ echo 'b' >b
24 $ echo 'b' >b
25 $ hg add b
25 $ hg add b
26 $ hg branch b
26 $ hg branch b
27 marked working directory as branch b
27 marked working directory as branch b
28 (branches are permanent and global, did you want a bookmark?)
28 (branches are permanent and global, did you want a bookmark?)
29 $ hg commit -d '2 0' -m "Adding b branch"
29 $ hg commit -d '2 0' -m "Adding b branch"
30
30
31 $ echo 'bh1' >bh1
31 $ echo 'bh1' >bh1
32 $ hg add bh1
32 $ hg add bh1
33 $ hg commit -d '3 0' -m "Adding b branch head 1"
33 $ hg commit -d '3 0' -m "Adding b branch head 1"
34
34
35 $ hg update -C 2
35 $ hg update -C 2
36 1 files updated, 0 files merged, 2 files removed, 0 files unresolved
36 1 files updated, 0 files merged, 2 files removed, 0 files unresolved
37 $ echo 'bh2' >bh2
37 $ echo 'bh2' >bh2
38 $ hg add bh2
38 $ hg add bh2
39 $ hg commit -d '4 0' -m "Adding b branch head 2"
39 $ hg commit -d '4 0' -m "Adding b branch head 2"
40
40
41 $ echo 'c' >c
41 $ echo 'c' >c
42 $ hg add c
42 $ hg add c
43 $ hg branch c
43 $ hg branch c
44 marked working directory as branch c
44 marked working directory as branch c
45 (branches are permanent and global, did you want a bookmark?)
45 (branches are permanent and global, did you want a bookmark?)
46 $ hg commit -d '5 0' -m "Adding c branch"
46 $ hg commit -d '5 0' -m "Adding c branch"
47
47
48 reserved names
48 reserved names
49
49
50 $ hg branch tip
50 $ hg branch tip
51 abort: the name 'tip' is reserved
51 abort: the name 'tip' is reserved
52 [255]
52 [255]
53 $ hg branch null
53 $ hg branch null
54 abort: the name 'null' is reserved
54 abort: the name 'null' is reserved
55 [255]
55 [255]
56 $ hg branch .
56 $ hg branch .
57 abort: the name '.' is reserved
57 abort: the name '.' is reserved
58 [255]
58 [255]
59
59
60 invalid characters
60 invalid characters
61
61
62 $ hg branch 'foo:bar'
62 $ hg branch 'foo:bar'
63 abort: ':' cannot be used in a branch name
63 abort: ':' cannot be used in a name
64 [255]
64 [255]
65
65
66 $ hg branch 'foo
66 $ hg branch 'foo
67 > bar'
67 > bar'
68 abort: '\n' cannot be used in a branch name
68 abort: '\n' cannot be used in a name
69 [255]
69 [255]
70
70
71 $ echo 'd' >d
71 $ echo 'd' >d
72 $ hg add d
72 $ hg add d
73 $ hg branch 'a branch name much longer than the default justification used by branches'
73 $ hg branch 'a branch name much longer than the default justification used by branches'
74 marked working directory as branch a branch name much longer than the default justification used by branches
74 marked working directory as branch a branch name much longer than the default justification used by branches
75 (branches are permanent and global, did you want a bookmark?)
75 (branches are permanent and global, did you want a bookmark?)
76 $ hg commit -d '6 0' -m "Adding d branch"
76 $ hg commit -d '6 0' -m "Adding d branch"
77
77
78 $ hg branches
78 $ hg branches
79 a branch name much longer than the default justification used by branches 7:10ff5895aa57
79 a branch name much longer than the default justification used by branches 7:10ff5895aa57
80 b 4:aee39cd168d0
80 b 4:aee39cd168d0
81 c 6:589736a22561 (inactive)
81 c 6:589736a22561 (inactive)
82 a 5:d8cbc61dbaa6 (inactive)
82 a 5:d8cbc61dbaa6 (inactive)
83 default 0:19709c5a4e75 (inactive)
83 default 0:19709c5a4e75 (inactive)
84
84
85 -------
85 -------
86
86
87 $ hg branches -a
87 $ hg branches -a
88 a branch name much longer than the default justification used by branches 7:10ff5895aa57
88 a branch name much longer than the default justification used by branches 7:10ff5895aa57
89 b 4:aee39cd168d0
89 b 4:aee39cd168d0
90
90
91 --- Branch a
91 --- Branch a
92
92
93 $ hg log -b a
93 $ hg log -b a
94 changeset: 5:d8cbc61dbaa6
94 changeset: 5:d8cbc61dbaa6
95 branch: a
95 branch: a
96 parent: 2:881fe2b92ad0
96 parent: 2:881fe2b92ad0
97 user: test
97 user: test
98 date: Thu Jan 01 00:00:04 1970 +0000
98 date: Thu Jan 01 00:00:04 1970 +0000
99 summary: Adding b branch head 2
99 summary: Adding b branch head 2
100
100
101 changeset: 2:881fe2b92ad0
101 changeset: 2:881fe2b92ad0
102 branch: a
102 branch: a
103 user: test
103 user: test
104 date: Thu Jan 01 00:00:02 1970 +0000
104 date: Thu Jan 01 00:00:02 1970 +0000
105 summary: Adding to a branch
105 summary: Adding to a branch
106
106
107 changeset: 1:dd6b440dd85a
107 changeset: 1:dd6b440dd85a
108 branch: a
108 branch: a
109 user: test
109 user: test
110 date: Thu Jan 01 00:00:01 1970 +0000
110 date: Thu Jan 01 00:00:01 1970 +0000
111 summary: Adding a branch
111 summary: Adding a branch
112
112
113
113
114 ---- Branch b
114 ---- Branch b
115
115
116 $ hg log -b b
116 $ hg log -b b
117 changeset: 4:aee39cd168d0
117 changeset: 4:aee39cd168d0
118 branch: b
118 branch: b
119 user: test
119 user: test
120 date: Thu Jan 01 00:00:03 1970 +0000
120 date: Thu Jan 01 00:00:03 1970 +0000
121 summary: Adding b branch head 1
121 summary: Adding b branch head 1
122
122
123 changeset: 3:ac22033332d1
123 changeset: 3:ac22033332d1
124 branch: b
124 branch: b
125 parent: 0:19709c5a4e75
125 parent: 0:19709c5a4e75
126 user: test
126 user: test
127 date: Thu Jan 01 00:00:02 1970 +0000
127 date: Thu Jan 01 00:00:02 1970 +0000
128 summary: Adding b branch
128 summary: Adding b branch
129
129
130
130
131 ---- going to test branch closing
131 ---- going to test branch closing
132
132
133 $ hg branches
133 $ hg branches
134 a branch name much longer than the default justification used by branches 7:10ff5895aa57
134 a branch name much longer than the default justification used by branches 7:10ff5895aa57
135 b 4:aee39cd168d0
135 b 4:aee39cd168d0
136 c 6:589736a22561 (inactive)
136 c 6:589736a22561 (inactive)
137 a 5:d8cbc61dbaa6 (inactive)
137 a 5:d8cbc61dbaa6 (inactive)
138 default 0:19709c5a4e75 (inactive)
138 default 0:19709c5a4e75 (inactive)
139 $ hg up -C b
139 $ hg up -C b
140 2 files updated, 0 files merged, 4 files removed, 0 files unresolved
140 2 files updated, 0 files merged, 4 files removed, 0 files unresolved
141 $ echo 'xxx1' >> b
141 $ echo 'xxx1' >> b
142 $ hg commit -d '7 0' -m 'adding cset to branch b'
142 $ hg commit -d '7 0' -m 'adding cset to branch b'
143 $ hg up -C aee39cd168d0
143 $ hg up -C aee39cd168d0
144 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
144 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
145 $ echo 'xxx2' >> b
145 $ echo 'xxx2' >> b
146 $ hg commit -d '8 0' -m 'adding head to branch b'
146 $ hg commit -d '8 0' -m 'adding head to branch b'
147 created new head
147 created new head
148 $ echo 'xxx3' >> b
148 $ echo 'xxx3' >> b
149 $ hg commit -d '9 0' -m 'adding another cset to branch b'
149 $ hg commit -d '9 0' -m 'adding another cset to branch b'
150 $ hg branches
150 $ hg branches
151 b 10:bfbe841b666e
151 b 10:bfbe841b666e
152 a branch name much longer than the default justification used by branches 7:10ff5895aa57
152 a branch name much longer than the default justification used by branches 7:10ff5895aa57
153 c 6:589736a22561 (inactive)
153 c 6:589736a22561 (inactive)
154 a 5:d8cbc61dbaa6 (inactive)
154 a 5:d8cbc61dbaa6 (inactive)
155 default 0:19709c5a4e75 (inactive)
155 default 0:19709c5a4e75 (inactive)
156 $ hg heads --closed
156 $ hg heads --closed
157 changeset: 10:bfbe841b666e
157 changeset: 10:bfbe841b666e
158 branch: b
158 branch: b
159 tag: tip
159 tag: tip
160 user: test
160 user: test
161 date: Thu Jan 01 00:00:09 1970 +0000
161 date: Thu Jan 01 00:00:09 1970 +0000
162 summary: adding another cset to branch b
162 summary: adding another cset to branch b
163
163
164 changeset: 8:eebb944467c9
164 changeset: 8:eebb944467c9
165 branch: b
165 branch: b
166 parent: 4:aee39cd168d0
166 parent: 4:aee39cd168d0
167 user: test
167 user: test
168 date: Thu Jan 01 00:00:07 1970 +0000
168 date: Thu Jan 01 00:00:07 1970 +0000
169 summary: adding cset to branch b
169 summary: adding cset to branch b
170
170
171 changeset: 7:10ff5895aa57
171 changeset: 7:10ff5895aa57
172 branch: a branch name much longer than the default justification used by branches
172 branch: a branch name much longer than the default justification used by branches
173 user: test
173 user: test
174 date: Thu Jan 01 00:00:06 1970 +0000
174 date: Thu Jan 01 00:00:06 1970 +0000
175 summary: Adding d branch
175 summary: Adding d branch
176
176
177 changeset: 6:589736a22561
177 changeset: 6:589736a22561
178 branch: c
178 branch: c
179 user: test
179 user: test
180 date: Thu Jan 01 00:00:05 1970 +0000
180 date: Thu Jan 01 00:00:05 1970 +0000
181 summary: Adding c branch
181 summary: Adding c branch
182
182
183 changeset: 5:d8cbc61dbaa6
183 changeset: 5:d8cbc61dbaa6
184 branch: a
184 branch: a
185 parent: 2:881fe2b92ad0
185 parent: 2:881fe2b92ad0
186 user: test
186 user: test
187 date: Thu Jan 01 00:00:04 1970 +0000
187 date: Thu Jan 01 00:00:04 1970 +0000
188 summary: Adding b branch head 2
188 summary: Adding b branch head 2
189
189
190 changeset: 0:19709c5a4e75
190 changeset: 0:19709c5a4e75
191 user: test
191 user: test
192 date: Thu Jan 01 00:00:00 1970 +0000
192 date: Thu Jan 01 00:00:00 1970 +0000
193 summary: Adding root node
193 summary: Adding root node
194
194
195 $ hg heads
195 $ hg heads
196 changeset: 10:bfbe841b666e
196 changeset: 10:bfbe841b666e
197 branch: b
197 branch: b
198 tag: tip
198 tag: tip
199 user: test
199 user: test
200 date: Thu Jan 01 00:00:09 1970 +0000
200 date: Thu Jan 01 00:00:09 1970 +0000
201 summary: adding another cset to branch b
201 summary: adding another cset to branch b
202
202
203 changeset: 8:eebb944467c9
203 changeset: 8:eebb944467c9
204 branch: b
204 branch: b
205 parent: 4:aee39cd168d0
205 parent: 4:aee39cd168d0
206 user: test
206 user: test
207 date: Thu Jan 01 00:00:07 1970 +0000
207 date: Thu Jan 01 00:00:07 1970 +0000
208 summary: adding cset to branch b
208 summary: adding cset to branch b
209
209
210 changeset: 7:10ff5895aa57
210 changeset: 7:10ff5895aa57
211 branch: a branch name much longer than the default justification used by branches
211 branch: a branch name much longer than the default justification used by branches
212 user: test
212 user: test
213 date: Thu Jan 01 00:00:06 1970 +0000
213 date: Thu Jan 01 00:00:06 1970 +0000
214 summary: Adding d branch
214 summary: Adding d branch
215
215
216 changeset: 6:589736a22561
216 changeset: 6:589736a22561
217 branch: c
217 branch: c
218 user: test
218 user: test
219 date: Thu Jan 01 00:00:05 1970 +0000
219 date: Thu Jan 01 00:00:05 1970 +0000
220 summary: Adding c branch
220 summary: Adding c branch
221
221
222 changeset: 5:d8cbc61dbaa6
222 changeset: 5:d8cbc61dbaa6
223 branch: a
223 branch: a
224 parent: 2:881fe2b92ad0
224 parent: 2:881fe2b92ad0
225 user: test
225 user: test
226 date: Thu Jan 01 00:00:04 1970 +0000
226 date: Thu Jan 01 00:00:04 1970 +0000
227 summary: Adding b branch head 2
227 summary: Adding b branch head 2
228
228
229 changeset: 0:19709c5a4e75
229 changeset: 0:19709c5a4e75
230 user: test
230 user: test
231 date: Thu Jan 01 00:00:00 1970 +0000
231 date: Thu Jan 01 00:00:00 1970 +0000
232 summary: Adding root node
232 summary: Adding root node
233
233
234 $ hg commit -d '9 0' --close-branch -m 'prune bad branch'
234 $ hg commit -d '9 0' --close-branch -m 'prune bad branch'
235 $ hg branches -a
235 $ hg branches -a
236 b 8:eebb944467c9
236 b 8:eebb944467c9
237 a branch name much longer than the default justification used by branches 7:10ff5895aa57
237 a branch name much longer than the default justification used by branches 7:10ff5895aa57
238 $ hg up -C b
238 $ hg up -C b
239 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
239 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
240 $ hg commit -d '9 0' --close-branch -m 'close this part branch too'
240 $ hg commit -d '9 0' --close-branch -m 'close this part branch too'
241
241
242 --- b branch should be inactive
242 --- b branch should be inactive
243
243
244 $ hg branches
244 $ hg branches
245 a branch name much longer than the default justification used by branches 7:10ff5895aa57
245 a branch name much longer than the default justification used by branches 7:10ff5895aa57
246 c 6:589736a22561 (inactive)
246 c 6:589736a22561 (inactive)
247 a 5:d8cbc61dbaa6 (inactive)
247 a 5:d8cbc61dbaa6 (inactive)
248 default 0:19709c5a4e75 (inactive)
248 default 0:19709c5a4e75 (inactive)
249 $ hg branches -c
249 $ hg branches -c
250 a branch name much longer than the default justification used by branches 7:10ff5895aa57
250 a branch name much longer than the default justification used by branches 7:10ff5895aa57
251 b 12:e3d49c0575d8 (closed)
251 b 12:e3d49c0575d8 (closed)
252 c 6:589736a22561 (inactive)
252 c 6:589736a22561 (inactive)
253 a 5:d8cbc61dbaa6 (inactive)
253 a 5:d8cbc61dbaa6 (inactive)
254 default 0:19709c5a4e75 (inactive)
254 default 0:19709c5a4e75 (inactive)
255 $ hg branches -a
255 $ hg branches -a
256 a branch name much longer than the default justification used by branches 7:10ff5895aa57
256 a branch name much longer than the default justification used by branches 7:10ff5895aa57
257 $ hg branches -q
257 $ hg branches -q
258 a branch name much longer than the default justification used by branches
258 a branch name much longer than the default justification used by branches
259 c
259 c
260 a
260 a
261 default
261 default
262 $ hg heads b
262 $ hg heads b
263 no open branch heads found on branches b
263 no open branch heads found on branches b
264 [1]
264 [1]
265 $ hg heads --closed b
265 $ hg heads --closed b
266 changeset: 12:e3d49c0575d8
266 changeset: 12:e3d49c0575d8
267 branch: b
267 branch: b
268 tag: tip
268 tag: tip
269 parent: 8:eebb944467c9
269 parent: 8:eebb944467c9
270 user: test
270 user: test
271 date: Thu Jan 01 00:00:09 1970 +0000
271 date: Thu Jan 01 00:00:09 1970 +0000
272 summary: close this part branch too
272 summary: close this part branch too
273
273
274 changeset: 11:d3f163457ebf
274 changeset: 11:d3f163457ebf
275 branch: b
275 branch: b
276 user: test
276 user: test
277 date: Thu Jan 01 00:00:09 1970 +0000
277 date: Thu Jan 01 00:00:09 1970 +0000
278 summary: prune bad branch
278 summary: prune bad branch
279
279
280 $ echo 'xxx4' >> b
280 $ echo 'xxx4' >> b
281 $ hg commit -d '9 0' -m 'reopen branch with a change'
281 $ hg commit -d '9 0' -m 'reopen branch with a change'
282 reopening closed branch head 12
282 reopening closed branch head 12
283
283
284 --- branch b is back in action
284 --- branch b is back in action
285
285
286 $ hg branches -a
286 $ hg branches -a
287 b 13:e23b5505d1ad
287 b 13:e23b5505d1ad
288 a branch name much longer than the default justification used by branches 7:10ff5895aa57
288 a branch name much longer than the default justification used by branches 7:10ff5895aa57
289
289
290 ---- test heads listings
290 ---- test heads listings
291
291
292 $ hg heads
292 $ hg heads
293 changeset: 13:e23b5505d1ad
293 changeset: 13:e23b5505d1ad
294 branch: b
294 branch: b
295 tag: tip
295 tag: tip
296 user: test
296 user: test
297 date: Thu Jan 01 00:00:09 1970 +0000
297 date: Thu Jan 01 00:00:09 1970 +0000
298 summary: reopen branch with a change
298 summary: reopen branch with a change
299
299
300 changeset: 7:10ff5895aa57
300 changeset: 7:10ff5895aa57
301 branch: a branch name much longer than the default justification used by branches
301 branch: a branch name much longer than the default justification used by branches
302 user: test
302 user: test
303 date: Thu Jan 01 00:00:06 1970 +0000
303 date: Thu Jan 01 00:00:06 1970 +0000
304 summary: Adding d branch
304 summary: Adding d branch
305
305
306 changeset: 6:589736a22561
306 changeset: 6:589736a22561
307 branch: c
307 branch: c
308 user: test
308 user: test
309 date: Thu Jan 01 00:00:05 1970 +0000
309 date: Thu Jan 01 00:00:05 1970 +0000
310 summary: Adding c branch
310 summary: Adding c branch
311
311
312 changeset: 5:d8cbc61dbaa6
312 changeset: 5:d8cbc61dbaa6
313 branch: a
313 branch: a
314 parent: 2:881fe2b92ad0
314 parent: 2:881fe2b92ad0
315 user: test
315 user: test
316 date: Thu Jan 01 00:00:04 1970 +0000
316 date: Thu Jan 01 00:00:04 1970 +0000
317 summary: Adding b branch head 2
317 summary: Adding b branch head 2
318
318
319 changeset: 0:19709c5a4e75
319 changeset: 0:19709c5a4e75
320 user: test
320 user: test
321 date: Thu Jan 01 00:00:00 1970 +0000
321 date: Thu Jan 01 00:00:00 1970 +0000
322 summary: Adding root node
322 summary: Adding root node
323
323
324
324
325 branch default
325 branch default
326
326
327 $ hg heads default
327 $ hg heads default
328 changeset: 0:19709c5a4e75
328 changeset: 0:19709c5a4e75
329 user: test
329 user: test
330 date: Thu Jan 01 00:00:00 1970 +0000
330 date: Thu Jan 01 00:00:00 1970 +0000
331 summary: Adding root node
331 summary: Adding root node
332
332
333
333
334 branch a
334 branch a
335
335
336 $ hg heads a
336 $ hg heads a
337 changeset: 5:d8cbc61dbaa6
337 changeset: 5:d8cbc61dbaa6
338 branch: a
338 branch: a
339 parent: 2:881fe2b92ad0
339 parent: 2:881fe2b92ad0
340 user: test
340 user: test
341 date: Thu Jan 01 00:00:04 1970 +0000
341 date: Thu Jan 01 00:00:04 1970 +0000
342 summary: Adding b branch head 2
342 summary: Adding b branch head 2
343
343
344 $ hg heads --active a
344 $ hg heads --active a
345 no open branch heads found on branches a
345 no open branch heads found on branches a
346 [1]
346 [1]
347
347
348 branch b
348 branch b
349
349
350 $ hg heads b
350 $ hg heads b
351 changeset: 13:e23b5505d1ad
351 changeset: 13:e23b5505d1ad
352 branch: b
352 branch: b
353 tag: tip
353 tag: tip
354 user: test
354 user: test
355 date: Thu Jan 01 00:00:09 1970 +0000
355 date: Thu Jan 01 00:00:09 1970 +0000
356 summary: reopen branch with a change
356 summary: reopen branch with a change
357
357
358 $ hg heads --closed b
358 $ hg heads --closed b
359 changeset: 13:e23b5505d1ad
359 changeset: 13:e23b5505d1ad
360 branch: b
360 branch: b
361 tag: tip
361 tag: tip
362 user: test
362 user: test
363 date: Thu Jan 01 00:00:09 1970 +0000
363 date: Thu Jan 01 00:00:09 1970 +0000
364 summary: reopen branch with a change
364 summary: reopen branch with a change
365
365
366 changeset: 11:d3f163457ebf
366 changeset: 11:d3f163457ebf
367 branch: b
367 branch: b
368 user: test
368 user: test
369 date: Thu Jan 01 00:00:09 1970 +0000
369 date: Thu Jan 01 00:00:09 1970 +0000
370 summary: prune bad branch
370 summary: prune bad branch
371
371
372 default branch colors:
372 default branch colors:
373
373
374 $ echo "[extensions]" >> $HGRCPATH
374 $ echo "[extensions]" >> $HGRCPATH
375 $ echo "color =" >> $HGRCPATH
375 $ echo "color =" >> $HGRCPATH
376 $ echo "[color]" >> $HGRCPATH
376 $ echo "[color]" >> $HGRCPATH
377 $ echo "mode = ansi" >> $HGRCPATH
377 $ echo "mode = ansi" >> $HGRCPATH
378
378
379 $ hg up -C c
379 $ hg up -C c
380 3 files updated, 0 files merged, 2 files removed, 0 files unresolved
380 3 files updated, 0 files merged, 2 files removed, 0 files unresolved
381 $ hg commit -d '9 0' --close-branch -m 'reclosing this branch'
381 $ hg commit -d '9 0' --close-branch -m 'reclosing this branch'
382 $ hg up -C b
382 $ hg up -C b
383 2 files updated, 0 files merged, 3 files removed, 0 files unresolved
383 2 files updated, 0 files merged, 3 files removed, 0 files unresolved
384 $ hg branches --color=always
384 $ hg branches --color=always
385 \x1b[0;32mb\x1b[0m \x1b[0;33m 13:e23b5505d1ad\x1b[0m (esc)
385 \x1b[0;32mb\x1b[0m \x1b[0;33m 13:e23b5505d1ad\x1b[0m (esc)
386 \x1b[0;0ma branch name much longer than the default justification used by branches\x1b[0m \x1b[0;33m7:10ff5895aa57\x1b[0m (esc)
386 \x1b[0;0ma branch name much longer than the default justification used by branches\x1b[0m \x1b[0;33m7:10ff5895aa57\x1b[0m (esc)
387 \x1b[0;0ma\x1b[0m \x1b[0;33m 5:d8cbc61dbaa6\x1b[0m (inactive) (esc)
387 \x1b[0;0ma\x1b[0m \x1b[0;33m 5:d8cbc61dbaa6\x1b[0m (inactive) (esc)
388 \x1b[0;0mdefault\x1b[0m \x1b[0;33m 0:19709c5a4e75\x1b[0m (inactive) (esc)
388 \x1b[0;0mdefault\x1b[0m \x1b[0;33m 0:19709c5a4e75\x1b[0m (inactive) (esc)
389
389
390 default closed branch color:
390 default closed branch color:
391
391
392 $ hg branches --color=always --closed
392 $ hg branches --color=always --closed
393 \x1b[0;32mb\x1b[0m \x1b[0;33m 13:e23b5505d1ad\x1b[0m (esc)
393 \x1b[0;32mb\x1b[0m \x1b[0;33m 13:e23b5505d1ad\x1b[0m (esc)
394 \x1b[0;0ma branch name much longer than the default justification used by branches\x1b[0m \x1b[0;33m7:10ff5895aa57\x1b[0m (esc)
394 \x1b[0;0ma branch name much longer than the default justification used by branches\x1b[0m \x1b[0;33m7:10ff5895aa57\x1b[0m (esc)
395 \x1b[0;30;1mc\x1b[0m \x1b[0;33m 14:f894c25619d3\x1b[0m (closed) (esc)
395 \x1b[0;30;1mc\x1b[0m \x1b[0;33m 14:f894c25619d3\x1b[0m (closed) (esc)
396 \x1b[0;0ma\x1b[0m \x1b[0;33m 5:d8cbc61dbaa6\x1b[0m (inactive) (esc)
396 \x1b[0;0ma\x1b[0m \x1b[0;33m 5:d8cbc61dbaa6\x1b[0m (inactive) (esc)
397 \x1b[0;0mdefault\x1b[0m \x1b[0;33m 0:19709c5a4e75\x1b[0m (inactive) (esc)
397 \x1b[0;0mdefault\x1b[0m \x1b[0;33m 0:19709c5a4e75\x1b[0m (inactive) (esc)
398
398
399 $ echo "[extensions]" >> $HGRCPATH
399 $ echo "[extensions]" >> $HGRCPATH
400 $ echo "color =" >> $HGRCPATH
400 $ echo "color =" >> $HGRCPATH
401 $ echo "[color]" >> $HGRCPATH
401 $ echo "[color]" >> $HGRCPATH
402 $ echo "branches.active = green" >> $HGRCPATH
402 $ echo "branches.active = green" >> $HGRCPATH
403 $ echo "branches.closed = blue" >> $HGRCPATH
403 $ echo "branches.closed = blue" >> $HGRCPATH
404 $ echo "branches.current = red" >> $HGRCPATH
404 $ echo "branches.current = red" >> $HGRCPATH
405 $ echo "branches.inactive = magenta" >> $HGRCPATH
405 $ echo "branches.inactive = magenta" >> $HGRCPATH
406 $ echo "log.changeset = cyan" >> $HGRCPATH
406 $ echo "log.changeset = cyan" >> $HGRCPATH
407
407
408 custom branch colors:
408 custom branch colors:
409
409
410 $ hg branches --color=always
410 $ hg branches --color=always
411 \x1b[0;31mb\x1b[0m \x1b[0;36m 13:e23b5505d1ad\x1b[0m (esc)
411 \x1b[0;31mb\x1b[0m \x1b[0;36m 13:e23b5505d1ad\x1b[0m (esc)
412 \x1b[0;32ma branch name much longer than the default justification used by branches\x1b[0m \x1b[0;36m7:10ff5895aa57\x1b[0m (esc)
412 \x1b[0;32ma branch name much longer than the default justification used by branches\x1b[0m \x1b[0;36m7:10ff5895aa57\x1b[0m (esc)
413 \x1b[0;35ma\x1b[0m \x1b[0;36m 5:d8cbc61dbaa6\x1b[0m (inactive) (esc)
413 \x1b[0;35ma\x1b[0m \x1b[0;36m 5:d8cbc61dbaa6\x1b[0m (inactive) (esc)
414 \x1b[0;35mdefault\x1b[0m \x1b[0;36m 0:19709c5a4e75\x1b[0m (inactive) (esc)
414 \x1b[0;35mdefault\x1b[0m \x1b[0;36m 0:19709c5a4e75\x1b[0m (inactive) (esc)
415
415
416 custom closed branch color:
416 custom closed branch color:
417
417
418 $ hg branches --color=always --closed
418 $ hg branches --color=always --closed
419 \x1b[0;31mb\x1b[0m \x1b[0;36m 13:e23b5505d1ad\x1b[0m (esc)
419 \x1b[0;31mb\x1b[0m \x1b[0;36m 13:e23b5505d1ad\x1b[0m (esc)
420 \x1b[0;32ma branch name much longer than the default justification used by branches\x1b[0m \x1b[0;36m7:10ff5895aa57\x1b[0m (esc)
420 \x1b[0;32ma branch name much longer than the default justification used by branches\x1b[0m \x1b[0;36m7:10ff5895aa57\x1b[0m (esc)
421 \x1b[0;34mc\x1b[0m \x1b[0;36m 14:f894c25619d3\x1b[0m (closed) (esc)
421 \x1b[0;34mc\x1b[0m \x1b[0;36m 14:f894c25619d3\x1b[0m (closed) (esc)
422 \x1b[0;35ma\x1b[0m \x1b[0;36m 5:d8cbc61dbaa6\x1b[0m (inactive) (esc)
422 \x1b[0;35ma\x1b[0m \x1b[0;36m 5:d8cbc61dbaa6\x1b[0m (inactive) (esc)
423 \x1b[0;35mdefault\x1b[0m \x1b[0;36m 0:19709c5a4e75\x1b[0m (inactive) (esc)
423 \x1b[0;35mdefault\x1b[0m \x1b[0;36m 0:19709c5a4e75\x1b[0m (inactive) (esc)
424
424
425 $ cd ..
425 $ cd ..
@@ -1,325 +1,325 b''
1 $ hg init test
1 $ hg init test
2 $ cd test
2 $ cd test
3
3
4 $ echo a > a
4 $ echo a > a
5 $ hg add a
5 $ hg add a
6 $ hg commit -m "test"
6 $ hg commit -m "test"
7 $ hg history
7 $ hg history
8 changeset: 0:acb14030fe0a
8 changeset: 0:acb14030fe0a
9 tag: tip
9 tag: tip
10 user: test
10 user: test
11 date: Thu Jan 01 00:00:00 1970 +0000
11 date: Thu Jan 01 00:00:00 1970 +0000
12 summary: test
12 summary: test
13
13
14
14
15 $ hg tag ' '
15 $ hg tag ' '
16 abort: tag names cannot consist entirely of whitespace
16 abort: tag names cannot consist entirely of whitespace
17 [255]
17 [255]
18
18
19 $ hg tag "bleah"
19 $ hg tag "bleah"
20 $ hg history
20 $ hg history
21 changeset: 1:d4f0d2909abc
21 changeset: 1:d4f0d2909abc
22 tag: tip
22 tag: tip
23 user: test
23 user: test
24 date: Thu Jan 01 00:00:00 1970 +0000
24 date: Thu Jan 01 00:00:00 1970 +0000
25 summary: Added tag bleah for changeset acb14030fe0a
25 summary: Added tag bleah for changeset acb14030fe0a
26
26
27 changeset: 0:acb14030fe0a
27 changeset: 0:acb14030fe0a
28 tag: bleah
28 tag: bleah
29 user: test
29 user: test
30 date: Thu Jan 01 00:00:00 1970 +0000
30 date: Thu Jan 01 00:00:00 1970 +0000
31 summary: test
31 summary: test
32
32
33
33
34 $ echo foo >> .hgtags
34 $ echo foo >> .hgtags
35 $ hg tag "bleah2"
35 $ hg tag "bleah2"
36 abort: working copy of .hgtags is changed (please commit .hgtags manually)
36 abort: working copy of .hgtags is changed (please commit .hgtags manually)
37 [255]
37 [255]
38
38
39 $ hg revert .hgtags
39 $ hg revert .hgtags
40 $ hg tag -r 0 x y z y y z
40 $ hg tag -r 0 x y z y y z
41 abort: tag names must be unique
41 abort: tag names must be unique
42 [255]
42 [255]
43 $ hg tag tap nada dot tip
43 $ hg tag tap nada dot tip
44 abort: the name 'tip' is reserved
44 abort: the name 'tip' is reserved
45 [255]
45 [255]
46 $ hg tag .
46 $ hg tag .
47 abort: the name '.' is reserved
47 abort: the name '.' is reserved
48 [255]
48 [255]
49 $ hg tag null
49 $ hg tag null
50 abort: the name 'null' is reserved
50 abort: the name 'null' is reserved
51 [255]
51 [255]
52 $ hg tag "bleah"
52 $ hg tag "bleah"
53 abort: tag 'bleah' already exists (use -f to force)
53 abort: tag 'bleah' already exists (use -f to force)
54 [255]
54 [255]
55 $ hg tag "blecch" "bleah"
55 $ hg tag "blecch" "bleah"
56 abort: tag 'bleah' already exists (use -f to force)
56 abort: tag 'bleah' already exists (use -f to force)
57 [255]
57 [255]
58
58
59 $ hg tag --remove "blecch"
59 $ hg tag --remove "blecch"
60 abort: tag 'blecch' does not exist
60 abort: tag 'blecch' does not exist
61 [255]
61 [255]
62 $ hg tag --remove "bleah" "blecch" "blough"
62 $ hg tag --remove "bleah" "blecch" "blough"
63 abort: tag 'blecch' does not exist
63 abort: tag 'blecch' does not exist
64 [255]
64 [255]
65
65
66 $ hg tag -r 0 "bleah0"
66 $ hg tag -r 0 "bleah0"
67 $ hg tag -l -r 1 "bleah1"
67 $ hg tag -l -r 1 "bleah1"
68 $ hg tag gack gawk gorp
68 $ hg tag gack gawk gorp
69 $ hg tag -f gack
69 $ hg tag -f gack
70 $ hg tag --remove gack gorp
70 $ hg tag --remove gack gorp
71
71
72 $ hg tag "bleah "
72 $ hg tag "bleah "
73 abort: tag 'bleah' already exists (use -f to force)
73 abort: tag 'bleah' already exists (use -f to force)
74 [255]
74 [255]
75 $ hg tag " bleah"
75 $ hg tag " bleah"
76 abort: tag 'bleah' already exists (use -f to force)
76 abort: tag 'bleah' already exists (use -f to force)
77 [255]
77 [255]
78 $ hg tag " bleah"
78 $ hg tag " bleah"
79 abort: tag 'bleah' already exists (use -f to force)
79 abort: tag 'bleah' already exists (use -f to force)
80 [255]
80 [255]
81 $ hg tag -r 0 " bleahbleah "
81 $ hg tag -r 0 " bleahbleah "
82 $ hg tag -r 0 " bleah bleah "
82 $ hg tag -r 0 " bleah bleah "
83
83
84 $ cat .hgtags
84 $ cat .hgtags
85 acb14030fe0a21b60322c440ad2d20cf7685a376 bleah
85 acb14030fe0a21b60322c440ad2d20cf7685a376 bleah
86 acb14030fe0a21b60322c440ad2d20cf7685a376 bleah0
86 acb14030fe0a21b60322c440ad2d20cf7685a376 bleah0
87 336fccc858a4eb69609a291105009e484a6b6b8d gack
87 336fccc858a4eb69609a291105009e484a6b6b8d gack
88 336fccc858a4eb69609a291105009e484a6b6b8d gawk
88 336fccc858a4eb69609a291105009e484a6b6b8d gawk
89 336fccc858a4eb69609a291105009e484a6b6b8d gorp
89 336fccc858a4eb69609a291105009e484a6b6b8d gorp
90 336fccc858a4eb69609a291105009e484a6b6b8d gack
90 336fccc858a4eb69609a291105009e484a6b6b8d gack
91 799667b6f2d9b957f73fa644a918c2df22bab58f gack
91 799667b6f2d9b957f73fa644a918c2df22bab58f gack
92 799667b6f2d9b957f73fa644a918c2df22bab58f gack
92 799667b6f2d9b957f73fa644a918c2df22bab58f gack
93 0000000000000000000000000000000000000000 gack
93 0000000000000000000000000000000000000000 gack
94 336fccc858a4eb69609a291105009e484a6b6b8d gorp
94 336fccc858a4eb69609a291105009e484a6b6b8d gorp
95 0000000000000000000000000000000000000000 gorp
95 0000000000000000000000000000000000000000 gorp
96 acb14030fe0a21b60322c440ad2d20cf7685a376 bleahbleah
96 acb14030fe0a21b60322c440ad2d20cf7685a376 bleahbleah
97 acb14030fe0a21b60322c440ad2d20cf7685a376 bleah bleah
97 acb14030fe0a21b60322c440ad2d20cf7685a376 bleah bleah
98
98
99 $ cat .hg/localtags
99 $ cat .hg/localtags
100 d4f0d2909abc9290e2773c08837d70c1794e3f5a bleah1
100 d4f0d2909abc9290e2773c08837d70c1794e3f5a bleah1
101
101
102 tagging on a non-head revision
102 tagging on a non-head revision
103
103
104 $ hg update 0
104 $ hg update 0
105 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
105 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
106 $ hg tag -l localblah
106 $ hg tag -l localblah
107 $ hg tag "foobar"
107 $ hg tag "foobar"
108 abort: not at a branch head (use -f to force)
108 abort: not at a branch head (use -f to force)
109 [255]
109 [255]
110 $ hg tag -f "foobar"
110 $ hg tag -f "foobar"
111 $ cat .hgtags
111 $ cat .hgtags
112 acb14030fe0a21b60322c440ad2d20cf7685a376 foobar
112 acb14030fe0a21b60322c440ad2d20cf7685a376 foobar
113 $ cat .hg/localtags
113 $ cat .hg/localtags
114 d4f0d2909abc9290e2773c08837d70c1794e3f5a bleah1
114 d4f0d2909abc9290e2773c08837d70c1794e3f5a bleah1
115 acb14030fe0a21b60322c440ad2d20cf7685a376 localblah
115 acb14030fe0a21b60322c440ad2d20cf7685a376 localblah
116
116
117 $ hg tag -l 'xx
117 $ hg tag -l 'xx
118 > newline'
118 > newline'
119 abort: '\n' cannot be used in a tag name
119 abort: '\n' cannot be used in a name
120 [255]
120 [255]
121 $ hg tag -l 'xx:xx'
121 $ hg tag -l 'xx:xx'
122 abort: ':' cannot be used in a tag name
122 abort: ':' cannot be used in a name
123 [255]
123 [255]
124
124
125 cloning local tags
125 cloning local tags
126
126
127 $ cd ..
127 $ cd ..
128 $ hg -R test log -r0:5
128 $ hg -R test log -r0:5
129 changeset: 0:acb14030fe0a
129 changeset: 0:acb14030fe0a
130 tag: bleah
130 tag: bleah
131 tag: bleah bleah
131 tag: bleah bleah
132 tag: bleah0
132 tag: bleah0
133 tag: bleahbleah
133 tag: bleahbleah
134 tag: foobar
134 tag: foobar
135 tag: localblah
135 tag: localblah
136 user: test
136 user: test
137 date: Thu Jan 01 00:00:00 1970 +0000
137 date: Thu Jan 01 00:00:00 1970 +0000
138 summary: test
138 summary: test
139
139
140 changeset: 1:d4f0d2909abc
140 changeset: 1:d4f0d2909abc
141 tag: bleah1
141 tag: bleah1
142 user: test
142 user: test
143 date: Thu Jan 01 00:00:00 1970 +0000
143 date: Thu Jan 01 00:00:00 1970 +0000
144 summary: Added tag bleah for changeset acb14030fe0a
144 summary: Added tag bleah for changeset acb14030fe0a
145
145
146 changeset: 2:336fccc858a4
146 changeset: 2:336fccc858a4
147 tag: gawk
147 tag: gawk
148 user: test
148 user: test
149 date: Thu Jan 01 00:00:00 1970 +0000
149 date: Thu Jan 01 00:00:00 1970 +0000
150 summary: Added tag bleah0 for changeset acb14030fe0a
150 summary: Added tag bleah0 for changeset acb14030fe0a
151
151
152 changeset: 3:799667b6f2d9
152 changeset: 3:799667b6f2d9
153 user: test
153 user: test
154 date: Thu Jan 01 00:00:00 1970 +0000
154 date: Thu Jan 01 00:00:00 1970 +0000
155 summary: Added tag gack, gawk, gorp for changeset 336fccc858a4
155 summary: Added tag gack, gawk, gorp for changeset 336fccc858a4
156
156
157 changeset: 4:154eeb7c0138
157 changeset: 4:154eeb7c0138
158 user: test
158 user: test
159 date: Thu Jan 01 00:00:00 1970 +0000
159 date: Thu Jan 01 00:00:00 1970 +0000
160 summary: Added tag gack for changeset 799667b6f2d9
160 summary: Added tag gack for changeset 799667b6f2d9
161
161
162 changeset: 5:b4bb47aaff09
162 changeset: 5:b4bb47aaff09
163 user: test
163 user: test
164 date: Thu Jan 01 00:00:00 1970 +0000
164 date: Thu Jan 01 00:00:00 1970 +0000
165 summary: Removed tag gack, gorp
165 summary: Removed tag gack, gorp
166
166
167 $ hg clone -q -rbleah1 test test1
167 $ hg clone -q -rbleah1 test test1
168 $ hg -R test1 parents --style=compact
168 $ hg -R test1 parents --style=compact
169 1[tip] d4f0d2909abc 1970-01-01 00:00 +0000 test
169 1[tip] d4f0d2909abc 1970-01-01 00:00 +0000 test
170 Added tag bleah for changeset acb14030fe0a
170 Added tag bleah for changeset acb14030fe0a
171
171
172 $ hg clone -q -r5 test#bleah1 test2
172 $ hg clone -q -r5 test#bleah1 test2
173 $ hg -R test2 parents --style=compact
173 $ hg -R test2 parents --style=compact
174 5[tip] b4bb47aaff09 1970-01-01 00:00 +0000 test
174 5[tip] b4bb47aaff09 1970-01-01 00:00 +0000 test
175 Removed tag gack, gorp
175 Removed tag gack, gorp
176
176
177 $ hg clone -q -U test#bleah1 test3
177 $ hg clone -q -U test#bleah1 test3
178 $ hg -R test3 parents --style=compact
178 $ hg -R test3 parents --style=compact
179
179
180 $ cd test
180 $ cd test
181
181
182 Issue601: hg tag doesn't do the right thing if .hgtags or localtags
182 Issue601: hg tag doesn't do the right thing if .hgtags or localtags
183 doesn't end with EOL
183 doesn't end with EOL
184
184
185 $ python << EOF
185 $ python << EOF
186 > f = file('.hg/localtags'); last = f.readlines()[-1][:-1]; f.close()
186 > f = file('.hg/localtags'); last = f.readlines()[-1][:-1]; f.close()
187 > f = file('.hg/localtags', 'w'); f.write(last); f.close()
187 > f = file('.hg/localtags', 'w'); f.write(last); f.close()
188 > EOF
188 > EOF
189 $ cat .hg/localtags; echo
189 $ cat .hg/localtags; echo
190 acb14030fe0a21b60322c440ad2d20cf7685a376 localblah
190 acb14030fe0a21b60322c440ad2d20cf7685a376 localblah
191 $ hg tag -l localnewline
191 $ hg tag -l localnewline
192 $ cat .hg/localtags; echo
192 $ cat .hg/localtags; echo
193 acb14030fe0a21b60322c440ad2d20cf7685a376 localblah
193 acb14030fe0a21b60322c440ad2d20cf7685a376 localblah
194 c2899151f4e76890c602a2597a650a72666681bf localnewline
194 c2899151f4e76890c602a2597a650a72666681bf localnewline
195
195
196
196
197 $ python << EOF
197 $ python << EOF
198 > f = file('.hgtags'); last = f.readlines()[-1][:-1]; f.close()
198 > f = file('.hgtags'); last = f.readlines()[-1][:-1]; f.close()
199 > f = file('.hgtags', 'w'); f.write(last); f.close()
199 > f = file('.hgtags', 'w'); f.write(last); f.close()
200 > EOF
200 > EOF
201 $ hg ci -m'broken manual edit of .hgtags'
201 $ hg ci -m'broken manual edit of .hgtags'
202 $ cat .hgtags; echo
202 $ cat .hgtags; echo
203 acb14030fe0a21b60322c440ad2d20cf7685a376 foobar
203 acb14030fe0a21b60322c440ad2d20cf7685a376 foobar
204 $ hg tag newline
204 $ hg tag newline
205 $ cat .hgtags; echo
205 $ cat .hgtags; echo
206 acb14030fe0a21b60322c440ad2d20cf7685a376 foobar
206 acb14030fe0a21b60322c440ad2d20cf7685a376 foobar
207 a0eea09de1eeec777b46f2085260a373b2fbc293 newline
207 a0eea09de1eeec777b46f2085260a373b2fbc293 newline
208
208
209
209
210 tag and branch using same name
210 tag and branch using same name
211
211
212 $ hg branch tag-and-branch-same-name
212 $ hg branch tag-and-branch-same-name
213 marked working directory as branch tag-and-branch-same-name
213 marked working directory as branch tag-and-branch-same-name
214 (branches are permanent and global, did you want a bookmark?)
214 (branches are permanent and global, did you want a bookmark?)
215 $ hg ci -m"discouraged"
215 $ hg ci -m"discouraged"
216 $ hg tag tag-and-branch-same-name
216 $ hg tag tag-and-branch-same-name
217 warning: tag tag-and-branch-same-name conflicts with existing branch name
217 warning: tag tag-and-branch-same-name conflicts with existing branch name
218
218
219 test custom commit messages
219 test custom commit messages
220
220
221 $ cat > editor.sh << '__EOF__'
221 $ cat > editor.sh << '__EOF__'
222 > echo "custom tag message" > "$1"
222 > echo "custom tag message" > "$1"
223 > echo "second line" >> "$1"
223 > echo "second line" >> "$1"
224 > __EOF__
224 > __EOF__
225 $ HGEDITOR="\"sh\" \"`pwd`/editor.sh\"" hg tag custom-tag -e
225 $ HGEDITOR="\"sh\" \"`pwd`/editor.sh\"" hg tag custom-tag -e
226 $ hg log -l1 --template "{desc}\n"
226 $ hg log -l1 --template "{desc}\n"
227 custom tag message
227 custom tag message
228 second line
228 second line
229
229
230
230
231 local tag with .hgtags modified
231 local tag with .hgtags modified
232
232
233 $ hg tag hgtags-modified
233 $ hg tag hgtags-modified
234 $ hg rollback
234 $ hg rollback
235 repository tip rolled back to revision 13 (undo commit)
235 repository tip rolled back to revision 13 (undo commit)
236 working directory now based on revision 13
236 working directory now based on revision 13
237 $ hg st
237 $ hg st
238 M .hgtags
238 M .hgtags
239 ? .hgtags.orig
239 ? .hgtags.orig
240 ? editor.sh
240 ? editor.sh
241 $ hg tag --local baz
241 $ hg tag --local baz
242 $ hg revert --no-backup .hgtags
242 $ hg revert --no-backup .hgtags
243
243
244
244
245 tagging when at named-branch-head that's not a topo-head
245 tagging when at named-branch-head that's not a topo-head
246
246
247 $ hg up default
247 $ hg up default
248 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
248 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
249 $ hg merge -t internal:local
249 $ hg merge -t internal:local
250 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
250 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
251 (branch merge, don't forget to commit)
251 (branch merge, don't forget to commit)
252 $ hg ci -m 'merge named branch'
252 $ hg ci -m 'merge named branch'
253 $ hg up 13
253 $ hg up 13
254 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
254 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
255 $ hg tag new-topo-head
255 $ hg tag new-topo-head
256
256
257 tagging on null rev
257 tagging on null rev
258
258
259 $ hg up null
259 $ hg up null
260 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
260 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
261 $ hg tag nullrev
261 $ hg tag nullrev
262 abort: not at a branch head (use -f to force)
262 abort: not at a branch head (use -f to force)
263 [255]
263 [255]
264
264
265 $ hg init empty
265 $ hg init empty
266 $ hg tag -R empty nullrev
266 $ hg tag -R empty nullrev
267 abort: null revision specified
267 abort: null revision specified
268 [255]
268 [255]
269
269
270 $ hg tag -R empty -r 00000000000 -f nulltag
270 $ hg tag -R empty -r 00000000000 -f nulltag
271 abort: null revision specified
271 abort: null revision specified
272 [255]
272 [255]
273
273
274 $ cd ..
274 $ cd ..
275
275
276 tagging on an uncommitted merge (issue2542)
276 tagging on an uncommitted merge (issue2542)
277
277
278 $ hg init repo-tag-uncommitted-merge
278 $ hg init repo-tag-uncommitted-merge
279 $ cd repo-tag-uncommitted-merge
279 $ cd repo-tag-uncommitted-merge
280 $ echo c1 > f1
280 $ echo c1 > f1
281 $ hg ci -Am0
281 $ hg ci -Am0
282 adding f1
282 adding f1
283 $ echo c2 > f2
283 $ echo c2 > f2
284 $ hg ci -Am1
284 $ hg ci -Am1
285 adding f2
285 adding f2
286 $ hg co -q 0
286 $ hg co -q 0
287 $ hg branch b1
287 $ hg branch b1
288 marked working directory as branch b1
288 marked working directory as branch b1
289 (branches are permanent and global, did you want a bookmark?)
289 (branches are permanent and global, did you want a bookmark?)
290 $ hg ci -m2
290 $ hg ci -m2
291 $ hg up default
291 $ hg up default
292 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
292 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
293 $ hg merge b1
293 $ hg merge b1
294 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
294 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
295 (branch merge, don't forget to commit)
295 (branch merge, don't forget to commit)
296
296
297 $ hg tag t1
297 $ hg tag t1
298 abort: uncommitted merge
298 abort: uncommitted merge
299 [255]
299 [255]
300 $ hg status
300 $ hg status
301 $ hg tag --rev 1 t2
301 $ hg tag --rev 1 t2
302 abort: uncommitted merge
302 abort: uncommitted merge
303 [255]
303 [255]
304 $ hg tag --rev 1 --local t3
304 $ hg tag --rev 1 --local t3
305 $ hg tags -v
305 $ hg tags -v
306 tip 2:2a156e8887cc
306 tip 2:2a156e8887cc
307 t3 1:c3adabd1a5f4 local
307 t3 1:c3adabd1a5f4 local
308
308
309 $ cd ..
309 $ cd ..
310
310
311 commit hook on tag used to be run without write lock - issue3344
311 commit hook on tag used to be run without write lock - issue3344
312
312
313 $ hg init repo-tag
313 $ hg init repo-tag
314 $ touch repo-tag/test
314 $ touch repo-tag/test
315 $ hg -R repo-tag commit -A -m "test"
315 $ hg -R repo-tag commit -A -m "test"
316 adding test
316 adding test
317 $ hg init repo-tag-target
317 $ hg init repo-tag-target
318 $ hg -R repo-tag --config hooks.commit="\"hg\" push \"`pwd`/repo-tag-target\"" tag tag
318 $ hg -R repo-tag --config hooks.commit="\"hg\" push \"`pwd`/repo-tag-target\"" tag tag
319 pushing to $TESTTMP/repo-tag-target
319 pushing to $TESTTMP/repo-tag-target
320 searching for changes
320 searching for changes
321 adding changesets
321 adding changesets
322 adding manifests
322 adding manifests
323 adding file changes
323 adding file changes
324 added 2 changesets with 2 changes to 2 files
324 added 2 changesets with 2 changes to 2 files
325
325
General Comments 0
You need to be logged in to leave comments. Login now