##// END OF EJS Templates
merge with stable
Matt Mackall -
r12987:4438875e merge 1.7.1 stable
parent child Browse files
Show More
@@ -0,0 +1,40
1 http://mercurial.selenic.com/bts/issue2493
2
3 Testing tagging with the EOL extension
4
5 $ cat > $HGRCPATH <<EOF
6 > [diff]
7 > git = True
8 >
9 > [extensions]
10 > eol =
11 >
12 > [eol]
13 > native = CRLF
14 > EOF
15
16 setup repository
17
18 $ hg init repo
19 $ cd repo
20 $ cat > .hgeol <<EOF
21 > [patterns]
22 > ** = native
23 > EOF
24 $ printf "first\r\nsecond\r\nthird\r\n" > a.txt
25 $ hg commit --addremove -m 'checkin'
26 adding .hgeol
27 adding a.txt
28
29 Tag:
30
31 $ hg tag 1.0
32
33 Rewrite .hgtags file as it would look on a new checkout:
34
35 $ hg update -q null
36 $ hg update -q
37
38 Touch .hgtags file again:
39
40 $ hg tag 2.0
@@ -1,257 +1,266
1 """automatically manage newlines in repository files
1 """automatically manage newlines in repository files
2
2
3 This extension allows you to manage the type of line endings (CRLF or
3 This extension allows you to manage the type of line endings (CRLF or
4 LF) that are used in the repository and in the local working
4 LF) that are used in the repository and in the local working
5 directory. That way you can get CRLF line endings on Windows and LF on
5 directory. That way you can get CRLF line endings on Windows and LF on
6 Unix/Mac, thereby letting everybody use their OS native line endings.
6 Unix/Mac, thereby letting everybody use their OS native line endings.
7
7
8 The extension reads its configuration from a versioned ``.hgeol``
8 The extension reads its configuration from a versioned ``.hgeol``
9 configuration file every time you run an ``hg`` command. The
9 configuration file every time you run an ``hg`` command. The
10 ``.hgeol`` file use the same syntax as all other Mercurial
10 ``.hgeol`` file use the same syntax as all other Mercurial
11 configuration files. It uses two sections, ``[patterns]`` and
11 configuration files. It uses two sections, ``[patterns]`` and
12 ``[repository]``.
12 ``[repository]``.
13
13
14 The ``[patterns]`` section specifies the line endings used in the
14 The ``[patterns]`` section specifies the line endings used in the
15 working directory. The format is specified by a file pattern. The
15 working directory. The format is specified by a file pattern. The
16 first match is used, so put more specific patterns first. The
16 first match is used, so put more specific patterns first. The
17 available line endings are ``LF``, ``CRLF``, and ``BIN``.
17 available line endings are ``LF``, ``CRLF``, and ``BIN``.
18
18
19 Files with the declared format of ``CRLF`` or ``LF`` are always
19 Files with the declared format of ``CRLF`` or ``LF`` are always
20 checked out in that format and files declared to be binary (``BIN``)
20 checked out in that format and files declared to be binary (``BIN``)
21 are left unchanged. Additionally, ``native`` is an alias for the
21 are left unchanged. Additionally, ``native`` is an alias for the
22 platform's default line ending: ``LF`` on Unix (including Mac OS X)
22 platform's default line ending: ``LF`` on Unix (including Mac OS X)
23 and ``CRLF`` on Windows. Note that ``BIN`` (do nothing to line
23 and ``CRLF`` on Windows. Note that ``BIN`` (do nothing to line
24 endings) is Mercurial's default behaviour; it is only needed if you
24 endings) is Mercurial's default behaviour; it is only needed if you
25 need to override a later, more general pattern.
25 need to override a later, more general pattern.
26
26
27 The optional ``[repository]`` section specifies the line endings to
27 The optional ``[repository]`` section specifies the line endings to
28 use for files stored in the repository. It has a single setting,
28 use for files stored in the repository. It has a single setting,
29 ``native``, which determines the storage line endings for files
29 ``native``, which determines the storage line endings for files
30 declared as ``native`` in the ``[patterns]`` section. It can be set to
30 declared as ``native`` in the ``[patterns]`` section. It can be set to
31 ``LF`` or ``CRLF``. The default is ``LF``. For example, this means
31 ``LF`` or ``CRLF``. The default is ``LF``. For example, this means
32 that on Windows, files configured as ``native`` (``CRLF`` by default)
32 that on Windows, files configured as ``native`` (``CRLF`` by default)
33 will be converted to ``LF`` when stored in the repository. Files
33 will be converted to ``LF`` when stored in the repository. Files
34 declared as ``LF``, ``CRLF``, or ``BIN`` in the ``[patterns]`` section
34 declared as ``LF``, ``CRLF``, or ``BIN`` in the ``[patterns]`` section
35 are always stored as-is in the repository.
35 are always stored as-is in the repository.
36
36
37 Example versioned ``.hgeol`` file::
37 Example versioned ``.hgeol`` file::
38
38
39 [patterns]
39 [patterns]
40 **.py = native
40 **.py = native
41 **.vcproj = CRLF
41 **.vcproj = CRLF
42 **.txt = native
42 **.txt = native
43 Makefile = LF
43 Makefile = LF
44 **.jpg = BIN
44 **.jpg = BIN
45
45
46 [repository]
46 [repository]
47 native = LF
47 native = LF
48
48
49 The extension uses an optional ``[eol]`` section in your hgrc file
49 The extension uses an optional ``[eol]`` section in your hgrc file
50 (not the ``.hgeol`` file) for settings that control the overall
50 (not the ``.hgeol`` file) for settings that control the overall
51 behavior. There are two settings:
51 behavior. There are two settings:
52
52
53 - ``eol.native`` (default ``os.linesep``) can be set to ``LF`` or
53 - ``eol.native`` (default ``os.linesep``) can be set to ``LF`` or
54 ``CRLF`` to override the default interpretation of ``native`` for
54 ``CRLF`` to override the default interpretation of ``native`` for
55 checkout. This can be used with :hg:`archive` on Unix, say, to
55 checkout. This can be used with :hg:`archive` on Unix, say, to
56 generate an archive where files have line endings for Windows.
56 generate an archive where files have line endings for Windows.
57
57
58 - ``eol.only-consistent`` (default True) can be set to False to make
58 - ``eol.only-consistent`` (default True) can be set to False to make
59 the extension convert files with inconsistent EOLs. Inconsistent
59 the extension convert files with inconsistent EOLs. Inconsistent
60 means that there is both ``CRLF`` and ``LF`` present in the file.
60 means that there is both ``CRLF`` and ``LF`` present in the file.
61 Such files are normally not touched under the assumption that they
61 Such files are normally not touched under the assumption that they
62 have mixed EOLs on purpose.
62 have mixed EOLs on purpose.
63
63
64 The ``win32text.forbid*`` hooks provided by the win32text extension
65 have been unified into a single hook named ``eol.hook``. The hook will
66 lookup the expected line endings from the ``.hgeol`` file, which means
67 you must migrate to a ``.hgeol`` file first before using the hook.
68
64 See :hg:`help patterns` for more information about the glob patterns
69 See :hg:`help patterns` for more information about the glob patterns
65 used.
70 used.
66 """
71 """
67
72
68 from mercurial.i18n import _
73 from mercurial.i18n import _
69 from mercurial import util, config, extensions, match
74 from mercurial import util, config, extensions, match
70 import re, os
75 import re, os
71
76
72 # Matches a lone LF, i.e., one that is not part of CRLF.
77 # Matches a lone LF, i.e., one that is not part of CRLF.
73 singlelf = re.compile('(^|[^\r])\n')
78 singlelf = re.compile('(^|[^\r])\n')
74 # Matches a single EOL which can either be a CRLF where repeated CR
79 # Matches a single EOL which can either be a CRLF where repeated CR
75 # are removed or a LF. We do not care about old Machintosh files, so a
80 # are removed or a LF. We do not care about old Machintosh files, so a
76 # stray CR is an error.
81 # stray CR is an error.
77 eolre = re.compile('\r*\n')
82 eolre = re.compile('\r*\n')
78
83
79
84
80 def inconsistenteol(data):
85 def inconsistenteol(data):
81 return '\r\n' in data and singlelf.search(data)
86 return '\r\n' in data and singlelf.search(data)
82
87
83 def tolf(s, params, ui, **kwargs):
88 def tolf(s, params, ui, **kwargs):
84 """Filter to convert to LF EOLs."""
89 """Filter to convert to LF EOLs."""
85 if util.binary(s):
90 if util.binary(s):
86 return s
91 return s
87 if ui.configbool('eol', 'only-consistent', True) and inconsistenteol(s):
92 if ui.configbool('eol', 'only-consistent', True) and inconsistenteol(s):
88 return s
93 return s
89 return eolre.sub('\n', s)
94 return eolre.sub('\n', s)
90
95
91 def tocrlf(s, params, ui, **kwargs):
96 def tocrlf(s, params, ui, **kwargs):
92 """Filter to convert to CRLF EOLs."""
97 """Filter to convert to CRLF EOLs."""
93 if util.binary(s):
98 if util.binary(s):
94 return s
99 return s
95 if ui.configbool('eol', 'only-consistent', True) and inconsistenteol(s):
100 if ui.configbool('eol', 'only-consistent', True) and inconsistenteol(s):
96 return s
101 return s
97 return eolre.sub('\r\n', s)
102 return eolre.sub('\r\n', s)
98
103
99 def isbinary(s, params):
104 def isbinary(s, params):
100 """Filter to do nothing with the file."""
105 """Filter to do nothing with the file."""
101 return s
106 return s
102
107
103 filters = {
108 filters = {
104 'to-lf': tolf,
109 'to-lf': tolf,
105 'to-crlf': tocrlf,
110 'to-crlf': tocrlf,
106 'is-binary': isbinary,
111 'is-binary': isbinary,
107 }
112 }
108
113
109
114
110 def hook(ui, repo, node, hooktype, **kwargs):
115 def hook(ui, repo, node, hooktype, **kwargs):
111 """verify that files have expected EOLs"""
116 """verify that files have expected EOLs"""
112 files = set()
117 files = set()
113 for rev in xrange(repo[node].rev(), len(repo)):
118 for rev in xrange(repo[node].rev(), len(repo)):
114 files.update(repo[rev].files())
119 files.update(repo[rev].files())
115 tip = repo['tip']
120 tip = repo['tip']
116 for f in files:
121 for f in files:
117 if f not in tip:
122 if f not in tip:
118 continue
123 continue
119 for pattern, target in ui.configitems('encode'):
124 for pattern, target in ui.configitems('encode'):
120 if match.match(repo.root, '', [pattern])(f):
125 if match.match(repo.root, '', [pattern])(f):
121 data = tip[f].data()
126 data = tip[f].data()
122 if target == "to-lf" and "\r\n" in data:
127 if target == "to-lf" and "\r\n" in data:
123 raise util.Abort(_("%s should not have CRLF line endings")
128 raise util.Abort(_("%s should not have CRLF line endings")
124 % f)
129 % f)
125 elif target == "to-crlf" and singlelf.search(data):
130 elif target == "to-crlf" and singlelf.search(data):
126 raise util.Abort(_("%s should not have LF line endings")
131 raise util.Abort(_("%s should not have LF line endings")
127 % f)
132 % f)
128
133
129
134
130 def preupdate(ui, repo, hooktype, parent1, parent2):
135 def preupdate(ui, repo, hooktype, parent1, parent2):
131 #print "preupdate for %s: %s -> %s" % (repo.root, parent1, parent2)
136 #print "preupdate for %s: %s -> %s" % (repo.root, parent1, parent2)
132 repo.readhgeol(parent1)
137 repo.readhgeol(parent1)
133 return False
138 return False
134
139
135 def uisetup(ui):
140 def uisetup(ui):
136 ui.setconfig('hooks', 'preupdate.eol', preupdate)
141 ui.setconfig('hooks', 'preupdate.eol', preupdate)
137
142
138 def extsetup(ui):
143 def extsetup(ui):
139 try:
144 try:
140 extensions.find('win32text')
145 extensions.find('win32text')
141 raise util.Abort(_("the eol extension is incompatible with the "
146 raise util.Abort(_("the eol extension is incompatible with the "
142 "win32text extension"))
147 "win32text extension"))
143 except KeyError:
148 except KeyError:
144 pass
149 pass
145
150
146
151
147 def reposetup(ui, repo):
152 def reposetup(ui, repo):
148 uisetup(repo.ui)
153 uisetup(repo.ui)
149 #print "reposetup for", repo.root
154 #print "reposetup for", repo.root
150
155
151 if not repo.local():
156 if not repo.local():
152 return
157 return
153 for name, fn in filters.iteritems():
158 for name, fn in filters.iteritems():
154 repo.adddatafilter(name, fn)
159 repo.adddatafilter(name, fn)
155
160
156 ui.setconfig('patch', 'eol', 'auto')
161 ui.setconfig('patch', 'eol', 'auto')
157
162
158 class eolrepo(repo.__class__):
163 class eolrepo(repo.__class__):
159
164
160 _decode = {'LF': 'to-lf', 'CRLF': 'to-crlf', 'BIN': 'is-binary'}
165 _decode = {'LF': 'to-lf', 'CRLF': 'to-crlf', 'BIN': 'is-binary'}
161 _encode = {'LF': 'to-lf', 'CRLF': 'to-crlf', 'BIN': 'is-binary'}
166 _encode = {'LF': 'to-lf', 'CRLF': 'to-crlf', 'BIN': 'is-binary'}
162
167
163 def readhgeol(self, node=None, data=None):
168 def readhgeol(self, node=None, data=None):
164 if data is None:
169 if data is None:
165 try:
170 try:
166 if node is None:
171 if node is None:
167 data = self.wfile('.hgeol').read()
172 data = self.wfile('.hgeol').read()
168 else:
173 else:
169 data = self[node]['.hgeol'].data()
174 data = self[node]['.hgeol'].data()
170 except (IOError, LookupError):
175 except (IOError, LookupError):
171 return None
176 return None
172
177
173 if self.ui.config('eol', 'native', os.linesep) in ('LF', '\n'):
178 if self.ui.config('eol', 'native', os.linesep) in ('LF', '\n'):
174 self._decode['NATIVE'] = 'to-lf'
179 self._decode['NATIVE'] = 'to-lf'
175 else:
180 else:
176 self._decode['NATIVE'] = 'to-crlf'
181 self._decode['NATIVE'] = 'to-crlf'
177
182
178 eol = config.config()
183 eol = config.config()
184 # Our files should not be touched. The pattern must be
185 # inserted first override a '** = native' pattern.
186 eol.set('patterns', '.hg*', 'BIN')
187 # We can then parse the user's patterns.
179 eol.parse('.hgeol', data)
188 eol.parse('.hgeol', data)
180
189
181 if eol.get('repository', 'native') == 'CRLF':
190 if eol.get('repository', 'native') == 'CRLF':
182 self._encode['NATIVE'] = 'to-crlf'
191 self._encode['NATIVE'] = 'to-crlf'
183 else:
192 else:
184 self._encode['NATIVE'] = 'to-lf'
193 self._encode['NATIVE'] = 'to-lf'
185
194
186 for pattern, style in eol.items('patterns'):
195 for pattern, style in eol.items('patterns'):
187 key = style.upper()
196 key = style.upper()
188 try:
197 try:
189 self.ui.setconfig('decode', pattern, self._decode[key])
198 self.ui.setconfig('decode', pattern, self._decode[key])
190 self.ui.setconfig('encode', pattern, self._encode[key])
199 self.ui.setconfig('encode', pattern, self._encode[key])
191 except KeyError:
200 except KeyError:
192 self.ui.warn(_("ignoring unknown EOL style '%s' from %s\n")
201 self.ui.warn(_("ignoring unknown EOL style '%s' from %s\n")
193 % (style, eol.source('patterns', pattern)))
202 % (style, eol.source('patterns', pattern)))
194
203
195 include = []
204 include = []
196 exclude = []
205 exclude = []
197 for pattern, style in eol.items('patterns'):
206 for pattern, style in eol.items('patterns'):
198 key = style.upper()
207 key = style.upper()
199 if key == 'BIN':
208 if key == 'BIN':
200 exclude.append(pattern)
209 exclude.append(pattern)
201 else:
210 else:
202 include.append(pattern)
211 include.append(pattern)
203
212
204 # This will match the files for which we need to care
213 # This will match the files for which we need to care
205 # about inconsistent newlines.
214 # about inconsistent newlines.
206 return match.match(self.root, '', [], include, exclude)
215 return match.match(self.root, '', [], include, exclude)
207
216
208 def _hgcleardirstate(self):
217 def _hgcleardirstate(self):
209 self._eolfile = self.readhgeol() or self.readhgeol('tip')
218 self._eolfile = self.readhgeol() or self.readhgeol('tip')
210
219
211 if not self._eolfile:
220 if not self._eolfile:
212 self._eolfile = util.never
221 self._eolfile = util.never
213 return
222 return
214
223
215 try:
224 try:
216 cachemtime = os.path.getmtime(self.join("eol.cache"))
225 cachemtime = os.path.getmtime(self.join("eol.cache"))
217 except OSError:
226 except OSError:
218 cachemtime = 0
227 cachemtime = 0
219
228
220 try:
229 try:
221 eolmtime = os.path.getmtime(self.wjoin(".hgeol"))
230 eolmtime = os.path.getmtime(self.wjoin(".hgeol"))
222 except OSError:
231 except OSError:
223 eolmtime = 0
232 eolmtime = 0
224
233
225 if eolmtime > cachemtime:
234 if eolmtime > cachemtime:
226 ui.debug("eol: detected change in .hgeol\n")
235 ui.debug("eol: detected change in .hgeol\n")
227 # TODO: we could introduce a method for this in dirstate.
236 # TODO: we could introduce a method for this in dirstate.
228 wlock = None
237 wlock = None
229 try:
238 try:
230 wlock = self.wlock()
239 wlock = self.wlock()
231 for f, e in self.dirstate._map.iteritems():
240 for f, e in self.dirstate._map.iteritems():
232 self.dirstate._map[f] = (e[0], e[1], -1, 0)
241 self.dirstate._map[f] = (e[0], e[1], -1, 0)
233 self.dirstate._dirty = True
242 self.dirstate._dirty = True
234 # Touch the cache to update mtime. TODO: are we sure this
243 # Touch the cache to update mtime. TODO: are we sure this
235 # always enought to update the mtime, or should we write a
244 # always enought to update the mtime, or should we write a
236 # bit to the file?
245 # bit to the file?
237 self.opener("eol.cache", "w").close()
246 self.opener("eol.cache", "w").close()
238 finally:
247 finally:
239 if wlock is not None:
248 if wlock is not None:
240 wlock.release()
249 wlock.release()
241
250
242 def commitctx(self, ctx, error=False):
251 def commitctx(self, ctx, error=False):
243 for f in sorted(ctx.added() + ctx.modified()):
252 for f in sorted(ctx.added() + ctx.modified()):
244 if not self._eolfile(f):
253 if not self._eolfile(f):
245 continue
254 continue
246 data = ctx[f].data()
255 data = ctx[f].data()
247 if util.binary(data):
256 if util.binary(data):
248 # We should not abort here, since the user should
257 # We should not abort here, since the user should
249 # be able to say "** = native" to automatically
258 # be able to say "** = native" to automatically
250 # have all non-binary files taken care of.
259 # have all non-binary files taken care of.
251 continue
260 continue
252 if inconsistenteol(data):
261 if inconsistenteol(data):
253 raise util.Abort(_("inconsistent newline style "
262 raise util.Abort(_("inconsistent newline style "
254 "in %s\n" % f))
263 "in %s\n" % f))
255 return super(eolrepo, self).commitctx(ctx, error)
264 return super(eolrepo, self).commitctx(ctx, error)
256 repo.__class__ = eolrepo
265 repo.__class__ = eolrepo
257 repo._hgcleardirstate()
266 repo._hgcleardirstate()
@@ -1,1365 +1,1374
1 # cmdutil.py - help for command processing in mercurial
1 # cmdutil.py - help for command processing in mercurial
2 #
2 #
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from node import hex, nullid, nullrev, short
8 from node import hex, nullid, nullrev, short
9 from i18n import _
9 from i18n import _
10 import os, sys, errno, re, glob, tempfile
10 import os, sys, errno, re, glob, tempfile
11 import util, templater, patch, error, encoding, templatekw
11 import util, templater, patch, error, encoding, templatekw
12 import match as matchmod
12 import match as matchmod
13 import similar, revset, subrepo
13 import similar, revset, subrepo
14
14
15 revrangesep = ':'
15 revrangesep = ':'
16
16
17 def parsealiases(cmd):
17 def parsealiases(cmd):
18 return cmd.lstrip("^").split("|")
18 return cmd.lstrip("^").split("|")
19
19
20 def findpossible(cmd, table, strict=False):
20 def findpossible(cmd, table, strict=False):
21 """
21 """
22 Return cmd -> (aliases, command table entry)
22 Return cmd -> (aliases, command table entry)
23 for each matching command.
23 for each matching command.
24 Return debug commands (or their aliases) only if no normal command matches.
24 Return debug commands (or their aliases) only if no normal command matches.
25 """
25 """
26 choice = {}
26 choice = {}
27 debugchoice = {}
27 debugchoice = {}
28 for e in table.keys():
28 for e in table.keys():
29 aliases = parsealiases(e)
29 aliases = parsealiases(e)
30 found = None
30 found = None
31 if cmd in aliases:
31 if cmd in aliases:
32 found = cmd
32 found = cmd
33 elif not strict:
33 elif not strict:
34 for a in aliases:
34 for a in aliases:
35 if a.startswith(cmd):
35 if a.startswith(cmd):
36 found = a
36 found = a
37 break
37 break
38 if found is not None:
38 if found is not None:
39 if aliases[0].startswith("debug") or found.startswith("debug"):
39 if aliases[0].startswith("debug") or found.startswith("debug"):
40 debugchoice[found] = (aliases, table[e])
40 debugchoice[found] = (aliases, table[e])
41 else:
41 else:
42 choice[found] = (aliases, table[e])
42 choice[found] = (aliases, table[e])
43
43
44 if not choice and debugchoice:
44 if not choice and debugchoice:
45 choice = debugchoice
45 choice = debugchoice
46
46
47 return choice
47 return choice
48
48
49 def findcmd(cmd, table, strict=True):
49 def findcmd(cmd, table, strict=True):
50 """Return (aliases, command table entry) for command string."""
50 """Return (aliases, command table entry) for command string."""
51 choice = findpossible(cmd, table, strict)
51 choice = findpossible(cmd, table, strict)
52
52
53 if cmd in choice:
53 if cmd in choice:
54 return choice[cmd]
54 return choice[cmd]
55
55
56 if len(choice) > 1:
56 if len(choice) > 1:
57 clist = choice.keys()
57 clist = choice.keys()
58 clist.sort()
58 clist.sort()
59 raise error.AmbiguousCommand(cmd, clist)
59 raise error.AmbiguousCommand(cmd, clist)
60
60
61 if choice:
61 if choice:
62 return choice.values()[0]
62 return choice.values()[0]
63
63
64 raise error.UnknownCommand(cmd)
64 raise error.UnknownCommand(cmd)
65
65
66 def findrepo(p):
66 def findrepo(p):
67 while not os.path.isdir(os.path.join(p, ".hg")):
67 while not os.path.isdir(os.path.join(p, ".hg")):
68 oldp, p = p, os.path.dirname(p)
68 oldp, p = p, os.path.dirname(p)
69 if p == oldp:
69 if p == oldp:
70 return None
70 return None
71
71
72 return p
72 return p
73
73
74 def bail_if_changed(repo):
74 def bail_if_changed(repo):
75 if repo.dirstate.parents()[1] != nullid:
75 if repo.dirstate.parents()[1] != nullid:
76 raise util.Abort(_('outstanding uncommitted merge'))
76 raise util.Abort(_('outstanding uncommitted merge'))
77 modified, added, removed, deleted = repo.status()[:4]
77 modified, added, removed, deleted = repo.status()[:4]
78 if modified or added or removed or deleted:
78 if modified or added or removed or deleted:
79 raise util.Abort(_("outstanding uncommitted changes"))
79 raise util.Abort(_("outstanding uncommitted changes"))
80
80
81 def logmessage(opts):
81 def logmessage(opts):
82 """ get the log message according to -m and -l option """
82 """ get the log message according to -m and -l option """
83 message = opts.get('message')
83 message = opts.get('message')
84 logfile = opts.get('logfile')
84 logfile = opts.get('logfile')
85
85
86 if message and logfile:
86 if message and logfile:
87 raise util.Abort(_('options --message and --logfile are mutually '
87 raise util.Abort(_('options --message and --logfile are mutually '
88 'exclusive'))
88 'exclusive'))
89 if not message and logfile:
89 if not message and logfile:
90 try:
90 try:
91 if logfile == '-':
91 if logfile == '-':
92 message = sys.stdin.read()
92 message = sys.stdin.read()
93 else:
93 else:
94 message = open(logfile).read()
94 message = open(logfile).read()
95 except IOError, inst:
95 except IOError, inst:
96 raise util.Abort(_("can't read commit message '%s': %s") %
96 raise util.Abort(_("can't read commit message '%s': %s") %
97 (logfile, inst.strerror))
97 (logfile, inst.strerror))
98 return message
98 return message
99
99
100 def loglimit(opts):
100 def loglimit(opts):
101 """get the log limit according to option -l/--limit"""
101 """get the log limit according to option -l/--limit"""
102 limit = opts.get('limit')
102 limit = opts.get('limit')
103 if limit:
103 if limit:
104 try:
104 try:
105 limit = int(limit)
105 limit = int(limit)
106 except ValueError:
106 except ValueError:
107 raise util.Abort(_('limit must be a positive integer'))
107 raise util.Abort(_('limit must be a positive integer'))
108 if limit <= 0:
108 if limit <= 0:
109 raise util.Abort(_('limit must be positive'))
109 raise util.Abort(_('limit must be positive'))
110 else:
110 else:
111 limit = None
111 limit = None
112 return limit
112 return limit
113
113
114 def revsingle(repo, revspec, default='.'):
114 def revsingle(repo, revspec, default='.'):
115 if not revspec:
115 if not revspec:
116 return repo[default]
116 return repo[default]
117
117
118 l = revrange(repo, [revspec])
118 l = revrange(repo, [revspec])
119 if len(l) < 1:
119 if len(l) < 1:
120 raise util.Abort(_('empty revision set'))
120 raise util.Abort(_('empty revision set'))
121 return repo[l[-1]]
121 return repo[l[-1]]
122
122
123 def revpair(repo, revs):
123 def revpair(repo, revs):
124 if not revs:
124 if not revs:
125 return repo.dirstate.parents()[0], None
125 return repo.dirstate.parents()[0], None
126
126
127 l = revrange(repo, revs)
127 l = revrange(repo, revs)
128
128
129 if len(l) == 0:
129 if len(l) == 0:
130 return repo.dirstate.parents()[0], None
130 return repo.dirstate.parents()[0], None
131
131
132 if len(l) == 1:
132 if len(l) == 1:
133 return repo.lookup(l[0]), None
133 return repo.lookup(l[0]), None
134
134
135 return repo.lookup(l[0]), repo.lookup(l[-1])
135 return repo.lookup(l[0]), repo.lookup(l[-1])
136
136
137 def revrange(repo, revs):
137 def revrange(repo, revs):
138 """Yield revision as strings from a list of revision specifications."""
138 """Yield revision as strings from a list of revision specifications."""
139
139
140 def revfix(repo, val, defval):
140 def revfix(repo, val, defval):
141 if not val and val != 0 and defval is not None:
141 if not val and val != 0 and defval is not None:
142 return defval
142 return defval
143 return repo.changelog.rev(repo.lookup(val))
143 return repo.changelog.rev(repo.lookup(val))
144
144
145 seen, l = set(), []
145 seen, l = set(), []
146 for spec in revs:
146 for spec in revs:
147 # attempt to parse old-style ranges first to deal with
147 # attempt to parse old-style ranges first to deal with
148 # things like old-tag which contain query metacharacters
148 # things like old-tag which contain query metacharacters
149 try:
149 try:
150 if revrangesep in spec:
150 if revrangesep in spec:
151 start, end = spec.split(revrangesep, 1)
151 start, end = spec.split(revrangesep, 1)
152 start = revfix(repo, start, 0)
152 start = revfix(repo, start, 0)
153 end = revfix(repo, end, len(repo) - 1)
153 end = revfix(repo, end, len(repo) - 1)
154 step = start > end and -1 or 1
154 step = start > end and -1 or 1
155 for rev in xrange(start, end + step, step):
155 for rev in xrange(start, end + step, step):
156 if rev in seen:
156 if rev in seen:
157 continue
157 continue
158 seen.add(rev)
158 seen.add(rev)
159 l.append(rev)
159 l.append(rev)
160 continue
160 continue
161 elif spec and spec in repo: # single unquoted rev
161 elif spec and spec in repo: # single unquoted rev
162 rev = revfix(repo, spec, None)
162 rev = revfix(repo, spec, None)
163 if rev in seen:
163 if rev in seen:
164 continue
164 continue
165 seen.add(rev)
165 seen.add(rev)
166 l.append(rev)
166 l.append(rev)
167 continue
167 continue
168 except error.RepoLookupError:
168 except error.RepoLookupError:
169 pass
169 pass
170
170
171 # fall through to new-style queries if old-style fails
171 # fall through to new-style queries if old-style fails
172 m = revset.match(spec)
172 m = revset.match(spec)
173 for r in m(repo, range(len(repo))):
173 for r in m(repo, range(len(repo))):
174 if r not in seen:
174 if r not in seen:
175 l.append(r)
175 l.append(r)
176 seen.update(l)
176 seen.update(l)
177
177
178 return l
178 return l
179
179
180 def make_filename(repo, pat, node,
180 def make_filename(repo, pat, node,
181 total=None, seqno=None, revwidth=None, pathname=None):
181 total=None, seqno=None, revwidth=None, pathname=None):
182 node_expander = {
182 node_expander = {
183 'H': lambda: hex(node),
183 'H': lambda: hex(node),
184 'R': lambda: str(repo.changelog.rev(node)),
184 'R': lambda: str(repo.changelog.rev(node)),
185 'h': lambda: short(node),
185 'h': lambda: short(node),
186 }
186 }
187 expander = {
187 expander = {
188 '%': lambda: '%',
188 '%': lambda: '%',
189 'b': lambda: os.path.basename(repo.root),
189 'b': lambda: os.path.basename(repo.root),
190 }
190 }
191
191
192 try:
192 try:
193 if node:
193 if node:
194 expander.update(node_expander)
194 expander.update(node_expander)
195 if node:
195 if node:
196 expander['r'] = (lambda:
196 expander['r'] = (lambda:
197 str(repo.changelog.rev(node)).zfill(revwidth or 0))
197 str(repo.changelog.rev(node)).zfill(revwidth or 0))
198 if total is not None:
198 if total is not None:
199 expander['N'] = lambda: str(total)
199 expander['N'] = lambda: str(total)
200 if seqno is not None:
200 if seqno is not None:
201 expander['n'] = lambda: str(seqno)
201 expander['n'] = lambda: str(seqno)
202 if total is not None and seqno is not None:
202 if total is not None and seqno is not None:
203 expander['n'] = lambda: str(seqno).zfill(len(str(total)))
203 expander['n'] = lambda: str(seqno).zfill(len(str(total)))
204 if pathname is not None:
204 if pathname is not None:
205 expander['s'] = lambda: os.path.basename(pathname)
205 expander['s'] = lambda: os.path.basename(pathname)
206 expander['d'] = lambda: os.path.dirname(pathname) or '.'
206 expander['d'] = lambda: os.path.dirname(pathname) or '.'
207 expander['p'] = lambda: pathname
207 expander['p'] = lambda: pathname
208
208
209 newname = []
209 newname = []
210 patlen = len(pat)
210 patlen = len(pat)
211 i = 0
211 i = 0
212 while i < patlen:
212 while i < patlen:
213 c = pat[i]
213 c = pat[i]
214 if c == '%':
214 if c == '%':
215 i += 1
215 i += 1
216 c = pat[i]
216 c = pat[i]
217 c = expander[c]()
217 c = expander[c]()
218 newname.append(c)
218 newname.append(c)
219 i += 1
219 i += 1
220 return ''.join(newname)
220 return ''.join(newname)
221 except KeyError, inst:
221 except KeyError, inst:
222 raise util.Abort(_("invalid format spec '%%%s' in output filename") %
222 raise util.Abort(_("invalid format spec '%%%s' in output filename") %
223 inst.args[0])
223 inst.args[0])
224
224
225 def make_file(repo, pat, node=None,
225 def make_file(repo, pat, node=None,
226 total=None, seqno=None, revwidth=None, mode='wb', pathname=None):
226 total=None, seqno=None, revwidth=None, mode='wb', pathname=None):
227
227
228 writable = 'w' in mode or 'a' in mode
228 writable = 'w' in mode or 'a' in mode
229
229
230 if not pat or pat == '-':
230 if not pat or pat == '-':
231 return writable and sys.stdout or sys.stdin
231 return writable and sys.stdout or sys.stdin
232 if hasattr(pat, 'write') and writable:
232 if hasattr(pat, 'write') and writable:
233 return pat
233 return pat
234 if hasattr(pat, 'read') and 'r' in mode:
234 if hasattr(pat, 'read') and 'r' in mode:
235 return pat
235 return pat
236 return open(make_filename(repo, pat, node, total, seqno, revwidth,
236 return open(make_filename(repo, pat, node, total, seqno, revwidth,
237 pathname),
237 pathname),
238 mode)
238 mode)
239
239
240 def expandpats(pats):
240 def expandpats(pats):
241 if not util.expandglobs:
241 if not util.expandglobs:
242 return list(pats)
242 return list(pats)
243 ret = []
243 ret = []
244 for p in pats:
244 for p in pats:
245 kind, name = matchmod._patsplit(p, None)
245 kind, name = matchmod._patsplit(p, None)
246 if kind is None:
246 if kind is None:
247 try:
247 try:
248 globbed = glob.glob(name)
248 globbed = glob.glob(name)
249 except re.error:
249 except re.error:
250 globbed = [name]
250 globbed = [name]
251 if globbed:
251 if globbed:
252 ret.extend(globbed)
252 ret.extend(globbed)
253 continue
253 continue
254 ret.append(p)
254 ret.append(p)
255 return ret
255 return ret
256
256
257 def match(repo, pats=[], opts={}, globbed=False, default='relpath'):
257 def match(repo, pats=[], opts={}, globbed=False, default='relpath'):
258 if not globbed and default == 'relpath':
258 if not globbed and default == 'relpath':
259 pats = expandpats(pats or [])
259 pats = expandpats(pats or [])
260 m = matchmod.match(repo.root, repo.getcwd(), pats,
260 m = matchmod.match(repo.root, repo.getcwd(), pats,
261 opts.get('include'), opts.get('exclude'), default,
261 opts.get('include'), opts.get('exclude'), default,
262 auditor=repo.auditor)
262 auditor=repo.auditor)
263 def badfn(f, msg):
263 def badfn(f, msg):
264 repo.ui.warn("%s: %s\n" % (m.rel(f), msg))
264 repo.ui.warn("%s: %s\n" % (m.rel(f), msg))
265 m.bad = badfn
265 m.bad = badfn
266 return m
266 return m
267
267
268 def matchall(repo):
268 def matchall(repo):
269 return matchmod.always(repo.root, repo.getcwd())
269 return matchmod.always(repo.root, repo.getcwd())
270
270
271 def matchfiles(repo, files):
271 def matchfiles(repo, files):
272 return matchmod.exact(repo.root, repo.getcwd(), files)
272 return matchmod.exact(repo.root, repo.getcwd(), files)
273
273
274 def addremove(repo, pats=[], opts={}, dry_run=None, similarity=None):
274 def addremove(repo, pats=[], opts={}, dry_run=None, similarity=None):
275 if dry_run is None:
275 if dry_run is None:
276 dry_run = opts.get('dry_run')
276 dry_run = opts.get('dry_run')
277 if similarity is None:
277 if similarity is None:
278 similarity = float(opts.get('similarity') or 0)
278 similarity = float(opts.get('similarity') or 0)
279 # we'd use status here, except handling of symlinks and ignore is tricky
279 # we'd use status here, except handling of symlinks and ignore is tricky
280 added, unknown, deleted, removed = [], [], [], []
280 added, unknown, deleted, removed = [], [], [], []
281 audit_path = util.path_auditor(repo.root)
281 audit_path = util.path_auditor(repo.root)
282 m = match(repo, pats, opts)
282 m = match(repo, pats, opts)
283 for abs in repo.walk(m):
283 for abs in repo.walk(m):
284 target = repo.wjoin(abs)
284 target = repo.wjoin(abs)
285 good = True
285 good = True
286 try:
286 try:
287 audit_path(abs)
287 audit_path(abs)
288 except:
288 except:
289 good = False
289 good = False
290 rel = m.rel(abs)
290 rel = m.rel(abs)
291 exact = m.exact(abs)
291 exact = m.exact(abs)
292 if good and abs not in repo.dirstate:
292 if good and abs not in repo.dirstate:
293 unknown.append(abs)
293 unknown.append(abs)
294 if repo.ui.verbose or not exact:
294 if repo.ui.verbose or not exact:
295 repo.ui.status(_('adding %s\n') % ((pats and rel) or abs))
295 repo.ui.status(_('adding %s\n') % ((pats and rel) or abs))
296 elif repo.dirstate[abs] != 'r' and (not good or not os.path.lexists(target)
296 elif repo.dirstate[abs] != 'r' and (not good or not os.path.lexists(target)
297 or (os.path.isdir(target) and not os.path.islink(target))):
297 or (os.path.isdir(target) and not os.path.islink(target))):
298 deleted.append(abs)
298 deleted.append(abs)
299 if repo.ui.verbose or not exact:
299 if repo.ui.verbose or not exact:
300 repo.ui.status(_('removing %s\n') % ((pats and rel) or abs))
300 repo.ui.status(_('removing %s\n') % ((pats and rel) or abs))
301 # for finding renames
301 # for finding renames
302 elif repo.dirstate[abs] == 'r':
302 elif repo.dirstate[abs] == 'r':
303 removed.append(abs)
303 removed.append(abs)
304 elif repo.dirstate[abs] == 'a':
304 elif repo.dirstate[abs] == 'a':
305 added.append(abs)
305 added.append(abs)
306 copies = {}
306 copies = {}
307 if similarity > 0:
307 if similarity > 0:
308 for old, new, score in similar.findrenames(repo,
308 for old, new, score in similar.findrenames(repo,
309 added + unknown, removed + deleted, similarity):
309 added + unknown, removed + deleted, similarity):
310 if repo.ui.verbose or not m.exact(old) or not m.exact(new):
310 if repo.ui.verbose or not m.exact(old) or not m.exact(new):
311 repo.ui.status(_('recording removal of %s as rename to %s '
311 repo.ui.status(_('recording removal of %s as rename to %s '
312 '(%d%% similar)\n') %
312 '(%d%% similar)\n') %
313 (m.rel(old), m.rel(new), score * 100))
313 (m.rel(old), m.rel(new), score * 100))
314 copies[new] = old
314 copies[new] = old
315
315
316 if not dry_run:
316 if not dry_run:
317 wctx = repo[None]
317 wctx = repo[None]
318 wlock = repo.wlock()
318 wlock = repo.wlock()
319 try:
319 try:
320 wctx.remove(deleted)
320 wctx.remove(deleted)
321 wctx.add(unknown)
321 wctx.add(unknown)
322 for new, old in copies.iteritems():
322 for new, old in copies.iteritems():
323 wctx.copy(old, new)
323 wctx.copy(old, new)
324 finally:
324 finally:
325 wlock.release()
325 wlock.release()
326
326
327 def updatedir(ui, repo, patches, similarity=0):
327 def updatedir(ui, repo, patches, similarity=0):
328 '''Update dirstate after patch application according to metadata'''
328 '''Update dirstate after patch application according to metadata'''
329 if not patches:
329 if not patches:
330 return
330 return
331 copies = []
331 copies = []
332 removes = set()
332 removes = set()
333 cfiles = patches.keys()
333 cfiles = patches.keys()
334 cwd = repo.getcwd()
334 cwd = repo.getcwd()
335 if cwd:
335 if cwd:
336 cfiles = [util.pathto(repo.root, cwd, f) for f in patches.keys()]
336 cfiles = [util.pathto(repo.root, cwd, f) for f in patches.keys()]
337 for f in patches:
337 for f in patches:
338 gp = patches[f]
338 gp = patches[f]
339 if not gp:
339 if not gp:
340 continue
340 continue
341 if gp.op == 'RENAME':
341 if gp.op == 'RENAME':
342 copies.append((gp.oldpath, gp.path))
342 copies.append((gp.oldpath, gp.path))
343 removes.add(gp.oldpath)
343 removes.add(gp.oldpath)
344 elif gp.op == 'COPY':
344 elif gp.op == 'COPY':
345 copies.append((gp.oldpath, gp.path))
345 copies.append((gp.oldpath, gp.path))
346 elif gp.op == 'DELETE':
346 elif gp.op == 'DELETE':
347 removes.add(gp.path)
347 removes.add(gp.path)
348
348
349 wctx = repo[None]
349 wctx = repo[None]
350 for src, dst in copies:
350 for src, dst in copies:
351 dirstatecopy(ui, repo, wctx, src, dst, cwd=cwd)
351 dirstatecopy(ui, repo, wctx, src, dst, cwd=cwd)
352 if (not similarity) and removes:
352 if (not similarity) and removes:
353 wctx.remove(sorted(removes), True)
353 wctx.remove(sorted(removes), True)
354
354
355 for f in patches:
355 for f in patches:
356 gp = patches[f]
356 gp = patches[f]
357 if gp and gp.mode:
357 if gp and gp.mode:
358 islink, isexec = gp.mode
358 islink, isexec = gp.mode
359 dst = repo.wjoin(gp.path)
359 dst = repo.wjoin(gp.path)
360 # patch won't create empty files
360 # patch won't create empty files
361 if gp.op == 'ADD' and not os.path.lexists(dst):
361 if gp.op == 'ADD' and not os.path.lexists(dst):
362 flags = (isexec and 'x' or '') + (islink and 'l' or '')
362 flags = (isexec and 'x' or '') + (islink and 'l' or '')
363 repo.wwrite(gp.path, '', flags)
363 repo.wwrite(gp.path, '', flags)
364 util.set_flags(dst, islink, isexec)
364 util.set_flags(dst, islink, isexec)
365 addremove(repo, cfiles, similarity=similarity)
365 addremove(repo, cfiles, similarity=similarity)
366 files = patches.keys()
366 files = patches.keys()
367 files.extend([r for r in removes if r not in files])
367 files.extend([r for r in removes if r not in files])
368 return sorted(files)
368 return sorted(files)
369
369
370 def dirstatecopy(ui, repo, wctx, src, dst, dryrun=False, cwd=None):
370 def dirstatecopy(ui, repo, wctx, src, dst, dryrun=False, cwd=None):
371 """Update the dirstate to reflect the intent of copying src to dst. For
371 """Update the dirstate to reflect the intent of copying src to dst. For
372 different reasons it might not end with dst being marked as copied from src.
372 different reasons it might not end with dst being marked as copied from src.
373 """
373 """
374 origsrc = repo.dirstate.copied(src) or src
374 origsrc = repo.dirstate.copied(src) or src
375 if dst == origsrc: # copying back a copy?
375 if dst == origsrc: # copying back a copy?
376 if repo.dirstate[dst] not in 'mn' and not dryrun:
376 if repo.dirstate[dst] not in 'mn' and not dryrun:
377 repo.dirstate.normallookup(dst)
377 repo.dirstate.normallookup(dst)
378 else:
378 else:
379 if repo.dirstate[origsrc] == 'a' and origsrc == src:
379 if repo.dirstate[origsrc] == 'a' and origsrc == src:
380 if not ui.quiet:
380 if not ui.quiet:
381 ui.warn(_("%s has not been committed yet, so no copy "
381 ui.warn(_("%s has not been committed yet, so no copy "
382 "data will be stored for %s.\n")
382 "data will be stored for %s.\n")
383 % (repo.pathto(origsrc, cwd), repo.pathto(dst, cwd)))
383 % (repo.pathto(origsrc, cwd), repo.pathto(dst, cwd)))
384 if repo.dirstate[dst] in '?r' and not dryrun:
384 if repo.dirstate[dst] in '?r' and not dryrun:
385 wctx.add([dst])
385 wctx.add([dst])
386 elif not dryrun:
386 elif not dryrun:
387 wctx.copy(origsrc, dst)
387 wctx.copy(origsrc, dst)
388
388
389 def copy(ui, repo, pats, opts, rename=False):
389 def copy(ui, repo, pats, opts, rename=False):
390 # called with the repo lock held
390 # called with the repo lock held
391 #
391 #
392 # hgsep => pathname that uses "/" to separate directories
392 # hgsep => pathname that uses "/" to separate directories
393 # ossep => pathname that uses os.sep to separate directories
393 # ossep => pathname that uses os.sep to separate directories
394 cwd = repo.getcwd()
394 cwd = repo.getcwd()
395 targets = {}
395 targets = {}
396 after = opts.get("after")
396 after = opts.get("after")
397 dryrun = opts.get("dry_run")
397 dryrun = opts.get("dry_run")
398 wctx = repo[None]
398 wctx = repo[None]
399
399
400 def walkpat(pat):
400 def walkpat(pat):
401 srcs = []
401 srcs = []
402 badstates = after and '?' or '?r'
402 badstates = after and '?' or '?r'
403 m = match(repo, [pat], opts, globbed=True)
403 m = match(repo, [pat], opts, globbed=True)
404 for abs in repo.walk(m):
404 for abs in repo.walk(m):
405 state = repo.dirstate[abs]
405 state = repo.dirstate[abs]
406 rel = m.rel(abs)
406 rel = m.rel(abs)
407 exact = m.exact(abs)
407 exact = m.exact(abs)
408 if state in badstates:
408 if state in badstates:
409 if exact and state == '?':
409 if exact and state == '?':
410 ui.warn(_('%s: not copying - file is not managed\n') % rel)
410 ui.warn(_('%s: not copying - file is not managed\n') % rel)
411 if exact and state == 'r':
411 if exact and state == 'r':
412 ui.warn(_('%s: not copying - file has been marked for'
412 ui.warn(_('%s: not copying - file has been marked for'
413 ' remove\n') % rel)
413 ' remove\n') % rel)
414 continue
414 continue
415 # abs: hgsep
415 # abs: hgsep
416 # rel: ossep
416 # rel: ossep
417 srcs.append((abs, rel, exact))
417 srcs.append((abs, rel, exact))
418 return srcs
418 return srcs
419
419
420 # abssrc: hgsep
420 # abssrc: hgsep
421 # relsrc: ossep
421 # relsrc: ossep
422 # otarget: ossep
422 # otarget: ossep
423 def copyfile(abssrc, relsrc, otarget, exact):
423 def copyfile(abssrc, relsrc, otarget, exact):
424 abstarget = util.canonpath(repo.root, cwd, otarget)
424 abstarget = util.canonpath(repo.root, cwd, otarget)
425 reltarget = repo.pathto(abstarget, cwd)
425 reltarget = repo.pathto(abstarget, cwd)
426 target = repo.wjoin(abstarget)
426 target = repo.wjoin(abstarget)
427 src = repo.wjoin(abssrc)
427 src = repo.wjoin(abssrc)
428 state = repo.dirstate[abstarget]
428 state = repo.dirstate[abstarget]
429
429
430 # check for collisions
430 # check for collisions
431 prevsrc = targets.get(abstarget)
431 prevsrc = targets.get(abstarget)
432 if prevsrc is not None:
432 if prevsrc is not None:
433 ui.warn(_('%s: not overwriting - %s collides with %s\n') %
433 ui.warn(_('%s: not overwriting - %s collides with %s\n') %
434 (reltarget, repo.pathto(abssrc, cwd),
434 (reltarget, repo.pathto(abssrc, cwd),
435 repo.pathto(prevsrc, cwd)))
435 repo.pathto(prevsrc, cwd)))
436 return
436 return
437
437
438 # check for overwrites
438 # check for overwrites
439 exists = os.path.lexists(target)
439 exists = os.path.lexists(target)
440 if not after and exists or after and state in 'mn':
440 if not after and exists or after and state in 'mn':
441 if not opts['force']:
441 if not opts['force']:
442 ui.warn(_('%s: not overwriting - file exists\n') %
442 ui.warn(_('%s: not overwriting - file exists\n') %
443 reltarget)
443 reltarget)
444 return
444 return
445
445
446 if after:
446 if after:
447 if not exists:
447 if not exists:
448 if rename:
448 if rename:
449 ui.warn(_('%s: not recording move - %s does not exist\n') %
449 ui.warn(_('%s: not recording move - %s does not exist\n') %
450 (relsrc, reltarget))
450 (relsrc, reltarget))
451 else:
451 else:
452 ui.warn(_('%s: not recording copy - %s does not exist\n') %
452 ui.warn(_('%s: not recording copy - %s does not exist\n') %
453 (relsrc, reltarget))
453 (relsrc, reltarget))
454 return
454 return
455 elif not dryrun:
455 elif not dryrun:
456 try:
456 try:
457 if exists:
457 if exists:
458 os.unlink(target)
458 os.unlink(target)
459 targetdir = os.path.dirname(target) or '.'
459 targetdir = os.path.dirname(target) or '.'
460 if not os.path.isdir(targetdir):
460 if not os.path.isdir(targetdir):
461 os.makedirs(targetdir)
461 os.makedirs(targetdir)
462 util.copyfile(src, target)
462 util.copyfile(src, target)
463 except IOError, inst:
463 except IOError, inst:
464 if inst.errno == errno.ENOENT:
464 if inst.errno == errno.ENOENT:
465 ui.warn(_('%s: deleted in working copy\n') % relsrc)
465 ui.warn(_('%s: deleted in working copy\n') % relsrc)
466 else:
466 else:
467 ui.warn(_('%s: cannot copy - %s\n') %
467 ui.warn(_('%s: cannot copy - %s\n') %
468 (relsrc, inst.strerror))
468 (relsrc, inst.strerror))
469 return True # report a failure
469 return True # report a failure
470
470
471 if ui.verbose or not exact:
471 if ui.verbose or not exact:
472 if rename:
472 if rename:
473 ui.status(_('moving %s to %s\n') % (relsrc, reltarget))
473 ui.status(_('moving %s to %s\n') % (relsrc, reltarget))
474 else:
474 else:
475 ui.status(_('copying %s to %s\n') % (relsrc, reltarget))
475 ui.status(_('copying %s to %s\n') % (relsrc, reltarget))
476
476
477 targets[abstarget] = abssrc
477 targets[abstarget] = abssrc
478
478
479 # fix up dirstate
479 # fix up dirstate
480 dirstatecopy(ui, repo, wctx, abssrc, abstarget, dryrun=dryrun, cwd=cwd)
480 dirstatecopy(ui, repo, wctx, abssrc, abstarget, dryrun=dryrun, cwd=cwd)
481 if rename and not dryrun:
481 if rename and not dryrun:
482 wctx.remove([abssrc], not after)
482 wctx.remove([abssrc], not after)
483
483
484 # pat: ossep
484 # pat: ossep
485 # dest ossep
485 # dest ossep
486 # srcs: list of (hgsep, hgsep, ossep, bool)
486 # srcs: list of (hgsep, hgsep, ossep, bool)
487 # return: function that takes hgsep and returns ossep
487 # return: function that takes hgsep and returns ossep
488 def targetpathfn(pat, dest, srcs):
488 def targetpathfn(pat, dest, srcs):
489 if os.path.isdir(pat):
489 if os.path.isdir(pat):
490 abspfx = util.canonpath(repo.root, cwd, pat)
490 abspfx = util.canonpath(repo.root, cwd, pat)
491 abspfx = util.localpath(abspfx)
491 abspfx = util.localpath(abspfx)
492 if destdirexists:
492 if destdirexists:
493 striplen = len(os.path.split(abspfx)[0])
493 striplen = len(os.path.split(abspfx)[0])
494 else:
494 else:
495 striplen = len(abspfx)
495 striplen = len(abspfx)
496 if striplen:
496 if striplen:
497 striplen += len(os.sep)
497 striplen += len(os.sep)
498 res = lambda p: os.path.join(dest, util.localpath(p)[striplen:])
498 res = lambda p: os.path.join(dest, util.localpath(p)[striplen:])
499 elif destdirexists:
499 elif destdirexists:
500 res = lambda p: os.path.join(dest,
500 res = lambda p: os.path.join(dest,
501 os.path.basename(util.localpath(p)))
501 os.path.basename(util.localpath(p)))
502 else:
502 else:
503 res = lambda p: dest
503 res = lambda p: dest
504 return res
504 return res
505
505
506 # pat: ossep
506 # pat: ossep
507 # dest ossep
507 # dest ossep
508 # srcs: list of (hgsep, hgsep, ossep, bool)
508 # srcs: list of (hgsep, hgsep, ossep, bool)
509 # return: function that takes hgsep and returns ossep
509 # return: function that takes hgsep and returns ossep
510 def targetpathafterfn(pat, dest, srcs):
510 def targetpathafterfn(pat, dest, srcs):
511 if matchmod.patkind(pat):
511 if matchmod.patkind(pat):
512 # a mercurial pattern
512 # a mercurial pattern
513 res = lambda p: os.path.join(dest,
513 res = lambda p: os.path.join(dest,
514 os.path.basename(util.localpath(p)))
514 os.path.basename(util.localpath(p)))
515 else:
515 else:
516 abspfx = util.canonpath(repo.root, cwd, pat)
516 abspfx = util.canonpath(repo.root, cwd, pat)
517 if len(abspfx) < len(srcs[0][0]):
517 if len(abspfx) < len(srcs[0][0]):
518 # A directory. Either the target path contains the last
518 # A directory. Either the target path contains the last
519 # component of the source path or it does not.
519 # component of the source path or it does not.
520 def evalpath(striplen):
520 def evalpath(striplen):
521 score = 0
521 score = 0
522 for s in srcs:
522 for s in srcs:
523 t = os.path.join(dest, util.localpath(s[0])[striplen:])
523 t = os.path.join(dest, util.localpath(s[0])[striplen:])
524 if os.path.lexists(t):
524 if os.path.lexists(t):
525 score += 1
525 score += 1
526 return score
526 return score
527
527
528 abspfx = util.localpath(abspfx)
528 abspfx = util.localpath(abspfx)
529 striplen = len(abspfx)
529 striplen = len(abspfx)
530 if striplen:
530 if striplen:
531 striplen += len(os.sep)
531 striplen += len(os.sep)
532 if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
532 if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
533 score = evalpath(striplen)
533 score = evalpath(striplen)
534 striplen1 = len(os.path.split(abspfx)[0])
534 striplen1 = len(os.path.split(abspfx)[0])
535 if striplen1:
535 if striplen1:
536 striplen1 += len(os.sep)
536 striplen1 += len(os.sep)
537 if evalpath(striplen1) > score:
537 if evalpath(striplen1) > score:
538 striplen = striplen1
538 striplen = striplen1
539 res = lambda p: os.path.join(dest,
539 res = lambda p: os.path.join(dest,
540 util.localpath(p)[striplen:])
540 util.localpath(p)[striplen:])
541 else:
541 else:
542 # a file
542 # a file
543 if destdirexists:
543 if destdirexists:
544 res = lambda p: os.path.join(dest,
544 res = lambda p: os.path.join(dest,
545 os.path.basename(util.localpath(p)))
545 os.path.basename(util.localpath(p)))
546 else:
546 else:
547 res = lambda p: dest
547 res = lambda p: dest
548 return res
548 return res
549
549
550
550
551 pats = expandpats(pats)
551 pats = expandpats(pats)
552 if not pats:
552 if not pats:
553 raise util.Abort(_('no source or destination specified'))
553 raise util.Abort(_('no source or destination specified'))
554 if len(pats) == 1:
554 if len(pats) == 1:
555 raise util.Abort(_('no destination specified'))
555 raise util.Abort(_('no destination specified'))
556 dest = pats.pop()
556 dest = pats.pop()
557 destdirexists = os.path.isdir(dest) and not os.path.islink(dest)
557 destdirexists = os.path.isdir(dest) and not os.path.islink(dest)
558 if not destdirexists:
558 if not destdirexists:
559 if len(pats) > 1 or matchmod.patkind(pats[0]):
559 if len(pats) > 1 or matchmod.patkind(pats[0]):
560 raise util.Abort(_('with multiple sources, destination must be an '
560 raise util.Abort(_('with multiple sources, destination must be an '
561 'existing directory'))
561 'existing directory'))
562 if util.endswithsep(dest):
562 if util.endswithsep(dest):
563 raise util.Abort(_('destination %s is not a directory') % dest)
563 raise util.Abort(_('destination %s is not a directory') % dest)
564
564
565 tfn = targetpathfn
565 tfn = targetpathfn
566 if after:
566 if after:
567 tfn = targetpathafterfn
567 tfn = targetpathafterfn
568 copylist = []
568 copylist = []
569 for pat in pats:
569 for pat in pats:
570 srcs = walkpat(pat)
570 srcs = walkpat(pat)
571 if not srcs:
571 if not srcs:
572 continue
572 continue
573 copylist.append((tfn(pat, dest, srcs), srcs))
573 copylist.append((tfn(pat, dest, srcs), srcs))
574 if not copylist:
574 if not copylist:
575 raise util.Abort(_('no files to copy'))
575 raise util.Abort(_('no files to copy'))
576
576
577 errors = 0
577 errors = 0
578 for targetpath, srcs in copylist:
578 for targetpath, srcs in copylist:
579 for abssrc, relsrc, exact in srcs:
579 for abssrc, relsrc, exact in srcs:
580 if copyfile(abssrc, relsrc, targetpath(abssrc), exact):
580 if copyfile(abssrc, relsrc, targetpath(abssrc), exact):
581 errors += 1
581 errors += 1
582
582
583 if errors:
583 if errors:
584 ui.warn(_('(consider using --after)\n'))
584 ui.warn(_('(consider using --after)\n'))
585
585
586 return errors != 0
586 return errors != 0
587
587
588 def service(opts, parentfn=None, initfn=None, runfn=None, logfile=None,
588 def service(opts, parentfn=None, initfn=None, runfn=None, logfile=None,
589 runargs=None, appendpid=False):
589 runargs=None, appendpid=False):
590 '''Run a command as a service.'''
590 '''Run a command as a service.'''
591
591
592 if opts['daemon'] and not opts['daemon_pipefds']:
592 if opts['daemon'] and not opts['daemon_pipefds']:
593 # Signal child process startup with file removal
593 # Signal child process startup with file removal
594 lockfd, lockpath = tempfile.mkstemp(prefix='hg-service-')
594 lockfd, lockpath = tempfile.mkstemp(prefix='hg-service-')
595 os.close(lockfd)
595 os.close(lockfd)
596 try:
596 try:
597 if not runargs:
597 if not runargs:
598 runargs = util.hgcmd() + sys.argv[1:]
598 runargs = util.hgcmd() + sys.argv[1:]
599 runargs.append('--daemon-pipefds=%s' % lockpath)
599 runargs.append('--daemon-pipefds=%s' % lockpath)
600 # Don't pass --cwd to the child process, because we've already
600 # Don't pass --cwd to the child process, because we've already
601 # changed directory.
601 # changed directory.
602 for i in xrange(1, len(runargs)):
602 for i in xrange(1, len(runargs)):
603 if runargs[i].startswith('--cwd='):
603 if runargs[i].startswith('--cwd='):
604 del runargs[i]
604 del runargs[i]
605 break
605 break
606 elif runargs[i].startswith('--cwd'):
606 elif runargs[i].startswith('--cwd'):
607 del runargs[i:i + 2]
607 del runargs[i:i + 2]
608 break
608 break
609 def condfn():
609 def condfn():
610 return not os.path.exists(lockpath)
610 return not os.path.exists(lockpath)
611 pid = util.rundetached(runargs, condfn)
611 pid = util.rundetached(runargs, condfn)
612 if pid < 0:
612 if pid < 0:
613 raise util.Abort(_('child process failed to start'))
613 raise util.Abort(_('child process failed to start'))
614 finally:
614 finally:
615 try:
615 try:
616 os.unlink(lockpath)
616 os.unlink(lockpath)
617 except OSError, e:
617 except OSError, e:
618 if e.errno != errno.ENOENT:
618 if e.errno != errno.ENOENT:
619 raise
619 raise
620 if parentfn:
620 if parentfn:
621 return parentfn(pid)
621 return parentfn(pid)
622 else:
622 else:
623 return
623 return
624
624
625 if initfn:
625 if initfn:
626 initfn()
626 initfn()
627
627
628 if opts['pid_file']:
628 if opts['pid_file']:
629 mode = appendpid and 'a' or 'w'
629 mode = appendpid and 'a' or 'w'
630 fp = open(opts['pid_file'], mode)
630 fp = open(opts['pid_file'], mode)
631 fp.write(str(os.getpid()) + '\n')
631 fp.write(str(os.getpid()) + '\n')
632 fp.close()
632 fp.close()
633
633
634 if opts['daemon_pipefds']:
634 if opts['daemon_pipefds']:
635 lockpath = opts['daemon_pipefds']
635 lockpath = opts['daemon_pipefds']
636 try:
636 try:
637 os.setsid()
637 os.setsid()
638 except AttributeError:
638 except AttributeError:
639 pass
639 pass
640 os.unlink(lockpath)
640 os.unlink(lockpath)
641 util.hidewindow()
641 util.hidewindow()
642 sys.stdout.flush()
642 sys.stdout.flush()
643 sys.stderr.flush()
643 sys.stderr.flush()
644
644
645 nullfd = os.open(util.nulldev, os.O_RDWR)
645 nullfd = os.open(util.nulldev, os.O_RDWR)
646 logfilefd = nullfd
646 logfilefd = nullfd
647 if logfile:
647 if logfile:
648 logfilefd = os.open(logfile, os.O_RDWR | os.O_CREAT | os.O_APPEND)
648 logfilefd = os.open(logfile, os.O_RDWR | os.O_CREAT | os.O_APPEND)
649 os.dup2(nullfd, 0)
649 os.dup2(nullfd, 0)
650 os.dup2(logfilefd, 1)
650 os.dup2(logfilefd, 1)
651 os.dup2(logfilefd, 2)
651 os.dup2(logfilefd, 2)
652 if nullfd not in (0, 1, 2):
652 if nullfd not in (0, 1, 2):
653 os.close(nullfd)
653 os.close(nullfd)
654 if logfile and logfilefd not in (0, 1, 2):
654 if logfile and logfilefd not in (0, 1, 2):
655 os.close(logfilefd)
655 os.close(logfilefd)
656
656
657 if runfn:
657 if runfn:
658 return runfn()
658 return runfn()
659
659
660 def export(repo, revs, template='hg-%h.patch', fp=None, switch_parent=False,
660 def export(repo, revs, template='hg-%h.patch', fp=None, switch_parent=False,
661 opts=None):
661 opts=None):
662 '''export changesets as hg patches.'''
662 '''export changesets as hg patches.'''
663
663
664 total = len(revs)
664 total = len(revs)
665 revwidth = max([len(str(rev)) for rev in revs])
665 revwidth = max([len(str(rev)) for rev in revs])
666
666
667 def single(rev, seqno, fp):
667 def single(rev, seqno, fp):
668 ctx = repo[rev]
668 ctx = repo[rev]
669 node = ctx.node()
669 node = ctx.node()
670 parents = [p.node() for p in ctx.parents() if p]
670 parents = [p.node() for p in ctx.parents() if p]
671 branch = ctx.branch()
671 branch = ctx.branch()
672 if switch_parent:
672 if switch_parent:
673 parents.reverse()
673 parents.reverse()
674 prev = (parents and parents[0]) or nullid
674 prev = (parents and parents[0]) or nullid
675
675
676 if not fp:
676 if not fp:
677 fp = make_file(repo, template, node, total=total, seqno=seqno,
677 fp = make_file(repo, template, node, total=total, seqno=seqno,
678 revwidth=revwidth, mode='ab')
678 revwidth=revwidth, mode='ab')
679 if fp != sys.stdout and hasattr(fp, 'name'):
679 if fp != sys.stdout and hasattr(fp, 'name'):
680 repo.ui.note("%s\n" % fp.name)
680 repo.ui.note("%s\n" % fp.name)
681
681
682 fp.write("# HG changeset patch\n")
682 fp.write("# HG changeset patch\n")
683 fp.write("# User %s\n" % ctx.user())
683 fp.write("# User %s\n" % ctx.user())
684 fp.write("# Date %d %d\n" % ctx.date())
684 fp.write("# Date %d %d\n" % ctx.date())
685 if branch and branch != 'default':
685 if branch and branch != 'default':
686 fp.write("# Branch %s\n" % branch)
686 fp.write("# Branch %s\n" % branch)
687 fp.write("# Node ID %s\n" % hex(node))
687 fp.write("# Node ID %s\n" % hex(node))
688 fp.write("# Parent %s\n" % hex(prev))
688 fp.write("# Parent %s\n" % hex(prev))
689 if len(parents) > 1:
689 if len(parents) > 1:
690 fp.write("# Parent %s\n" % hex(parents[1]))
690 fp.write("# Parent %s\n" % hex(parents[1]))
691 fp.write(ctx.description().rstrip())
691 fp.write(ctx.description().rstrip())
692 fp.write("\n\n")
692 fp.write("\n\n")
693
693
694 for chunk in patch.diff(repo, prev, node, opts=opts):
694 for chunk in patch.diff(repo, prev, node, opts=opts):
695 fp.write(chunk)
695 fp.write(chunk)
696
696
697 for seqno, rev in enumerate(revs):
697 for seqno, rev in enumerate(revs):
698 single(rev, seqno + 1, fp)
698 single(rev, seqno + 1, fp)
699
699
700 def diffordiffstat(ui, repo, diffopts, node1, node2, match,
700 def diffordiffstat(ui, repo, diffopts, node1, node2, match,
701 changes=None, stat=False, fp=None, prefix='',
701 changes=None, stat=False, fp=None, prefix='',
702 listsubrepos=False):
702 listsubrepos=False):
703 '''show diff or diffstat.'''
703 '''show diff or diffstat.'''
704 if fp is None:
704 if fp is None:
705 write = ui.write
705 write = ui.write
706 else:
706 else:
707 def write(s, **kw):
707 def write(s, **kw):
708 fp.write(s)
708 fp.write(s)
709
709
710 if stat:
710 if stat:
711 diffopts = diffopts.copy(context=0)
711 diffopts = diffopts.copy(context=0)
712 width = 80
712 width = 80
713 if not ui.plain():
713 if not ui.plain():
714 width = ui.termwidth()
714 width = ui.termwidth()
715 chunks = patch.diff(repo, node1, node2, match, changes, diffopts,
715 chunks = patch.diff(repo, node1, node2, match, changes, diffopts,
716 prefix=prefix)
716 prefix=prefix)
717 for chunk, label in patch.diffstatui(util.iterlines(chunks),
717 for chunk, label in patch.diffstatui(util.iterlines(chunks),
718 width=width,
718 width=width,
719 git=diffopts.git):
719 git=diffopts.git):
720 write(chunk, label=label)
720 write(chunk, label=label)
721 else:
721 else:
722 for chunk, label in patch.diffui(repo, node1, node2, match,
722 for chunk, label in patch.diffui(repo, node1, node2, match,
723 changes, diffopts, prefix=prefix):
723 changes, diffopts, prefix=prefix):
724 write(chunk, label=label)
724 write(chunk, label=label)
725
725
726 if listsubrepos:
726 if listsubrepos:
727 ctx1 = repo[node1]
727 ctx1 = repo[node1]
728 ctx2 = repo[node2]
728 ctx2 = repo[node2]
729 for subpath, sub in subrepo.itersubrepos(ctx1, ctx2):
729 for subpath, sub in subrepo.itersubrepos(ctx1, ctx2):
730 if node2 is not None:
730 if node2 is not None:
731 node2 = ctx2.substate[subpath][1]
731 node2 = ctx2.substate[subpath][1]
732 submatch = matchmod.narrowmatcher(subpath, match)
732 submatch = matchmod.narrowmatcher(subpath, match)
733 sub.diff(diffopts, node2, submatch, changes=changes,
733 sub.diff(diffopts, node2, submatch, changes=changes,
734 stat=stat, fp=fp, prefix=prefix)
734 stat=stat, fp=fp, prefix=prefix)
735
735
736 class changeset_printer(object):
736 class changeset_printer(object):
737 '''show changeset information when templating not requested.'''
737 '''show changeset information when templating not requested.'''
738
738
739 def __init__(self, ui, repo, patch, diffopts, buffered):
739 def __init__(self, ui, repo, patch, diffopts, buffered):
740 self.ui = ui
740 self.ui = ui
741 self.repo = repo
741 self.repo = repo
742 self.buffered = buffered
742 self.buffered = buffered
743 self.patch = patch
743 self.patch = patch
744 self.diffopts = diffopts
744 self.diffopts = diffopts
745 self.header = {}
745 self.header = {}
746 self.hunk = {}
746 self.hunk = {}
747 self.lastheader = None
747 self.lastheader = None
748 self.footer = None
748 self.footer = None
749
749
750 def flush(self, rev):
750 def flush(self, rev):
751 if rev in self.header:
751 if rev in self.header:
752 h = self.header[rev]
752 h = self.header[rev]
753 if h != self.lastheader:
753 if h != self.lastheader:
754 self.lastheader = h
754 self.lastheader = h
755 self.ui.write(h)
755 self.ui.write(h)
756 del self.header[rev]
756 del self.header[rev]
757 if rev in self.hunk:
757 if rev in self.hunk:
758 self.ui.write(self.hunk[rev])
758 self.ui.write(self.hunk[rev])
759 del self.hunk[rev]
759 del self.hunk[rev]
760 return 1
760 return 1
761 return 0
761 return 0
762
762
763 def close(self):
763 def close(self):
764 if self.footer:
764 if self.footer:
765 self.ui.write(self.footer)
765 self.ui.write(self.footer)
766
766
767 def show(self, ctx, copies=None, matchfn=None, **props):
767 def show(self, ctx, copies=None, matchfn=None, **props):
768 if self.buffered:
768 if self.buffered:
769 self.ui.pushbuffer()
769 self.ui.pushbuffer()
770 self._show(ctx, copies, matchfn, props)
770 self._show(ctx, copies, matchfn, props)
771 self.hunk[ctx.rev()] = self.ui.popbuffer(labeled=True)
771 self.hunk[ctx.rev()] = self.ui.popbuffer(labeled=True)
772 else:
772 else:
773 self._show(ctx, copies, matchfn, props)
773 self._show(ctx, copies, matchfn, props)
774
774
775 def _show(self, ctx, copies, matchfn, props):
775 def _show(self, ctx, copies, matchfn, props):
776 '''show a single changeset or file revision'''
776 '''show a single changeset or file revision'''
777 changenode = ctx.node()
777 changenode = ctx.node()
778 rev = ctx.rev()
778 rev = ctx.rev()
779
779
780 if self.ui.quiet:
780 if self.ui.quiet:
781 self.ui.write("%d:%s\n" % (rev, short(changenode)),
781 self.ui.write("%d:%s\n" % (rev, short(changenode)),
782 label='log.node')
782 label='log.node')
783 return
783 return
784
784
785 log = self.repo.changelog
785 log = self.repo.changelog
786 date = util.datestr(ctx.date())
786 date = util.datestr(ctx.date())
787
787
788 hexfunc = self.ui.debugflag and hex or short
788 hexfunc = self.ui.debugflag and hex or short
789
789
790 parents = [(p, hexfunc(log.node(p)))
790 parents = [(p, hexfunc(log.node(p)))
791 for p in self._meaningful_parentrevs(log, rev)]
791 for p in self._meaningful_parentrevs(log, rev)]
792
792
793 self.ui.write(_("changeset: %d:%s\n") % (rev, hexfunc(changenode)),
793 self.ui.write(_("changeset: %d:%s\n") % (rev, hexfunc(changenode)),
794 label='log.changeset')
794 label='log.changeset')
795
795
796 branch = ctx.branch()
796 branch = ctx.branch()
797 # don't show the default branch name
797 # don't show the default branch name
798 if branch != 'default':
798 if branch != 'default':
799 branch = encoding.tolocal(branch)
799 branch = encoding.tolocal(branch)
800 self.ui.write(_("branch: %s\n") % branch,
800 self.ui.write(_("branch: %s\n") % branch,
801 label='log.branch')
801 label='log.branch')
802 for tag in self.repo.nodetags(changenode):
802 for tag in self.repo.nodetags(changenode):
803 self.ui.write(_("tag: %s\n") % tag,
803 self.ui.write(_("tag: %s\n") % tag,
804 label='log.tag')
804 label='log.tag')
805 for parent in parents:
805 for parent in parents:
806 self.ui.write(_("parent: %d:%s\n") % parent,
806 self.ui.write(_("parent: %d:%s\n") % parent,
807 label='log.parent')
807 label='log.parent')
808
808
809 if self.ui.debugflag:
809 if self.ui.debugflag:
810 mnode = ctx.manifestnode()
810 mnode = ctx.manifestnode()
811 self.ui.write(_("manifest: %d:%s\n") %
811 self.ui.write(_("manifest: %d:%s\n") %
812 (self.repo.manifest.rev(mnode), hex(mnode)),
812 (self.repo.manifest.rev(mnode), hex(mnode)),
813 label='ui.debug log.manifest')
813 label='ui.debug log.manifest')
814 self.ui.write(_("user: %s\n") % ctx.user(),
814 self.ui.write(_("user: %s\n") % ctx.user(),
815 label='log.user')
815 label='log.user')
816 self.ui.write(_("date: %s\n") % date,
816 self.ui.write(_("date: %s\n") % date,
817 label='log.date')
817 label='log.date')
818
818
819 if self.ui.debugflag:
819 if self.ui.debugflag:
820 files = self.repo.status(log.parents(changenode)[0], changenode)[:3]
820 files = self.repo.status(log.parents(changenode)[0], changenode)[:3]
821 for key, value in zip([_("files:"), _("files+:"), _("files-:")],
821 for key, value in zip([_("files:"), _("files+:"), _("files-:")],
822 files):
822 files):
823 if value:
823 if value:
824 self.ui.write("%-12s %s\n" % (key, " ".join(value)),
824 self.ui.write("%-12s %s\n" % (key, " ".join(value)),
825 label='ui.debug log.files')
825 label='ui.debug log.files')
826 elif ctx.files() and self.ui.verbose:
826 elif ctx.files() and self.ui.verbose:
827 self.ui.write(_("files: %s\n") % " ".join(ctx.files()),
827 self.ui.write(_("files: %s\n") % " ".join(ctx.files()),
828 label='ui.note log.files')
828 label='ui.note log.files')
829 if copies and self.ui.verbose:
829 if copies and self.ui.verbose:
830 copies = ['%s (%s)' % c for c in copies]
830 copies = ['%s (%s)' % c for c in copies]
831 self.ui.write(_("copies: %s\n") % ' '.join(copies),
831 self.ui.write(_("copies: %s\n") % ' '.join(copies),
832 label='ui.note log.copies')
832 label='ui.note log.copies')
833
833
834 extra = ctx.extra()
834 extra = ctx.extra()
835 if extra and self.ui.debugflag:
835 if extra and self.ui.debugflag:
836 for key, value in sorted(extra.items()):
836 for key, value in sorted(extra.items()):
837 self.ui.write(_("extra: %s=%s\n")
837 self.ui.write(_("extra: %s=%s\n")
838 % (key, value.encode('string_escape')),
838 % (key, value.encode('string_escape')),
839 label='ui.debug log.extra')
839 label='ui.debug log.extra')
840
840
841 description = ctx.description().strip()
841 description = ctx.description().strip()
842 if description:
842 if description:
843 if self.ui.verbose:
843 if self.ui.verbose:
844 self.ui.write(_("description:\n"),
844 self.ui.write(_("description:\n"),
845 label='ui.note log.description')
845 label='ui.note log.description')
846 self.ui.write(description,
846 self.ui.write(description,
847 label='ui.note log.description')
847 label='ui.note log.description')
848 self.ui.write("\n\n")
848 self.ui.write("\n\n")
849 else:
849 else:
850 self.ui.write(_("summary: %s\n") %
850 self.ui.write(_("summary: %s\n") %
851 description.splitlines()[0],
851 description.splitlines()[0],
852 label='log.summary')
852 label='log.summary')
853 self.ui.write("\n")
853 self.ui.write("\n")
854
854
855 self.showpatch(changenode, matchfn)
855 self.showpatch(changenode, matchfn)
856
856
857 def showpatch(self, node, matchfn):
857 def showpatch(self, node, matchfn):
858 if not matchfn:
858 if not matchfn:
859 matchfn = self.patch
859 matchfn = self.patch
860 if matchfn:
860 if matchfn:
861 stat = self.diffopts.get('stat')
861 stat = self.diffopts.get('stat')
862 diff = self.diffopts.get('patch')
862 diff = self.diffopts.get('patch')
863 diffopts = patch.diffopts(self.ui, self.diffopts)
863 diffopts = patch.diffopts(self.ui, self.diffopts)
864 prev = self.repo.changelog.parents(node)[0]
864 prev = self.repo.changelog.parents(node)[0]
865 if stat:
865 if stat:
866 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
866 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
867 match=matchfn, stat=True)
867 match=matchfn, stat=True)
868 if diff:
868 if diff:
869 if stat:
869 if stat:
870 self.ui.write("\n")
870 self.ui.write("\n")
871 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
871 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
872 match=matchfn, stat=False)
872 match=matchfn, stat=False)
873 self.ui.write("\n")
873 self.ui.write("\n")
874
874
875 def _meaningful_parentrevs(self, log, rev):
875 def _meaningful_parentrevs(self, log, rev):
876 """Return list of meaningful (or all if debug) parentrevs for rev.
876 """Return list of meaningful (or all if debug) parentrevs for rev.
877
877
878 For merges (two non-nullrev revisions) both parents are meaningful.
878 For merges (two non-nullrev revisions) both parents are meaningful.
879 Otherwise the first parent revision is considered meaningful if it
879 Otherwise the first parent revision is considered meaningful if it
880 is not the preceding revision.
880 is not the preceding revision.
881 """
881 """
882 parents = log.parentrevs(rev)
882 parents = log.parentrevs(rev)
883 if not self.ui.debugflag and parents[1] == nullrev:
883 if not self.ui.debugflag and parents[1] == nullrev:
884 if parents[0] >= rev - 1:
884 if parents[0] >= rev - 1:
885 parents = []
885 parents = []
886 else:
886 else:
887 parents = [parents[0]]
887 parents = [parents[0]]
888 return parents
888 return parents
889
889
890
890
891 class changeset_templater(changeset_printer):
891 class changeset_templater(changeset_printer):
892 '''format changeset information.'''
892 '''format changeset information.'''
893
893
894 def __init__(self, ui, repo, patch, diffopts, mapfile, buffered):
894 def __init__(self, ui, repo, patch, diffopts, mapfile, buffered):
895 changeset_printer.__init__(self, ui, repo, patch, diffopts, buffered)
895 changeset_printer.__init__(self, ui, repo, patch, diffopts, buffered)
896 formatnode = ui.debugflag and (lambda x: x) or (lambda x: x[:12])
896 formatnode = ui.debugflag and (lambda x: x) or (lambda x: x[:12])
897 defaulttempl = {
897 defaulttempl = {
898 'parent': '{rev}:{node|formatnode} ',
898 'parent': '{rev}:{node|formatnode} ',
899 'manifest': '{rev}:{node|formatnode}',
899 'manifest': '{rev}:{node|formatnode}',
900 'file_copy': '{name} ({source})',
900 'file_copy': '{name} ({source})',
901 'extra': '{key}={value|stringescape}'
901 'extra': '{key}={value|stringescape}'
902 }
902 }
903 # filecopy is preserved for compatibility reasons
903 # filecopy is preserved for compatibility reasons
904 defaulttempl['filecopy'] = defaulttempl['file_copy']
904 defaulttempl['filecopy'] = defaulttempl['file_copy']
905 self.t = templater.templater(mapfile, {'formatnode': formatnode},
905 self.t = templater.templater(mapfile, {'formatnode': formatnode},
906 cache=defaulttempl)
906 cache=defaulttempl)
907 self.cache = {}
907 self.cache = {}
908
908
909 def use_template(self, t):
909 def use_template(self, t):
910 '''set template string to use'''
910 '''set template string to use'''
911 self.t.cache['changeset'] = t
911 self.t.cache['changeset'] = t
912
912
913 def _meaningful_parentrevs(self, ctx):
913 def _meaningful_parentrevs(self, ctx):
914 """Return list of meaningful (or all if debug) parentrevs for rev.
914 """Return list of meaningful (or all if debug) parentrevs for rev.
915 """
915 """
916 parents = ctx.parents()
916 parents = ctx.parents()
917 if len(parents) > 1:
917 if len(parents) > 1:
918 return parents
918 return parents
919 if self.ui.debugflag:
919 if self.ui.debugflag:
920 return [parents[0], self.repo['null']]
920 return [parents[0], self.repo['null']]
921 if parents[0].rev() >= ctx.rev() - 1:
921 if parents[0].rev() >= ctx.rev() - 1:
922 return []
922 return []
923 return parents
923 return parents
924
924
925 def _show(self, ctx, copies, matchfn, props):
925 def _show(self, ctx, copies, matchfn, props):
926 '''show a single changeset or file revision'''
926 '''show a single changeset or file revision'''
927
927
928 showlist = templatekw.showlist
928 showlist = templatekw.showlist
929
929
930 # showparents() behaviour depends on ui trace level which
930 # showparents() behaviour depends on ui trace level which
931 # causes unexpected behaviours at templating level and makes
931 # causes unexpected behaviours at templating level and makes
932 # it harder to extract it in a standalone function. Its
932 # it harder to extract it in a standalone function. Its
933 # behaviour cannot be changed so leave it here for now.
933 # behaviour cannot be changed so leave it here for now.
934 def showparents(**args):
934 def showparents(**args):
935 ctx = args['ctx']
935 ctx = args['ctx']
936 parents = [[('rev', p.rev()), ('node', p.hex())]
936 parents = [[('rev', p.rev()), ('node', p.hex())]
937 for p in self._meaningful_parentrevs(ctx)]
937 for p in self._meaningful_parentrevs(ctx)]
938 return showlist('parent', parents, **args)
938 return showlist('parent', parents, **args)
939
939
940 props = props.copy()
940 props = props.copy()
941 props.update(templatekw.keywords)
941 props.update(templatekw.keywords)
942 props['parents'] = showparents
942 props['parents'] = showparents
943 props['templ'] = self.t
943 props['templ'] = self.t
944 props['ctx'] = ctx
944 props['ctx'] = ctx
945 props['repo'] = self.repo
945 props['repo'] = self.repo
946 props['revcache'] = {'copies': copies}
946 props['revcache'] = {'copies': copies}
947 props['cache'] = self.cache
947 props['cache'] = self.cache
948
948
949 # find correct templates for current mode
949 # find correct templates for current mode
950
950
951 tmplmodes = [
951 tmplmodes = [
952 (True, None),
952 (True, None),
953 (self.ui.verbose, 'verbose'),
953 (self.ui.verbose, 'verbose'),
954 (self.ui.quiet, 'quiet'),
954 (self.ui.quiet, 'quiet'),
955 (self.ui.debugflag, 'debug'),
955 (self.ui.debugflag, 'debug'),
956 ]
956 ]
957
957
958 types = {'header': '', 'footer':'', 'changeset': 'changeset'}
958 types = {'header': '', 'footer':'', 'changeset': 'changeset'}
959 for mode, postfix in tmplmodes:
959 for mode, postfix in tmplmodes:
960 for type in types:
960 for type in types:
961 cur = postfix and ('%s_%s' % (type, postfix)) or type
961 cur = postfix and ('%s_%s' % (type, postfix)) or type
962 if mode and cur in self.t:
962 if mode and cur in self.t:
963 types[type] = cur
963 types[type] = cur
964
964
965 try:
965 try:
966
966
967 # write header
967 # write header
968 if types['header']:
968 if types['header']:
969 h = templater.stringify(self.t(types['header'], **props))
969 h = templater.stringify(self.t(types['header'], **props))
970 if self.buffered:
970 if self.buffered:
971 self.header[ctx.rev()] = h
971 self.header[ctx.rev()] = h
972 else:
972 else:
973 if self.lastheader != h:
973 if self.lastheader != h:
974 self.lastheader = h
974 self.lastheader = h
975 self.ui.write(h)
975 self.ui.write(h)
976
976
977 # write changeset metadata, then patch if requested
977 # write changeset metadata, then patch if requested
978 key = types['changeset']
978 key = types['changeset']
979 self.ui.write(templater.stringify(self.t(key, **props)))
979 self.ui.write(templater.stringify(self.t(key, **props)))
980 self.showpatch(ctx.node(), matchfn)
980 self.showpatch(ctx.node(), matchfn)
981
981
982 if types['footer']:
982 if types['footer']:
983 if not self.footer:
983 if not self.footer:
984 self.footer = templater.stringify(self.t(types['footer'],
984 self.footer = templater.stringify(self.t(types['footer'],
985 **props))
985 **props))
986
986
987 except KeyError, inst:
987 except KeyError, inst:
988 msg = _("%s: no key named '%s'")
988 msg = _("%s: no key named '%s'")
989 raise util.Abort(msg % (self.t.mapfile, inst.args[0]))
989 raise util.Abort(msg % (self.t.mapfile, inst.args[0]))
990 except SyntaxError, inst:
990 except SyntaxError, inst:
991 raise util.Abort('%s: %s' % (self.t.mapfile, inst.args[0]))
991 raise util.Abort('%s: %s' % (self.t.mapfile, inst.args[0]))
992
992
993 def show_changeset(ui, repo, opts, buffered=False):
993 def show_changeset(ui, repo, opts, buffered=False):
994 """show one changeset using template or regular display.
994 """show one changeset using template or regular display.
995
995
996 Display format will be the first non-empty hit of:
996 Display format will be the first non-empty hit of:
997 1. option 'template'
997 1. option 'template'
998 2. option 'style'
998 2. option 'style'
999 3. [ui] setting 'logtemplate'
999 3. [ui] setting 'logtemplate'
1000 4. [ui] setting 'style'
1000 4. [ui] setting 'style'
1001 If all of these values are either the unset or the empty string,
1001 If all of these values are either the unset or the empty string,
1002 regular display via changeset_printer() is done.
1002 regular display via changeset_printer() is done.
1003 """
1003 """
1004 # options
1004 # options
1005 patch = False
1005 patch = False
1006 if opts.get('patch') or opts.get('stat'):
1006 if opts.get('patch') or opts.get('stat'):
1007 patch = matchall(repo)
1007 patch = matchall(repo)
1008
1008
1009 tmpl = opts.get('template')
1009 tmpl = opts.get('template')
1010 style = None
1010 style = None
1011 if tmpl:
1011 if tmpl:
1012 tmpl = templater.parsestring(tmpl, quoted=False)
1012 tmpl = templater.parsestring(tmpl, quoted=False)
1013 else:
1013 else:
1014 style = opts.get('style')
1014 style = opts.get('style')
1015
1015
1016 # ui settings
1016 # ui settings
1017 if not (tmpl or style):
1017 if not (tmpl or style):
1018 tmpl = ui.config('ui', 'logtemplate')
1018 tmpl = ui.config('ui', 'logtemplate')
1019 if tmpl:
1019 if tmpl:
1020 tmpl = templater.parsestring(tmpl)
1020 tmpl = templater.parsestring(tmpl)
1021 else:
1021 else:
1022 style = util.expandpath(ui.config('ui', 'style', ''))
1022 style = util.expandpath(ui.config('ui', 'style', ''))
1023
1023
1024 if not (tmpl or style):
1024 if not (tmpl or style):
1025 return changeset_printer(ui, repo, patch, opts, buffered)
1025 return changeset_printer(ui, repo, patch, opts, buffered)
1026
1026
1027 mapfile = None
1027 mapfile = None
1028 if style and not tmpl:
1028 if style and not tmpl:
1029 mapfile = style
1029 mapfile = style
1030 if not os.path.split(mapfile)[0]:
1030 if not os.path.split(mapfile)[0]:
1031 mapname = (templater.templatepath('map-cmdline.' + mapfile)
1031 mapname = (templater.templatepath('map-cmdline.' + mapfile)
1032 or templater.templatepath(mapfile))
1032 or templater.templatepath(mapfile))
1033 if mapname:
1033 if mapname:
1034 mapfile = mapname
1034 mapfile = mapname
1035
1035
1036 try:
1036 try:
1037 t = changeset_templater(ui, repo, patch, opts, mapfile, buffered)
1037 t = changeset_templater(ui, repo, patch, opts, mapfile, buffered)
1038 except SyntaxError, inst:
1038 except SyntaxError, inst:
1039 raise util.Abort(inst.args[0])
1039 raise util.Abort(inst.args[0])
1040 if tmpl:
1040 if tmpl:
1041 t.use_template(tmpl)
1041 t.use_template(tmpl)
1042 return t
1042 return t
1043
1043
1044 def finddate(ui, repo, date):
1044 def finddate(ui, repo, date):
1045 """Find the tipmost changeset that matches the given date spec"""
1045 """Find the tipmost changeset that matches the given date spec"""
1046
1046
1047 df = util.matchdate(date)
1047 df = util.matchdate(date)
1048 m = matchall(repo)
1048 m = matchall(repo)
1049 results = {}
1049 results = {}
1050
1050
1051 def prep(ctx, fns):
1051 def prep(ctx, fns):
1052 d = ctx.date()
1052 d = ctx.date()
1053 if df(d[0]):
1053 if df(d[0]):
1054 results[ctx.rev()] = d
1054 results[ctx.rev()] = d
1055
1055
1056 for ctx in walkchangerevs(repo, m, {'rev': None}, prep):
1056 for ctx in walkchangerevs(repo, m, {'rev': None}, prep):
1057 rev = ctx.rev()
1057 rev = ctx.rev()
1058 if rev in results:
1058 if rev in results:
1059 ui.status(_("Found revision %s from %s\n") %
1059 ui.status(_("Found revision %s from %s\n") %
1060 (rev, util.datestr(results[rev])))
1060 (rev, util.datestr(results[rev])))
1061 return str(rev)
1061 return str(rev)
1062
1062
1063 raise util.Abort(_("revision matching date not found"))
1063 raise util.Abort(_("revision matching date not found"))
1064
1064
1065 def walkchangerevs(repo, match, opts, prepare):
1065 def walkchangerevs(repo, match, opts, prepare):
1066 '''Iterate over files and the revs in which they changed.
1066 '''Iterate over files and the revs in which they changed.
1067
1067
1068 Callers most commonly need to iterate backwards over the history
1068 Callers most commonly need to iterate backwards over the history
1069 in which they are interested. Doing so has awful (quadratic-looking)
1069 in which they are interested. Doing so has awful (quadratic-looking)
1070 performance, so we use iterators in a "windowed" way.
1070 performance, so we use iterators in a "windowed" way.
1071
1071
1072 We walk a window of revisions in the desired order. Within the
1072 We walk a window of revisions in the desired order. Within the
1073 window, we first walk forwards to gather data, then in the desired
1073 window, we first walk forwards to gather data, then in the desired
1074 order (usually backwards) to display it.
1074 order (usually backwards) to display it.
1075
1075
1076 This function returns an iterator yielding contexts. Before
1076 This function returns an iterator yielding contexts. Before
1077 yielding each context, the iterator will first call the prepare
1077 yielding each context, the iterator will first call the prepare
1078 function on each context in the window in forward order.'''
1078 function on each context in the window in forward order.'''
1079
1079
1080 def increasing_windows(start, end, windowsize=8, sizelimit=512):
1080 def increasing_windows(start, end, windowsize=8, sizelimit=512):
1081 if start < end:
1081 if start < end:
1082 while start < end:
1082 while start < end:
1083 yield start, min(windowsize, end - start)
1083 yield start, min(windowsize, end - start)
1084 start += windowsize
1084 start += windowsize
1085 if windowsize < sizelimit:
1085 if windowsize < sizelimit:
1086 windowsize *= 2
1086 windowsize *= 2
1087 else:
1087 else:
1088 while start > end:
1088 while start > end:
1089 yield start, min(windowsize, start - end - 1)
1089 yield start, min(windowsize, start - end - 1)
1090 start -= windowsize
1090 start -= windowsize
1091 if windowsize < sizelimit:
1091 if windowsize < sizelimit:
1092 windowsize *= 2
1092 windowsize *= 2
1093
1093
1094 follow = opts.get('follow') or opts.get('follow_first')
1094 follow = opts.get('follow') or opts.get('follow_first')
1095
1095
1096 if not len(repo):
1096 if not len(repo):
1097 return []
1097 return []
1098
1098
1099 if follow:
1099 if follow:
1100 defrange = '%s:0' % repo['.'].rev()
1100 defrange = '%s:0' % repo['.'].rev()
1101 else:
1101 else:
1102 defrange = '-1:0'
1102 defrange = '-1:0'
1103 revs = revrange(repo, opts['rev'] or [defrange])
1103 revs = revrange(repo, opts['rev'] or [defrange])
1104 if not revs:
1104 if not revs:
1105 return []
1105 return []
1106 wanted = set()
1106 wanted = set()
1107 slowpath = match.anypats() or (match.files() and opts.get('removed'))
1107 slowpath = match.anypats() or (match.files() and opts.get('removed'))
1108 fncache = {}
1108 fncache = {}
1109 change = util.cachefunc(repo.changectx)
1109 change = util.cachefunc(repo.changectx)
1110
1110
1111 # First step is to fill wanted, the set of revisions that we want to yield.
1111 # First step is to fill wanted, the set of revisions that we want to yield.
1112 # When it does not induce extra cost, we also fill fncache for revisions in
1112 # When it does not induce extra cost, we also fill fncache for revisions in
1113 # wanted: a cache of filenames that were changed (ctx.files()) and that
1113 # wanted: a cache of filenames that were changed (ctx.files()) and that
1114 # match the file filtering conditions.
1114 # match the file filtering conditions.
1115
1115
1116 if not slowpath and not match.files():
1116 if not slowpath and not match.files():
1117 # No files, no patterns. Display all revs.
1117 # No files, no patterns. Display all revs.
1118 wanted = set(revs)
1118 wanted = set(revs)
1119 copies = []
1119 copies = []
1120
1120
1121 if not slowpath:
1121 if not slowpath:
1122 # We only have to read through the filelog to find wanted revisions
1122 # We only have to read through the filelog to find wanted revisions
1123
1123
1124 minrev, maxrev = min(revs), max(revs)
1124 minrev, maxrev = min(revs), max(revs)
1125 def filerevgen(filelog, last):
1125 def filerevgen(filelog, last):
1126 """
1126 """
1127 Only files, no patterns. Check the history of each file.
1127 Only files, no patterns. Check the history of each file.
1128
1128
1129 Examines filelog entries within minrev, maxrev linkrev range
1129 Examines filelog entries within minrev, maxrev linkrev range
1130 Returns an iterator yielding (linkrev, parentlinkrevs, copied)
1130 Returns an iterator yielding (linkrev, parentlinkrevs, copied)
1131 tuples in backwards order
1131 tuples in backwards order
1132 """
1132 """
1133 cl_count = len(repo)
1133 cl_count = len(repo)
1134 revs = []
1134 revs = []
1135 for j in xrange(0, last + 1):
1135 for j in xrange(0, last + 1):
1136 linkrev = filelog.linkrev(j)
1136 linkrev = filelog.linkrev(j)
1137 if linkrev < minrev:
1137 if linkrev < minrev:
1138 continue
1138 continue
1139 # only yield rev for which we have the changelog, it can
1139 # only yield rev for which we have the changelog, it can
1140 # happen while doing "hg log" during a pull or commit
1140 # happen while doing "hg log" during a pull or commit
1141 if linkrev > maxrev or linkrev >= cl_count:
1141 if linkrev >= cl_count:
1142 break
1142 break
1143
1143
1144 parentlinkrevs = []
1144 parentlinkrevs = []
1145 for p in filelog.parentrevs(j):
1145 for p in filelog.parentrevs(j):
1146 if p != nullrev:
1146 if p != nullrev:
1147 parentlinkrevs.append(filelog.linkrev(p))
1147 parentlinkrevs.append(filelog.linkrev(p))
1148 n = filelog.node(j)
1148 n = filelog.node(j)
1149 revs.append((linkrev, parentlinkrevs,
1149 revs.append((linkrev, parentlinkrevs,
1150 follow and filelog.renamed(n)))
1150 follow and filelog.renamed(n)))
1151
1151
1152 return reversed(revs)
1152 return reversed(revs)
1153 def iterfiles():
1153 def iterfiles():
1154 for filename in match.files():
1154 for filename in match.files():
1155 yield filename, None
1155 yield filename, None
1156 for filename_node in copies:
1156 for filename_node in copies:
1157 yield filename_node
1157 yield filename_node
1158 for file_, node in iterfiles():
1158 for file_, node in iterfiles():
1159 filelog = repo.file(file_)
1159 filelog = repo.file(file_)
1160 if not len(filelog):
1160 if not len(filelog):
1161 if node is None:
1161 if node is None:
1162 # A zero count may be a directory or deleted file, so
1162 # A zero count may be a directory or deleted file, so
1163 # try to find matching entries on the slow path.
1163 # try to find matching entries on the slow path.
1164 if follow:
1164 if follow:
1165 raise util.Abort(
1165 raise util.Abort(
1166 _('cannot follow nonexistent file: "%s"') % file_)
1166 _('cannot follow nonexistent file: "%s"') % file_)
1167 slowpath = True
1167 slowpath = True
1168 break
1168 break
1169 else:
1169 else:
1170 continue
1170 continue
1171
1171
1172 if node is None:
1172 if node is None:
1173 last = len(filelog) - 1
1173 last = len(filelog) - 1
1174 else:
1174 else:
1175 last = filelog.rev(node)
1175 last = filelog.rev(node)
1176
1176
1177
1177
1178 # keep track of all ancestors of the file
1178 # keep track of all ancestors of the file
1179 ancestors = set([filelog.linkrev(last)])
1179 ancestors = set([filelog.linkrev(last)])
1180
1180
1181 # iterate from latest to oldest revision
1181 # iterate from latest to oldest revision
1182 for rev, flparentlinkrevs, copied in filerevgen(filelog, last):
1182 for rev, flparentlinkrevs, copied in filerevgen(filelog, last):
1183 if not follow:
1184 if rev > maxrev:
1185 continue
1186 else:
1187 # Note that last might not be the first interesting
1188 # rev to us:
1189 # if the file has been changed after maxrev, we'll
1190 # have linkrev(last) > maxrev, and we still need
1191 # to explore the file graph
1183 if rev not in ancestors:
1192 if rev not in ancestors:
1184 continue
1193 continue
1185 # XXX insert 1327 fix here
1194 # XXX insert 1327 fix here
1186 if flparentlinkrevs:
1195 if flparentlinkrevs:
1187 ancestors.update(flparentlinkrevs)
1196 ancestors.update(flparentlinkrevs)
1188
1197
1189 fncache.setdefault(rev, []).append(file_)
1198 fncache.setdefault(rev, []).append(file_)
1190 wanted.add(rev)
1199 wanted.add(rev)
1191 if copied:
1200 if copied:
1192 copies.append(copied)
1201 copies.append(copied)
1193 if slowpath:
1202 if slowpath:
1194 # We have to read the changelog to match filenames against
1203 # We have to read the changelog to match filenames against
1195 # changed files
1204 # changed files
1196
1205
1197 if follow:
1206 if follow:
1198 raise util.Abort(_('can only follow copies/renames for explicit '
1207 raise util.Abort(_('can only follow copies/renames for explicit '
1199 'filenames'))
1208 'filenames'))
1200
1209
1201 # The slow path checks files modified in every changeset.
1210 # The slow path checks files modified in every changeset.
1202 for i in sorted(revs):
1211 for i in sorted(revs):
1203 ctx = change(i)
1212 ctx = change(i)
1204 matches = filter(match, ctx.files())
1213 matches = filter(match, ctx.files())
1205 if matches:
1214 if matches:
1206 fncache[i] = matches
1215 fncache[i] = matches
1207 wanted.add(i)
1216 wanted.add(i)
1208
1217
1209 class followfilter(object):
1218 class followfilter(object):
1210 def __init__(self, onlyfirst=False):
1219 def __init__(self, onlyfirst=False):
1211 self.startrev = nullrev
1220 self.startrev = nullrev
1212 self.roots = set()
1221 self.roots = set()
1213 self.onlyfirst = onlyfirst
1222 self.onlyfirst = onlyfirst
1214
1223
1215 def match(self, rev):
1224 def match(self, rev):
1216 def realparents(rev):
1225 def realparents(rev):
1217 if self.onlyfirst:
1226 if self.onlyfirst:
1218 return repo.changelog.parentrevs(rev)[0:1]
1227 return repo.changelog.parentrevs(rev)[0:1]
1219 else:
1228 else:
1220 return filter(lambda x: x != nullrev,
1229 return filter(lambda x: x != nullrev,
1221 repo.changelog.parentrevs(rev))
1230 repo.changelog.parentrevs(rev))
1222
1231
1223 if self.startrev == nullrev:
1232 if self.startrev == nullrev:
1224 self.startrev = rev
1233 self.startrev = rev
1225 return True
1234 return True
1226
1235
1227 if rev > self.startrev:
1236 if rev > self.startrev:
1228 # forward: all descendants
1237 # forward: all descendants
1229 if not self.roots:
1238 if not self.roots:
1230 self.roots.add(self.startrev)
1239 self.roots.add(self.startrev)
1231 for parent in realparents(rev):
1240 for parent in realparents(rev):
1232 if parent in self.roots:
1241 if parent in self.roots:
1233 self.roots.add(rev)
1242 self.roots.add(rev)
1234 return True
1243 return True
1235 else:
1244 else:
1236 # backwards: all parents
1245 # backwards: all parents
1237 if not self.roots:
1246 if not self.roots:
1238 self.roots.update(realparents(self.startrev))
1247 self.roots.update(realparents(self.startrev))
1239 if rev in self.roots:
1248 if rev in self.roots:
1240 self.roots.remove(rev)
1249 self.roots.remove(rev)
1241 self.roots.update(realparents(rev))
1250 self.roots.update(realparents(rev))
1242 return True
1251 return True
1243
1252
1244 return False
1253 return False
1245
1254
1246 # it might be worthwhile to do this in the iterator if the rev range
1255 # it might be worthwhile to do this in the iterator if the rev range
1247 # is descending and the prune args are all within that range
1256 # is descending and the prune args are all within that range
1248 for rev in opts.get('prune', ()):
1257 for rev in opts.get('prune', ()):
1249 rev = repo.changelog.rev(repo.lookup(rev))
1258 rev = repo.changelog.rev(repo.lookup(rev))
1250 ff = followfilter()
1259 ff = followfilter()
1251 stop = min(revs[0], revs[-1])
1260 stop = min(revs[0], revs[-1])
1252 for x in xrange(rev, stop - 1, -1):
1261 for x in xrange(rev, stop - 1, -1):
1253 if ff.match(x):
1262 if ff.match(x):
1254 wanted.discard(x)
1263 wanted.discard(x)
1255
1264
1256 # Now that wanted is correctly initialized, we can iterate over the
1265 # Now that wanted is correctly initialized, we can iterate over the
1257 # revision range, yielding only revisions in wanted.
1266 # revision range, yielding only revisions in wanted.
1258 def iterate():
1267 def iterate():
1259 if follow and not match.files():
1268 if follow and not match.files():
1260 ff = followfilter(onlyfirst=opts.get('follow_first'))
1269 ff = followfilter(onlyfirst=opts.get('follow_first'))
1261 def want(rev):
1270 def want(rev):
1262 return ff.match(rev) and rev in wanted
1271 return ff.match(rev) and rev in wanted
1263 else:
1272 else:
1264 def want(rev):
1273 def want(rev):
1265 return rev in wanted
1274 return rev in wanted
1266
1275
1267 for i, window in increasing_windows(0, len(revs)):
1276 for i, window in increasing_windows(0, len(revs)):
1268 nrevs = [rev for rev in revs[i:i + window] if want(rev)]
1277 nrevs = [rev for rev in revs[i:i + window] if want(rev)]
1269 for rev in sorted(nrevs):
1278 for rev in sorted(nrevs):
1270 fns = fncache.get(rev)
1279 fns = fncache.get(rev)
1271 ctx = change(rev)
1280 ctx = change(rev)
1272 if not fns:
1281 if not fns:
1273 def fns_generator():
1282 def fns_generator():
1274 for f in ctx.files():
1283 for f in ctx.files():
1275 if match(f):
1284 if match(f):
1276 yield f
1285 yield f
1277 fns = fns_generator()
1286 fns = fns_generator()
1278 prepare(ctx, fns)
1287 prepare(ctx, fns)
1279 for rev in nrevs:
1288 for rev in nrevs:
1280 yield change(rev)
1289 yield change(rev)
1281 return iterate()
1290 return iterate()
1282
1291
1283 def add(ui, repo, match, dryrun, listsubrepos, prefix):
1292 def add(ui, repo, match, dryrun, listsubrepos, prefix):
1284 join = lambda f: os.path.join(prefix, f)
1293 join = lambda f: os.path.join(prefix, f)
1285 bad = []
1294 bad = []
1286 oldbad = match.bad
1295 oldbad = match.bad
1287 match.bad = lambda x, y: bad.append(x) or oldbad(x, y)
1296 match.bad = lambda x, y: bad.append(x) or oldbad(x, y)
1288 names = []
1297 names = []
1289 wctx = repo[None]
1298 wctx = repo[None]
1290 for f in repo.walk(match):
1299 for f in repo.walk(match):
1291 exact = match.exact(f)
1300 exact = match.exact(f)
1292 if exact or f not in repo.dirstate:
1301 if exact or f not in repo.dirstate:
1293 names.append(f)
1302 names.append(f)
1294 if ui.verbose or not exact:
1303 if ui.verbose or not exact:
1295 ui.status(_('adding %s\n') % match.rel(join(f)))
1304 ui.status(_('adding %s\n') % match.rel(join(f)))
1296
1305
1297 if listsubrepos:
1306 if listsubrepos:
1298 for subpath in wctx.substate:
1307 for subpath in wctx.substate:
1299 sub = wctx.sub(subpath)
1308 sub = wctx.sub(subpath)
1300 try:
1309 try:
1301 submatch = matchmod.narrowmatcher(subpath, match)
1310 submatch = matchmod.narrowmatcher(subpath, match)
1302 bad.extend(sub.add(ui, submatch, dryrun, prefix))
1311 bad.extend(sub.add(ui, submatch, dryrun, prefix))
1303 except error.LookupError:
1312 except error.LookupError:
1304 ui.status(_("skipping missing subrepository: %s\n")
1313 ui.status(_("skipping missing subrepository: %s\n")
1305 % join(subpath))
1314 % join(subpath))
1306
1315
1307 if not dryrun:
1316 if not dryrun:
1308 rejected = wctx.add(names, prefix)
1317 rejected = wctx.add(names, prefix)
1309 bad.extend(f for f in rejected if f in match.files())
1318 bad.extend(f for f in rejected if f in match.files())
1310 return bad
1319 return bad
1311
1320
1312 def commit(ui, repo, commitfunc, pats, opts):
1321 def commit(ui, repo, commitfunc, pats, opts):
1313 '''commit the specified files or all outstanding changes'''
1322 '''commit the specified files or all outstanding changes'''
1314 date = opts.get('date')
1323 date = opts.get('date')
1315 if date:
1324 if date:
1316 opts['date'] = util.parsedate(date)
1325 opts['date'] = util.parsedate(date)
1317 message = logmessage(opts)
1326 message = logmessage(opts)
1318
1327
1319 # extract addremove carefully -- this function can be called from a command
1328 # extract addremove carefully -- this function can be called from a command
1320 # that doesn't support addremove
1329 # that doesn't support addremove
1321 if opts.get('addremove'):
1330 if opts.get('addremove'):
1322 addremove(repo, pats, opts)
1331 addremove(repo, pats, opts)
1323
1332
1324 return commitfunc(ui, repo, message, match(repo, pats, opts), opts)
1333 return commitfunc(ui, repo, message, match(repo, pats, opts), opts)
1325
1334
1326 def commiteditor(repo, ctx, subs):
1335 def commiteditor(repo, ctx, subs):
1327 if ctx.description():
1336 if ctx.description():
1328 return ctx.description()
1337 return ctx.description()
1329 return commitforceeditor(repo, ctx, subs)
1338 return commitforceeditor(repo, ctx, subs)
1330
1339
1331 def commitforceeditor(repo, ctx, subs):
1340 def commitforceeditor(repo, ctx, subs):
1332 edittext = []
1341 edittext = []
1333 modified, added, removed = ctx.modified(), ctx.added(), ctx.removed()
1342 modified, added, removed = ctx.modified(), ctx.added(), ctx.removed()
1334 if ctx.description():
1343 if ctx.description():
1335 edittext.append(ctx.description())
1344 edittext.append(ctx.description())
1336 edittext.append("")
1345 edittext.append("")
1337 edittext.append("") # Empty line between message and comments.
1346 edittext.append("") # Empty line between message and comments.
1338 edittext.append(_("HG: Enter commit message."
1347 edittext.append(_("HG: Enter commit message."
1339 " Lines beginning with 'HG:' are removed."))
1348 " Lines beginning with 'HG:' are removed."))
1340 edittext.append(_("HG: Leave message empty to abort commit."))
1349 edittext.append(_("HG: Leave message empty to abort commit."))
1341 edittext.append("HG: --")
1350 edittext.append("HG: --")
1342 edittext.append(_("HG: user: %s") % ctx.user())
1351 edittext.append(_("HG: user: %s") % ctx.user())
1343 if ctx.p2():
1352 if ctx.p2():
1344 edittext.append(_("HG: branch merge"))
1353 edittext.append(_("HG: branch merge"))
1345 if ctx.branch():
1354 if ctx.branch():
1346 edittext.append(_("HG: branch '%s'")
1355 edittext.append(_("HG: branch '%s'")
1347 % encoding.tolocal(ctx.branch()))
1356 % encoding.tolocal(ctx.branch()))
1348 edittext.extend([_("HG: subrepo %s") % s for s in subs])
1357 edittext.extend([_("HG: subrepo %s") % s for s in subs])
1349 edittext.extend([_("HG: added %s") % f for f in added])
1358 edittext.extend([_("HG: added %s") % f for f in added])
1350 edittext.extend([_("HG: changed %s") % f for f in modified])
1359 edittext.extend([_("HG: changed %s") % f for f in modified])
1351 edittext.extend([_("HG: removed %s") % f for f in removed])
1360 edittext.extend([_("HG: removed %s") % f for f in removed])
1352 if not added and not modified and not removed:
1361 if not added and not modified and not removed:
1353 edittext.append(_("HG: no files changed"))
1362 edittext.append(_("HG: no files changed"))
1354 edittext.append("")
1363 edittext.append("")
1355 # run editor in the repository root
1364 # run editor in the repository root
1356 olddir = os.getcwd()
1365 olddir = os.getcwd()
1357 os.chdir(repo.root)
1366 os.chdir(repo.root)
1358 text = repo.ui.edit("\n".join(edittext), ctx.user())
1367 text = repo.ui.edit("\n".join(edittext), ctx.user())
1359 text = re.sub("(?m)^HG:.*(\n|$)", "", text)
1368 text = re.sub("(?m)^HG:.*(\n|$)", "", text)
1360 os.chdir(olddir)
1369 os.chdir(olddir)
1361
1370
1362 if not text.strip():
1371 if not text.strip():
1363 raise util.Abort(_("empty commit message"))
1372 raise util.Abort(_("empty commit message"))
1364
1373
1365 return text
1374 return text
@@ -1,1094 +1,1119
1 $ hg init a
1 $ hg init a
2
2
3 $ cd a
3 $ cd a
4 $ echo a > a
4 $ echo a > a
5 $ hg ci -Ama -d '1 0'
5 $ hg ci -Ama -d '1 0'
6 adding a
6 adding a
7
7
8 $ hg cp a b
8 $ hg cp a b
9 $ hg ci -mb -d '2 0'
9 $ hg ci -mb -d '2 0'
10
10
11 $ mkdir dir
11 $ mkdir dir
12 $ hg mv b dir
12 $ hg mv b dir
13 $ hg ci -mc -d '3 0'
13 $ hg ci -mc -d '3 0'
14
14
15 $ hg mv a b
15 $ hg mv a b
16 $ echo a > d
16 $ echo a > d
17 $ hg add d
17 $ hg add d
18 $ hg ci -md -d '4 0'
18 $ hg ci -md -d '4 0'
19
19
20 $ hg mv dir/b e
20 $ hg mv dir/b e
21 $ hg ci -me -d '5 0'
21 $ hg ci -me -d '5 0'
22
22
23 $ hg log a
23 $ hg log a
24 changeset: 0:8580ff50825a
24 changeset: 0:8580ff50825a
25 user: test
25 user: test
26 date: Thu Jan 01 00:00:01 1970 +0000
26 date: Thu Jan 01 00:00:01 1970 +0000
27 summary: a
27 summary: a
28
28
29
29
30 -f, directory
30 -f, directory
31
31
32 $ hg log -f dir
32 $ hg log -f dir
33 abort: cannot follow nonexistent file: "dir"
33 abort: cannot follow nonexistent file: "dir"
34 [255]
34 [255]
35
35
36 -f, but no args
36 -f, but no args
37
37
38 $ hg log -f
38 $ hg log -f
39 changeset: 4:66c1345dc4f9
39 changeset: 4:66c1345dc4f9
40 tag: tip
40 tag: tip
41 user: test
41 user: test
42 date: Thu Jan 01 00:00:05 1970 +0000
42 date: Thu Jan 01 00:00:05 1970 +0000
43 summary: e
43 summary: e
44
44
45 changeset: 3:7c6c671bb7cc
45 changeset: 3:7c6c671bb7cc
46 user: test
46 user: test
47 date: Thu Jan 01 00:00:04 1970 +0000
47 date: Thu Jan 01 00:00:04 1970 +0000
48 summary: d
48 summary: d
49
49
50 changeset: 2:41dd4284081e
50 changeset: 2:41dd4284081e
51 user: test
51 user: test
52 date: Thu Jan 01 00:00:03 1970 +0000
52 date: Thu Jan 01 00:00:03 1970 +0000
53 summary: c
53 summary: c
54
54
55 changeset: 1:784de7cef101
55 changeset: 1:784de7cef101
56 user: test
56 user: test
57 date: Thu Jan 01 00:00:02 1970 +0000
57 date: Thu Jan 01 00:00:02 1970 +0000
58 summary: b
58 summary: b
59
59
60 changeset: 0:8580ff50825a
60 changeset: 0:8580ff50825a
61 user: test
61 user: test
62 date: Thu Jan 01 00:00:01 1970 +0000
62 date: Thu Jan 01 00:00:01 1970 +0000
63 summary: a
63 summary: a
64
64
65
65
66 one rename
66 one rename
67
67
68 $ hg log -vf a
68 $ hg log -vf a
69 changeset: 0:8580ff50825a
69 changeset: 0:8580ff50825a
70 user: test
70 user: test
71 date: Thu Jan 01 00:00:01 1970 +0000
71 date: Thu Jan 01 00:00:01 1970 +0000
72 files: a
72 files: a
73 description:
73 description:
74 a
74 a
75
75
76
76
77
77
78 many renames
78 many renames
79
79
80 $ hg log -vf e
80 $ hg log -vf e
81 changeset: 4:66c1345dc4f9
81 changeset: 4:66c1345dc4f9
82 tag: tip
82 tag: tip
83 user: test
83 user: test
84 date: Thu Jan 01 00:00:05 1970 +0000
84 date: Thu Jan 01 00:00:05 1970 +0000
85 files: dir/b e
85 files: dir/b e
86 description:
86 description:
87 e
87 e
88
88
89
89
90 changeset: 2:41dd4284081e
90 changeset: 2:41dd4284081e
91 user: test
91 user: test
92 date: Thu Jan 01 00:00:03 1970 +0000
92 date: Thu Jan 01 00:00:03 1970 +0000
93 files: b dir/b
93 files: b dir/b
94 description:
94 description:
95 c
95 c
96
96
97
97
98 changeset: 1:784de7cef101
98 changeset: 1:784de7cef101
99 user: test
99 user: test
100 date: Thu Jan 01 00:00:02 1970 +0000
100 date: Thu Jan 01 00:00:02 1970 +0000
101 files: b
101 files: b
102 description:
102 description:
103 b
103 b
104
104
105
105
106 changeset: 0:8580ff50825a
106 changeset: 0:8580ff50825a
107 user: test
107 user: test
108 date: Thu Jan 01 00:00:01 1970 +0000
108 date: Thu Jan 01 00:00:01 1970 +0000
109 files: a
109 files: a
110 description:
110 description:
111 a
111 a
112
112
113
113
114
114
115
115
116 log -pf dir/b
116 log -pf dir/b
117
117
118 $ hg log -pf dir/b
118 $ hg log -pf dir/b
119 changeset: 2:41dd4284081e
119 changeset: 2:41dd4284081e
120 user: test
120 user: test
121 date: Thu Jan 01 00:00:03 1970 +0000
121 date: Thu Jan 01 00:00:03 1970 +0000
122 summary: c
122 summary: c
123
123
124 diff -r 784de7cef101 -r 41dd4284081e dir/b
124 diff -r 784de7cef101 -r 41dd4284081e dir/b
125 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
125 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
126 +++ b/dir/b Thu Jan 01 00:00:03 1970 +0000
126 +++ b/dir/b Thu Jan 01 00:00:03 1970 +0000
127 @@ -0,0 +1,1 @@
127 @@ -0,0 +1,1 @@
128 +a
128 +a
129
129
130 changeset: 1:784de7cef101
130 changeset: 1:784de7cef101
131 user: test
131 user: test
132 date: Thu Jan 01 00:00:02 1970 +0000
132 date: Thu Jan 01 00:00:02 1970 +0000
133 summary: b
133 summary: b
134
134
135 diff -r 8580ff50825a -r 784de7cef101 b
135 diff -r 8580ff50825a -r 784de7cef101 b
136 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
136 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
137 +++ b/b Thu Jan 01 00:00:02 1970 +0000
137 +++ b/b Thu Jan 01 00:00:02 1970 +0000
138 @@ -0,0 +1,1 @@
138 @@ -0,0 +1,1 @@
139 +a
139 +a
140
140
141 changeset: 0:8580ff50825a
141 changeset: 0:8580ff50825a
142 user: test
142 user: test
143 date: Thu Jan 01 00:00:01 1970 +0000
143 date: Thu Jan 01 00:00:01 1970 +0000
144 summary: a
144 summary: a
145
145
146 diff -r 000000000000 -r 8580ff50825a a
146 diff -r 000000000000 -r 8580ff50825a a
147 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
147 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
148 +++ b/a Thu Jan 01 00:00:01 1970 +0000
148 +++ b/a Thu Jan 01 00:00:01 1970 +0000
149 @@ -0,0 +1,1 @@
149 @@ -0,0 +1,1 @@
150 +a
150 +a
151
151
152
152
153 log -vf dir/b
153 log -vf dir/b
154
154
155 $ hg log -vf dir/b
155 $ hg log -vf dir/b
156 changeset: 2:41dd4284081e
156 changeset: 2:41dd4284081e
157 user: test
157 user: test
158 date: Thu Jan 01 00:00:03 1970 +0000
158 date: Thu Jan 01 00:00:03 1970 +0000
159 files: b dir/b
159 files: b dir/b
160 description:
160 description:
161 c
161 c
162
162
163
163
164 changeset: 1:784de7cef101
164 changeset: 1:784de7cef101
165 user: test
165 user: test
166 date: Thu Jan 01 00:00:02 1970 +0000
166 date: Thu Jan 01 00:00:02 1970 +0000
167 files: b
167 files: b
168 description:
168 description:
169 b
169 b
170
170
171
171
172 changeset: 0:8580ff50825a
172 changeset: 0:8580ff50825a
173 user: test
173 user: test
174 date: Thu Jan 01 00:00:01 1970 +0000
174 date: Thu Jan 01 00:00:01 1970 +0000
175 files: a
175 files: a
176 description:
176 description:
177 a
177 a
178
178
179
179
180
180
181
181
182 log copies with --copies
182 log copies with --copies
183
183
184 $ hg log -vC --template '{rev} {file_copies}\n'
184 $ hg log -vC --template '{rev} {file_copies}\n'
185 4 e (dir/b)
185 4 e (dir/b)
186 3 b (a)
186 3 b (a)
187 2 dir/b (b)
187 2 dir/b (b)
188 1 b (a)
188 1 b (a)
189 0
189 0
190
190
191 log copies switch without --copies, with old filecopy template
191 log copies switch without --copies, with old filecopy template
192
192
193 $ hg log -v --template '{rev} {file_copies_switch%filecopy}\n'
193 $ hg log -v --template '{rev} {file_copies_switch%filecopy}\n'
194 4
194 4
195 3
195 3
196 2
196 2
197 1
197 1
198 0
198 0
199
199
200 log copies switch with --copies
200 log copies switch with --copies
201
201
202 $ hg log -vC --template '{rev} {file_copies_switch}\n'
202 $ hg log -vC --template '{rev} {file_copies_switch}\n'
203 4 e (dir/b)
203 4 e (dir/b)
204 3 b (a)
204 3 b (a)
205 2 dir/b (b)
205 2 dir/b (b)
206 1 b (a)
206 1 b (a)
207 0
207 0
208
208
209
209
210 log copies with hardcoded style and with --style=default
210 log copies with hardcoded style and with --style=default
211
211
212 $ hg log -vC -r4
212 $ hg log -vC -r4
213 changeset: 4:66c1345dc4f9
213 changeset: 4:66c1345dc4f9
214 tag: tip
214 tag: tip
215 user: test
215 user: test
216 date: Thu Jan 01 00:00:05 1970 +0000
216 date: Thu Jan 01 00:00:05 1970 +0000
217 files: dir/b e
217 files: dir/b e
218 copies: e (dir/b)
218 copies: e (dir/b)
219 description:
219 description:
220 e
220 e
221
221
222
222
223 $ hg log -vC -r4 --style=default
223 $ hg log -vC -r4 --style=default
224 changeset: 4:66c1345dc4f9
224 changeset: 4:66c1345dc4f9
225 tag: tip
225 tag: tip
226 user: test
226 user: test
227 date: Thu Jan 01 00:00:05 1970 +0000
227 date: Thu Jan 01 00:00:05 1970 +0000
228 files: dir/b e
228 files: dir/b e
229 copies: e (dir/b)
229 copies: e (dir/b)
230 description:
230 description:
231 e
231 e
232
232
233
233
234
234
235
235
236 log copies, non-linear manifest
236 log copies, non-linear manifest
237
237
238 $ hg up -C 3
238 $ hg up -C 3
239 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
239 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
240 $ hg mv dir/b e
240 $ hg mv dir/b e
241 $ echo foo > foo
241 $ echo foo > foo
242 $ hg ci -Ame2 -d '6 0'
242 $ hg ci -Ame2 -d '6 0'
243 adding foo
243 adding foo
244 created new head
244 created new head
245 $ hg log -v --template '{rev} {file_copies}\n' -r 5
245 $ hg log -v --template '{rev} {file_copies}\n' -r 5
246 5 e (dir/b)
246 5 e (dir/b)
247
247
248
248
249 log copies, execute bit set
249 log copies, execute bit set
250
250
251 $ chmod +x e
251 $ chmod +x e
252 $ hg ci -me3 -d '7 0'
252 $ hg ci -me3 -d '7 0'
253 $ hg log -v --template '{rev} {file_copies}\n' -r 6
253 $ hg log -v --template '{rev} {file_copies}\n' -r 6
254 6
254 6
255
255
256
256
257 log -p d
257 log -p d
258
258
259 $ hg log -pv d
259 $ hg log -pv d
260 changeset: 3:7c6c671bb7cc
260 changeset: 3:7c6c671bb7cc
261 user: test
261 user: test
262 date: Thu Jan 01 00:00:04 1970 +0000
262 date: Thu Jan 01 00:00:04 1970 +0000
263 files: a b d
263 files: a b d
264 description:
264 description:
265 d
265 d
266
266
267
267
268 diff -r 41dd4284081e -r 7c6c671bb7cc d
268 diff -r 41dd4284081e -r 7c6c671bb7cc d
269 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
269 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
270 +++ b/d Thu Jan 01 00:00:04 1970 +0000
270 +++ b/d Thu Jan 01 00:00:04 1970 +0000
271 @@ -0,0 +1,1 @@
271 @@ -0,0 +1,1 @@
272 +a
272 +a
273
273
274
274
275
275
276 log --removed file
276 log --removed file
277
277
278 $ hg log --removed -v a
278 $ hg log --removed -v a
279 changeset: 3:7c6c671bb7cc
279 changeset: 3:7c6c671bb7cc
280 user: test
280 user: test
281 date: Thu Jan 01 00:00:04 1970 +0000
281 date: Thu Jan 01 00:00:04 1970 +0000
282 files: a b d
282 files: a b d
283 description:
283 description:
284 d
284 d
285
285
286
286
287 changeset: 0:8580ff50825a
287 changeset: 0:8580ff50825a
288 user: test
288 user: test
289 date: Thu Jan 01 00:00:01 1970 +0000
289 date: Thu Jan 01 00:00:01 1970 +0000
290 files: a
290 files: a
291 description:
291 description:
292 a
292 a
293
293
294
294
295
295
296 log --removed revrange file
296 log --removed revrange file
297
297
298 $ hg log --removed -v -r0:2 a
298 $ hg log --removed -v -r0:2 a
299 changeset: 0:8580ff50825a
299 changeset: 0:8580ff50825a
300 user: test
300 user: test
301 date: Thu Jan 01 00:00:01 1970 +0000
301 date: Thu Jan 01 00:00:01 1970 +0000
302 files: a
302 files: a
303 description:
303 description:
304 a
304 a
305
305
306
306
307
307
308
308
309 log --follow tests
309 log --follow tests
310
310
311 $ hg init ../follow
311 $ hg init ../follow
312 $ cd ../follow
312 $ cd ../follow
313
313
314 $ echo base > base
314 $ echo base > base
315 $ hg ci -Ambase -d '1 0'
315 $ hg ci -Ambase -d '1 0'
316 adding base
316 adding base
317
317
318 $ echo r1 >> base
318 $ echo r1 >> base
319 $ hg ci -Amr1 -d '1 0'
319 $ hg ci -Amr1 -d '1 0'
320 $ echo r2 >> base
320 $ echo r2 >> base
321 $ hg ci -Amr2 -d '1 0'
321 $ hg ci -Amr2 -d '1 0'
322
322
323 $ hg up -C 1
323 $ hg up -C 1
324 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
324 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
325 $ echo b1 > b1
325 $ echo b1 > b1
326 $ hg ci -Amb1 -d '1 0'
326 $ hg ci -Amb1 -d '1 0'
327 adding b1
327 adding b1
328 created new head
328 created new head
329
329
330
330
331 log -f
331 log -f
332
332
333 $ hg log -f
333 $ hg log -f
334 changeset: 3:e62f78d544b4
334 changeset: 3:e62f78d544b4
335 tag: tip
335 tag: tip
336 parent: 1:3d5bf5654eda
336 parent: 1:3d5bf5654eda
337 user: test
337 user: test
338 date: Thu Jan 01 00:00:01 1970 +0000
338 date: Thu Jan 01 00:00:01 1970 +0000
339 summary: b1
339 summary: b1
340
340
341 changeset: 1:3d5bf5654eda
341 changeset: 1:3d5bf5654eda
342 user: test
342 user: test
343 date: Thu Jan 01 00:00:01 1970 +0000
343 date: Thu Jan 01 00:00:01 1970 +0000
344 summary: r1
344 summary: r1
345
345
346 changeset: 0:67e992f2c4f3
346 changeset: 0:67e992f2c4f3
347 user: test
347 user: test
348 date: Thu Jan 01 00:00:01 1970 +0000
348 date: Thu Jan 01 00:00:01 1970 +0000
349 summary: base
349 summary: base
350
350
351
351
352
352
353 log -f -r 1:tip
353 log -f -r 1:tip
354
354
355 $ hg up -C 0
355 $ hg up -C 0
356 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
356 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
357 $ echo b2 > b2
357 $ echo b2 > b2
358 $ hg ci -Amb2 -d '1 0'
358 $ hg ci -Amb2 -d '1 0'
359 adding b2
359 adding b2
360 created new head
360 created new head
361 $ hg log -f -r 1:tip
361 $ hg log -f -r 1:tip
362 changeset: 1:3d5bf5654eda
362 changeset: 1:3d5bf5654eda
363 user: test
363 user: test
364 date: Thu Jan 01 00:00:01 1970 +0000
364 date: Thu Jan 01 00:00:01 1970 +0000
365 summary: r1
365 summary: r1
366
366
367 changeset: 2:60c670bf5b30
367 changeset: 2:60c670bf5b30
368 user: test
368 user: test
369 date: Thu Jan 01 00:00:01 1970 +0000
369 date: Thu Jan 01 00:00:01 1970 +0000
370 summary: r2
370 summary: r2
371
371
372 changeset: 3:e62f78d544b4
372 changeset: 3:e62f78d544b4
373 parent: 1:3d5bf5654eda
373 parent: 1:3d5bf5654eda
374 user: test
374 user: test
375 date: Thu Jan 01 00:00:01 1970 +0000
375 date: Thu Jan 01 00:00:01 1970 +0000
376 summary: b1
376 summary: b1
377
377
378
378
379
379
380 log -r . with two parents
380 log -r . with two parents
381
381
382 $ hg up -C 3
382 $ hg up -C 3
383 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
383 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
384 $ hg merge tip
384 $ hg merge tip
385 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
385 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
386 (branch merge, don't forget to commit)
386 (branch merge, don't forget to commit)
387 $ hg log -r .
387 $ hg log -r .
388 changeset: 3:e62f78d544b4
388 changeset: 3:e62f78d544b4
389 parent: 1:3d5bf5654eda
389 parent: 1:3d5bf5654eda
390 user: test
390 user: test
391 date: Thu Jan 01 00:00:01 1970 +0000
391 date: Thu Jan 01 00:00:01 1970 +0000
392 summary: b1
392 summary: b1
393
393
394
394
395
395
396 log -r . with one parent
396 log -r . with one parent
397
397
398 $ hg ci -mm12 -d '1 0'
398 $ hg ci -mm12 -d '1 0'
399 $ hg log -r .
399 $ hg log -r .
400 changeset: 5:302e9dd6890d
400 changeset: 5:302e9dd6890d
401 tag: tip
401 tag: tip
402 parent: 3:e62f78d544b4
402 parent: 3:e62f78d544b4
403 parent: 4:ddb82e70d1a1
403 parent: 4:ddb82e70d1a1
404 user: test
404 user: test
405 date: Thu Jan 01 00:00:01 1970 +0000
405 date: Thu Jan 01 00:00:01 1970 +0000
406 summary: m12
406 summary: m12
407
407
408
408
409 $ echo postm >> b1
409 $ echo postm >> b1
410 $ hg ci -Amb1.1 -d'1 0'
410 $ hg ci -Amb1.1 -d'1 0'
411
411
412
412
413 log --follow-first
413 log --follow-first
414
414
415 $ hg log --follow-first
415 $ hg log --follow-first
416 changeset: 6:2404bbcab562
416 changeset: 6:2404bbcab562
417 tag: tip
417 tag: tip
418 user: test
418 user: test
419 date: Thu Jan 01 00:00:01 1970 +0000
419 date: Thu Jan 01 00:00:01 1970 +0000
420 summary: b1.1
420 summary: b1.1
421
421
422 changeset: 5:302e9dd6890d
422 changeset: 5:302e9dd6890d
423 parent: 3:e62f78d544b4
423 parent: 3:e62f78d544b4
424 parent: 4:ddb82e70d1a1
424 parent: 4:ddb82e70d1a1
425 user: test
425 user: test
426 date: Thu Jan 01 00:00:01 1970 +0000
426 date: Thu Jan 01 00:00:01 1970 +0000
427 summary: m12
427 summary: m12
428
428
429 changeset: 3:e62f78d544b4
429 changeset: 3:e62f78d544b4
430 parent: 1:3d5bf5654eda
430 parent: 1:3d5bf5654eda
431 user: test
431 user: test
432 date: Thu Jan 01 00:00:01 1970 +0000
432 date: Thu Jan 01 00:00:01 1970 +0000
433 summary: b1
433 summary: b1
434
434
435 changeset: 1:3d5bf5654eda
435 changeset: 1:3d5bf5654eda
436 user: test
436 user: test
437 date: Thu Jan 01 00:00:01 1970 +0000
437 date: Thu Jan 01 00:00:01 1970 +0000
438 summary: r1
438 summary: r1
439
439
440 changeset: 0:67e992f2c4f3
440 changeset: 0:67e992f2c4f3
441 user: test
441 user: test
442 date: Thu Jan 01 00:00:01 1970 +0000
442 date: Thu Jan 01 00:00:01 1970 +0000
443 summary: base
443 summary: base
444
444
445
445
446
446
447 log -P 2
447 log -P 2
448
448
449 $ hg log -P 2
449 $ hg log -P 2
450 changeset: 6:2404bbcab562
450 changeset: 6:2404bbcab562
451 tag: tip
451 tag: tip
452 user: test
452 user: test
453 date: Thu Jan 01 00:00:01 1970 +0000
453 date: Thu Jan 01 00:00:01 1970 +0000
454 summary: b1.1
454 summary: b1.1
455
455
456 changeset: 5:302e9dd6890d
456 changeset: 5:302e9dd6890d
457 parent: 3:e62f78d544b4
457 parent: 3:e62f78d544b4
458 parent: 4:ddb82e70d1a1
458 parent: 4:ddb82e70d1a1
459 user: test
459 user: test
460 date: Thu Jan 01 00:00:01 1970 +0000
460 date: Thu Jan 01 00:00:01 1970 +0000
461 summary: m12
461 summary: m12
462
462
463 changeset: 4:ddb82e70d1a1
463 changeset: 4:ddb82e70d1a1
464 parent: 0:67e992f2c4f3
464 parent: 0:67e992f2c4f3
465 user: test
465 user: test
466 date: Thu Jan 01 00:00:01 1970 +0000
466 date: Thu Jan 01 00:00:01 1970 +0000
467 summary: b2
467 summary: b2
468
468
469 changeset: 3:e62f78d544b4
469 changeset: 3:e62f78d544b4
470 parent: 1:3d5bf5654eda
470 parent: 1:3d5bf5654eda
471 user: test
471 user: test
472 date: Thu Jan 01 00:00:01 1970 +0000
472 date: Thu Jan 01 00:00:01 1970 +0000
473 summary: b1
473 summary: b1
474
474
475
475
476
476
477 log -r tip -p --git
477 log -r tip -p --git
478
478
479 $ hg log -r tip -p --git
479 $ hg log -r tip -p --git
480 changeset: 6:2404bbcab562
480 changeset: 6:2404bbcab562
481 tag: tip
481 tag: tip
482 user: test
482 user: test
483 date: Thu Jan 01 00:00:01 1970 +0000
483 date: Thu Jan 01 00:00:01 1970 +0000
484 summary: b1.1
484 summary: b1.1
485
485
486 diff --git a/b1 b/b1
486 diff --git a/b1 b/b1
487 --- a/b1
487 --- a/b1
488 +++ b/b1
488 +++ b/b1
489 @@ -1,1 +1,2 @@
489 @@ -1,1 +1,2 @@
490 b1
490 b1
491 +postm
491 +postm
492
492
493
493
494
494
495 log -r ""
495 log -r ""
496
496
497 $ hg log -r ''
497 $ hg log -r ''
498 hg: parse error: empty query
498 hg: parse error: empty query
499 [255]
499 [255]
500
500
501 log -r <some unknown node id>
501 log -r <some unknown node id>
502
502
503 $ hg log -r 1000000000000000000000000000000000000000
503 $ hg log -r 1000000000000000000000000000000000000000
504 abort: unknown revision '1000000000000000000000000000000000000000'!
504 abort: unknown revision '1000000000000000000000000000000000000000'!
505 [255]
505 [255]
506
506
507 log -k r1
507 log -k r1
508
508
509 $ hg log -k r1
509 $ hg log -k r1
510 changeset: 1:3d5bf5654eda
510 changeset: 1:3d5bf5654eda
511 user: test
511 user: test
512 date: Thu Jan 01 00:00:01 1970 +0000
512 date: Thu Jan 01 00:00:01 1970 +0000
513 summary: r1
513 summary: r1
514
514
515
515
516
516
517 log -d -1
517 log -d -1
518
518
519 $ hg log -d -1
519 $ hg log -d -1
520
520
521
521
522 log -p -l2 --color=always
522 log -p -l2 --color=always
523
523
524 $ hg --config extensions.color= --config color.mode=ansi \
524 $ hg --config extensions.color= --config color.mode=ansi \
525 > log -p -l2 --color=always
525 > log -p -l2 --color=always
526 \x1b[0;33mchangeset: 6:2404bbcab562\x1b[0m (esc)
526 \x1b[0;33mchangeset: 6:2404bbcab562\x1b[0m (esc)
527 tag: tip
527 tag: tip
528 user: test
528 user: test
529 date: Thu Jan 01 00:00:01 1970 +0000
529 date: Thu Jan 01 00:00:01 1970 +0000
530 summary: b1.1
530 summary: b1.1
531
531
532 \x1b[0;1mdiff -r 302e9dd6890d -r 2404bbcab562 b1\x1b[0m (esc)
532 \x1b[0;1mdiff -r 302e9dd6890d -r 2404bbcab562 b1\x1b[0m (esc)
533 \x1b[0;31;1m--- a/b1 Thu Jan 01 00:00:01 1970 +0000\x1b[0m (esc)
533 \x1b[0;31;1m--- a/b1 Thu Jan 01 00:00:01 1970 +0000\x1b[0m (esc)
534 \x1b[0;32;1m+++ b/b1 Thu Jan 01 00:00:01 1970 +0000\x1b[0m (esc)
534 \x1b[0;32;1m+++ b/b1 Thu Jan 01 00:00:01 1970 +0000\x1b[0m (esc)
535 \x1b[0;35m@@ -1,1 +1,2 @@\x1b[0m (esc)
535 \x1b[0;35m@@ -1,1 +1,2 @@\x1b[0m (esc)
536 b1
536 b1
537 \x1b[0;32m+postm\x1b[0m (esc)
537 \x1b[0;32m+postm\x1b[0m (esc)
538
538
539 \x1b[0;33mchangeset: 5:302e9dd6890d\x1b[0m (esc)
539 \x1b[0;33mchangeset: 5:302e9dd6890d\x1b[0m (esc)
540 parent: 3:e62f78d544b4
540 parent: 3:e62f78d544b4
541 parent: 4:ddb82e70d1a1
541 parent: 4:ddb82e70d1a1
542 user: test
542 user: test
543 date: Thu Jan 01 00:00:01 1970 +0000
543 date: Thu Jan 01 00:00:01 1970 +0000
544 summary: m12
544 summary: m12
545
545
546 \x1b[0;1mdiff -r e62f78d544b4 -r 302e9dd6890d b2\x1b[0m (esc)
546 \x1b[0;1mdiff -r e62f78d544b4 -r 302e9dd6890d b2\x1b[0m (esc)
547 \x1b[0;31;1m--- /dev/null Thu Jan 01 00:00:00 1970 +0000\x1b[0m (esc)
547 \x1b[0;31;1m--- /dev/null Thu Jan 01 00:00:00 1970 +0000\x1b[0m (esc)
548 \x1b[0;32;1m+++ b/b2 Thu Jan 01 00:00:01 1970 +0000\x1b[0m (esc)
548 \x1b[0;32;1m+++ b/b2 Thu Jan 01 00:00:01 1970 +0000\x1b[0m (esc)
549 \x1b[0;35m@@ -0,0 +1,1 @@\x1b[0m (esc)
549 \x1b[0;35m@@ -0,0 +1,1 @@\x1b[0m (esc)
550 \x1b[0;32m+b2\x1b[0m (esc)
550 \x1b[0;32m+b2\x1b[0m (esc)
551
551
552
552
553
553
554 log -r tip --stat
554 log -r tip --stat
555
555
556 $ hg log -r tip --stat
556 $ hg log -r tip --stat
557 changeset: 6:2404bbcab562
557 changeset: 6:2404bbcab562
558 tag: tip
558 tag: tip
559 user: test
559 user: test
560 date: Thu Jan 01 00:00:01 1970 +0000
560 date: Thu Jan 01 00:00:01 1970 +0000
561 summary: b1.1
561 summary: b1.1
562
562
563 b1 | 1 +
563 b1 | 1 +
564 1 files changed, 1 insertions(+), 0 deletions(-)
564 1 files changed, 1 insertions(+), 0 deletions(-)
565
565
566
566
567 $ cd ..
567 $ cd ..
568
568
569 $ hg init usertest
569 $ hg init usertest
570 $ cd usertest
570 $ cd usertest
571
571
572 $ echo a > a
572 $ echo a > a
573 $ hg ci -A -m "a" -u "User One <user1@example.org>"
573 $ hg ci -A -m "a" -u "User One <user1@example.org>"
574 adding a
574 adding a
575 $ echo b > b
575 $ echo b > b
576 $ hg ci -A -m "b" -u "User Two <user2@example.org>"
576 $ hg ci -A -m "b" -u "User Two <user2@example.org>"
577 adding b
577 adding b
578
578
579 $ hg log -u "User One <user1@example.org>"
579 $ hg log -u "User One <user1@example.org>"
580 changeset: 0:29a4c94f1924
580 changeset: 0:29a4c94f1924
581 user: User One <user1@example.org>
581 user: User One <user1@example.org>
582 date: Thu Jan 01 00:00:00 1970 +0000
582 date: Thu Jan 01 00:00:00 1970 +0000
583 summary: a
583 summary: a
584
584
585 $ hg log -u "user1" -u "user2"
585 $ hg log -u "user1" -u "user2"
586 changeset: 1:e834b5e69c0e
586 changeset: 1:e834b5e69c0e
587 tag: tip
587 tag: tip
588 user: User Two <user2@example.org>
588 user: User Two <user2@example.org>
589 date: Thu Jan 01 00:00:00 1970 +0000
589 date: Thu Jan 01 00:00:00 1970 +0000
590 summary: b
590 summary: b
591
591
592 changeset: 0:29a4c94f1924
592 changeset: 0:29a4c94f1924
593 user: User One <user1@example.org>
593 user: User One <user1@example.org>
594 date: Thu Jan 01 00:00:00 1970 +0000
594 date: Thu Jan 01 00:00:00 1970 +0000
595 summary: a
595 summary: a
596
596
597 $ hg log -u "user3"
597 $ hg log -u "user3"
598
598
599 $ cd ..
599 $ cd ..
600
600
601 $ hg init branches
601 $ hg init branches
602 $ cd branches
602 $ cd branches
603
603
604 $ echo a > a
604 $ echo a > a
605 $ hg ci -A -m "commit on default"
605 $ hg ci -A -m "commit on default"
606 adding a
606 adding a
607 $ hg branch test
607 $ hg branch test
608 marked working directory as branch test
608 marked working directory as branch test
609 $ echo b > b
609 $ echo b > b
610 $ hg ci -A -m "commit on test"
610 $ hg ci -A -m "commit on test"
611 adding b
611 adding b
612
612
613 $ hg up default
613 $ hg up default
614 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
614 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
615 $ echo c > c
615 $ echo c > c
616 $ hg ci -A -m "commit on default"
616 $ hg ci -A -m "commit on default"
617 adding c
617 adding c
618 $ hg up test
618 $ hg up test
619 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
619 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
620 $ echo c > c
620 $ echo c > c
621 $ hg ci -A -m "commit on test"
621 $ hg ci -A -m "commit on test"
622 adding c
622 adding c
623
623
624
624
625 log -b default
625 log -b default
626
626
627 $ hg log -b default
627 $ hg log -b default
628 changeset: 2:c3a4f03cc9a7
628 changeset: 2:c3a4f03cc9a7
629 parent: 0:24427303d56f
629 parent: 0:24427303d56f
630 user: test
630 user: test
631 date: Thu Jan 01 00:00:00 1970 +0000
631 date: Thu Jan 01 00:00:00 1970 +0000
632 summary: commit on default
632 summary: commit on default
633
633
634 changeset: 0:24427303d56f
634 changeset: 0:24427303d56f
635 user: test
635 user: test
636 date: Thu Jan 01 00:00:00 1970 +0000
636 date: Thu Jan 01 00:00:00 1970 +0000
637 summary: commit on default
637 summary: commit on default
638
638
639
639
640
640
641 log -b test
641 log -b test
642
642
643 $ hg log -b test
643 $ hg log -b test
644 changeset: 3:f5d8de11c2e2
644 changeset: 3:f5d8de11c2e2
645 branch: test
645 branch: test
646 tag: tip
646 tag: tip
647 parent: 1:d32277701ccb
647 parent: 1:d32277701ccb
648 user: test
648 user: test
649 date: Thu Jan 01 00:00:00 1970 +0000
649 date: Thu Jan 01 00:00:00 1970 +0000
650 summary: commit on test
650 summary: commit on test
651
651
652 changeset: 1:d32277701ccb
652 changeset: 1:d32277701ccb
653 branch: test
653 branch: test
654 user: test
654 user: test
655 date: Thu Jan 01 00:00:00 1970 +0000
655 date: Thu Jan 01 00:00:00 1970 +0000
656 summary: commit on test
656 summary: commit on test
657
657
658
658
659
659
660 log -b dummy
660 log -b dummy
661
661
662 $ hg log -b dummy
662 $ hg log -b dummy
663 abort: unknown revision 'dummy'!
663 abort: unknown revision 'dummy'!
664 [255]
664 [255]
665
665
666
666
667 log -b .
667 log -b .
668
668
669 $ hg log -b .
669 $ hg log -b .
670 changeset: 3:f5d8de11c2e2
670 changeset: 3:f5d8de11c2e2
671 branch: test
671 branch: test
672 tag: tip
672 tag: tip
673 parent: 1:d32277701ccb
673 parent: 1:d32277701ccb
674 user: test
674 user: test
675 date: Thu Jan 01 00:00:00 1970 +0000
675 date: Thu Jan 01 00:00:00 1970 +0000
676 summary: commit on test
676 summary: commit on test
677
677
678 changeset: 1:d32277701ccb
678 changeset: 1:d32277701ccb
679 branch: test
679 branch: test
680 user: test
680 user: test
681 date: Thu Jan 01 00:00:00 1970 +0000
681 date: Thu Jan 01 00:00:00 1970 +0000
682 summary: commit on test
682 summary: commit on test
683
683
684
684
685
685
686 log -b default -b test
686 log -b default -b test
687
687
688 $ hg log -b default -b test
688 $ hg log -b default -b test
689 changeset: 3:f5d8de11c2e2
689 changeset: 3:f5d8de11c2e2
690 branch: test
690 branch: test
691 tag: tip
691 tag: tip
692 parent: 1:d32277701ccb
692 parent: 1:d32277701ccb
693 user: test
693 user: test
694 date: Thu Jan 01 00:00:00 1970 +0000
694 date: Thu Jan 01 00:00:00 1970 +0000
695 summary: commit on test
695 summary: commit on test
696
696
697 changeset: 2:c3a4f03cc9a7
697 changeset: 2:c3a4f03cc9a7
698 parent: 0:24427303d56f
698 parent: 0:24427303d56f
699 user: test
699 user: test
700 date: Thu Jan 01 00:00:00 1970 +0000
700 date: Thu Jan 01 00:00:00 1970 +0000
701 summary: commit on default
701 summary: commit on default
702
702
703 changeset: 1:d32277701ccb
703 changeset: 1:d32277701ccb
704 branch: test
704 branch: test
705 user: test
705 user: test
706 date: Thu Jan 01 00:00:00 1970 +0000
706 date: Thu Jan 01 00:00:00 1970 +0000
707 summary: commit on test
707 summary: commit on test
708
708
709 changeset: 0:24427303d56f
709 changeset: 0:24427303d56f
710 user: test
710 user: test
711 date: Thu Jan 01 00:00:00 1970 +0000
711 date: Thu Jan 01 00:00:00 1970 +0000
712 summary: commit on default
712 summary: commit on default
713
713
714
714
715
715
716 log -b default -b .
716 log -b default -b .
717
717
718 $ hg log -b default -b .
718 $ hg log -b default -b .
719 changeset: 3:f5d8de11c2e2
719 changeset: 3:f5d8de11c2e2
720 branch: test
720 branch: test
721 tag: tip
721 tag: tip
722 parent: 1:d32277701ccb
722 parent: 1:d32277701ccb
723 user: test
723 user: test
724 date: Thu Jan 01 00:00:00 1970 +0000
724 date: Thu Jan 01 00:00:00 1970 +0000
725 summary: commit on test
725 summary: commit on test
726
726
727 changeset: 2:c3a4f03cc9a7
727 changeset: 2:c3a4f03cc9a7
728 parent: 0:24427303d56f
728 parent: 0:24427303d56f
729 user: test
729 user: test
730 date: Thu Jan 01 00:00:00 1970 +0000
730 date: Thu Jan 01 00:00:00 1970 +0000
731 summary: commit on default
731 summary: commit on default
732
732
733 changeset: 1:d32277701ccb
733 changeset: 1:d32277701ccb
734 branch: test
734 branch: test
735 user: test
735 user: test
736 date: Thu Jan 01 00:00:00 1970 +0000
736 date: Thu Jan 01 00:00:00 1970 +0000
737 summary: commit on test
737 summary: commit on test
738
738
739 changeset: 0:24427303d56f
739 changeset: 0:24427303d56f
740 user: test
740 user: test
741 date: Thu Jan 01 00:00:00 1970 +0000
741 date: Thu Jan 01 00:00:00 1970 +0000
742 summary: commit on default
742 summary: commit on default
743
743
744
744
745
745
746 log -b . -b test
746 log -b . -b test
747
747
748 $ hg log -b . -b test
748 $ hg log -b . -b test
749 changeset: 3:f5d8de11c2e2
749 changeset: 3:f5d8de11c2e2
750 branch: test
750 branch: test
751 tag: tip
751 tag: tip
752 parent: 1:d32277701ccb
752 parent: 1:d32277701ccb
753 user: test
753 user: test
754 date: Thu Jan 01 00:00:00 1970 +0000
754 date: Thu Jan 01 00:00:00 1970 +0000
755 summary: commit on test
755 summary: commit on test
756
756
757 changeset: 1:d32277701ccb
757 changeset: 1:d32277701ccb
758 branch: test
758 branch: test
759 user: test
759 user: test
760 date: Thu Jan 01 00:00:00 1970 +0000
760 date: Thu Jan 01 00:00:00 1970 +0000
761 summary: commit on test
761 summary: commit on test
762
762
763
763
764
764
765 log -b 2
765 log -b 2
766
766
767 $ hg log -b 2
767 $ hg log -b 2
768 changeset: 2:c3a4f03cc9a7
768 changeset: 2:c3a4f03cc9a7
769 parent: 0:24427303d56f
769 parent: 0:24427303d56f
770 user: test
770 user: test
771 date: Thu Jan 01 00:00:00 1970 +0000
771 date: Thu Jan 01 00:00:00 1970 +0000
772 summary: commit on default
772 summary: commit on default
773
773
774 changeset: 0:24427303d56f
774 changeset: 0:24427303d56f
775 user: test
775 user: test
776 date: Thu Jan 01 00:00:00 1970 +0000
776 date: Thu Jan 01 00:00:00 1970 +0000
777 summary: commit on default
777 summary: commit on default
778
778
779
779
780
780
781 log -p --cwd dir (in subdir)
781 log -p --cwd dir (in subdir)
782
782
783 $ mkdir dir
783 $ mkdir dir
784 $ hg log -p --cwd dir
784 $ hg log -p --cwd dir
785 changeset: 3:f5d8de11c2e2
785 changeset: 3:f5d8de11c2e2
786 branch: test
786 branch: test
787 tag: tip
787 tag: tip
788 parent: 1:d32277701ccb
788 parent: 1:d32277701ccb
789 user: test
789 user: test
790 date: Thu Jan 01 00:00:00 1970 +0000
790 date: Thu Jan 01 00:00:00 1970 +0000
791 summary: commit on test
791 summary: commit on test
792
792
793 diff -r d32277701ccb -r f5d8de11c2e2 c
793 diff -r d32277701ccb -r f5d8de11c2e2 c
794 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
794 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
795 +++ b/c Thu Jan 01 00:00:00 1970 +0000
795 +++ b/c Thu Jan 01 00:00:00 1970 +0000
796 @@ -0,0 +1,1 @@
796 @@ -0,0 +1,1 @@
797 +c
797 +c
798
798
799 changeset: 2:c3a4f03cc9a7
799 changeset: 2:c3a4f03cc9a7
800 parent: 0:24427303d56f
800 parent: 0:24427303d56f
801 user: test
801 user: test
802 date: Thu Jan 01 00:00:00 1970 +0000
802 date: Thu Jan 01 00:00:00 1970 +0000
803 summary: commit on default
803 summary: commit on default
804
804
805 diff -r 24427303d56f -r c3a4f03cc9a7 c
805 diff -r 24427303d56f -r c3a4f03cc9a7 c
806 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
806 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
807 +++ b/c Thu Jan 01 00:00:00 1970 +0000
807 +++ b/c Thu Jan 01 00:00:00 1970 +0000
808 @@ -0,0 +1,1 @@
808 @@ -0,0 +1,1 @@
809 +c
809 +c
810
810
811 changeset: 1:d32277701ccb
811 changeset: 1:d32277701ccb
812 branch: test
812 branch: test
813 user: test
813 user: test
814 date: Thu Jan 01 00:00:00 1970 +0000
814 date: Thu Jan 01 00:00:00 1970 +0000
815 summary: commit on test
815 summary: commit on test
816
816
817 diff -r 24427303d56f -r d32277701ccb b
817 diff -r 24427303d56f -r d32277701ccb b
818 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
818 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
819 +++ b/b Thu Jan 01 00:00:00 1970 +0000
819 +++ b/b Thu Jan 01 00:00:00 1970 +0000
820 @@ -0,0 +1,1 @@
820 @@ -0,0 +1,1 @@
821 +b
821 +b
822
822
823 changeset: 0:24427303d56f
823 changeset: 0:24427303d56f
824 user: test
824 user: test
825 date: Thu Jan 01 00:00:00 1970 +0000
825 date: Thu Jan 01 00:00:00 1970 +0000
826 summary: commit on default
826 summary: commit on default
827
827
828 diff -r 000000000000 -r 24427303d56f a
828 diff -r 000000000000 -r 24427303d56f a
829 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
829 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
830 +++ b/a Thu Jan 01 00:00:00 1970 +0000
830 +++ b/a Thu Jan 01 00:00:00 1970 +0000
831 @@ -0,0 +1,1 @@
831 @@ -0,0 +1,1 @@
832 +a
832 +a
833
833
834
834
835
835
836 log -p -R repo
836 log -p -R repo
837
837
838 $ cd dir
838 $ cd dir
839 $ hg log -p -R .. ../a
839 $ hg log -p -R .. ../a
840 changeset: 0:24427303d56f
840 changeset: 0:24427303d56f
841 user: test
841 user: test
842 date: Thu Jan 01 00:00:00 1970 +0000
842 date: Thu Jan 01 00:00:00 1970 +0000
843 summary: commit on default
843 summary: commit on default
844
844
845 diff -r 000000000000 -r 24427303d56f a
845 diff -r 000000000000 -r 24427303d56f a
846 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
846 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
847 +++ b/a Thu Jan 01 00:00:00 1970 +0000
847 +++ b/a Thu Jan 01 00:00:00 1970 +0000
848 @@ -0,0 +1,1 @@
848 @@ -0,0 +1,1 @@
849 +a
849 +a
850
850
851
851
852
852
853 $ cd ..
853 $ cd ..
854 $ hg init follow2
854 $ hg init follow2
855 $ cd follow2
855 $ cd follow2
856
856
857
857
858 # Build the following history:
858 # Build the following history:
859 # tip - o - x - o - x - x
859 # tip - o - x - o - x - x
860 # \ /
860 # \ /
861 # o - o - o - x
861 # o - o - o - x
862 # \ /
862 # \ /
863 # o
863 # o
864 #
864 #
865 # Where "o" is a revision containing "foo" and
865 # Where "o" is a revision containing "foo" and
866 # "x" is a revision without "foo"
866 # "x" is a revision without "foo"
867
867
868 $ touch init
868 $ touch init
869 $ hg ci -A -m "init, unrelated"
869 $ hg ci -A -m "init, unrelated"
870 adding init
870 adding init
871 $ echo 'foo' > init
871 $ echo 'foo' > init
872 $ hg ci -m "change, unrelated"
872 $ hg ci -m "change, unrelated"
873 $ echo 'foo' > foo
873 $ echo 'foo' > foo
874 $ hg ci -A -m "add unrelated old foo"
874 $ hg ci -A -m "add unrelated old foo"
875 adding foo
875 adding foo
876 $ hg rm foo
876 $ hg rm foo
877 $ hg ci -m "delete foo, unrelated"
877 $ hg ci -m "delete foo, unrelated"
878 $ echo 'related' > foo
878 $ echo 'related' > foo
879 $ hg ci -A -m "add foo, related"
879 $ hg ci -A -m "add foo, related"
880 adding foo
880 adding foo
881
881
882 $ hg up 0
882 $ hg up 0
883 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
883 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
884 $ touch branch
884 $ touch branch
885 $ hg ci -A -m "first branch, unrelated"
885 $ hg ci -A -m "first branch, unrelated"
886 adding branch
886 adding branch
887 created new head
887 created new head
888 $ touch foo
888 $ touch foo
889 $ hg ci -A -m "create foo, related"
889 $ hg ci -A -m "create foo, related"
890 adding foo
890 adding foo
891 $ echo 'change' > foo
891 $ echo 'change' > foo
892 $ hg ci -m "change foo, related"
892 $ hg ci -m "change foo, related"
893
893
894 $ hg up 6
894 $ hg up 6
895 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
895 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
896 $ echo 'change foo in branch' > foo
896 $ echo 'change foo in branch' > foo
897 $ hg ci -m "change foo in branch, related"
897 $ hg ci -m "change foo in branch, related"
898 created new head
898 created new head
899 $ hg merge 7
899 $ hg merge 7
900 merging foo
900 merging foo
901 warning: conflicts during merge.
901 warning: conflicts during merge.
902 merging foo failed!
902 merging foo failed!
903 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
903 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
904 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
904 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
905 [1]
905 [1]
906 $ echo 'merge 1' > foo
906 $ echo 'merge 1' > foo
907 $ hg resolve -m foo
907 $ hg resolve -m foo
908 $ hg ci -m "First merge, related"
908 $ hg ci -m "First merge, related"
909
909
910 $ hg merge 4
910 $ hg merge 4
911 merging foo
911 merging foo
912 warning: conflicts during merge.
912 warning: conflicts during merge.
913 merging foo failed!
913 merging foo failed!
914 1 files updated, 0 files merged, 0 files removed, 1 files unresolved
914 1 files updated, 0 files merged, 0 files removed, 1 files unresolved
915 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
915 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
916 [1]
916 [1]
917 $ echo 'merge 2' > foo
917 $ echo 'merge 2' > foo
918 $ hg resolve -m foo
918 $ hg resolve -m foo
919 $ hg ci -m "Last merge, related"
919 $ hg ci -m "Last merge, related"
920
920
921 $ hg --config "extensions.graphlog=" glog
921 $ hg --config "extensions.graphlog=" glog
922 @ changeset: 10:4dae8563d2c5
922 @ changeset: 10:4dae8563d2c5
923 |\ tag: tip
923 |\ tag: tip
924 | | parent: 9:7b35701b003e
924 | | parent: 9:7b35701b003e
925 | | parent: 4:88176d361b69
925 | | parent: 4:88176d361b69
926 | | user: test
926 | | user: test
927 | | date: Thu Jan 01 00:00:00 1970 +0000
927 | | date: Thu Jan 01 00:00:00 1970 +0000
928 | | summary: Last merge, related
928 | | summary: Last merge, related
929 | |
929 | |
930 | o changeset: 9:7b35701b003e
930 | o changeset: 9:7b35701b003e
931 | |\ parent: 8:e5416ad8a855
931 | |\ parent: 8:e5416ad8a855
932 | | | parent: 7:87fe3144dcfa
932 | | | parent: 7:87fe3144dcfa
933 | | | user: test
933 | | | user: test
934 | | | date: Thu Jan 01 00:00:00 1970 +0000
934 | | | date: Thu Jan 01 00:00:00 1970 +0000
935 | | | summary: First merge, related
935 | | | summary: First merge, related
936 | | |
936 | | |
937 | | o changeset: 8:e5416ad8a855
937 | | o changeset: 8:e5416ad8a855
938 | | | parent: 6:dc6c325fe5ee
938 | | | parent: 6:dc6c325fe5ee
939 | | | user: test
939 | | | user: test
940 | | | date: Thu Jan 01 00:00:00 1970 +0000
940 | | | date: Thu Jan 01 00:00:00 1970 +0000
941 | | | summary: change foo in branch, related
941 | | | summary: change foo in branch, related
942 | | |
942 | | |
943 | o | changeset: 7:87fe3144dcfa
943 | o | changeset: 7:87fe3144dcfa
944 | |/ user: test
944 | |/ user: test
945 | | date: Thu Jan 01 00:00:00 1970 +0000
945 | | date: Thu Jan 01 00:00:00 1970 +0000
946 | | summary: change foo, related
946 | | summary: change foo, related
947 | |
947 | |
948 | o changeset: 6:dc6c325fe5ee
948 | o changeset: 6:dc6c325fe5ee
949 | | user: test
949 | | user: test
950 | | date: Thu Jan 01 00:00:00 1970 +0000
950 | | date: Thu Jan 01 00:00:00 1970 +0000
951 | | summary: create foo, related
951 | | summary: create foo, related
952 | |
952 | |
953 | o changeset: 5:73db34516eb9
953 | o changeset: 5:73db34516eb9
954 | | parent: 0:e87515fd044a
954 | | parent: 0:e87515fd044a
955 | | user: test
955 | | user: test
956 | | date: Thu Jan 01 00:00:00 1970 +0000
956 | | date: Thu Jan 01 00:00:00 1970 +0000
957 | | summary: first branch, unrelated
957 | | summary: first branch, unrelated
958 | |
958 | |
959 o | changeset: 4:88176d361b69
959 o | changeset: 4:88176d361b69
960 | | user: test
960 | | user: test
961 | | date: Thu Jan 01 00:00:00 1970 +0000
961 | | date: Thu Jan 01 00:00:00 1970 +0000
962 | | summary: add foo, related
962 | | summary: add foo, related
963 | |
963 | |
964 o | changeset: 3:dd78ae4afb56
964 o | changeset: 3:dd78ae4afb56
965 | | user: test
965 | | user: test
966 | | date: Thu Jan 01 00:00:00 1970 +0000
966 | | date: Thu Jan 01 00:00:00 1970 +0000
967 | | summary: delete foo, unrelated
967 | | summary: delete foo, unrelated
968 | |
968 | |
969 o | changeset: 2:c4c64aedf0f7
969 o | changeset: 2:c4c64aedf0f7
970 | | user: test
970 | | user: test
971 | | date: Thu Jan 01 00:00:00 1970 +0000
971 | | date: Thu Jan 01 00:00:00 1970 +0000
972 | | summary: add unrelated old foo
972 | | summary: add unrelated old foo
973 | |
973 | |
974 o | changeset: 1:e5faa7440653
974 o | changeset: 1:e5faa7440653
975 |/ user: test
975 |/ user: test
976 | date: Thu Jan 01 00:00:00 1970 +0000
976 | date: Thu Jan 01 00:00:00 1970 +0000
977 | summary: change, unrelated
977 | summary: change, unrelated
978 |
978 |
979 o changeset: 0:e87515fd044a
979 o changeset: 0:e87515fd044a
980 user: test
980 user: test
981 date: Thu Jan 01 00:00:00 1970 +0000
981 date: Thu Jan 01 00:00:00 1970 +0000
982 summary: init, unrelated
982 summary: init, unrelated
983
983
984
984
985 $ hg --traceback log -f foo
985 $ hg --traceback log -f foo
986 changeset: 10:4dae8563d2c5
986 changeset: 10:4dae8563d2c5
987 tag: tip
987 tag: tip
988 parent: 9:7b35701b003e
988 parent: 9:7b35701b003e
989 parent: 4:88176d361b69
989 parent: 4:88176d361b69
990 user: test
990 user: test
991 date: Thu Jan 01 00:00:00 1970 +0000
991 date: Thu Jan 01 00:00:00 1970 +0000
992 summary: Last merge, related
992 summary: Last merge, related
993
993
994 changeset: 9:7b35701b003e
994 changeset: 9:7b35701b003e
995 parent: 8:e5416ad8a855
995 parent: 8:e5416ad8a855
996 parent: 7:87fe3144dcfa
996 parent: 7:87fe3144dcfa
997 user: test
997 user: test
998 date: Thu Jan 01 00:00:00 1970 +0000
998 date: Thu Jan 01 00:00:00 1970 +0000
999 summary: First merge, related
999 summary: First merge, related
1000
1000
1001 changeset: 8:e5416ad8a855
1001 changeset: 8:e5416ad8a855
1002 parent: 6:dc6c325fe5ee
1002 parent: 6:dc6c325fe5ee
1003 user: test
1003 user: test
1004 date: Thu Jan 01 00:00:00 1970 +0000
1004 date: Thu Jan 01 00:00:00 1970 +0000
1005 summary: change foo in branch, related
1005 summary: change foo in branch, related
1006
1006
1007 changeset: 7:87fe3144dcfa
1007 changeset: 7:87fe3144dcfa
1008 user: test
1008 user: test
1009 date: Thu Jan 01 00:00:00 1970 +0000
1009 date: Thu Jan 01 00:00:00 1970 +0000
1010 summary: change foo, related
1010 summary: change foo, related
1011
1011
1012 changeset: 6:dc6c325fe5ee
1012 changeset: 6:dc6c325fe5ee
1013 user: test
1013 user: test
1014 date: Thu Jan 01 00:00:00 1970 +0000
1014 date: Thu Jan 01 00:00:00 1970 +0000
1015 summary: create foo, related
1015 summary: create foo, related
1016
1016
1017 changeset: 4:88176d361b69
1017 changeset: 4:88176d361b69
1018 user: test
1018 user: test
1019 date: Thu Jan 01 00:00:00 1970 +0000
1019 date: Thu Jan 01 00:00:00 1970 +0000
1020 summary: add foo, related
1020 summary: add foo, related
1021
1021
1022
1022
1023 Also check when maxrev < lastrevfilelog
1024
1025 $ hg --traceback log -f -r4 foo
1026 changeset: 4:88176d361b69
1027 user: test
1028 date: Thu Jan 01 00:00:00 1970 +0000
1029 summary: add foo, related
1030
1031
1023 Issue2383: hg log showing _less_ differences than hg diff
1032 Issue2383: hg log showing _less_ differences than hg diff
1024
1033
1025 $ hg init issue2383
1034 $ hg init issue2383
1026 $ cd issue2383
1035 $ cd issue2383
1027
1036
1028 Create a test repo:
1037 Create a test repo:
1029
1038
1030 $ echo a > a
1039 $ echo a > a
1031 $ hg ci -Am0
1040 $ hg ci -Am0
1032 adding a
1041 adding a
1033 $ echo b > b
1042 $ echo b > b
1034 $ hg ci -Am1
1043 $ hg ci -Am1
1035 adding b
1044 adding b
1036 $ hg co 0
1045 $ hg co 0
1037 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
1046 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
1038 $ echo b > a
1047 $ echo b > a
1039 $ hg ci -m2
1048 $ hg ci -m2
1040 created new head
1049 created new head
1041
1050
1042 Merge:
1051 Merge:
1043
1052
1044 $ hg merge
1053 $ hg merge
1045 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1054 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1046 (branch merge, don't forget to commit)
1055 (branch merge, don't forget to commit)
1047
1056
1048 Make sure there's a file listed in the merge to trigger the bug:
1057 Make sure there's a file listed in the merge to trigger the bug:
1049
1058
1050 $ echo c > a
1059 $ echo c > a
1051 $ hg ci -m3
1060 $ hg ci -m3
1052
1061
1053 Two files shown here in diff:
1062 Two files shown here in diff:
1054
1063
1055 $ hg diff --rev 2:3
1064 $ hg diff --rev 2:3
1056 diff -r b09be438c43a -r 8e07aafe1edc a
1065 diff -r b09be438c43a -r 8e07aafe1edc a
1057 --- a/a Thu Jan 01 00:00:00 1970 +0000
1066 --- a/a Thu Jan 01 00:00:00 1970 +0000
1058 +++ b/a Thu Jan 01 00:00:00 1970 +0000
1067 +++ b/a Thu Jan 01 00:00:00 1970 +0000
1059 @@ -1,1 +1,1 @@
1068 @@ -1,1 +1,1 @@
1060 -b
1069 -b
1061 +c
1070 +c
1062 diff -r b09be438c43a -r 8e07aafe1edc b
1071 diff -r b09be438c43a -r 8e07aafe1edc b
1063 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1072 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1064 +++ b/b Thu Jan 01 00:00:00 1970 +0000
1073 +++ b/b Thu Jan 01 00:00:00 1970 +0000
1065 @@ -0,0 +1,1 @@
1074 @@ -0,0 +1,1 @@
1066 +b
1075 +b
1067
1076
1068 Diff here should be the same:
1077 Diff here should be the same:
1069
1078
1070 $ hg log -vpr 3
1079 $ hg log -vpr 3
1071 changeset: 3:8e07aafe1edc
1080 changeset: 3:8e07aafe1edc
1072 tag: tip
1081 tag: tip
1073 parent: 2:b09be438c43a
1082 parent: 2:b09be438c43a
1074 parent: 1:925d80f479bb
1083 parent: 1:925d80f479bb
1075 user: test
1084 user: test
1076 date: Thu Jan 01 00:00:00 1970 +0000
1085 date: Thu Jan 01 00:00:00 1970 +0000
1077 files: a
1086 files: a
1078 description:
1087 description:
1079 3
1088 3
1080
1089
1081
1090
1082 diff -r b09be438c43a -r 8e07aafe1edc a
1091 diff -r b09be438c43a -r 8e07aafe1edc a
1083 --- a/a Thu Jan 01 00:00:00 1970 +0000
1092 --- a/a Thu Jan 01 00:00:00 1970 +0000
1084 +++ b/a Thu Jan 01 00:00:00 1970 +0000
1093 +++ b/a Thu Jan 01 00:00:00 1970 +0000
1085 @@ -1,1 +1,1 @@
1094 @@ -1,1 +1,1 @@
1086 -b
1095 -b
1087 +c
1096 +c
1088 diff -r b09be438c43a -r 8e07aafe1edc b
1097 diff -r b09be438c43a -r 8e07aafe1edc b
1089 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1098 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1090 +++ b/b Thu Jan 01 00:00:00 1970 +0000
1099 +++ b/b Thu Jan 01 00:00:00 1970 +0000
1091 @@ -0,0 +1,1 @@
1100 @@ -0,0 +1,1 @@
1092 +b
1101 +b
1093
1102
1094 $ cd ..
1103 $ cd ..
1104
1105 'hg log -r rev fn' when last(filelog(fn)) != rev
1106
1107 $ hg init simplelog; cd simplelog
1108 $ echo f > a
1109 $ hg ci -Am'a' -d '0 0'
1110 adding a
1111 $ echo f >> a
1112 $ hg ci -Am'a bis' -d '1 0'
1113
1114 $ hg log -r0 a
1115 changeset: 0:9f758d63dcde
1116 user: test
1117 date: Thu Jan 01 00:00:00 1970 +0000
1118 summary: a
1119
General Comments 0
You need to be logged in to leave comments. Login now