##// END OF EJS Templates
diff: use second filename for --stat reporting on git patches (issue4221)
Matt Mackall -
r20972:4e2fb0ad default
parent child Browse files
Show More
@@ -1,1931 +1,1931 b''
1 # patch.py - patch file parsing routines
1 # patch.py - patch file parsing routines
2 #
2 #
3 # Copyright 2006 Brendan Cully <brendan@kublai.com>
3 # Copyright 2006 Brendan Cully <brendan@kublai.com>
4 # Copyright 2007 Chris Mason <chris.mason@oracle.com>
4 # Copyright 2007 Chris Mason <chris.mason@oracle.com>
5 #
5 #
6 # This software may be used and distributed according to the terms of the
6 # This software may be used and distributed according to the terms of the
7 # GNU General Public License version 2 or any later version.
7 # GNU General Public License version 2 or any later version.
8
8
9 import cStringIO, email, os, errno, re, posixpath
9 import cStringIO, email, os, errno, re, posixpath
10 import tempfile, zlib, shutil
10 import tempfile, zlib, shutil
11 # On python2.4 you have to import these by name or they fail to
11 # On python2.4 you have to import these by name or they fail to
12 # load. This was not a problem on Python 2.7.
12 # load. This was not a problem on Python 2.7.
13 import email.Generator
13 import email.Generator
14 import email.Parser
14 import email.Parser
15
15
16 from i18n import _
16 from i18n import _
17 from node import hex, short
17 from node import hex, short
18 import base85, mdiff, scmutil, util, diffhelpers, copies, encoding, error
18 import base85, mdiff, scmutil, util, diffhelpers, copies, encoding, error
19
19
20 gitre = re.compile('diff --git a/(.*) b/(.*)')
20 gitre = re.compile('diff --git a/(.*) b/(.*)')
21
21
22 class PatchError(Exception):
22 class PatchError(Exception):
23 pass
23 pass
24
24
25
25
26 # public functions
26 # public functions
27
27
28 def split(stream):
28 def split(stream):
29 '''return an iterator of individual patches from a stream'''
29 '''return an iterator of individual patches from a stream'''
30 def isheader(line, inheader):
30 def isheader(line, inheader):
31 if inheader and line[0] in (' ', '\t'):
31 if inheader and line[0] in (' ', '\t'):
32 # continuation
32 # continuation
33 return True
33 return True
34 if line[0] in (' ', '-', '+'):
34 if line[0] in (' ', '-', '+'):
35 # diff line - don't check for header pattern in there
35 # diff line - don't check for header pattern in there
36 return False
36 return False
37 l = line.split(': ', 1)
37 l = line.split(': ', 1)
38 return len(l) == 2 and ' ' not in l[0]
38 return len(l) == 2 and ' ' not in l[0]
39
39
40 def chunk(lines):
40 def chunk(lines):
41 return cStringIO.StringIO(''.join(lines))
41 return cStringIO.StringIO(''.join(lines))
42
42
43 def hgsplit(stream, cur):
43 def hgsplit(stream, cur):
44 inheader = True
44 inheader = True
45
45
46 for line in stream:
46 for line in stream:
47 if not line.strip():
47 if not line.strip():
48 inheader = False
48 inheader = False
49 if not inheader and line.startswith('# HG changeset patch'):
49 if not inheader and line.startswith('# HG changeset patch'):
50 yield chunk(cur)
50 yield chunk(cur)
51 cur = []
51 cur = []
52 inheader = True
52 inheader = True
53
53
54 cur.append(line)
54 cur.append(line)
55
55
56 if cur:
56 if cur:
57 yield chunk(cur)
57 yield chunk(cur)
58
58
59 def mboxsplit(stream, cur):
59 def mboxsplit(stream, cur):
60 for line in stream:
60 for line in stream:
61 if line.startswith('From '):
61 if line.startswith('From '):
62 for c in split(chunk(cur[1:])):
62 for c in split(chunk(cur[1:])):
63 yield c
63 yield c
64 cur = []
64 cur = []
65
65
66 cur.append(line)
66 cur.append(line)
67
67
68 if cur:
68 if cur:
69 for c in split(chunk(cur[1:])):
69 for c in split(chunk(cur[1:])):
70 yield c
70 yield c
71
71
72 def mimesplit(stream, cur):
72 def mimesplit(stream, cur):
73 def msgfp(m):
73 def msgfp(m):
74 fp = cStringIO.StringIO()
74 fp = cStringIO.StringIO()
75 g = email.Generator.Generator(fp, mangle_from_=False)
75 g = email.Generator.Generator(fp, mangle_from_=False)
76 g.flatten(m)
76 g.flatten(m)
77 fp.seek(0)
77 fp.seek(0)
78 return fp
78 return fp
79
79
80 for line in stream:
80 for line in stream:
81 cur.append(line)
81 cur.append(line)
82 c = chunk(cur)
82 c = chunk(cur)
83
83
84 m = email.Parser.Parser().parse(c)
84 m = email.Parser.Parser().parse(c)
85 if not m.is_multipart():
85 if not m.is_multipart():
86 yield msgfp(m)
86 yield msgfp(m)
87 else:
87 else:
88 ok_types = ('text/plain', 'text/x-diff', 'text/x-patch')
88 ok_types = ('text/plain', 'text/x-diff', 'text/x-patch')
89 for part in m.walk():
89 for part in m.walk():
90 ct = part.get_content_type()
90 ct = part.get_content_type()
91 if ct not in ok_types:
91 if ct not in ok_types:
92 continue
92 continue
93 yield msgfp(part)
93 yield msgfp(part)
94
94
95 def headersplit(stream, cur):
95 def headersplit(stream, cur):
96 inheader = False
96 inheader = False
97
97
98 for line in stream:
98 for line in stream:
99 if not inheader and isheader(line, inheader):
99 if not inheader and isheader(line, inheader):
100 yield chunk(cur)
100 yield chunk(cur)
101 cur = []
101 cur = []
102 inheader = True
102 inheader = True
103 if inheader and not isheader(line, inheader):
103 if inheader and not isheader(line, inheader):
104 inheader = False
104 inheader = False
105
105
106 cur.append(line)
106 cur.append(line)
107
107
108 if cur:
108 if cur:
109 yield chunk(cur)
109 yield chunk(cur)
110
110
111 def remainder(cur):
111 def remainder(cur):
112 yield chunk(cur)
112 yield chunk(cur)
113
113
114 class fiter(object):
114 class fiter(object):
115 def __init__(self, fp):
115 def __init__(self, fp):
116 self.fp = fp
116 self.fp = fp
117
117
118 def __iter__(self):
118 def __iter__(self):
119 return self
119 return self
120
120
121 def next(self):
121 def next(self):
122 l = self.fp.readline()
122 l = self.fp.readline()
123 if not l:
123 if not l:
124 raise StopIteration
124 raise StopIteration
125 return l
125 return l
126
126
127 inheader = False
127 inheader = False
128 cur = []
128 cur = []
129
129
130 mimeheaders = ['content-type']
130 mimeheaders = ['content-type']
131
131
132 if not util.safehasattr(stream, 'next'):
132 if not util.safehasattr(stream, 'next'):
133 # http responses, for example, have readline but not next
133 # http responses, for example, have readline but not next
134 stream = fiter(stream)
134 stream = fiter(stream)
135
135
136 for line in stream:
136 for line in stream:
137 cur.append(line)
137 cur.append(line)
138 if line.startswith('# HG changeset patch'):
138 if line.startswith('# HG changeset patch'):
139 return hgsplit(stream, cur)
139 return hgsplit(stream, cur)
140 elif line.startswith('From '):
140 elif line.startswith('From '):
141 return mboxsplit(stream, cur)
141 return mboxsplit(stream, cur)
142 elif isheader(line, inheader):
142 elif isheader(line, inheader):
143 inheader = True
143 inheader = True
144 if line.split(':', 1)[0].lower() in mimeheaders:
144 if line.split(':', 1)[0].lower() in mimeheaders:
145 # let email parser handle this
145 # let email parser handle this
146 return mimesplit(stream, cur)
146 return mimesplit(stream, cur)
147 elif line.startswith('--- ') and inheader:
147 elif line.startswith('--- ') and inheader:
148 # No evil headers seen by diff start, split by hand
148 # No evil headers seen by diff start, split by hand
149 return headersplit(stream, cur)
149 return headersplit(stream, cur)
150 # Not enough info, keep reading
150 # Not enough info, keep reading
151
151
152 # if we are here, we have a very plain patch
152 # if we are here, we have a very plain patch
153 return remainder(cur)
153 return remainder(cur)
154
154
155 def extract(ui, fileobj):
155 def extract(ui, fileobj):
156 '''extract patch from data read from fileobj.
156 '''extract patch from data read from fileobj.
157
157
158 patch can be a normal patch or contained in an email message.
158 patch can be a normal patch or contained in an email message.
159
159
160 return tuple (filename, message, user, date, branch, node, p1, p2).
160 return tuple (filename, message, user, date, branch, node, p1, p2).
161 Any item in the returned tuple can be None. If filename is None,
161 Any item in the returned tuple can be None. If filename is None,
162 fileobj did not contain a patch. Caller must unlink filename when done.'''
162 fileobj did not contain a patch. Caller must unlink filename when done.'''
163
163
164 # attempt to detect the start of a patch
164 # attempt to detect the start of a patch
165 # (this heuristic is borrowed from quilt)
165 # (this heuristic is borrowed from quilt)
166 diffre = re.compile(r'^(?:Index:[ \t]|diff[ \t]|RCS file: |'
166 diffre = re.compile(r'^(?:Index:[ \t]|diff[ \t]|RCS file: |'
167 r'retrieving revision [0-9]+(\.[0-9]+)*$|'
167 r'retrieving revision [0-9]+(\.[0-9]+)*$|'
168 r'---[ \t].*?^\+\+\+[ \t]|'
168 r'---[ \t].*?^\+\+\+[ \t]|'
169 r'\*\*\*[ \t].*?^---[ \t])', re.MULTILINE|re.DOTALL)
169 r'\*\*\*[ \t].*?^---[ \t])', re.MULTILINE|re.DOTALL)
170
170
171 fd, tmpname = tempfile.mkstemp(prefix='hg-patch-')
171 fd, tmpname = tempfile.mkstemp(prefix='hg-patch-')
172 tmpfp = os.fdopen(fd, 'w')
172 tmpfp = os.fdopen(fd, 'w')
173 try:
173 try:
174 msg = email.Parser.Parser().parse(fileobj)
174 msg = email.Parser.Parser().parse(fileobj)
175
175
176 subject = msg['Subject']
176 subject = msg['Subject']
177 user = msg['From']
177 user = msg['From']
178 if not subject and not user:
178 if not subject and not user:
179 # Not an email, restore parsed headers if any
179 # Not an email, restore parsed headers if any
180 subject = '\n'.join(': '.join(h) for h in msg.items()) + '\n'
180 subject = '\n'.join(': '.join(h) for h in msg.items()) + '\n'
181
181
182 # should try to parse msg['Date']
182 # should try to parse msg['Date']
183 date = None
183 date = None
184 nodeid = None
184 nodeid = None
185 branch = None
185 branch = None
186 parents = []
186 parents = []
187
187
188 if subject:
188 if subject:
189 if subject.startswith('[PATCH'):
189 if subject.startswith('[PATCH'):
190 pend = subject.find(']')
190 pend = subject.find(']')
191 if pend >= 0:
191 if pend >= 0:
192 subject = subject[pend + 1:].lstrip()
192 subject = subject[pend + 1:].lstrip()
193 subject = re.sub(r'\n[ \t]+', ' ', subject)
193 subject = re.sub(r'\n[ \t]+', ' ', subject)
194 ui.debug('Subject: %s\n' % subject)
194 ui.debug('Subject: %s\n' % subject)
195 if user:
195 if user:
196 ui.debug('From: %s\n' % user)
196 ui.debug('From: %s\n' % user)
197 diffs_seen = 0
197 diffs_seen = 0
198 ok_types = ('text/plain', 'text/x-diff', 'text/x-patch')
198 ok_types = ('text/plain', 'text/x-diff', 'text/x-patch')
199 message = ''
199 message = ''
200 for part in msg.walk():
200 for part in msg.walk():
201 content_type = part.get_content_type()
201 content_type = part.get_content_type()
202 ui.debug('Content-Type: %s\n' % content_type)
202 ui.debug('Content-Type: %s\n' % content_type)
203 if content_type not in ok_types:
203 if content_type not in ok_types:
204 continue
204 continue
205 payload = part.get_payload(decode=True)
205 payload = part.get_payload(decode=True)
206 m = diffre.search(payload)
206 m = diffre.search(payload)
207 if m:
207 if m:
208 hgpatch = False
208 hgpatch = False
209 hgpatchheader = False
209 hgpatchheader = False
210 ignoretext = False
210 ignoretext = False
211
211
212 ui.debug('found patch at byte %d\n' % m.start(0))
212 ui.debug('found patch at byte %d\n' % m.start(0))
213 diffs_seen += 1
213 diffs_seen += 1
214 cfp = cStringIO.StringIO()
214 cfp = cStringIO.StringIO()
215 for line in payload[:m.start(0)].splitlines():
215 for line in payload[:m.start(0)].splitlines():
216 if line.startswith('# HG changeset patch') and not hgpatch:
216 if line.startswith('# HG changeset patch') and not hgpatch:
217 ui.debug('patch generated by hg export\n')
217 ui.debug('patch generated by hg export\n')
218 hgpatch = True
218 hgpatch = True
219 hgpatchheader = True
219 hgpatchheader = True
220 # drop earlier commit message content
220 # drop earlier commit message content
221 cfp.seek(0)
221 cfp.seek(0)
222 cfp.truncate()
222 cfp.truncate()
223 subject = None
223 subject = None
224 elif hgpatchheader:
224 elif hgpatchheader:
225 if line.startswith('# User '):
225 if line.startswith('# User '):
226 user = line[7:]
226 user = line[7:]
227 ui.debug('From: %s\n' % user)
227 ui.debug('From: %s\n' % user)
228 elif line.startswith("# Date "):
228 elif line.startswith("# Date "):
229 date = line[7:]
229 date = line[7:]
230 elif line.startswith("# Branch "):
230 elif line.startswith("# Branch "):
231 branch = line[9:]
231 branch = line[9:]
232 elif line.startswith("# Node ID "):
232 elif line.startswith("# Node ID "):
233 nodeid = line[10:]
233 nodeid = line[10:]
234 elif line.startswith("# Parent "):
234 elif line.startswith("# Parent "):
235 parents.append(line[9:].lstrip())
235 parents.append(line[9:].lstrip())
236 elif not line.startswith("# "):
236 elif not line.startswith("# "):
237 hgpatchheader = False
237 hgpatchheader = False
238 elif line == '---':
238 elif line == '---':
239 ignoretext = True
239 ignoretext = True
240 if not hgpatchheader and not ignoretext:
240 if not hgpatchheader and not ignoretext:
241 cfp.write(line)
241 cfp.write(line)
242 cfp.write('\n')
242 cfp.write('\n')
243 message = cfp.getvalue()
243 message = cfp.getvalue()
244 if tmpfp:
244 if tmpfp:
245 tmpfp.write(payload)
245 tmpfp.write(payload)
246 if not payload.endswith('\n'):
246 if not payload.endswith('\n'):
247 tmpfp.write('\n')
247 tmpfp.write('\n')
248 elif not diffs_seen and message and content_type == 'text/plain':
248 elif not diffs_seen and message and content_type == 'text/plain':
249 message += '\n' + payload
249 message += '\n' + payload
250 except: # re-raises
250 except: # re-raises
251 tmpfp.close()
251 tmpfp.close()
252 os.unlink(tmpname)
252 os.unlink(tmpname)
253 raise
253 raise
254
254
255 if subject and not message.startswith(subject):
255 if subject and not message.startswith(subject):
256 message = '%s\n%s' % (subject, message)
256 message = '%s\n%s' % (subject, message)
257 tmpfp.close()
257 tmpfp.close()
258 if not diffs_seen:
258 if not diffs_seen:
259 os.unlink(tmpname)
259 os.unlink(tmpname)
260 return None, message, user, date, branch, None, None, None
260 return None, message, user, date, branch, None, None, None
261 p1 = parents and parents.pop(0) or None
261 p1 = parents and parents.pop(0) or None
262 p2 = parents and parents.pop(0) or None
262 p2 = parents and parents.pop(0) or None
263 return tmpname, message, user, date, branch, nodeid, p1, p2
263 return tmpname, message, user, date, branch, nodeid, p1, p2
264
264
265 class patchmeta(object):
265 class patchmeta(object):
266 """Patched file metadata
266 """Patched file metadata
267
267
268 'op' is the performed operation within ADD, DELETE, RENAME, MODIFY
268 'op' is the performed operation within ADD, DELETE, RENAME, MODIFY
269 or COPY. 'path' is patched file path. 'oldpath' is set to the
269 or COPY. 'path' is patched file path. 'oldpath' is set to the
270 origin file when 'op' is either COPY or RENAME, None otherwise. If
270 origin file when 'op' is either COPY or RENAME, None otherwise. If
271 file mode is changed, 'mode' is a tuple (islink, isexec) where
271 file mode is changed, 'mode' is a tuple (islink, isexec) where
272 'islink' is True if the file is a symlink and 'isexec' is True if
272 'islink' is True if the file is a symlink and 'isexec' is True if
273 the file is executable. Otherwise, 'mode' is None.
273 the file is executable. Otherwise, 'mode' is None.
274 """
274 """
275 def __init__(self, path):
275 def __init__(self, path):
276 self.path = path
276 self.path = path
277 self.oldpath = None
277 self.oldpath = None
278 self.mode = None
278 self.mode = None
279 self.op = 'MODIFY'
279 self.op = 'MODIFY'
280 self.binary = False
280 self.binary = False
281
281
282 def setmode(self, mode):
282 def setmode(self, mode):
283 islink = mode & 020000
283 islink = mode & 020000
284 isexec = mode & 0100
284 isexec = mode & 0100
285 self.mode = (islink, isexec)
285 self.mode = (islink, isexec)
286
286
287 def copy(self):
287 def copy(self):
288 other = patchmeta(self.path)
288 other = patchmeta(self.path)
289 other.oldpath = self.oldpath
289 other.oldpath = self.oldpath
290 other.mode = self.mode
290 other.mode = self.mode
291 other.op = self.op
291 other.op = self.op
292 other.binary = self.binary
292 other.binary = self.binary
293 return other
293 return other
294
294
295 def _ispatchinga(self, afile):
295 def _ispatchinga(self, afile):
296 if afile == '/dev/null':
296 if afile == '/dev/null':
297 return self.op == 'ADD'
297 return self.op == 'ADD'
298 return afile == 'a/' + (self.oldpath or self.path)
298 return afile == 'a/' + (self.oldpath or self.path)
299
299
300 def _ispatchingb(self, bfile):
300 def _ispatchingb(self, bfile):
301 if bfile == '/dev/null':
301 if bfile == '/dev/null':
302 return self.op == 'DELETE'
302 return self.op == 'DELETE'
303 return bfile == 'b/' + self.path
303 return bfile == 'b/' + self.path
304
304
305 def ispatching(self, afile, bfile):
305 def ispatching(self, afile, bfile):
306 return self._ispatchinga(afile) and self._ispatchingb(bfile)
306 return self._ispatchinga(afile) and self._ispatchingb(bfile)
307
307
308 def __repr__(self):
308 def __repr__(self):
309 return "<patchmeta %s %r>" % (self.op, self.path)
309 return "<patchmeta %s %r>" % (self.op, self.path)
310
310
311 def readgitpatch(lr):
311 def readgitpatch(lr):
312 """extract git-style metadata about patches from <patchname>"""
312 """extract git-style metadata about patches from <patchname>"""
313
313
314 # Filter patch for git information
314 # Filter patch for git information
315 gp = None
315 gp = None
316 gitpatches = []
316 gitpatches = []
317 for line in lr:
317 for line in lr:
318 line = line.rstrip(' \r\n')
318 line = line.rstrip(' \r\n')
319 if line.startswith('diff --git a/'):
319 if line.startswith('diff --git a/'):
320 m = gitre.match(line)
320 m = gitre.match(line)
321 if m:
321 if m:
322 if gp:
322 if gp:
323 gitpatches.append(gp)
323 gitpatches.append(gp)
324 dst = m.group(2)
324 dst = m.group(2)
325 gp = patchmeta(dst)
325 gp = patchmeta(dst)
326 elif gp:
326 elif gp:
327 if line.startswith('--- '):
327 if line.startswith('--- '):
328 gitpatches.append(gp)
328 gitpatches.append(gp)
329 gp = None
329 gp = None
330 continue
330 continue
331 if line.startswith('rename from '):
331 if line.startswith('rename from '):
332 gp.op = 'RENAME'
332 gp.op = 'RENAME'
333 gp.oldpath = line[12:]
333 gp.oldpath = line[12:]
334 elif line.startswith('rename to '):
334 elif line.startswith('rename to '):
335 gp.path = line[10:]
335 gp.path = line[10:]
336 elif line.startswith('copy from '):
336 elif line.startswith('copy from '):
337 gp.op = 'COPY'
337 gp.op = 'COPY'
338 gp.oldpath = line[10:]
338 gp.oldpath = line[10:]
339 elif line.startswith('copy to '):
339 elif line.startswith('copy to '):
340 gp.path = line[8:]
340 gp.path = line[8:]
341 elif line.startswith('deleted file'):
341 elif line.startswith('deleted file'):
342 gp.op = 'DELETE'
342 gp.op = 'DELETE'
343 elif line.startswith('new file mode '):
343 elif line.startswith('new file mode '):
344 gp.op = 'ADD'
344 gp.op = 'ADD'
345 gp.setmode(int(line[-6:], 8))
345 gp.setmode(int(line[-6:], 8))
346 elif line.startswith('new mode '):
346 elif line.startswith('new mode '):
347 gp.setmode(int(line[-6:], 8))
347 gp.setmode(int(line[-6:], 8))
348 elif line.startswith('GIT binary patch'):
348 elif line.startswith('GIT binary patch'):
349 gp.binary = True
349 gp.binary = True
350 if gp:
350 if gp:
351 gitpatches.append(gp)
351 gitpatches.append(gp)
352
352
353 return gitpatches
353 return gitpatches
354
354
355 class linereader(object):
355 class linereader(object):
356 # simple class to allow pushing lines back into the input stream
356 # simple class to allow pushing lines back into the input stream
357 def __init__(self, fp):
357 def __init__(self, fp):
358 self.fp = fp
358 self.fp = fp
359 self.buf = []
359 self.buf = []
360
360
361 def push(self, line):
361 def push(self, line):
362 if line is not None:
362 if line is not None:
363 self.buf.append(line)
363 self.buf.append(line)
364
364
365 def readline(self):
365 def readline(self):
366 if self.buf:
366 if self.buf:
367 l = self.buf[0]
367 l = self.buf[0]
368 del self.buf[0]
368 del self.buf[0]
369 return l
369 return l
370 return self.fp.readline()
370 return self.fp.readline()
371
371
372 def __iter__(self):
372 def __iter__(self):
373 while True:
373 while True:
374 l = self.readline()
374 l = self.readline()
375 if not l:
375 if not l:
376 break
376 break
377 yield l
377 yield l
378
378
379 class abstractbackend(object):
379 class abstractbackend(object):
380 def __init__(self, ui):
380 def __init__(self, ui):
381 self.ui = ui
381 self.ui = ui
382
382
383 def getfile(self, fname):
383 def getfile(self, fname):
384 """Return target file data and flags as a (data, (islink,
384 """Return target file data and flags as a (data, (islink,
385 isexec)) tuple.
385 isexec)) tuple.
386 """
386 """
387 raise NotImplementedError
387 raise NotImplementedError
388
388
389 def setfile(self, fname, data, mode, copysource):
389 def setfile(self, fname, data, mode, copysource):
390 """Write data to target file fname and set its mode. mode is a
390 """Write data to target file fname and set its mode. mode is a
391 (islink, isexec) tuple. If data is None, the file content should
391 (islink, isexec) tuple. If data is None, the file content should
392 be left unchanged. If the file is modified after being copied,
392 be left unchanged. If the file is modified after being copied,
393 copysource is set to the original file name.
393 copysource is set to the original file name.
394 """
394 """
395 raise NotImplementedError
395 raise NotImplementedError
396
396
397 def unlink(self, fname):
397 def unlink(self, fname):
398 """Unlink target file."""
398 """Unlink target file."""
399 raise NotImplementedError
399 raise NotImplementedError
400
400
401 def writerej(self, fname, failed, total, lines):
401 def writerej(self, fname, failed, total, lines):
402 """Write rejected lines for fname. total is the number of hunks
402 """Write rejected lines for fname. total is the number of hunks
403 which failed to apply and total the total number of hunks for this
403 which failed to apply and total the total number of hunks for this
404 files.
404 files.
405 """
405 """
406 pass
406 pass
407
407
408 def exists(self, fname):
408 def exists(self, fname):
409 raise NotImplementedError
409 raise NotImplementedError
410
410
411 class fsbackend(abstractbackend):
411 class fsbackend(abstractbackend):
412 def __init__(self, ui, basedir):
412 def __init__(self, ui, basedir):
413 super(fsbackend, self).__init__(ui)
413 super(fsbackend, self).__init__(ui)
414 self.opener = scmutil.opener(basedir)
414 self.opener = scmutil.opener(basedir)
415
415
416 def _join(self, f):
416 def _join(self, f):
417 return os.path.join(self.opener.base, f)
417 return os.path.join(self.opener.base, f)
418
418
419 def getfile(self, fname):
419 def getfile(self, fname):
420 path = self._join(fname)
420 path = self._join(fname)
421 if os.path.islink(path):
421 if os.path.islink(path):
422 return (os.readlink(path), (True, False))
422 return (os.readlink(path), (True, False))
423 isexec = False
423 isexec = False
424 try:
424 try:
425 isexec = os.lstat(path).st_mode & 0100 != 0
425 isexec = os.lstat(path).st_mode & 0100 != 0
426 except OSError, e:
426 except OSError, e:
427 if e.errno != errno.ENOENT:
427 if e.errno != errno.ENOENT:
428 raise
428 raise
429 return (self.opener.read(fname), (False, isexec))
429 return (self.opener.read(fname), (False, isexec))
430
430
431 def setfile(self, fname, data, mode, copysource):
431 def setfile(self, fname, data, mode, copysource):
432 islink, isexec = mode
432 islink, isexec = mode
433 if data is None:
433 if data is None:
434 util.setflags(self._join(fname), islink, isexec)
434 util.setflags(self._join(fname), islink, isexec)
435 return
435 return
436 if islink:
436 if islink:
437 self.opener.symlink(data, fname)
437 self.opener.symlink(data, fname)
438 else:
438 else:
439 self.opener.write(fname, data)
439 self.opener.write(fname, data)
440 if isexec:
440 if isexec:
441 util.setflags(self._join(fname), False, True)
441 util.setflags(self._join(fname), False, True)
442
442
443 def unlink(self, fname):
443 def unlink(self, fname):
444 util.unlinkpath(self._join(fname), ignoremissing=True)
444 util.unlinkpath(self._join(fname), ignoremissing=True)
445
445
446 def writerej(self, fname, failed, total, lines):
446 def writerej(self, fname, failed, total, lines):
447 fname = fname + ".rej"
447 fname = fname + ".rej"
448 self.ui.warn(
448 self.ui.warn(
449 _("%d out of %d hunks FAILED -- saving rejects to file %s\n") %
449 _("%d out of %d hunks FAILED -- saving rejects to file %s\n") %
450 (failed, total, fname))
450 (failed, total, fname))
451 fp = self.opener(fname, 'w')
451 fp = self.opener(fname, 'w')
452 fp.writelines(lines)
452 fp.writelines(lines)
453 fp.close()
453 fp.close()
454
454
455 def exists(self, fname):
455 def exists(self, fname):
456 return os.path.lexists(self._join(fname))
456 return os.path.lexists(self._join(fname))
457
457
458 class workingbackend(fsbackend):
458 class workingbackend(fsbackend):
459 def __init__(self, ui, repo, similarity):
459 def __init__(self, ui, repo, similarity):
460 super(workingbackend, self).__init__(ui, repo.root)
460 super(workingbackend, self).__init__(ui, repo.root)
461 self.repo = repo
461 self.repo = repo
462 self.similarity = similarity
462 self.similarity = similarity
463 self.removed = set()
463 self.removed = set()
464 self.changed = set()
464 self.changed = set()
465 self.copied = []
465 self.copied = []
466
466
467 def _checkknown(self, fname):
467 def _checkknown(self, fname):
468 if self.repo.dirstate[fname] == '?' and self.exists(fname):
468 if self.repo.dirstate[fname] == '?' and self.exists(fname):
469 raise PatchError(_('cannot patch %s: file is not tracked') % fname)
469 raise PatchError(_('cannot patch %s: file is not tracked') % fname)
470
470
471 def setfile(self, fname, data, mode, copysource):
471 def setfile(self, fname, data, mode, copysource):
472 self._checkknown(fname)
472 self._checkknown(fname)
473 super(workingbackend, self).setfile(fname, data, mode, copysource)
473 super(workingbackend, self).setfile(fname, data, mode, copysource)
474 if copysource is not None:
474 if copysource is not None:
475 self.copied.append((copysource, fname))
475 self.copied.append((copysource, fname))
476 self.changed.add(fname)
476 self.changed.add(fname)
477
477
478 def unlink(self, fname):
478 def unlink(self, fname):
479 self._checkknown(fname)
479 self._checkknown(fname)
480 super(workingbackend, self).unlink(fname)
480 super(workingbackend, self).unlink(fname)
481 self.removed.add(fname)
481 self.removed.add(fname)
482 self.changed.add(fname)
482 self.changed.add(fname)
483
483
484 def close(self):
484 def close(self):
485 wctx = self.repo[None]
485 wctx = self.repo[None]
486 changed = set(self.changed)
486 changed = set(self.changed)
487 for src, dst in self.copied:
487 for src, dst in self.copied:
488 scmutil.dirstatecopy(self.ui, self.repo, wctx, src, dst)
488 scmutil.dirstatecopy(self.ui, self.repo, wctx, src, dst)
489 if self.removed:
489 if self.removed:
490 wctx.forget(sorted(self.removed))
490 wctx.forget(sorted(self.removed))
491 for f in self.removed:
491 for f in self.removed:
492 if f not in self.repo.dirstate:
492 if f not in self.repo.dirstate:
493 # File was deleted and no longer belongs to the
493 # File was deleted and no longer belongs to the
494 # dirstate, it was probably marked added then
494 # dirstate, it was probably marked added then
495 # deleted, and should not be considered by
495 # deleted, and should not be considered by
496 # marktouched().
496 # marktouched().
497 changed.discard(f)
497 changed.discard(f)
498 if changed:
498 if changed:
499 scmutil.marktouched(self.repo, changed, self.similarity)
499 scmutil.marktouched(self.repo, changed, self.similarity)
500 return sorted(self.changed)
500 return sorted(self.changed)
501
501
502 class filestore(object):
502 class filestore(object):
503 def __init__(self, maxsize=None):
503 def __init__(self, maxsize=None):
504 self.opener = None
504 self.opener = None
505 self.files = {}
505 self.files = {}
506 self.created = 0
506 self.created = 0
507 self.maxsize = maxsize
507 self.maxsize = maxsize
508 if self.maxsize is None:
508 if self.maxsize is None:
509 self.maxsize = 4*(2**20)
509 self.maxsize = 4*(2**20)
510 self.size = 0
510 self.size = 0
511 self.data = {}
511 self.data = {}
512
512
513 def setfile(self, fname, data, mode, copied=None):
513 def setfile(self, fname, data, mode, copied=None):
514 if self.maxsize < 0 or (len(data) + self.size) <= self.maxsize:
514 if self.maxsize < 0 or (len(data) + self.size) <= self.maxsize:
515 self.data[fname] = (data, mode, copied)
515 self.data[fname] = (data, mode, copied)
516 self.size += len(data)
516 self.size += len(data)
517 else:
517 else:
518 if self.opener is None:
518 if self.opener is None:
519 root = tempfile.mkdtemp(prefix='hg-patch-')
519 root = tempfile.mkdtemp(prefix='hg-patch-')
520 self.opener = scmutil.opener(root)
520 self.opener = scmutil.opener(root)
521 # Avoid filename issues with these simple names
521 # Avoid filename issues with these simple names
522 fn = str(self.created)
522 fn = str(self.created)
523 self.opener.write(fn, data)
523 self.opener.write(fn, data)
524 self.created += 1
524 self.created += 1
525 self.files[fname] = (fn, mode, copied)
525 self.files[fname] = (fn, mode, copied)
526
526
527 def getfile(self, fname):
527 def getfile(self, fname):
528 if fname in self.data:
528 if fname in self.data:
529 return self.data[fname]
529 return self.data[fname]
530 if not self.opener or fname not in self.files:
530 if not self.opener or fname not in self.files:
531 raise IOError
531 raise IOError
532 fn, mode, copied = self.files[fname]
532 fn, mode, copied = self.files[fname]
533 return self.opener.read(fn), mode, copied
533 return self.opener.read(fn), mode, copied
534
534
535 def close(self):
535 def close(self):
536 if self.opener:
536 if self.opener:
537 shutil.rmtree(self.opener.base)
537 shutil.rmtree(self.opener.base)
538
538
539 class repobackend(abstractbackend):
539 class repobackend(abstractbackend):
540 def __init__(self, ui, repo, ctx, store):
540 def __init__(self, ui, repo, ctx, store):
541 super(repobackend, self).__init__(ui)
541 super(repobackend, self).__init__(ui)
542 self.repo = repo
542 self.repo = repo
543 self.ctx = ctx
543 self.ctx = ctx
544 self.store = store
544 self.store = store
545 self.changed = set()
545 self.changed = set()
546 self.removed = set()
546 self.removed = set()
547 self.copied = {}
547 self.copied = {}
548
548
549 def _checkknown(self, fname):
549 def _checkknown(self, fname):
550 if fname not in self.ctx:
550 if fname not in self.ctx:
551 raise PatchError(_('cannot patch %s: file is not tracked') % fname)
551 raise PatchError(_('cannot patch %s: file is not tracked') % fname)
552
552
553 def getfile(self, fname):
553 def getfile(self, fname):
554 try:
554 try:
555 fctx = self.ctx[fname]
555 fctx = self.ctx[fname]
556 except error.LookupError:
556 except error.LookupError:
557 raise IOError
557 raise IOError
558 flags = fctx.flags()
558 flags = fctx.flags()
559 return fctx.data(), ('l' in flags, 'x' in flags)
559 return fctx.data(), ('l' in flags, 'x' in flags)
560
560
561 def setfile(self, fname, data, mode, copysource):
561 def setfile(self, fname, data, mode, copysource):
562 if copysource:
562 if copysource:
563 self._checkknown(copysource)
563 self._checkknown(copysource)
564 if data is None:
564 if data is None:
565 data = self.ctx[fname].data()
565 data = self.ctx[fname].data()
566 self.store.setfile(fname, data, mode, copysource)
566 self.store.setfile(fname, data, mode, copysource)
567 self.changed.add(fname)
567 self.changed.add(fname)
568 if copysource:
568 if copysource:
569 self.copied[fname] = copysource
569 self.copied[fname] = copysource
570
570
571 def unlink(self, fname):
571 def unlink(self, fname):
572 self._checkknown(fname)
572 self._checkknown(fname)
573 self.removed.add(fname)
573 self.removed.add(fname)
574
574
575 def exists(self, fname):
575 def exists(self, fname):
576 return fname in self.ctx
576 return fname in self.ctx
577
577
578 def close(self):
578 def close(self):
579 return self.changed | self.removed
579 return self.changed | self.removed
580
580
581 # @@ -start,len +start,len @@ or @@ -start +start @@ if len is 1
581 # @@ -start,len +start,len @@ or @@ -start +start @@ if len is 1
582 unidesc = re.compile('@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@')
582 unidesc = re.compile('@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@')
583 contextdesc = re.compile('(?:---|\*\*\*) (\d+)(?:,(\d+))? (?:---|\*\*\*)')
583 contextdesc = re.compile('(?:---|\*\*\*) (\d+)(?:,(\d+))? (?:---|\*\*\*)')
584 eolmodes = ['strict', 'crlf', 'lf', 'auto']
584 eolmodes = ['strict', 'crlf', 'lf', 'auto']
585
585
586 class patchfile(object):
586 class patchfile(object):
587 def __init__(self, ui, gp, backend, store, eolmode='strict'):
587 def __init__(self, ui, gp, backend, store, eolmode='strict'):
588 self.fname = gp.path
588 self.fname = gp.path
589 self.eolmode = eolmode
589 self.eolmode = eolmode
590 self.eol = None
590 self.eol = None
591 self.backend = backend
591 self.backend = backend
592 self.ui = ui
592 self.ui = ui
593 self.lines = []
593 self.lines = []
594 self.exists = False
594 self.exists = False
595 self.missing = True
595 self.missing = True
596 self.mode = gp.mode
596 self.mode = gp.mode
597 self.copysource = gp.oldpath
597 self.copysource = gp.oldpath
598 self.create = gp.op in ('ADD', 'COPY', 'RENAME')
598 self.create = gp.op in ('ADD', 'COPY', 'RENAME')
599 self.remove = gp.op == 'DELETE'
599 self.remove = gp.op == 'DELETE'
600 try:
600 try:
601 if self.copysource is None:
601 if self.copysource is None:
602 data, mode = backend.getfile(self.fname)
602 data, mode = backend.getfile(self.fname)
603 self.exists = True
603 self.exists = True
604 else:
604 else:
605 data, mode = store.getfile(self.copysource)[:2]
605 data, mode = store.getfile(self.copysource)[:2]
606 self.exists = backend.exists(self.fname)
606 self.exists = backend.exists(self.fname)
607 self.missing = False
607 self.missing = False
608 if data:
608 if data:
609 self.lines = mdiff.splitnewlines(data)
609 self.lines = mdiff.splitnewlines(data)
610 if self.mode is None:
610 if self.mode is None:
611 self.mode = mode
611 self.mode = mode
612 if self.lines:
612 if self.lines:
613 # Normalize line endings
613 # Normalize line endings
614 if self.lines[0].endswith('\r\n'):
614 if self.lines[0].endswith('\r\n'):
615 self.eol = '\r\n'
615 self.eol = '\r\n'
616 elif self.lines[0].endswith('\n'):
616 elif self.lines[0].endswith('\n'):
617 self.eol = '\n'
617 self.eol = '\n'
618 if eolmode != 'strict':
618 if eolmode != 'strict':
619 nlines = []
619 nlines = []
620 for l in self.lines:
620 for l in self.lines:
621 if l.endswith('\r\n'):
621 if l.endswith('\r\n'):
622 l = l[:-2] + '\n'
622 l = l[:-2] + '\n'
623 nlines.append(l)
623 nlines.append(l)
624 self.lines = nlines
624 self.lines = nlines
625 except IOError:
625 except IOError:
626 if self.create:
626 if self.create:
627 self.missing = False
627 self.missing = False
628 if self.mode is None:
628 if self.mode is None:
629 self.mode = (False, False)
629 self.mode = (False, False)
630 if self.missing:
630 if self.missing:
631 self.ui.warn(_("unable to find '%s' for patching\n") % self.fname)
631 self.ui.warn(_("unable to find '%s' for patching\n") % self.fname)
632
632
633 self.hash = {}
633 self.hash = {}
634 self.dirty = 0
634 self.dirty = 0
635 self.offset = 0
635 self.offset = 0
636 self.skew = 0
636 self.skew = 0
637 self.rej = []
637 self.rej = []
638 self.fileprinted = False
638 self.fileprinted = False
639 self.printfile(False)
639 self.printfile(False)
640 self.hunks = 0
640 self.hunks = 0
641
641
642 def writelines(self, fname, lines, mode):
642 def writelines(self, fname, lines, mode):
643 if self.eolmode == 'auto':
643 if self.eolmode == 'auto':
644 eol = self.eol
644 eol = self.eol
645 elif self.eolmode == 'crlf':
645 elif self.eolmode == 'crlf':
646 eol = '\r\n'
646 eol = '\r\n'
647 else:
647 else:
648 eol = '\n'
648 eol = '\n'
649
649
650 if self.eolmode != 'strict' and eol and eol != '\n':
650 if self.eolmode != 'strict' and eol and eol != '\n':
651 rawlines = []
651 rawlines = []
652 for l in lines:
652 for l in lines:
653 if l and l[-1] == '\n':
653 if l and l[-1] == '\n':
654 l = l[:-1] + eol
654 l = l[:-1] + eol
655 rawlines.append(l)
655 rawlines.append(l)
656 lines = rawlines
656 lines = rawlines
657
657
658 self.backend.setfile(fname, ''.join(lines), mode, self.copysource)
658 self.backend.setfile(fname, ''.join(lines), mode, self.copysource)
659
659
660 def printfile(self, warn):
660 def printfile(self, warn):
661 if self.fileprinted:
661 if self.fileprinted:
662 return
662 return
663 if warn or self.ui.verbose:
663 if warn or self.ui.verbose:
664 self.fileprinted = True
664 self.fileprinted = True
665 s = _("patching file %s\n") % self.fname
665 s = _("patching file %s\n") % self.fname
666 if warn:
666 if warn:
667 self.ui.warn(s)
667 self.ui.warn(s)
668 else:
668 else:
669 self.ui.note(s)
669 self.ui.note(s)
670
670
671
671
672 def findlines(self, l, linenum):
672 def findlines(self, l, linenum):
673 # looks through the hash and finds candidate lines. The
673 # looks through the hash and finds candidate lines. The
674 # result is a list of line numbers sorted based on distance
674 # result is a list of line numbers sorted based on distance
675 # from linenum
675 # from linenum
676
676
677 cand = self.hash.get(l, [])
677 cand = self.hash.get(l, [])
678 if len(cand) > 1:
678 if len(cand) > 1:
679 # resort our list of potentials forward then back.
679 # resort our list of potentials forward then back.
680 cand.sort(key=lambda x: abs(x - linenum))
680 cand.sort(key=lambda x: abs(x - linenum))
681 return cand
681 return cand
682
682
683 def write_rej(self):
683 def write_rej(self):
684 # our rejects are a little different from patch(1). This always
684 # our rejects are a little different from patch(1). This always
685 # creates rejects in the same form as the original patch. A file
685 # creates rejects in the same form as the original patch. A file
686 # header is inserted so that you can run the reject through patch again
686 # header is inserted so that you can run the reject through patch again
687 # without having to type the filename.
687 # without having to type the filename.
688 if not self.rej:
688 if not self.rej:
689 return
689 return
690 base = os.path.basename(self.fname)
690 base = os.path.basename(self.fname)
691 lines = ["--- %s\n+++ %s\n" % (base, base)]
691 lines = ["--- %s\n+++ %s\n" % (base, base)]
692 for x in self.rej:
692 for x in self.rej:
693 for l in x.hunk:
693 for l in x.hunk:
694 lines.append(l)
694 lines.append(l)
695 if l[-1] != '\n':
695 if l[-1] != '\n':
696 lines.append("\n\ No newline at end of file\n")
696 lines.append("\n\ No newline at end of file\n")
697 self.backend.writerej(self.fname, len(self.rej), self.hunks, lines)
697 self.backend.writerej(self.fname, len(self.rej), self.hunks, lines)
698
698
699 def apply(self, h):
699 def apply(self, h):
700 if not h.complete():
700 if not h.complete():
701 raise PatchError(_("bad hunk #%d %s (%d %d %d %d)") %
701 raise PatchError(_("bad hunk #%d %s (%d %d %d %d)") %
702 (h.number, h.desc, len(h.a), h.lena, len(h.b),
702 (h.number, h.desc, len(h.a), h.lena, len(h.b),
703 h.lenb))
703 h.lenb))
704
704
705 self.hunks += 1
705 self.hunks += 1
706
706
707 if self.missing:
707 if self.missing:
708 self.rej.append(h)
708 self.rej.append(h)
709 return -1
709 return -1
710
710
711 if self.exists and self.create:
711 if self.exists and self.create:
712 if self.copysource:
712 if self.copysource:
713 self.ui.warn(_("cannot create %s: destination already "
713 self.ui.warn(_("cannot create %s: destination already "
714 "exists\n") % self.fname)
714 "exists\n") % self.fname)
715 else:
715 else:
716 self.ui.warn(_("file %s already exists\n") % self.fname)
716 self.ui.warn(_("file %s already exists\n") % self.fname)
717 self.rej.append(h)
717 self.rej.append(h)
718 return -1
718 return -1
719
719
720 if isinstance(h, binhunk):
720 if isinstance(h, binhunk):
721 if self.remove:
721 if self.remove:
722 self.backend.unlink(self.fname)
722 self.backend.unlink(self.fname)
723 else:
723 else:
724 l = h.new(self.lines)
724 l = h.new(self.lines)
725 self.lines[:] = l
725 self.lines[:] = l
726 self.offset += len(l)
726 self.offset += len(l)
727 self.dirty = True
727 self.dirty = True
728 return 0
728 return 0
729
729
730 horig = h
730 horig = h
731 if (self.eolmode in ('crlf', 'lf')
731 if (self.eolmode in ('crlf', 'lf')
732 or self.eolmode == 'auto' and self.eol):
732 or self.eolmode == 'auto' and self.eol):
733 # If new eols are going to be normalized, then normalize
733 # If new eols are going to be normalized, then normalize
734 # hunk data before patching. Otherwise, preserve input
734 # hunk data before patching. Otherwise, preserve input
735 # line-endings.
735 # line-endings.
736 h = h.getnormalized()
736 h = h.getnormalized()
737
737
738 # fast case first, no offsets, no fuzz
738 # fast case first, no offsets, no fuzz
739 old, oldstart, new, newstart = h.fuzzit(0, False)
739 old, oldstart, new, newstart = h.fuzzit(0, False)
740 oldstart += self.offset
740 oldstart += self.offset
741 orig_start = oldstart
741 orig_start = oldstart
742 # if there's skew we want to emit the "(offset %d lines)" even
742 # if there's skew we want to emit the "(offset %d lines)" even
743 # when the hunk cleanly applies at start + skew, so skip the
743 # when the hunk cleanly applies at start + skew, so skip the
744 # fast case code
744 # fast case code
745 if (self.skew == 0 and
745 if (self.skew == 0 and
746 diffhelpers.testhunk(old, self.lines, oldstart) == 0):
746 diffhelpers.testhunk(old, self.lines, oldstart) == 0):
747 if self.remove:
747 if self.remove:
748 self.backend.unlink(self.fname)
748 self.backend.unlink(self.fname)
749 else:
749 else:
750 self.lines[oldstart:oldstart + len(old)] = new
750 self.lines[oldstart:oldstart + len(old)] = new
751 self.offset += len(new) - len(old)
751 self.offset += len(new) - len(old)
752 self.dirty = True
752 self.dirty = True
753 return 0
753 return 0
754
754
755 # ok, we couldn't match the hunk. Lets look for offsets and fuzz it
755 # ok, we couldn't match the hunk. Lets look for offsets and fuzz it
756 self.hash = {}
756 self.hash = {}
757 for x, s in enumerate(self.lines):
757 for x, s in enumerate(self.lines):
758 self.hash.setdefault(s, []).append(x)
758 self.hash.setdefault(s, []).append(x)
759
759
760 for fuzzlen in xrange(3):
760 for fuzzlen in xrange(3):
761 for toponly in [True, False]:
761 for toponly in [True, False]:
762 old, oldstart, new, newstart = h.fuzzit(fuzzlen, toponly)
762 old, oldstart, new, newstart = h.fuzzit(fuzzlen, toponly)
763 oldstart = oldstart + self.offset + self.skew
763 oldstart = oldstart + self.offset + self.skew
764 oldstart = min(oldstart, len(self.lines))
764 oldstart = min(oldstart, len(self.lines))
765 if old:
765 if old:
766 cand = self.findlines(old[0][1:], oldstart)
766 cand = self.findlines(old[0][1:], oldstart)
767 else:
767 else:
768 # Only adding lines with no or fuzzed context, just
768 # Only adding lines with no or fuzzed context, just
769 # take the skew in account
769 # take the skew in account
770 cand = [oldstart]
770 cand = [oldstart]
771
771
772 for l in cand:
772 for l in cand:
773 if not old or diffhelpers.testhunk(old, self.lines, l) == 0:
773 if not old or diffhelpers.testhunk(old, self.lines, l) == 0:
774 self.lines[l : l + len(old)] = new
774 self.lines[l : l + len(old)] = new
775 self.offset += len(new) - len(old)
775 self.offset += len(new) - len(old)
776 self.skew = l - orig_start
776 self.skew = l - orig_start
777 self.dirty = True
777 self.dirty = True
778 offset = l - orig_start - fuzzlen
778 offset = l - orig_start - fuzzlen
779 if fuzzlen:
779 if fuzzlen:
780 msg = _("Hunk #%d succeeded at %d "
780 msg = _("Hunk #%d succeeded at %d "
781 "with fuzz %d "
781 "with fuzz %d "
782 "(offset %d lines).\n")
782 "(offset %d lines).\n")
783 self.printfile(True)
783 self.printfile(True)
784 self.ui.warn(msg %
784 self.ui.warn(msg %
785 (h.number, l + 1, fuzzlen, offset))
785 (h.number, l + 1, fuzzlen, offset))
786 else:
786 else:
787 msg = _("Hunk #%d succeeded at %d "
787 msg = _("Hunk #%d succeeded at %d "
788 "(offset %d lines).\n")
788 "(offset %d lines).\n")
789 self.ui.note(msg % (h.number, l + 1, offset))
789 self.ui.note(msg % (h.number, l + 1, offset))
790 return fuzzlen
790 return fuzzlen
791 self.printfile(True)
791 self.printfile(True)
792 self.ui.warn(_("Hunk #%d FAILED at %d\n") % (h.number, orig_start))
792 self.ui.warn(_("Hunk #%d FAILED at %d\n") % (h.number, orig_start))
793 self.rej.append(horig)
793 self.rej.append(horig)
794 return -1
794 return -1
795
795
796 def close(self):
796 def close(self):
797 if self.dirty:
797 if self.dirty:
798 self.writelines(self.fname, self.lines, self.mode)
798 self.writelines(self.fname, self.lines, self.mode)
799 self.write_rej()
799 self.write_rej()
800 return len(self.rej)
800 return len(self.rej)
801
801
802 class hunk(object):
802 class hunk(object):
803 def __init__(self, desc, num, lr, context):
803 def __init__(self, desc, num, lr, context):
804 self.number = num
804 self.number = num
805 self.desc = desc
805 self.desc = desc
806 self.hunk = [desc]
806 self.hunk = [desc]
807 self.a = []
807 self.a = []
808 self.b = []
808 self.b = []
809 self.starta = self.lena = None
809 self.starta = self.lena = None
810 self.startb = self.lenb = None
810 self.startb = self.lenb = None
811 if lr is not None:
811 if lr is not None:
812 if context:
812 if context:
813 self.read_context_hunk(lr)
813 self.read_context_hunk(lr)
814 else:
814 else:
815 self.read_unified_hunk(lr)
815 self.read_unified_hunk(lr)
816
816
817 def getnormalized(self):
817 def getnormalized(self):
818 """Return a copy with line endings normalized to LF."""
818 """Return a copy with line endings normalized to LF."""
819
819
820 def normalize(lines):
820 def normalize(lines):
821 nlines = []
821 nlines = []
822 for line in lines:
822 for line in lines:
823 if line.endswith('\r\n'):
823 if line.endswith('\r\n'):
824 line = line[:-2] + '\n'
824 line = line[:-2] + '\n'
825 nlines.append(line)
825 nlines.append(line)
826 return nlines
826 return nlines
827
827
828 # Dummy object, it is rebuilt manually
828 # Dummy object, it is rebuilt manually
829 nh = hunk(self.desc, self.number, None, None)
829 nh = hunk(self.desc, self.number, None, None)
830 nh.number = self.number
830 nh.number = self.number
831 nh.desc = self.desc
831 nh.desc = self.desc
832 nh.hunk = self.hunk
832 nh.hunk = self.hunk
833 nh.a = normalize(self.a)
833 nh.a = normalize(self.a)
834 nh.b = normalize(self.b)
834 nh.b = normalize(self.b)
835 nh.starta = self.starta
835 nh.starta = self.starta
836 nh.startb = self.startb
836 nh.startb = self.startb
837 nh.lena = self.lena
837 nh.lena = self.lena
838 nh.lenb = self.lenb
838 nh.lenb = self.lenb
839 return nh
839 return nh
840
840
841 def read_unified_hunk(self, lr):
841 def read_unified_hunk(self, lr):
842 m = unidesc.match(self.desc)
842 m = unidesc.match(self.desc)
843 if not m:
843 if not m:
844 raise PatchError(_("bad hunk #%d") % self.number)
844 raise PatchError(_("bad hunk #%d") % self.number)
845 self.starta, self.lena, self.startb, self.lenb = m.groups()
845 self.starta, self.lena, self.startb, self.lenb = m.groups()
846 if self.lena is None:
846 if self.lena is None:
847 self.lena = 1
847 self.lena = 1
848 else:
848 else:
849 self.lena = int(self.lena)
849 self.lena = int(self.lena)
850 if self.lenb is None:
850 if self.lenb is None:
851 self.lenb = 1
851 self.lenb = 1
852 else:
852 else:
853 self.lenb = int(self.lenb)
853 self.lenb = int(self.lenb)
854 self.starta = int(self.starta)
854 self.starta = int(self.starta)
855 self.startb = int(self.startb)
855 self.startb = int(self.startb)
856 diffhelpers.addlines(lr, self.hunk, self.lena, self.lenb, self.a,
856 diffhelpers.addlines(lr, self.hunk, self.lena, self.lenb, self.a,
857 self.b)
857 self.b)
858 # if we hit eof before finishing out the hunk, the last line will
858 # if we hit eof before finishing out the hunk, the last line will
859 # be zero length. Lets try to fix it up.
859 # be zero length. Lets try to fix it up.
860 while len(self.hunk[-1]) == 0:
860 while len(self.hunk[-1]) == 0:
861 del self.hunk[-1]
861 del self.hunk[-1]
862 del self.a[-1]
862 del self.a[-1]
863 del self.b[-1]
863 del self.b[-1]
864 self.lena -= 1
864 self.lena -= 1
865 self.lenb -= 1
865 self.lenb -= 1
866 self._fixnewline(lr)
866 self._fixnewline(lr)
867
867
868 def read_context_hunk(self, lr):
868 def read_context_hunk(self, lr):
869 self.desc = lr.readline()
869 self.desc = lr.readline()
870 m = contextdesc.match(self.desc)
870 m = contextdesc.match(self.desc)
871 if not m:
871 if not m:
872 raise PatchError(_("bad hunk #%d") % self.number)
872 raise PatchError(_("bad hunk #%d") % self.number)
873 self.starta, aend = m.groups()
873 self.starta, aend = m.groups()
874 self.starta = int(self.starta)
874 self.starta = int(self.starta)
875 if aend is None:
875 if aend is None:
876 aend = self.starta
876 aend = self.starta
877 self.lena = int(aend) - self.starta
877 self.lena = int(aend) - self.starta
878 if self.starta:
878 if self.starta:
879 self.lena += 1
879 self.lena += 1
880 for x in xrange(self.lena):
880 for x in xrange(self.lena):
881 l = lr.readline()
881 l = lr.readline()
882 if l.startswith('---'):
882 if l.startswith('---'):
883 # lines addition, old block is empty
883 # lines addition, old block is empty
884 lr.push(l)
884 lr.push(l)
885 break
885 break
886 s = l[2:]
886 s = l[2:]
887 if l.startswith('- ') or l.startswith('! '):
887 if l.startswith('- ') or l.startswith('! '):
888 u = '-' + s
888 u = '-' + s
889 elif l.startswith(' '):
889 elif l.startswith(' '):
890 u = ' ' + s
890 u = ' ' + s
891 else:
891 else:
892 raise PatchError(_("bad hunk #%d old text line %d") %
892 raise PatchError(_("bad hunk #%d old text line %d") %
893 (self.number, x))
893 (self.number, x))
894 self.a.append(u)
894 self.a.append(u)
895 self.hunk.append(u)
895 self.hunk.append(u)
896
896
897 l = lr.readline()
897 l = lr.readline()
898 if l.startswith('\ '):
898 if l.startswith('\ '):
899 s = self.a[-1][:-1]
899 s = self.a[-1][:-1]
900 self.a[-1] = s
900 self.a[-1] = s
901 self.hunk[-1] = s
901 self.hunk[-1] = s
902 l = lr.readline()
902 l = lr.readline()
903 m = contextdesc.match(l)
903 m = contextdesc.match(l)
904 if not m:
904 if not m:
905 raise PatchError(_("bad hunk #%d") % self.number)
905 raise PatchError(_("bad hunk #%d") % self.number)
906 self.startb, bend = m.groups()
906 self.startb, bend = m.groups()
907 self.startb = int(self.startb)
907 self.startb = int(self.startb)
908 if bend is None:
908 if bend is None:
909 bend = self.startb
909 bend = self.startb
910 self.lenb = int(bend) - self.startb
910 self.lenb = int(bend) - self.startb
911 if self.startb:
911 if self.startb:
912 self.lenb += 1
912 self.lenb += 1
913 hunki = 1
913 hunki = 1
914 for x in xrange(self.lenb):
914 for x in xrange(self.lenb):
915 l = lr.readline()
915 l = lr.readline()
916 if l.startswith('\ '):
916 if l.startswith('\ '):
917 # XXX: the only way to hit this is with an invalid line range.
917 # XXX: the only way to hit this is with an invalid line range.
918 # The no-eol marker is not counted in the line range, but I
918 # The no-eol marker is not counted in the line range, but I
919 # guess there are diff(1) out there which behave differently.
919 # guess there are diff(1) out there which behave differently.
920 s = self.b[-1][:-1]
920 s = self.b[-1][:-1]
921 self.b[-1] = s
921 self.b[-1] = s
922 self.hunk[hunki - 1] = s
922 self.hunk[hunki - 1] = s
923 continue
923 continue
924 if not l:
924 if not l:
925 # line deletions, new block is empty and we hit EOF
925 # line deletions, new block is empty and we hit EOF
926 lr.push(l)
926 lr.push(l)
927 break
927 break
928 s = l[2:]
928 s = l[2:]
929 if l.startswith('+ ') or l.startswith('! '):
929 if l.startswith('+ ') or l.startswith('! '):
930 u = '+' + s
930 u = '+' + s
931 elif l.startswith(' '):
931 elif l.startswith(' '):
932 u = ' ' + s
932 u = ' ' + s
933 elif len(self.b) == 0:
933 elif len(self.b) == 0:
934 # line deletions, new block is empty
934 # line deletions, new block is empty
935 lr.push(l)
935 lr.push(l)
936 break
936 break
937 else:
937 else:
938 raise PatchError(_("bad hunk #%d old text line %d") %
938 raise PatchError(_("bad hunk #%d old text line %d") %
939 (self.number, x))
939 (self.number, x))
940 self.b.append(s)
940 self.b.append(s)
941 while True:
941 while True:
942 if hunki >= len(self.hunk):
942 if hunki >= len(self.hunk):
943 h = ""
943 h = ""
944 else:
944 else:
945 h = self.hunk[hunki]
945 h = self.hunk[hunki]
946 hunki += 1
946 hunki += 1
947 if h == u:
947 if h == u:
948 break
948 break
949 elif h.startswith('-'):
949 elif h.startswith('-'):
950 continue
950 continue
951 else:
951 else:
952 self.hunk.insert(hunki - 1, u)
952 self.hunk.insert(hunki - 1, u)
953 break
953 break
954
954
955 if not self.a:
955 if not self.a:
956 # this happens when lines were only added to the hunk
956 # this happens when lines were only added to the hunk
957 for x in self.hunk:
957 for x in self.hunk:
958 if x.startswith('-') or x.startswith(' '):
958 if x.startswith('-') or x.startswith(' '):
959 self.a.append(x)
959 self.a.append(x)
960 if not self.b:
960 if not self.b:
961 # this happens when lines were only deleted from the hunk
961 # this happens when lines were only deleted from the hunk
962 for x in self.hunk:
962 for x in self.hunk:
963 if x.startswith('+') or x.startswith(' '):
963 if x.startswith('+') or x.startswith(' '):
964 self.b.append(x[1:])
964 self.b.append(x[1:])
965 # @@ -start,len +start,len @@
965 # @@ -start,len +start,len @@
966 self.desc = "@@ -%d,%d +%d,%d @@\n" % (self.starta, self.lena,
966 self.desc = "@@ -%d,%d +%d,%d @@\n" % (self.starta, self.lena,
967 self.startb, self.lenb)
967 self.startb, self.lenb)
968 self.hunk[0] = self.desc
968 self.hunk[0] = self.desc
969 self._fixnewline(lr)
969 self._fixnewline(lr)
970
970
971 def _fixnewline(self, lr):
971 def _fixnewline(self, lr):
972 l = lr.readline()
972 l = lr.readline()
973 if l.startswith('\ '):
973 if l.startswith('\ '):
974 diffhelpers.fix_newline(self.hunk, self.a, self.b)
974 diffhelpers.fix_newline(self.hunk, self.a, self.b)
975 else:
975 else:
976 lr.push(l)
976 lr.push(l)
977
977
978 def complete(self):
978 def complete(self):
979 return len(self.a) == self.lena and len(self.b) == self.lenb
979 return len(self.a) == self.lena and len(self.b) == self.lenb
980
980
981 def _fuzzit(self, old, new, fuzz, toponly):
981 def _fuzzit(self, old, new, fuzz, toponly):
982 # this removes context lines from the top and bottom of list 'l'. It
982 # this removes context lines from the top and bottom of list 'l'. It
983 # checks the hunk to make sure only context lines are removed, and then
983 # checks the hunk to make sure only context lines are removed, and then
984 # returns a new shortened list of lines.
984 # returns a new shortened list of lines.
985 fuzz = min(fuzz, len(old))
985 fuzz = min(fuzz, len(old))
986 if fuzz:
986 if fuzz:
987 top = 0
987 top = 0
988 bot = 0
988 bot = 0
989 hlen = len(self.hunk)
989 hlen = len(self.hunk)
990 for x in xrange(hlen - 1):
990 for x in xrange(hlen - 1):
991 # the hunk starts with the @@ line, so use x+1
991 # the hunk starts with the @@ line, so use x+1
992 if self.hunk[x + 1][0] == ' ':
992 if self.hunk[x + 1][0] == ' ':
993 top += 1
993 top += 1
994 else:
994 else:
995 break
995 break
996 if not toponly:
996 if not toponly:
997 for x in xrange(hlen - 1):
997 for x in xrange(hlen - 1):
998 if self.hunk[hlen - bot - 1][0] == ' ':
998 if self.hunk[hlen - bot - 1][0] == ' ':
999 bot += 1
999 bot += 1
1000 else:
1000 else:
1001 break
1001 break
1002
1002
1003 bot = min(fuzz, bot)
1003 bot = min(fuzz, bot)
1004 top = min(fuzz, top)
1004 top = min(fuzz, top)
1005 return old[top:len(old) - bot], new[top:len(new) - bot], top
1005 return old[top:len(old) - bot], new[top:len(new) - bot], top
1006 return old, new, 0
1006 return old, new, 0
1007
1007
1008 def fuzzit(self, fuzz, toponly):
1008 def fuzzit(self, fuzz, toponly):
1009 old, new, top = self._fuzzit(self.a, self.b, fuzz, toponly)
1009 old, new, top = self._fuzzit(self.a, self.b, fuzz, toponly)
1010 oldstart = self.starta + top
1010 oldstart = self.starta + top
1011 newstart = self.startb + top
1011 newstart = self.startb + top
1012 # zero length hunk ranges already have their start decremented
1012 # zero length hunk ranges already have their start decremented
1013 if self.lena and oldstart > 0:
1013 if self.lena and oldstart > 0:
1014 oldstart -= 1
1014 oldstart -= 1
1015 if self.lenb and newstart > 0:
1015 if self.lenb and newstart > 0:
1016 newstart -= 1
1016 newstart -= 1
1017 return old, oldstart, new, newstart
1017 return old, oldstart, new, newstart
1018
1018
1019 class binhunk(object):
1019 class binhunk(object):
1020 'A binary patch file.'
1020 'A binary patch file.'
1021 def __init__(self, lr, fname):
1021 def __init__(self, lr, fname):
1022 self.text = None
1022 self.text = None
1023 self.delta = False
1023 self.delta = False
1024 self.hunk = ['GIT binary patch\n']
1024 self.hunk = ['GIT binary patch\n']
1025 self._fname = fname
1025 self._fname = fname
1026 self._read(lr)
1026 self._read(lr)
1027
1027
1028 def complete(self):
1028 def complete(self):
1029 return self.text is not None
1029 return self.text is not None
1030
1030
1031 def new(self, lines):
1031 def new(self, lines):
1032 if self.delta:
1032 if self.delta:
1033 return [applybindelta(self.text, ''.join(lines))]
1033 return [applybindelta(self.text, ''.join(lines))]
1034 return [self.text]
1034 return [self.text]
1035
1035
1036 def _read(self, lr):
1036 def _read(self, lr):
1037 def getline(lr, hunk):
1037 def getline(lr, hunk):
1038 l = lr.readline()
1038 l = lr.readline()
1039 hunk.append(l)
1039 hunk.append(l)
1040 return l.rstrip('\r\n')
1040 return l.rstrip('\r\n')
1041
1041
1042 size = 0
1042 size = 0
1043 while True:
1043 while True:
1044 line = getline(lr, self.hunk)
1044 line = getline(lr, self.hunk)
1045 if not line:
1045 if not line:
1046 raise PatchError(_('could not extract "%s" binary data')
1046 raise PatchError(_('could not extract "%s" binary data')
1047 % self._fname)
1047 % self._fname)
1048 if line.startswith('literal '):
1048 if line.startswith('literal '):
1049 size = int(line[8:].rstrip())
1049 size = int(line[8:].rstrip())
1050 break
1050 break
1051 if line.startswith('delta '):
1051 if line.startswith('delta '):
1052 size = int(line[6:].rstrip())
1052 size = int(line[6:].rstrip())
1053 self.delta = True
1053 self.delta = True
1054 break
1054 break
1055 dec = []
1055 dec = []
1056 line = getline(lr, self.hunk)
1056 line = getline(lr, self.hunk)
1057 while len(line) > 1:
1057 while len(line) > 1:
1058 l = line[0]
1058 l = line[0]
1059 if l <= 'Z' and l >= 'A':
1059 if l <= 'Z' and l >= 'A':
1060 l = ord(l) - ord('A') + 1
1060 l = ord(l) - ord('A') + 1
1061 else:
1061 else:
1062 l = ord(l) - ord('a') + 27
1062 l = ord(l) - ord('a') + 27
1063 try:
1063 try:
1064 dec.append(base85.b85decode(line[1:])[:l])
1064 dec.append(base85.b85decode(line[1:])[:l])
1065 except ValueError, e:
1065 except ValueError, e:
1066 raise PatchError(_('could not decode "%s" binary patch: %s')
1066 raise PatchError(_('could not decode "%s" binary patch: %s')
1067 % (self._fname, str(e)))
1067 % (self._fname, str(e)))
1068 line = getline(lr, self.hunk)
1068 line = getline(lr, self.hunk)
1069 text = zlib.decompress(''.join(dec))
1069 text = zlib.decompress(''.join(dec))
1070 if len(text) != size:
1070 if len(text) != size:
1071 raise PatchError(_('"%s" length is %d bytes, should be %d')
1071 raise PatchError(_('"%s" length is %d bytes, should be %d')
1072 % (self._fname, len(text), size))
1072 % (self._fname, len(text), size))
1073 self.text = text
1073 self.text = text
1074
1074
1075 def parsefilename(str):
1075 def parsefilename(str):
1076 # --- filename \t|space stuff
1076 # --- filename \t|space stuff
1077 s = str[4:].rstrip('\r\n')
1077 s = str[4:].rstrip('\r\n')
1078 i = s.find('\t')
1078 i = s.find('\t')
1079 if i < 0:
1079 if i < 0:
1080 i = s.find(' ')
1080 i = s.find(' ')
1081 if i < 0:
1081 if i < 0:
1082 return s
1082 return s
1083 return s[:i]
1083 return s[:i]
1084
1084
1085 def pathstrip(path, strip):
1085 def pathstrip(path, strip):
1086 pathlen = len(path)
1086 pathlen = len(path)
1087 i = 0
1087 i = 0
1088 if strip == 0:
1088 if strip == 0:
1089 return '', path.rstrip()
1089 return '', path.rstrip()
1090 count = strip
1090 count = strip
1091 while count > 0:
1091 while count > 0:
1092 i = path.find('/', i)
1092 i = path.find('/', i)
1093 if i == -1:
1093 if i == -1:
1094 raise PatchError(_("unable to strip away %d of %d dirs from %s") %
1094 raise PatchError(_("unable to strip away %d of %d dirs from %s") %
1095 (count, strip, path))
1095 (count, strip, path))
1096 i += 1
1096 i += 1
1097 # consume '//' in the path
1097 # consume '//' in the path
1098 while i < pathlen - 1 and path[i] == '/':
1098 while i < pathlen - 1 and path[i] == '/':
1099 i += 1
1099 i += 1
1100 count -= 1
1100 count -= 1
1101 return path[:i].lstrip(), path[i:].rstrip()
1101 return path[:i].lstrip(), path[i:].rstrip()
1102
1102
1103 def makepatchmeta(backend, afile_orig, bfile_orig, hunk, strip):
1103 def makepatchmeta(backend, afile_orig, bfile_orig, hunk, strip):
1104 nulla = afile_orig == "/dev/null"
1104 nulla = afile_orig == "/dev/null"
1105 nullb = bfile_orig == "/dev/null"
1105 nullb = bfile_orig == "/dev/null"
1106 create = nulla and hunk.starta == 0 and hunk.lena == 0
1106 create = nulla and hunk.starta == 0 and hunk.lena == 0
1107 remove = nullb and hunk.startb == 0 and hunk.lenb == 0
1107 remove = nullb and hunk.startb == 0 and hunk.lenb == 0
1108 abase, afile = pathstrip(afile_orig, strip)
1108 abase, afile = pathstrip(afile_orig, strip)
1109 gooda = not nulla and backend.exists(afile)
1109 gooda = not nulla and backend.exists(afile)
1110 bbase, bfile = pathstrip(bfile_orig, strip)
1110 bbase, bfile = pathstrip(bfile_orig, strip)
1111 if afile == bfile:
1111 if afile == bfile:
1112 goodb = gooda
1112 goodb = gooda
1113 else:
1113 else:
1114 goodb = not nullb and backend.exists(bfile)
1114 goodb = not nullb and backend.exists(bfile)
1115 missing = not goodb and not gooda and not create
1115 missing = not goodb and not gooda and not create
1116
1116
1117 # some diff programs apparently produce patches where the afile is
1117 # some diff programs apparently produce patches where the afile is
1118 # not /dev/null, but afile starts with bfile
1118 # not /dev/null, but afile starts with bfile
1119 abasedir = afile[:afile.rfind('/') + 1]
1119 abasedir = afile[:afile.rfind('/') + 1]
1120 bbasedir = bfile[:bfile.rfind('/') + 1]
1120 bbasedir = bfile[:bfile.rfind('/') + 1]
1121 if (missing and abasedir == bbasedir and afile.startswith(bfile)
1121 if (missing and abasedir == bbasedir and afile.startswith(bfile)
1122 and hunk.starta == 0 and hunk.lena == 0):
1122 and hunk.starta == 0 and hunk.lena == 0):
1123 create = True
1123 create = True
1124 missing = False
1124 missing = False
1125
1125
1126 # If afile is "a/b/foo" and bfile is "a/b/foo.orig" we assume the
1126 # If afile is "a/b/foo" and bfile is "a/b/foo.orig" we assume the
1127 # diff is between a file and its backup. In this case, the original
1127 # diff is between a file and its backup. In this case, the original
1128 # file should be patched (see original mpatch code).
1128 # file should be patched (see original mpatch code).
1129 isbackup = (abase == bbase and bfile.startswith(afile))
1129 isbackup = (abase == bbase and bfile.startswith(afile))
1130 fname = None
1130 fname = None
1131 if not missing:
1131 if not missing:
1132 if gooda and goodb:
1132 if gooda and goodb:
1133 fname = isbackup and afile or bfile
1133 fname = isbackup and afile or bfile
1134 elif gooda:
1134 elif gooda:
1135 fname = afile
1135 fname = afile
1136
1136
1137 if not fname:
1137 if not fname:
1138 if not nullb:
1138 if not nullb:
1139 fname = isbackup and afile or bfile
1139 fname = isbackup and afile or bfile
1140 elif not nulla:
1140 elif not nulla:
1141 fname = afile
1141 fname = afile
1142 else:
1142 else:
1143 raise PatchError(_("undefined source and destination files"))
1143 raise PatchError(_("undefined source and destination files"))
1144
1144
1145 gp = patchmeta(fname)
1145 gp = patchmeta(fname)
1146 if create:
1146 if create:
1147 gp.op = 'ADD'
1147 gp.op = 'ADD'
1148 elif remove:
1148 elif remove:
1149 gp.op = 'DELETE'
1149 gp.op = 'DELETE'
1150 return gp
1150 return gp
1151
1151
1152 def scangitpatch(lr, firstline):
1152 def scangitpatch(lr, firstline):
1153 """
1153 """
1154 Git patches can emit:
1154 Git patches can emit:
1155 - rename a to b
1155 - rename a to b
1156 - change b
1156 - change b
1157 - copy a to c
1157 - copy a to c
1158 - change c
1158 - change c
1159
1159
1160 We cannot apply this sequence as-is, the renamed 'a' could not be
1160 We cannot apply this sequence as-is, the renamed 'a' could not be
1161 found for it would have been renamed already. And we cannot copy
1161 found for it would have been renamed already. And we cannot copy
1162 from 'b' instead because 'b' would have been changed already. So
1162 from 'b' instead because 'b' would have been changed already. So
1163 we scan the git patch for copy and rename commands so we can
1163 we scan the git patch for copy and rename commands so we can
1164 perform the copies ahead of time.
1164 perform the copies ahead of time.
1165 """
1165 """
1166 pos = 0
1166 pos = 0
1167 try:
1167 try:
1168 pos = lr.fp.tell()
1168 pos = lr.fp.tell()
1169 fp = lr.fp
1169 fp = lr.fp
1170 except IOError:
1170 except IOError:
1171 fp = cStringIO.StringIO(lr.fp.read())
1171 fp = cStringIO.StringIO(lr.fp.read())
1172 gitlr = linereader(fp)
1172 gitlr = linereader(fp)
1173 gitlr.push(firstline)
1173 gitlr.push(firstline)
1174 gitpatches = readgitpatch(gitlr)
1174 gitpatches = readgitpatch(gitlr)
1175 fp.seek(pos)
1175 fp.seek(pos)
1176 return gitpatches
1176 return gitpatches
1177
1177
1178 def iterhunks(fp):
1178 def iterhunks(fp):
1179 """Read a patch and yield the following events:
1179 """Read a patch and yield the following events:
1180 - ("file", afile, bfile, firsthunk): select a new target file.
1180 - ("file", afile, bfile, firsthunk): select a new target file.
1181 - ("hunk", hunk): a new hunk is ready to be applied, follows a
1181 - ("hunk", hunk): a new hunk is ready to be applied, follows a
1182 "file" event.
1182 "file" event.
1183 - ("git", gitchanges): current diff is in git format, gitchanges
1183 - ("git", gitchanges): current diff is in git format, gitchanges
1184 maps filenames to gitpatch records. Unique event.
1184 maps filenames to gitpatch records. Unique event.
1185 """
1185 """
1186 afile = ""
1186 afile = ""
1187 bfile = ""
1187 bfile = ""
1188 state = None
1188 state = None
1189 hunknum = 0
1189 hunknum = 0
1190 emitfile = newfile = False
1190 emitfile = newfile = False
1191 gitpatches = None
1191 gitpatches = None
1192
1192
1193 # our states
1193 # our states
1194 BFILE = 1
1194 BFILE = 1
1195 context = None
1195 context = None
1196 lr = linereader(fp)
1196 lr = linereader(fp)
1197
1197
1198 while True:
1198 while True:
1199 x = lr.readline()
1199 x = lr.readline()
1200 if not x:
1200 if not x:
1201 break
1201 break
1202 if state == BFILE and (
1202 if state == BFILE and (
1203 (not context and x[0] == '@')
1203 (not context and x[0] == '@')
1204 or (context is not False and x.startswith('***************'))
1204 or (context is not False and x.startswith('***************'))
1205 or x.startswith('GIT binary patch')):
1205 or x.startswith('GIT binary patch')):
1206 gp = None
1206 gp = None
1207 if (gitpatches and
1207 if (gitpatches and
1208 gitpatches[-1].ispatching(afile, bfile)):
1208 gitpatches[-1].ispatching(afile, bfile)):
1209 gp = gitpatches.pop()
1209 gp = gitpatches.pop()
1210 if x.startswith('GIT binary patch'):
1210 if x.startswith('GIT binary patch'):
1211 h = binhunk(lr, gp.path)
1211 h = binhunk(lr, gp.path)
1212 else:
1212 else:
1213 if context is None and x.startswith('***************'):
1213 if context is None and x.startswith('***************'):
1214 context = True
1214 context = True
1215 h = hunk(x, hunknum + 1, lr, context)
1215 h = hunk(x, hunknum + 1, lr, context)
1216 hunknum += 1
1216 hunknum += 1
1217 if emitfile:
1217 if emitfile:
1218 emitfile = False
1218 emitfile = False
1219 yield 'file', (afile, bfile, h, gp and gp.copy() or None)
1219 yield 'file', (afile, bfile, h, gp and gp.copy() or None)
1220 yield 'hunk', h
1220 yield 'hunk', h
1221 elif x.startswith('diff --git a/'):
1221 elif x.startswith('diff --git a/'):
1222 m = gitre.match(x.rstrip(' \r\n'))
1222 m = gitre.match(x.rstrip(' \r\n'))
1223 if not m:
1223 if not m:
1224 continue
1224 continue
1225 if gitpatches is None:
1225 if gitpatches is None:
1226 # scan whole input for git metadata
1226 # scan whole input for git metadata
1227 gitpatches = scangitpatch(lr, x)
1227 gitpatches = scangitpatch(lr, x)
1228 yield 'git', [g.copy() for g in gitpatches
1228 yield 'git', [g.copy() for g in gitpatches
1229 if g.op in ('COPY', 'RENAME')]
1229 if g.op in ('COPY', 'RENAME')]
1230 gitpatches.reverse()
1230 gitpatches.reverse()
1231 afile = 'a/' + m.group(1)
1231 afile = 'a/' + m.group(1)
1232 bfile = 'b/' + m.group(2)
1232 bfile = 'b/' + m.group(2)
1233 while gitpatches and not gitpatches[-1].ispatching(afile, bfile):
1233 while gitpatches and not gitpatches[-1].ispatching(afile, bfile):
1234 gp = gitpatches.pop()
1234 gp = gitpatches.pop()
1235 yield 'file', ('a/' + gp.path, 'b/' + gp.path, None, gp.copy())
1235 yield 'file', ('a/' + gp.path, 'b/' + gp.path, None, gp.copy())
1236 if not gitpatches:
1236 if not gitpatches:
1237 raise PatchError(_('failed to synchronize metadata for "%s"')
1237 raise PatchError(_('failed to synchronize metadata for "%s"')
1238 % afile[2:])
1238 % afile[2:])
1239 gp = gitpatches[-1]
1239 gp = gitpatches[-1]
1240 newfile = True
1240 newfile = True
1241 elif x.startswith('---'):
1241 elif x.startswith('---'):
1242 # check for a unified diff
1242 # check for a unified diff
1243 l2 = lr.readline()
1243 l2 = lr.readline()
1244 if not l2.startswith('+++'):
1244 if not l2.startswith('+++'):
1245 lr.push(l2)
1245 lr.push(l2)
1246 continue
1246 continue
1247 newfile = True
1247 newfile = True
1248 context = False
1248 context = False
1249 afile = parsefilename(x)
1249 afile = parsefilename(x)
1250 bfile = parsefilename(l2)
1250 bfile = parsefilename(l2)
1251 elif x.startswith('***'):
1251 elif x.startswith('***'):
1252 # check for a context diff
1252 # check for a context diff
1253 l2 = lr.readline()
1253 l2 = lr.readline()
1254 if not l2.startswith('---'):
1254 if not l2.startswith('---'):
1255 lr.push(l2)
1255 lr.push(l2)
1256 continue
1256 continue
1257 l3 = lr.readline()
1257 l3 = lr.readline()
1258 lr.push(l3)
1258 lr.push(l3)
1259 if not l3.startswith("***************"):
1259 if not l3.startswith("***************"):
1260 lr.push(l2)
1260 lr.push(l2)
1261 continue
1261 continue
1262 newfile = True
1262 newfile = True
1263 context = True
1263 context = True
1264 afile = parsefilename(x)
1264 afile = parsefilename(x)
1265 bfile = parsefilename(l2)
1265 bfile = parsefilename(l2)
1266
1266
1267 if newfile:
1267 if newfile:
1268 newfile = False
1268 newfile = False
1269 emitfile = True
1269 emitfile = True
1270 state = BFILE
1270 state = BFILE
1271 hunknum = 0
1271 hunknum = 0
1272
1272
1273 while gitpatches:
1273 while gitpatches:
1274 gp = gitpatches.pop()
1274 gp = gitpatches.pop()
1275 yield 'file', ('a/' + gp.path, 'b/' + gp.path, None, gp.copy())
1275 yield 'file', ('a/' + gp.path, 'b/' + gp.path, None, gp.copy())
1276
1276
1277 def applybindelta(binchunk, data):
1277 def applybindelta(binchunk, data):
1278 """Apply a binary delta hunk
1278 """Apply a binary delta hunk
1279 The algorithm used is the algorithm from git's patch-delta.c
1279 The algorithm used is the algorithm from git's patch-delta.c
1280 """
1280 """
1281 def deltahead(binchunk):
1281 def deltahead(binchunk):
1282 i = 0
1282 i = 0
1283 for c in binchunk:
1283 for c in binchunk:
1284 i += 1
1284 i += 1
1285 if not (ord(c) & 0x80):
1285 if not (ord(c) & 0x80):
1286 return i
1286 return i
1287 return i
1287 return i
1288 out = ""
1288 out = ""
1289 s = deltahead(binchunk)
1289 s = deltahead(binchunk)
1290 binchunk = binchunk[s:]
1290 binchunk = binchunk[s:]
1291 s = deltahead(binchunk)
1291 s = deltahead(binchunk)
1292 binchunk = binchunk[s:]
1292 binchunk = binchunk[s:]
1293 i = 0
1293 i = 0
1294 while i < len(binchunk):
1294 while i < len(binchunk):
1295 cmd = ord(binchunk[i])
1295 cmd = ord(binchunk[i])
1296 i += 1
1296 i += 1
1297 if (cmd & 0x80):
1297 if (cmd & 0x80):
1298 offset = 0
1298 offset = 0
1299 size = 0
1299 size = 0
1300 if (cmd & 0x01):
1300 if (cmd & 0x01):
1301 offset = ord(binchunk[i])
1301 offset = ord(binchunk[i])
1302 i += 1
1302 i += 1
1303 if (cmd & 0x02):
1303 if (cmd & 0x02):
1304 offset |= ord(binchunk[i]) << 8
1304 offset |= ord(binchunk[i]) << 8
1305 i += 1
1305 i += 1
1306 if (cmd & 0x04):
1306 if (cmd & 0x04):
1307 offset |= ord(binchunk[i]) << 16
1307 offset |= ord(binchunk[i]) << 16
1308 i += 1
1308 i += 1
1309 if (cmd & 0x08):
1309 if (cmd & 0x08):
1310 offset |= ord(binchunk[i]) << 24
1310 offset |= ord(binchunk[i]) << 24
1311 i += 1
1311 i += 1
1312 if (cmd & 0x10):
1312 if (cmd & 0x10):
1313 size = ord(binchunk[i])
1313 size = ord(binchunk[i])
1314 i += 1
1314 i += 1
1315 if (cmd & 0x20):
1315 if (cmd & 0x20):
1316 size |= ord(binchunk[i]) << 8
1316 size |= ord(binchunk[i]) << 8
1317 i += 1
1317 i += 1
1318 if (cmd & 0x40):
1318 if (cmd & 0x40):
1319 size |= ord(binchunk[i]) << 16
1319 size |= ord(binchunk[i]) << 16
1320 i += 1
1320 i += 1
1321 if size == 0:
1321 if size == 0:
1322 size = 0x10000
1322 size = 0x10000
1323 offset_end = offset + size
1323 offset_end = offset + size
1324 out += data[offset:offset_end]
1324 out += data[offset:offset_end]
1325 elif cmd != 0:
1325 elif cmd != 0:
1326 offset_end = i + cmd
1326 offset_end = i + cmd
1327 out += binchunk[i:offset_end]
1327 out += binchunk[i:offset_end]
1328 i += cmd
1328 i += cmd
1329 else:
1329 else:
1330 raise PatchError(_('unexpected delta opcode 0'))
1330 raise PatchError(_('unexpected delta opcode 0'))
1331 return out
1331 return out
1332
1332
1333 def applydiff(ui, fp, backend, store, strip=1, eolmode='strict'):
1333 def applydiff(ui, fp, backend, store, strip=1, eolmode='strict'):
1334 """Reads a patch from fp and tries to apply it.
1334 """Reads a patch from fp and tries to apply it.
1335
1335
1336 Returns 0 for a clean patch, -1 if any rejects were found and 1 if
1336 Returns 0 for a clean patch, -1 if any rejects were found and 1 if
1337 there was any fuzz.
1337 there was any fuzz.
1338
1338
1339 If 'eolmode' is 'strict', the patch content and patched file are
1339 If 'eolmode' is 'strict', the patch content and patched file are
1340 read in binary mode. Otherwise, line endings are ignored when
1340 read in binary mode. Otherwise, line endings are ignored when
1341 patching then normalized according to 'eolmode'.
1341 patching then normalized according to 'eolmode'.
1342 """
1342 """
1343 return _applydiff(ui, fp, patchfile, backend, store, strip=strip,
1343 return _applydiff(ui, fp, patchfile, backend, store, strip=strip,
1344 eolmode=eolmode)
1344 eolmode=eolmode)
1345
1345
1346 def _applydiff(ui, fp, patcher, backend, store, strip=1,
1346 def _applydiff(ui, fp, patcher, backend, store, strip=1,
1347 eolmode='strict'):
1347 eolmode='strict'):
1348
1348
1349 def pstrip(p):
1349 def pstrip(p):
1350 return pathstrip(p, strip - 1)[1]
1350 return pathstrip(p, strip - 1)[1]
1351
1351
1352 rejects = 0
1352 rejects = 0
1353 err = 0
1353 err = 0
1354 current_file = None
1354 current_file = None
1355
1355
1356 for state, values in iterhunks(fp):
1356 for state, values in iterhunks(fp):
1357 if state == 'hunk':
1357 if state == 'hunk':
1358 if not current_file:
1358 if not current_file:
1359 continue
1359 continue
1360 ret = current_file.apply(values)
1360 ret = current_file.apply(values)
1361 if ret > 0:
1361 if ret > 0:
1362 err = 1
1362 err = 1
1363 elif state == 'file':
1363 elif state == 'file':
1364 if current_file:
1364 if current_file:
1365 rejects += current_file.close()
1365 rejects += current_file.close()
1366 current_file = None
1366 current_file = None
1367 afile, bfile, first_hunk, gp = values
1367 afile, bfile, first_hunk, gp = values
1368 if gp:
1368 if gp:
1369 gp.path = pstrip(gp.path)
1369 gp.path = pstrip(gp.path)
1370 if gp.oldpath:
1370 if gp.oldpath:
1371 gp.oldpath = pstrip(gp.oldpath)
1371 gp.oldpath = pstrip(gp.oldpath)
1372 else:
1372 else:
1373 gp = makepatchmeta(backend, afile, bfile, first_hunk, strip)
1373 gp = makepatchmeta(backend, afile, bfile, first_hunk, strip)
1374 if gp.op == 'RENAME':
1374 if gp.op == 'RENAME':
1375 backend.unlink(gp.oldpath)
1375 backend.unlink(gp.oldpath)
1376 if not first_hunk:
1376 if not first_hunk:
1377 if gp.op == 'DELETE':
1377 if gp.op == 'DELETE':
1378 backend.unlink(gp.path)
1378 backend.unlink(gp.path)
1379 continue
1379 continue
1380 data, mode = None, None
1380 data, mode = None, None
1381 if gp.op in ('RENAME', 'COPY'):
1381 if gp.op in ('RENAME', 'COPY'):
1382 data, mode = store.getfile(gp.oldpath)[:2]
1382 data, mode = store.getfile(gp.oldpath)[:2]
1383 if gp.mode:
1383 if gp.mode:
1384 mode = gp.mode
1384 mode = gp.mode
1385 if gp.op == 'ADD':
1385 if gp.op == 'ADD':
1386 # Added files without content have no hunk and
1386 # Added files without content have no hunk and
1387 # must be created
1387 # must be created
1388 data = ''
1388 data = ''
1389 if data or mode:
1389 if data or mode:
1390 if (gp.op in ('ADD', 'RENAME', 'COPY')
1390 if (gp.op in ('ADD', 'RENAME', 'COPY')
1391 and backend.exists(gp.path)):
1391 and backend.exists(gp.path)):
1392 raise PatchError(_("cannot create %s: destination "
1392 raise PatchError(_("cannot create %s: destination "
1393 "already exists") % gp.path)
1393 "already exists") % gp.path)
1394 backend.setfile(gp.path, data, mode, gp.oldpath)
1394 backend.setfile(gp.path, data, mode, gp.oldpath)
1395 continue
1395 continue
1396 try:
1396 try:
1397 current_file = patcher(ui, gp, backend, store,
1397 current_file = patcher(ui, gp, backend, store,
1398 eolmode=eolmode)
1398 eolmode=eolmode)
1399 except PatchError, inst:
1399 except PatchError, inst:
1400 ui.warn(str(inst) + '\n')
1400 ui.warn(str(inst) + '\n')
1401 current_file = None
1401 current_file = None
1402 rejects += 1
1402 rejects += 1
1403 continue
1403 continue
1404 elif state == 'git':
1404 elif state == 'git':
1405 for gp in values:
1405 for gp in values:
1406 path = pstrip(gp.oldpath)
1406 path = pstrip(gp.oldpath)
1407 try:
1407 try:
1408 data, mode = backend.getfile(path)
1408 data, mode = backend.getfile(path)
1409 except IOError, e:
1409 except IOError, e:
1410 if e.errno != errno.ENOENT:
1410 if e.errno != errno.ENOENT:
1411 raise
1411 raise
1412 # The error ignored here will trigger a getfile()
1412 # The error ignored here will trigger a getfile()
1413 # error in a place more appropriate for error
1413 # error in a place more appropriate for error
1414 # handling, and will not interrupt the patching
1414 # handling, and will not interrupt the patching
1415 # process.
1415 # process.
1416 else:
1416 else:
1417 store.setfile(path, data, mode)
1417 store.setfile(path, data, mode)
1418 else:
1418 else:
1419 raise util.Abort(_('unsupported parser state: %s') % state)
1419 raise util.Abort(_('unsupported parser state: %s') % state)
1420
1420
1421 if current_file:
1421 if current_file:
1422 rejects += current_file.close()
1422 rejects += current_file.close()
1423
1423
1424 if rejects:
1424 if rejects:
1425 return -1
1425 return -1
1426 return err
1426 return err
1427
1427
1428 def _externalpatch(ui, repo, patcher, patchname, strip, files,
1428 def _externalpatch(ui, repo, patcher, patchname, strip, files,
1429 similarity):
1429 similarity):
1430 """use <patcher> to apply <patchname> to the working directory.
1430 """use <patcher> to apply <patchname> to the working directory.
1431 returns whether patch was applied with fuzz factor."""
1431 returns whether patch was applied with fuzz factor."""
1432
1432
1433 fuzz = False
1433 fuzz = False
1434 args = []
1434 args = []
1435 cwd = repo.root
1435 cwd = repo.root
1436 if cwd:
1436 if cwd:
1437 args.append('-d %s' % util.shellquote(cwd))
1437 args.append('-d %s' % util.shellquote(cwd))
1438 fp = util.popen('%s %s -p%d < %s' % (patcher, ' '.join(args), strip,
1438 fp = util.popen('%s %s -p%d < %s' % (patcher, ' '.join(args), strip,
1439 util.shellquote(patchname)))
1439 util.shellquote(patchname)))
1440 try:
1440 try:
1441 for line in fp:
1441 for line in fp:
1442 line = line.rstrip()
1442 line = line.rstrip()
1443 ui.note(line + '\n')
1443 ui.note(line + '\n')
1444 if line.startswith('patching file '):
1444 if line.startswith('patching file '):
1445 pf = util.parsepatchoutput(line)
1445 pf = util.parsepatchoutput(line)
1446 printed_file = False
1446 printed_file = False
1447 files.add(pf)
1447 files.add(pf)
1448 elif line.find('with fuzz') >= 0:
1448 elif line.find('with fuzz') >= 0:
1449 fuzz = True
1449 fuzz = True
1450 if not printed_file:
1450 if not printed_file:
1451 ui.warn(pf + '\n')
1451 ui.warn(pf + '\n')
1452 printed_file = True
1452 printed_file = True
1453 ui.warn(line + '\n')
1453 ui.warn(line + '\n')
1454 elif line.find('saving rejects to file') >= 0:
1454 elif line.find('saving rejects to file') >= 0:
1455 ui.warn(line + '\n')
1455 ui.warn(line + '\n')
1456 elif line.find('FAILED') >= 0:
1456 elif line.find('FAILED') >= 0:
1457 if not printed_file:
1457 if not printed_file:
1458 ui.warn(pf + '\n')
1458 ui.warn(pf + '\n')
1459 printed_file = True
1459 printed_file = True
1460 ui.warn(line + '\n')
1460 ui.warn(line + '\n')
1461 finally:
1461 finally:
1462 if files:
1462 if files:
1463 scmutil.marktouched(repo, files, similarity)
1463 scmutil.marktouched(repo, files, similarity)
1464 code = fp.close()
1464 code = fp.close()
1465 if code:
1465 if code:
1466 raise PatchError(_("patch command failed: %s") %
1466 raise PatchError(_("patch command failed: %s") %
1467 util.explainexit(code)[0])
1467 util.explainexit(code)[0])
1468 return fuzz
1468 return fuzz
1469
1469
1470 def patchbackend(ui, backend, patchobj, strip, files=None, eolmode='strict'):
1470 def patchbackend(ui, backend, patchobj, strip, files=None, eolmode='strict'):
1471 if files is None:
1471 if files is None:
1472 files = set()
1472 files = set()
1473 if eolmode is None:
1473 if eolmode is None:
1474 eolmode = ui.config('patch', 'eol', 'strict')
1474 eolmode = ui.config('patch', 'eol', 'strict')
1475 if eolmode.lower() not in eolmodes:
1475 if eolmode.lower() not in eolmodes:
1476 raise util.Abort(_('unsupported line endings type: %s') % eolmode)
1476 raise util.Abort(_('unsupported line endings type: %s') % eolmode)
1477 eolmode = eolmode.lower()
1477 eolmode = eolmode.lower()
1478
1478
1479 store = filestore()
1479 store = filestore()
1480 try:
1480 try:
1481 fp = open(patchobj, 'rb')
1481 fp = open(patchobj, 'rb')
1482 except TypeError:
1482 except TypeError:
1483 fp = patchobj
1483 fp = patchobj
1484 try:
1484 try:
1485 ret = applydiff(ui, fp, backend, store, strip=strip,
1485 ret = applydiff(ui, fp, backend, store, strip=strip,
1486 eolmode=eolmode)
1486 eolmode=eolmode)
1487 finally:
1487 finally:
1488 if fp != patchobj:
1488 if fp != patchobj:
1489 fp.close()
1489 fp.close()
1490 files.update(backend.close())
1490 files.update(backend.close())
1491 store.close()
1491 store.close()
1492 if ret < 0:
1492 if ret < 0:
1493 raise PatchError(_('patch failed to apply'))
1493 raise PatchError(_('patch failed to apply'))
1494 return ret > 0
1494 return ret > 0
1495
1495
1496 def internalpatch(ui, repo, patchobj, strip, files=None, eolmode='strict',
1496 def internalpatch(ui, repo, patchobj, strip, files=None, eolmode='strict',
1497 similarity=0):
1497 similarity=0):
1498 """use builtin patch to apply <patchobj> to the working directory.
1498 """use builtin patch to apply <patchobj> to the working directory.
1499 returns whether patch was applied with fuzz factor."""
1499 returns whether patch was applied with fuzz factor."""
1500 backend = workingbackend(ui, repo, similarity)
1500 backend = workingbackend(ui, repo, similarity)
1501 return patchbackend(ui, backend, patchobj, strip, files, eolmode)
1501 return patchbackend(ui, backend, patchobj, strip, files, eolmode)
1502
1502
1503 def patchrepo(ui, repo, ctx, store, patchobj, strip, files=None,
1503 def patchrepo(ui, repo, ctx, store, patchobj, strip, files=None,
1504 eolmode='strict'):
1504 eolmode='strict'):
1505 backend = repobackend(ui, repo, ctx, store)
1505 backend = repobackend(ui, repo, ctx, store)
1506 return patchbackend(ui, backend, patchobj, strip, files, eolmode)
1506 return patchbackend(ui, backend, patchobj, strip, files, eolmode)
1507
1507
1508 def patch(ui, repo, patchname, strip=1, files=None, eolmode='strict',
1508 def patch(ui, repo, patchname, strip=1, files=None, eolmode='strict',
1509 similarity=0):
1509 similarity=0):
1510 """Apply <patchname> to the working directory.
1510 """Apply <patchname> to the working directory.
1511
1511
1512 'eolmode' specifies how end of lines should be handled. It can be:
1512 'eolmode' specifies how end of lines should be handled. It can be:
1513 - 'strict': inputs are read in binary mode, EOLs are preserved
1513 - 'strict': inputs are read in binary mode, EOLs are preserved
1514 - 'crlf': EOLs are ignored when patching and reset to CRLF
1514 - 'crlf': EOLs are ignored when patching and reset to CRLF
1515 - 'lf': EOLs are ignored when patching and reset to LF
1515 - 'lf': EOLs are ignored when patching and reset to LF
1516 - None: get it from user settings, default to 'strict'
1516 - None: get it from user settings, default to 'strict'
1517 'eolmode' is ignored when using an external patcher program.
1517 'eolmode' is ignored when using an external patcher program.
1518
1518
1519 Returns whether patch was applied with fuzz factor.
1519 Returns whether patch was applied with fuzz factor.
1520 """
1520 """
1521 patcher = ui.config('ui', 'patch')
1521 patcher = ui.config('ui', 'patch')
1522 if files is None:
1522 if files is None:
1523 files = set()
1523 files = set()
1524 try:
1524 try:
1525 if patcher:
1525 if patcher:
1526 return _externalpatch(ui, repo, patcher, patchname, strip,
1526 return _externalpatch(ui, repo, patcher, patchname, strip,
1527 files, similarity)
1527 files, similarity)
1528 return internalpatch(ui, repo, patchname, strip, files, eolmode,
1528 return internalpatch(ui, repo, patchname, strip, files, eolmode,
1529 similarity)
1529 similarity)
1530 except PatchError, err:
1530 except PatchError, err:
1531 raise util.Abort(str(err))
1531 raise util.Abort(str(err))
1532
1532
1533 def changedfiles(ui, repo, patchpath, strip=1):
1533 def changedfiles(ui, repo, patchpath, strip=1):
1534 backend = fsbackend(ui, repo.root)
1534 backend = fsbackend(ui, repo.root)
1535 fp = open(patchpath, 'rb')
1535 fp = open(patchpath, 'rb')
1536 try:
1536 try:
1537 changed = set()
1537 changed = set()
1538 for state, values in iterhunks(fp):
1538 for state, values in iterhunks(fp):
1539 if state == 'file':
1539 if state == 'file':
1540 afile, bfile, first_hunk, gp = values
1540 afile, bfile, first_hunk, gp = values
1541 if gp:
1541 if gp:
1542 gp.path = pathstrip(gp.path, strip - 1)[1]
1542 gp.path = pathstrip(gp.path, strip - 1)[1]
1543 if gp.oldpath:
1543 if gp.oldpath:
1544 gp.oldpath = pathstrip(gp.oldpath, strip - 1)[1]
1544 gp.oldpath = pathstrip(gp.oldpath, strip - 1)[1]
1545 else:
1545 else:
1546 gp = makepatchmeta(backend, afile, bfile, first_hunk, strip)
1546 gp = makepatchmeta(backend, afile, bfile, first_hunk, strip)
1547 changed.add(gp.path)
1547 changed.add(gp.path)
1548 if gp.op == 'RENAME':
1548 if gp.op == 'RENAME':
1549 changed.add(gp.oldpath)
1549 changed.add(gp.oldpath)
1550 elif state not in ('hunk', 'git'):
1550 elif state not in ('hunk', 'git'):
1551 raise util.Abort(_('unsupported parser state: %s') % state)
1551 raise util.Abort(_('unsupported parser state: %s') % state)
1552 return changed
1552 return changed
1553 finally:
1553 finally:
1554 fp.close()
1554 fp.close()
1555
1555
1556 class GitDiffRequired(Exception):
1556 class GitDiffRequired(Exception):
1557 pass
1557 pass
1558
1558
1559 def diffopts(ui, opts=None, untrusted=False, section='diff'):
1559 def diffopts(ui, opts=None, untrusted=False, section='diff'):
1560 def get(key, name=None, getter=ui.configbool):
1560 def get(key, name=None, getter=ui.configbool):
1561 return ((opts and opts.get(key)) or
1561 return ((opts and opts.get(key)) or
1562 getter(section, name or key, None, untrusted=untrusted))
1562 getter(section, name or key, None, untrusted=untrusted))
1563 return mdiff.diffopts(
1563 return mdiff.diffopts(
1564 text=opts and opts.get('text'),
1564 text=opts and opts.get('text'),
1565 git=get('git'),
1565 git=get('git'),
1566 nodates=get('nodates'),
1566 nodates=get('nodates'),
1567 showfunc=get('show_function', 'showfunc'),
1567 showfunc=get('show_function', 'showfunc'),
1568 ignorews=get('ignore_all_space', 'ignorews'),
1568 ignorews=get('ignore_all_space', 'ignorews'),
1569 ignorewsamount=get('ignore_space_change', 'ignorewsamount'),
1569 ignorewsamount=get('ignore_space_change', 'ignorewsamount'),
1570 ignoreblanklines=get('ignore_blank_lines', 'ignoreblanklines'),
1570 ignoreblanklines=get('ignore_blank_lines', 'ignoreblanklines'),
1571 context=get('unified', getter=ui.config))
1571 context=get('unified', getter=ui.config))
1572
1572
1573 def diff(repo, node1=None, node2=None, match=None, changes=None, opts=None,
1573 def diff(repo, node1=None, node2=None, match=None, changes=None, opts=None,
1574 losedatafn=None, prefix=''):
1574 losedatafn=None, prefix=''):
1575 '''yields diff of changes to files between two nodes, or node and
1575 '''yields diff of changes to files between two nodes, or node and
1576 working directory.
1576 working directory.
1577
1577
1578 if node1 is None, use first dirstate parent instead.
1578 if node1 is None, use first dirstate parent instead.
1579 if node2 is None, compare node1 with working directory.
1579 if node2 is None, compare node1 with working directory.
1580
1580
1581 losedatafn(**kwarg) is a callable run when opts.upgrade=True and
1581 losedatafn(**kwarg) is a callable run when opts.upgrade=True and
1582 every time some change cannot be represented with the current
1582 every time some change cannot be represented with the current
1583 patch format. Return False to upgrade to git patch format, True to
1583 patch format. Return False to upgrade to git patch format, True to
1584 accept the loss or raise an exception to abort the diff. It is
1584 accept the loss or raise an exception to abort the diff. It is
1585 called with the name of current file being diffed as 'fn'. If set
1585 called with the name of current file being diffed as 'fn'. If set
1586 to None, patches will always be upgraded to git format when
1586 to None, patches will always be upgraded to git format when
1587 necessary.
1587 necessary.
1588
1588
1589 prefix is a filename prefix that is prepended to all filenames on
1589 prefix is a filename prefix that is prepended to all filenames on
1590 display (used for subrepos).
1590 display (used for subrepos).
1591 '''
1591 '''
1592
1592
1593 if opts is None:
1593 if opts is None:
1594 opts = mdiff.defaultopts
1594 opts = mdiff.defaultopts
1595
1595
1596 if not node1 and not node2:
1596 if not node1 and not node2:
1597 node1 = repo.dirstate.p1()
1597 node1 = repo.dirstate.p1()
1598
1598
1599 def lrugetfilectx():
1599 def lrugetfilectx():
1600 cache = {}
1600 cache = {}
1601 order = util.deque()
1601 order = util.deque()
1602 def getfilectx(f, ctx):
1602 def getfilectx(f, ctx):
1603 fctx = ctx.filectx(f, filelog=cache.get(f))
1603 fctx = ctx.filectx(f, filelog=cache.get(f))
1604 if f not in cache:
1604 if f not in cache:
1605 if len(cache) > 20:
1605 if len(cache) > 20:
1606 del cache[order.popleft()]
1606 del cache[order.popleft()]
1607 cache[f] = fctx.filelog()
1607 cache[f] = fctx.filelog()
1608 else:
1608 else:
1609 order.remove(f)
1609 order.remove(f)
1610 order.append(f)
1610 order.append(f)
1611 return fctx
1611 return fctx
1612 return getfilectx
1612 return getfilectx
1613 getfilectx = lrugetfilectx()
1613 getfilectx = lrugetfilectx()
1614
1614
1615 ctx1 = repo[node1]
1615 ctx1 = repo[node1]
1616 ctx2 = repo[node2]
1616 ctx2 = repo[node2]
1617
1617
1618 if not changes:
1618 if not changes:
1619 changes = repo.status(ctx1, ctx2, match=match)
1619 changes = repo.status(ctx1, ctx2, match=match)
1620 modified, added, removed = changes[:3]
1620 modified, added, removed = changes[:3]
1621
1621
1622 if not modified and not added and not removed:
1622 if not modified and not added and not removed:
1623 return []
1623 return []
1624
1624
1625 revs = None
1625 revs = None
1626 hexfunc = repo.ui.debugflag and hex or short
1626 hexfunc = repo.ui.debugflag and hex or short
1627 revs = [hexfunc(node) for node in [node1, node2] if node]
1627 revs = [hexfunc(node) for node in [node1, node2] if node]
1628
1628
1629 copy = {}
1629 copy = {}
1630 if opts.git or opts.upgrade:
1630 if opts.git or opts.upgrade:
1631 copy = copies.pathcopies(ctx1, ctx2)
1631 copy = copies.pathcopies(ctx1, ctx2)
1632
1632
1633 def difffn(opts, losedata):
1633 def difffn(opts, losedata):
1634 return trydiff(repo, revs, ctx1, ctx2, modified, added, removed,
1634 return trydiff(repo, revs, ctx1, ctx2, modified, added, removed,
1635 copy, getfilectx, opts, losedata, prefix)
1635 copy, getfilectx, opts, losedata, prefix)
1636 if opts.upgrade and not opts.git:
1636 if opts.upgrade and not opts.git:
1637 try:
1637 try:
1638 def losedata(fn):
1638 def losedata(fn):
1639 if not losedatafn or not losedatafn(fn=fn):
1639 if not losedatafn or not losedatafn(fn=fn):
1640 raise GitDiffRequired
1640 raise GitDiffRequired
1641 # Buffer the whole output until we are sure it can be generated
1641 # Buffer the whole output until we are sure it can be generated
1642 return list(difffn(opts.copy(git=False), losedata))
1642 return list(difffn(opts.copy(git=False), losedata))
1643 except GitDiffRequired:
1643 except GitDiffRequired:
1644 return difffn(opts.copy(git=True), None)
1644 return difffn(opts.copy(git=True), None)
1645 else:
1645 else:
1646 return difffn(opts, None)
1646 return difffn(opts, None)
1647
1647
1648 def difflabel(func, *args, **kw):
1648 def difflabel(func, *args, **kw):
1649 '''yields 2-tuples of (output, label) based on the output of func()'''
1649 '''yields 2-tuples of (output, label) based on the output of func()'''
1650 headprefixes = [('diff', 'diff.diffline'),
1650 headprefixes = [('diff', 'diff.diffline'),
1651 ('copy', 'diff.extended'),
1651 ('copy', 'diff.extended'),
1652 ('rename', 'diff.extended'),
1652 ('rename', 'diff.extended'),
1653 ('old', 'diff.extended'),
1653 ('old', 'diff.extended'),
1654 ('new', 'diff.extended'),
1654 ('new', 'diff.extended'),
1655 ('deleted', 'diff.extended'),
1655 ('deleted', 'diff.extended'),
1656 ('---', 'diff.file_a'),
1656 ('---', 'diff.file_a'),
1657 ('+++', 'diff.file_b')]
1657 ('+++', 'diff.file_b')]
1658 textprefixes = [('@', 'diff.hunk'),
1658 textprefixes = [('@', 'diff.hunk'),
1659 ('-', 'diff.deleted'),
1659 ('-', 'diff.deleted'),
1660 ('+', 'diff.inserted')]
1660 ('+', 'diff.inserted')]
1661 head = False
1661 head = False
1662 for chunk in func(*args, **kw):
1662 for chunk in func(*args, **kw):
1663 lines = chunk.split('\n')
1663 lines = chunk.split('\n')
1664 for i, line in enumerate(lines):
1664 for i, line in enumerate(lines):
1665 if i != 0:
1665 if i != 0:
1666 yield ('\n', '')
1666 yield ('\n', '')
1667 if head:
1667 if head:
1668 if line.startswith('@'):
1668 if line.startswith('@'):
1669 head = False
1669 head = False
1670 else:
1670 else:
1671 if line and line[0] not in ' +-@\\':
1671 if line and line[0] not in ' +-@\\':
1672 head = True
1672 head = True
1673 stripline = line
1673 stripline = line
1674 if not head and line and line[0] in '+-':
1674 if not head and line and line[0] in '+-':
1675 # highlight trailing whitespace, but only in changed lines
1675 # highlight trailing whitespace, but only in changed lines
1676 stripline = line.rstrip()
1676 stripline = line.rstrip()
1677 prefixes = textprefixes
1677 prefixes = textprefixes
1678 if head:
1678 if head:
1679 prefixes = headprefixes
1679 prefixes = headprefixes
1680 for prefix, label in prefixes:
1680 for prefix, label in prefixes:
1681 if stripline.startswith(prefix):
1681 if stripline.startswith(prefix):
1682 yield (stripline, label)
1682 yield (stripline, label)
1683 break
1683 break
1684 else:
1684 else:
1685 yield (line, '')
1685 yield (line, '')
1686 if line != stripline:
1686 if line != stripline:
1687 yield (line[len(stripline):], 'diff.trailingwhitespace')
1687 yield (line[len(stripline):], 'diff.trailingwhitespace')
1688
1688
1689 def diffui(*args, **kw):
1689 def diffui(*args, **kw):
1690 '''like diff(), but yields 2-tuples of (output, label) for ui.write()'''
1690 '''like diff(), but yields 2-tuples of (output, label) for ui.write()'''
1691 return difflabel(diff, *args, **kw)
1691 return difflabel(diff, *args, **kw)
1692
1692
1693 def trydiff(repo, revs, ctx1, ctx2, modified, added, removed,
1693 def trydiff(repo, revs, ctx1, ctx2, modified, added, removed,
1694 copy, getfilectx, opts, losedatafn, prefix):
1694 copy, getfilectx, opts, losedatafn, prefix):
1695
1695
1696 def join(f):
1696 def join(f):
1697 return posixpath.join(prefix, f)
1697 return posixpath.join(prefix, f)
1698
1698
1699 def addmodehdr(header, omode, nmode):
1699 def addmodehdr(header, omode, nmode):
1700 if omode != nmode:
1700 if omode != nmode:
1701 header.append('old mode %s\n' % omode)
1701 header.append('old mode %s\n' % omode)
1702 header.append('new mode %s\n' % nmode)
1702 header.append('new mode %s\n' % nmode)
1703
1703
1704 def addindexmeta(meta, revs):
1704 def addindexmeta(meta, revs):
1705 if opts.git:
1705 if opts.git:
1706 i = len(revs)
1706 i = len(revs)
1707 if i==2:
1707 if i==2:
1708 meta.append('index %s..%s\n' % tuple(revs))
1708 meta.append('index %s..%s\n' % tuple(revs))
1709 elif i==3:
1709 elif i==3:
1710 meta.append('index %s,%s..%s\n' % tuple(revs))
1710 meta.append('index %s,%s..%s\n' % tuple(revs))
1711
1711
1712 def gitindex(text):
1712 def gitindex(text):
1713 if not text:
1713 if not text:
1714 text = ""
1714 text = ""
1715 l = len(text)
1715 l = len(text)
1716 s = util.sha1('blob %d\0' % l)
1716 s = util.sha1('blob %d\0' % l)
1717 s.update(text)
1717 s.update(text)
1718 return s.hexdigest()
1718 return s.hexdigest()
1719
1719
1720 def diffline(a, b, revs):
1720 def diffline(a, b, revs):
1721 if opts.git:
1721 if opts.git:
1722 line = 'diff --git a/%s b/%s\n' % (a, b)
1722 line = 'diff --git a/%s b/%s\n' % (a, b)
1723 elif not repo.ui.quiet:
1723 elif not repo.ui.quiet:
1724 if revs:
1724 if revs:
1725 revinfo = ' '.join(["-r %s" % rev for rev in revs])
1725 revinfo = ' '.join(["-r %s" % rev for rev in revs])
1726 line = 'diff %s %s\n' % (revinfo, a)
1726 line = 'diff %s %s\n' % (revinfo, a)
1727 else:
1727 else:
1728 line = 'diff %s\n' % a
1728 line = 'diff %s\n' % a
1729 else:
1729 else:
1730 line = ''
1730 line = ''
1731 return line
1731 return line
1732
1732
1733 date1 = util.datestr(ctx1.date())
1733 date1 = util.datestr(ctx1.date())
1734 man1 = ctx1.manifest()
1734 man1 = ctx1.manifest()
1735
1735
1736 gone = set()
1736 gone = set()
1737 gitmode = {'l': '120000', 'x': '100755', '': '100644'}
1737 gitmode = {'l': '120000', 'x': '100755', '': '100644'}
1738
1738
1739 copyto = dict([(v, k) for k, v in copy.items()])
1739 copyto = dict([(v, k) for k, v in copy.items()])
1740
1740
1741 if opts.git:
1741 if opts.git:
1742 revs = None
1742 revs = None
1743
1743
1744 for f in sorted(modified + added + removed):
1744 for f in sorted(modified + added + removed):
1745 to = None
1745 to = None
1746 tn = None
1746 tn = None
1747 dodiff = True
1747 dodiff = True
1748 header = []
1748 header = []
1749 if f in man1:
1749 if f in man1:
1750 to = getfilectx(f, ctx1).data()
1750 to = getfilectx(f, ctx1).data()
1751 if f not in removed:
1751 if f not in removed:
1752 tn = getfilectx(f, ctx2).data()
1752 tn = getfilectx(f, ctx2).data()
1753 a, b = f, f
1753 a, b = f, f
1754 if opts.git or losedatafn:
1754 if opts.git or losedatafn:
1755 if f in added or (f in modified and to is None):
1755 if f in added or (f in modified and to is None):
1756 mode = gitmode[ctx2.flags(f)]
1756 mode = gitmode[ctx2.flags(f)]
1757 if f in copy or f in copyto:
1757 if f in copy or f in copyto:
1758 if opts.git:
1758 if opts.git:
1759 if f in copy:
1759 if f in copy:
1760 a = copy[f]
1760 a = copy[f]
1761 else:
1761 else:
1762 a = copyto[f]
1762 a = copyto[f]
1763 omode = gitmode[man1.flags(a)]
1763 omode = gitmode[man1.flags(a)]
1764 addmodehdr(header, omode, mode)
1764 addmodehdr(header, omode, mode)
1765 if a in removed and a not in gone:
1765 if a in removed and a not in gone:
1766 op = 'rename'
1766 op = 'rename'
1767 gone.add(a)
1767 gone.add(a)
1768 else:
1768 else:
1769 op = 'copy'
1769 op = 'copy'
1770 header.append('%s from %s\n' % (op, join(a)))
1770 header.append('%s from %s\n' % (op, join(a)))
1771 header.append('%s to %s\n' % (op, join(f)))
1771 header.append('%s to %s\n' % (op, join(f)))
1772 to = getfilectx(a, ctx1).data()
1772 to = getfilectx(a, ctx1).data()
1773 else:
1773 else:
1774 losedatafn(f)
1774 losedatafn(f)
1775 else:
1775 else:
1776 if opts.git:
1776 if opts.git:
1777 header.append('new file mode %s\n' % mode)
1777 header.append('new file mode %s\n' % mode)
1778 elif ctx2.flags(f):
1778 elif ctx2.flags(f):
1779 losedatafn(f)
1779 losedatafn(f)
1780 # In theory, if tn was copied or renamed we should check
1780 # In theory, if tn was copied or renamed we should check
1781 # if the source is binary too but the copy record already
1781 # if the source is binary too but the copy record already
1782 # forces git mode.
1782 # forces git mode.
1783 if util.binary(tn):
1783 if util.binary(tn):
1784 if opts.git:
1784 if opts.git:
1785 dodiff = 'binary'
1785 dodiff = 'binary'
1786 else:
1786 else:
1787 losedatafn(f)
1787 losedatafn(f)
1788 if not opts.git and not tn:
1788 if not opts.git and not tn:
1789 # regular diffs cannot represent new empty file
1789 # regular diffs cannot represent new empty file
1790 losedatafn(f)
1790 losedatafn(f)
1791 elif f in removed or (f in modified and tn is None):
1791 elif f in removed or (f in modified and tn is None):
1792 if opts.git:
1792 if opts.git:
1793 # have we already reported a copy above?
1793 # have we already reported a copy above?
1794 if ((f in copy and copy[f] in added
1794 if ((f in copy and copy[f] in added
1795 and copyto[copy[f]] == f) or
1795 and copyto[copy[f]] == f) or
1796 (f in copyto and copyto[f] in added
1796 (f in copyto and copyto[f] in added
1797 and copy[copyto[f]] == f)):
1797 and copy[copyto[f]] == f)):
1798 dodiff = False
1798 dodiff = False
1799 else:
1799 else:
1800 header.append('deleted file mode %s\n' %
1800 header.append('deleted file mode %s\n' %
1801 gitmode[man1.flags(f)])
1801 gitmode[man1.flags(f)])
1802 if util.binary(to):
1802 if util.binary(to):
1803 dodiff = 'binary'
1803 dodiff = 'binary'
1804 elif not to or util.binary(to):
1804 elif not to or util.binary(to):
1805 # regular diffs cannot represent empty file deletion
1805 # regular diffs cannot represent empty file deletion
1806 losedatafn(f)
1806 losedatafn(f)
1807 else:
1807 else:
1808 oflag = man1.flags(f)
1808 oflag = man1.flags(f)
1809 nflag = ctx2.flags(f)
1809 nflag = ctx2.flags(f)
1810 binary = util.binary(to) or util.binary(tn)
1810 binary = util.binary(to) or util.binary(tn)
1811 if opts.git:
1811 if opts.git:
1812 addmodehdr(header, gitmode[oflag], gitmode[nflag])
1812 addmodehdr(header, gitmode[oflag], gitmode[nflag])
1813 if binary:
1813 if binary:
1814 dodiff = 'binary'
1814 dodiff = 'binary'
1815 elif binary or nflag != oflag:
1815 elif binary or nflag != oflag:
1816 losedatafn(f)
1816 losedatafn(f)
1817
1817
1818 if dodiff:
1818 if dodiff:
1819 if opts.git or revs:
1819 if opts.git or revs:
1820 header.insert(0, diffline(join(a), join(b), revs))
1820 header.insert(0, diffline(join(a), join(b), revs))
1821 if dodiff == 'binary':
1821 if dodiff == 'binary':
1822 text = mdiff.b85diff(to, tn)
1822 text = mdiff.b85diff(to, tn)
1823 if text:
1823 if text:
1824 addindexmeta(header, [gitindex(to), gitindex(tn)])
1824 addindexmeta(header, [gitindex(to), gitindex(tn)])
1825 else:
1825 else:
1826 text = mdiff.unidiff(to, date1,
1826 text = mdiff.unidiff(to, date1,
1827 # ctx2 date may be dynamic
1827 # ctx2 date may be dynamic
1828 tn, util.datestr(ctx2.date()),
1828 tn, util.datestr(ctx2.date()),
1829 join(a), join(b), opts=opts)
1829 join(a), join(b), opts=opts)
1830 if header and (text or len(header) > 1):
1830 if header and (text or len(header) > 1):
1831 yield ''.join(header)
1831 yield ''.join(header)
1832 if text:
1832 if text:
1833 yield text
1833 yield text
1834
1834
1835 def diffstatsum(stats):
1835 def diffstatsum(stats):
1836 maxfile, maxtotal, addtotal, removetotal, binary = 0, 0, 0, 0, False
1836 maxfile, maxtotal, addtotal, removetotal, binary = 0, 0, 0, 0, False
1837 for f, a, r, b in stats:
1837 for f, a, r, b in stats:
1838 maxfile = max(maxfile, encoding.colwidth(f))
1838 maxfile = max(maxfile, encoding.colwidth(f))
1839 maxtotal = max(maxtotal, a + r)
1839 maxtotal = max(maxtotal, a + r)
1840 addtotal += a
1840 addtotal += a
1841 removetotal += r
1841 removetotal += r
1842 binary = binary or b
1842 binary = binary or b
1843
1843
1844 return maxfile, maxtotal, addtotal, removetotal, binary
1844 return maxfile, maxtotal, addtotal, removetotal, binary
1845
1845
1846 def diffstatdata(lines):
1846 def diffstatdata(lines):
1847 diffre = re.compile('^diff .*-r [a-z0-9]+\s(.*)$')
1847 diffre = re.compile('^diff .*-r [a-z0-9]+\s(.*)$')
1848
1848
1849 results = []
1849 results = []
1850 filename, adds, removes, isbinary = None, 0, 0, False
1850 filename, adds, removes, isbinary = None, 0, 0, False
1851
1851
1852 def addresult():
1852 def addresult():
1853 if filename:
1853 if filename:
1854 results.append((filename, adds, removes, isbinary))
1854 results.append((filename, adds, removes, isbinary))
1855
1855
1856 for line in lines:
1856 for line in lines:
1857 if line.startswith('diff'):
1857 if line.startswith('diff'):
1858 addresult()
1858 addresult()
1859 # set numbers to 0 anyway when starting new file
1859 # set numbers to 0 anyway when starting new file
1860 adds, removes, isbinary = 0, 0, False
1860 adds, removes, isbinary = 0, 0, False
1861 if line.startswith('diff --git a/'):
1861 if line.startswith('diff --git a/'):
1862 filename = gitre.search(line).group(1)
1862 filename = gitre.search(line).group(2)
1863 elif line.startswith('diff -r'):
1863 elif line.startswith('diff -r'):
1864 # format: "diff -r ... -r ... filename"
1864 # format: "diff -r ... -r ... filename"
1865 filename = diffre.search(line).group(1)
1865 filename = diffre.search(line).group(1)
1866 elif line.startswith('+') and not line.startswith('+++ '):
1866 elif line.startswith('+') and not line.startswith('+++ '):
1867 adds += 1
1867 adds += 1
1868 elif line.startswith('-') and not line.startswith('--- '):
1868 elif line.startswith('-') and not line.startswith('--- '):
1869 removes += 1
1869 removes += 1
1870 elif (line.startswith('GIT binary patch') or
1870 elif (line.startswith('GIT binary patch') or
1871 line.startswith('Binary file')):
1871 line.startswith('Binary file')):
1872 isbinary = True
1872 isbinary = True
1873 addresult()
1873 addresult()
1874 return results
1874 return results
1875
1875
1876 def diffstat(lines, width=80, git=False):
1876 def diffstat(lines, width=80, git=False):
1877 output = []
1877 output = []
1878 stats = diffstatdata(lines)
1878 stats = diffstatdata(lines)
1879 maxname, maxtotal, totaladds, totalremoves, hasbinary = diffstatsum(stats)
1879 maxname, maxtotal, totaladds, totalremoves, hasbinary = diffstatsum(stats)
1880
1880
1881 countwidth = len(str(maxtotal))
1881 countwidth = len(str(maxtotal))
1882 if hasbinary and countwidth < 3:
1882 if hasbinary and countwidth < 3:
1883 countwidth = 3
1883 countwidth = 3
1884 graphwidth = width - countwidth - maxname - 6
1884 graphwidth = width - countwidth - maxname - 6
1885 if graphwidth < 10:
1885 if graphwidth < 10:
1886 graphwidth = 10
1886 graphwidth = 10
1887
1887
1888 def scale(i):
1888 def scale(i):
1889 if maxtotal <= graphwidth:
1889 if maxtotal <= graphwidth:
1890 return i
1890 return i
1891 # If diffstat runs out of room it doesn't print anything,
1891 # If diffstat runs out of room it doesn't print anything,
1892 # which isn't very useful, so always print at least one + or -
1892 # which isn't very useful, so always print at least one + or -
1893 # if there were at least some changes.
1893 # if there were at least some changes.
1894 return max(i * graphwidth // maxtotal, int(bool(i)))
1894 return max(i * graphwidth // maxtotal, int(bool(i)))
1895
1895
1896 for filename, adds, removes, isbinary in stats:
1896 for filename, adds, removes, isbinary in stats:
1897 if isbinary:
1897 if isbinary:
1898 count = 'Bin'
1898 count = 'Bin'
1899 else:
1899 else:
1900 count = adds + removes
1900 count = adds + removes
1901 pluses = '+' * scale(adds)
1901 pluses = '+' * scale(adds)
1902 minuses = '-' * scale(removes)
1902 minuses = '-' * scale(removes)
1903 output.append(' %s%s | %*s %s%s\n' %
1903 output.append(' %s%s | %*s %s%s\n' %
1904 (filename, ' ' * (maxname - encoding.colwidth(filename)),
1904 (filename, ' ' * (maxname - encoding.colwidth(filename)),
1905 countwidth, count, pluses, minuses))
1905 countwidth, count, pluses, minuses))
1906
1906
1907 if stats:
1907 if stats:
1908 output.append(_(' %d files changed, %d insertions(+), '
1908 output.append(_(' %d files changed, %d insertions(+), '
1909 '%d deletions(-)\n')
1909 '%d deletions(-)\n')
1910 % (len(stats), totaladds, totalremoves))
1910 % (len(stats), totaladds, totalremoves))
1911
1911
1912 return ''.join(output)
1912 return ''.join(output)
1913
1913
1914 def diffstatui(*args, **kw):
1914 def diffstatui(*args, **kw):
1915 '''like diffstat(), but yields 2-tuples of (output, label) for
1915 '''like diffstat(), but yields 2-tuples of (output, label) for
1916 ui.write()
1916 ui.write()
1917 '''
1917 '''
1918
1918
1919 for line in diffstat(*args, **kw).splitlines():
1919 for line in diffstat(*args, **kw).splitlines():
1920 if line and line[-1] in '+-':
1920 if line and line[-1] in '+-':
1921 name, graph = line.rsplit(' ', 1)
1921 name, graph = line.rsplit(' ', 1)
1922 yield (name + ' ', '')
1922 yield (name + ' ', '')
1923 m = re.search(r'\++', graph)
1923 m = re.search(r'\++', graph)
1924 if m:
1924 if m:
1925 yield (m.group(0), 'diffstat.inserted')
1925 yield (m.group(0), 'diffstat.inserted')
1926 m = re.search(r'-+', graph)
1926 m = re.search(r'-+', graph)
1927 if m:
1927 if m:
1928 yield (m.group(0), 'diffstat.deleted')
1928 yield (m.group(0), 'diffstat.deleted')
1929 else:
1929 else:
1930 yield (line, '')
1930 yield (line, '')
1931 yield ('\n', '')
1931 yield ('\n', '')
@@ -1,2129 +1,2129 b''
1 @ (34) head
1 @ (34) head
2 |
2 |
3 | o (33) head
3 | o (33) head
4 | |
4 | |
5 o | (32) expand
5 o | (32) expand
6 |\ \
6 |\ \
7 | o \ (31) expand
7 | o \ (31) expand
8 | |\ \
8 | |\ \
9 | | o \ (30) expand
9 | | o \ (30) expand
10 | | |\ \
10 | | |\ \
11 | | | o | (29) regular commit
11 | | | o | (29) regular commit
12 | | | | |
12 | | | | |
13 | | o | | (28) merge zero known
13 | | o | | (28) merge zero known
14 | | |\ \ \
14 | | |\ \ \
15 o | | | | | (27) collapse
15 o | | | | | (27) collapse
16 |/ / / / /
16 |/ / / / /
17 | | o---+ (26) merge one known; far right
17 | | o---+ (26) merge one known; far right
18 | | | | |
18 | | | | |
19 +---o | | (25) merge one known; far left
19 +---o | | (25) merge one known; far left
20 | | | | |
20 | | | | |
21 | | o | | (24) merge one known; immediate right
21 | | o | | (24) merge one known; immediate right
22 | | |\| |
22 | | |\| |
23 | | o | | (23) merge one known; immediate left
23 | | o | | (23) merge one known; immediate left
24 | |/| | |
24 | |/| | |
25 +---o---+ (22) merge two known; one far left, one far right
25 +---o---+ (22) merge two known; one far left, one far right
26 | | / /
26 | | / /
27 o | | | (21) expand
27 o | | | (21) expand
28 |\ \ \ \
28 |\ \ \ \
29 | o---+-+ (20) merge two known; two far right
29 | o---+-+ (20) merge two known; two far right
30 | / / /
30 | / / /
31 o | | | (19) expand
31 o | | | (19) expand
32 |\ \ \ \
32 |\ \ \ \
33 +---+---o (18) merge two known; two far left
33 +---+---o (18) merge two known; two far left
34 | | | |
34 | | | |
35 | o | | (17) expand
35 | o | | (17) expand
36 | |\ \ \
36 | |\ \ \
37 | | o---+ (16) merge two known; one immediate right, one near right
37 | | o---+ (16) merge two known; one immediate right, one near right
38 | | |/ /
38 | | |/ /
39 o | | | (15) expand
39 o | | | (15) expand
40 |\ \ \ \
40 |\ \ \ \
41 | o-----+ (14) merge two known; one immediate right, one far right
41 | o-----+ (14) merge two known; one immediate right, one far right
42 | |/ / /
42 | |/ / /
43 o | | | (13) expand
43 o | | | (13) expand
44 |\ \ \ \
44 |\ \ \ \
45 +---o | | (12) merge two known; one immediate right, one far left
45 +---o | | (12) merge two known; one immediate right, one far left
46 | | |/ /
46 | | |/ /
47 | o | | (11) expand
47 | o | | (11) expand
48 | |\ \ \
48 | |\ \ \
49 | | o---+ (10) merge two known; one immediate left, one near right
49 | | o---+ (10) merge two known; one immediate left, one near right
50 | |/ / /
50 | |/ / /
51 o | | | (9) expand
51 o | | | (9) expand
52 |\ \ \ \
52 |\ \ \ \
53 | o-----+ (8) merge two known; one immediate left, one far right
53 | o-----+ (8) merge two known; one immediate left, one far right
54 |/ / / /
54 |/ / / /
55 o | | | (7) expand
55 o | | | (7) expand
56 |\ \ \ \
56 |\ \ \ \
57 +---o | | (6) merge two known; one immediate left, one far left
57 +---o | | (6) merge two known; one immediate left, one far left
58 | |/ / /
58 | |/ / /
59 | o | | (5) expand
59 | o | | (5) expand
60 | |\ \ \
60 | |\ \ \
61 | | o | | (4) merge two known; one immediate left, one immediate right
61 | | o | | (4) merge two known; one immediate left, one immediate right
62 | |/|/ /
62 | |/|/ /
63 | o / / (3) collapse
63 | o / / (3) collapse
64 |/ / /
64 |/ / /
65 o / / (2) collapse
65 o / / (2) collapse
66 |/ /
66 |/ /
67 o / (1) collapse
67 o / (1) collapse
68 |/
68 |/
69 o (0) root
69 o (0) root
70
70
71
71
72 $ commit()
72 $ commit()
73 > {
73 > {
74 > rev=$1
74 > rev=$1
75 > msg=$2
75 > msg=$2
76 > shift 2
76 > shift 2
77 > if [ "$#" -gt 0 ]; then
77 > if [ "$#" -gt 0 ]; then
78 > hg debugsetparents "$@"
78 > hg debugsetparents "$@"
79 > fi
79 > fi
80 > echo $rev > a
80 > echo $rev > a
81 > hg commit -Aqd "$rev 0" -m "($rev) $msg"
81 > hg commit -Aqd "$rev 0" -m "($rev) $msg"
82 > }
82 > }
83
83
84 $ cat > printrevset.py <<EOF
84 $ cat > printrevset.py <<EOF
85 > from mercurial import extensions, revset, commands, cmdutil
85 > from mercurial import extensions, revset, commands, cmdutil
86 >
86 >
87 > def uisetup(ui):
87 > def uisetup(ui):
88 > def printrevset(orig, ui, repo, *pats, **opts):
88 > def printrevset(orig, ui, repo, *pats, **opts):
89 > if opts.get('print_revset'):
89 > if opts.get('print_revset'):
90 > expr = cmdutil.getgraphlogrevs(repo, pats, opts)[1]
90 > expr = cmdutil.getgraphlogrevs(repo, pats, opts)[1]
91 > if expr:
91 > if expr:
92 > tree = revset.parse(expr)[0]
92 > tree = revset.parse(expr)[0]
93 > else:
93 > else:
94 > tree = []
94 > tree = []
95 > ui.write('%r\n' % (opts.get('rev', []),))
95 > ui.write('%r\n' % (opts.get('rev', []),))
96 > ui.write(revset.prettyformat(tree) + '\n')
96 > ui.write(revset.prettyformat(tree) + '\n')
97 > return 0
97 > return 0
98 > return orig(ui, repo, *pats, **opts)
98 > return orig(ui, repo, *pats, **opts)
99 > entry = extensions.wrapcommand(commands.table, 'log', printrevset)
99 > entry = extensions.wrapcommand(commands.table, 'log', printrevset)
100 > entry[1].append(('', 'print-revset', False,
100 > entry[1].append(('', 'print-revset', False,
101 > 'print generated revset and exit (DEPRECATED)'))
101 > 'print generated revset and exit (DEPRECATED)'))
102 > EOF
102 > EOF
103
103
104 $ echo "[extensions]" >> $HGRCPATH
104 $ echo "[extensions]" >> $HGRCPATH
105 $ echo "printrevset=`pwd`/printrevset.py" >> $HGRCPATH
105 $ echo "printrevset=`pwd`/printrevset.py" >> $HGRCPATH
106
106
107 $ hg init repo
107 $ hg init repo
108 $ cd repo
108 $ cd repo
109
109
110 Empty repo:
110 Empty repo:
111
111
112 $ hg log -G
112 $ hg log -G
113
113
114
114
115 Building DAG:
115 Building DAG:
116
116
117 $ commit 0 "root"
117 $ commit 0 "root"
118 $ commit 1 "collapse" 0
118 $ commit 1 "collapse" 0
119 $ commit 2 "collapse" 1
119 $ commit 2 "collapse" 1
120 $ commit 3 "collapse" 2
120 $ commit 3 "collapse" 2
121 $ commit 4 "merge two known; one immediate left, one immediate right" 1 3
121 $ commit 4 "merge two known; one immediate left, one immediate right" 1 3
122 $ commit 5 "expand" 3 4
122 $ commit 5 "expand" 3 4
123 $ commit 6 "merge two known; one immediate left, one far left" 2 5
123 $ commit 6 "merge two known; one immediate left, one far left" 2 5
124 $ commit 7 "expand" 2 5
124 $ commit 7 "expand" 2 5
125 $ commit 8 "merge two known; one immediate left, one far right" 0 7
125 $ commit 8 "merge two known; one immediate left, one far right" 0 7
126 $ commit 9 "expand" 7 8
126 $ commit 9 "expand" 7 8
127 $ commit 10 "merge two known; one immediate left, one near right" 0 6
127 $ commit 10 "merge two known; one immediate left, one near right" 0 6
128 $ commit 11 "expand" 6 10
128 $ commit 11 "expand" 6 10
129 $ commit 12 "merge two known; one immediate right, one far left" 1 9
129 $ commit 12 "merge two known; one immediate right, one far left" 1 9
130 $ commit 13 "expand" 9 11
130 $ commit 13 "expand" 9 11
131 $ commit 14 "merge two known; one immediate right, one far right" 0 12
131 $ commit 14 "merge two known; one immediate right, one far right" 0 12
132 $ commit 15 "expand" 13 14
132 $ commit 15 "expand" 13 14
133 $ commit 16 "merge two known; one immediate right, one near right" 0 1
133 $ commit 16 "merge two known; one immediate right, one near right" 0 1
134 $ commit 17 "expand" 12 16
134 $ commit 17 "expand" 12 16
135 $ commit 18 "merge two known; two far left" 1 15
135 $ commit 18 "merge two known; two far left" 1 15
136 $ commit 19 "expand" 15 17
136 $ commit 19 "expand" 15 17
137 $ commit 20 "merge two known; two far right" 0 18
137 $ commit 20 "merge two known; two far right" 0 18
138 $ commit 21 "expand" 19 20
138 $ commit 21 "expand" 19 20
139 $ commit 22 "merge two known; one far left, one far right" 18 21
139 $ commit 22 "merge two known; one far left, one far right" 18 21
140 $ commit 23 "merge one known; immediate left" 1 22
140 $ commit 23 "merge one known; immediate left" 1 22
141 $ commit 24 "merge one known; immediate right" 0 23
141 $ commit 24 "merge one known; immediate right" 0 23
142 $ commit 25 "merge one known; far left" 21 24
142 $ commit 25 "merge one known; far left" 21 24
143 $ commit 26 "merge one known; far right" 18 25
143 $ commit 26 "merge one known; far right" 18 25
144 $ commit 27 "collapse" 21
144 $ commit 27 "collapse" 21
145 $ commit 28 "merge zero known" 1 26
145 $ commit 28 "merge zero known" 1 26
146 $ commit 29 "regular commit" 0
146 $ commit 29 "regular commit" 0
147 $ commit 30 "expand" 28 29
147 $ commit 30 "expand" 28 29
148 $ commit 31 "expand" 21 30
148 $ commit 31 "expand" 21 30
149 $ commit 32 "expand" 27 31
149 $ commit 32 "expand" 27 31
150 $ commit 33 "head" 18
150 $ commit 33 "head" 18
151 $ commit 34 "head" 32
151 $ commit 34 "head" 32
152
152
153
153
154 $ hg log -G -q
154 $ hg log -G -q
155 @ 34:fea3ac5810e0
155 @ 34:fea3ac5810e0
156 |
156 |
157 | o 33:68608f5145f9
157 | o 33:68608f5145f9
158 | |
158 | |
159 o | 32:d06dffa21a31
159 o | 32:d06dffa21a31
160 |\ \
160 |\ \
161 | o \ 31:621d83e11f67
161 | o \ 31:621d83e11f67
162 | |\ \
162 | |\ \
163 | | o \ 30:6e11cd4b648f
163 | | o \ 30:6e11cd4b648f
164 | | |\ \
164 | | |\ \
165 | | | o | 29:cd9bb2be7593
165 | | | o | 29:cd9bb2be7593
166 | | | | |
166 | | | | |
167 | | o | | 28:44ecd0b9ae99
167 | | o | | 28:44ecd0b9ae99
168 | | |\ \ \
168 | | |\ \ \
169 o | | | | | 27:886ed638191b
169 o | | | | | 27:886ed638191b
170 |/ / / / /
170 |/ / / / /
171 | | o---+ 26:7f25b6c2f0b9
171 | | o---+ 26:7f25b6c2f0b9
172 | | | | |
172 | | | | |
173 +---o | | 25:91da8ed57247
173 +---o | | 25:91da8ed57247
174 | | | | |
174 | | | | |
175 | | o | | 24:a9c19a3d96b7
175 | | o | | 24:a9c19a3d96b7
176 | | |\| |
176 | | |\| |
177 | | o | | 23:a01cddf0766d
177 | | o | | 23:a01cddf0766d
178 | |/| | |
178 | |/| | |
179 +---o---+ 22:e0d9cccacb5d
179 +---o---+ 22:e0d9cccacb5d
180 | | / /
180 | | / /
181 o | | | 21:d42a756af44d
181 o | | | 21:d42a756af44d
182 |\ \ \ \
182 |\ \ \ \
183 | o---+-+ 20:d30ed6450e32
183 | o---+-+ 20:d30ed6450e32
184 | / / /
184 | / / /
185 o | | | 19:31ddc2c1573b
185 o | | | 19:31ddc2c1573b
186 |\ \ \ \
186 |\ \ \ \
187 +---+---o 18:1aa84d96232a
187 +---+---o 18:1aa84d96232a
188 | | | |
188 | | | |
189 | o | | 17:44765d7c06e0
189 | o | | 17:44765d7c06e0
190 | |\ \ \
190 | |\ \ \
191 | | o---+ 16:3677d192927d
191 | | o---+ 16:3677d192927d
192 | | |/ /
192 | | |/ /
193 o | | | 15:1dda3f72782d
193 o | | | 15:1dda3f72782d
194 |\ \ \ \
194 |\ \ \ \
195 | o-----+ 14:8eac370358ef
195 | o-----+ 14:8eac370358ef
196 | |/ / /
196 | |/ / /
197 o | | | 13:22d8966a97e3
197 o | | | 13:22d8966a97e3
198 |\ \ \ \
198 |\ \ \ \
199 +---o | | 12:86b91144a6e9
199 +---o | | 12:86b91144a6e9
200 | | |/ /
200 | | |/ /
201 | o | | 11:832d76e6bdf2
201 | o | | 11:832d76e6bdf2
202 | |\ \ \
202 | |\ \ \
203 | | o---+ 10:74c64d036d72
203 | | o---+ 10:74c64d036d72
204 | |/ / /
204 | |/ / /
205 o | | | 9:7010c0af0a35
205 o | | | 9:7010c0af0a35
206 |\ \ \ \
206 |\ \ \ \
207 | o-----+ 8:7a0b11f71937
207 | o-----+ 8:7a0b11f71937
208 |/ / / /
208 |/ / / /
209 o | | | 7:b632bb1b1224
209 o | | | 7:b632bb1b1224
210 |\ \ \ \
210 |\ \ \ \
211 +---o | | 6:b105a072e251
211 +---o | | 6:b105a072e251
212 | |/ / /
212 | |/ / /
213 | o | | 5:4409d547b708
213 | o | | 5:4409d547b708
214 | |\ \ \
214 | |\ \ \
215 | | o | | 4:26a8bac39d9f
215 | | o | | 4:26a8bac39d9f
216 | |/|/ /
216 | |/|/ /
217 | o / / 3:27eef8ed80b4
217 | o / / 3:27eef8ed80b4
218 |/ / /
218 |/ / /
219 o / / 2:3d9a33b8d1e1
219 o / / 2:3d9a33b8d1e1
220 |/ /
220 |/ /
221 o / 1:6db2ef61d156
221 o / 1:6db2ef61d156
222 |/
222 |/
223 o 0:e6eb3150255d
223 o 0:e6eb3150255d
224
224
225
225
226 $ hg log -G
226 $ hg log -G
227 @ changeset: 34:fea3ac5810e0
227 @ changeset: 34:fea3ac5810e0
228 | tag: tip
228 | tag: tip
229 | parent: 32:d06dffa21a31
229 | parent: 32:d06dffa21a31
230 | user: test
230 | user: test
231 | date: Thu Jan 01 00:00:34 1970 +0000
231 | date: Thu Jan 01 00:00:34 1970 +0000
232 | summary: (34) head
232 | summary: (34) head
233 |
233 |
234 | o changeset: 33:68608f5145f9
234 | o changeset: 33:68608f5145f9
235 | | parent: 18:1aa84d96232a
235 | | parent: 18:1aa84d96232a
236 | | user: test
236 | | user: test
237 | | date: Thu Jan 01 00:00:33 1970 +0000
237 | | date: Thu Jan 01 00:00:33 1970 +0000
238 | | summary: (33) head
238 | | summary: (33) head
239 | |
239 | |
240 o | changeset: 32:d06dffa21a31
240 o | changeset: 32:d06dffa21a31
241 |\ \ parent: 27:886ed638191b
241 |\ \ parent: 27:886ed638191b
242 | | | parent: 31:621d83e11f67
242 | | | parent: 31:621d83e11f67
243 | | | user: test
243 | | | user: test
244 | | | date: Thu Jan 01 00:00:32 1970 +0000
244 | | | date: Thu Jan 01 00:00:32 1970 +0000
245 | | | summary: (32) expand
245 | | | summary: (32) expand
246 | | |
246 | | |
247 | o | changeset: 31:621d83e11f67
247 | o | changeset: 31:621d83e11f67
248 | |\ \ parent: 21:d42a756af44d
248 | |\ \ parent: 21:d42a756af44d
249 | | | | parent: 30:6e11cd4b648f
249 | | | | parent: 30:6e11cd4b648f
250 | | | | user: test
250 | | | | user: test
251 | | | | date: Thu Jan 01 00:00:31 1970 +0000
251 | | | | date: Thu Jan 01 00:00:31 1970 +0000
252 | | | | summary: (31) expand
252 | | | | summary: (31) expand
253 | | | |
253 | | | |
254 | | o | changeset: 30:6e11cd4b648f
254 | | o | changeset: 30:6e11cd4b648f
255 | | |\ \ parent: 28:44ecd0b9ae99
255 | | |\ \ parent: 28:44ecd0b9ae99
256 | | | | | parent: 29:cd9bb2be7593
256 | | | | | parent: 29:cd9bb2be7593
257 | | | | | user: test
257 | | | | | user: test
258 | | | | | date: Thu Jan 01 00:00:30 1970 +0000
258 | | | | | date: Thu Jan 01 00:00:30 1970 +0000
259 | | | | | summary: (30) expand
259 | | | | | summary: (30) expand
260 | | | | |
260 | | | | |
261 | | | o | changeset: 29:cd9bb2be7593
261 | | | o | changeset: 29:cd9bb2be7593
262 | | | | | parent: 0:e6eb3150255d
262 | | | | | parent: 0:e6eb3150255d
263 | | | | | user: test
263 | | | | | user: test
264 | | | | | date: Thu Jan 01 00:00:29 1970 +0000
264 | | | | | date: Thu Jan 01 00:00:29 1970 +0000
265 | | | | | summary: (29) regular commit
265 | | | | | summary: (29) regular commit
266 | | | | |
266 | | | | |
267 | | o | | changeset: 28:44ecd0b9ae99
267 | | o | | changeset: 28:44ecd0b9ae99
268 | | |\ \ \ parent: 1:6db2ef61d156
268 | | |\ \ \ parent: 1:6db2ef61d156
269 | | | | | | parent: 26:7f25b6c2f0b9
269 | | | | | | parent: 26:7f25b6c2f0b9
270 | | | | | | user: test
270 | | | | | | user: test
271 | | | | | | date: Thu Jan 01 00:00:28 1970 +0000
271 | | | | | | date: Thu Jan 01 00:00:28 1970 +0000
272 | | | | | | summary: (28) merge zero known
272 | | | | | | summary: (28) merge zero known
273 | | | | | |
273 | | | | | |
274 o | | | | | changeset: 27:886ed638191b
274 o | | | | | changeset: 27:886ed638191b
275 |/ / / / / parent: 21:d42a756af44d
275 |/ / / / / parent: 21:d42a756af44d
276 | | | | | user: test
276 | | | | | user: test
277 | | | | | date: Thu Jan 01 00:00:27 1970 +0000
277 | | | | | date: Thu Jan 01 00:00:27 1970 +0000
278 | | | | | summary: (27) collapse
278 | | | | | summary: (27) collapse
279 | | | | |
279 | | | | |
280 | | o---+ changeset: 26:7f25b6c2f0b9
280 | | o---+ changeset: 26:7f25b6c2f0b9
281 | | | | | parent: 18:1aa84d96232a
281 | | | | | parent: 18:1aa84d96232a
282 | | | | | parent: 25:91da8ed57247
282 | | | | | parent: 25:91da8ed57247
283 | | | | | user: test
283 | | | | | user: test
284 | | | | | date: Thu Jan 01 00:00:26 1970 +0000
284 | | | | | date: Thu Jan 01 00:00:26 1970 +0000
285 | | | | | summary: (26) merge one known; far right
285 | | | | | summary: (26) merge one known; far right
286 | | | | |
286 | | | | |
287 +---o | | changeset: 25:91da8ed57247
287 +---o | | changeset: 25:91da8ed57247
288 | | | | | parent: 21:d42a756af44d
288 | | | | | parent: 21:d42a756af44d
289 | | | | | parent: 24:a9c19a3d96b7
289 | | | | | parent: 24:a9c19a3d96b7
290 | | | | | user: test
290 | | | | | user: test
291 | | | | | date: Thu Jan 01 00:00:25 1970 +0000
291 | | | | | date: Thu Jan 01 00:00:25 1970 +0000
292 | | | | | summary: (25) merge one known; far left
292 | | | | | summary: (25) merge one known; far left
293 | | | | |
293 | | | | |
294 | | o | | changeset: 24:a9c19a3d96b7
294 | | o | | changeset: 24:a9c19a3d96b7
295 | | |\| | parent: 0:e6eb3150255d
295 | | |\| | parent: 0:e6eb3150255d
296 | | | | | parent: 23:a01cddf0766d
296 | | | | | parent: 23:a01cddf0766d
297 | | | | | user: test
297 | | | | | user: test
298 | | | | | date: Thu Jan 01 00:00:24 1970 +0000
298 | | | | | date: Thu Jan 01 00:00:24 1970 +0000
299 | | | | | summary: (24) merge one known; immediate right
299 | | | | | summary: (24) merge one known; immediate right
300 | | | | |
300 | | | | |
301 | | o | | changeset: 23:a01cddf0766d
301 | | o | | changeset: 23:a01cddf0766d
302 | |/| | | parent: 1:6db2ef61d156
302 | |/| | | parent: 1:6db2ef61d156
303 | | | | | parent: 22:e0d9cccacb5d
303 | | | | | parent: 22:e0d9cccacb5d
304 | | | | | user: test
304 | | | | | user: test
305 | | | | | date: Thu Jan 01 00:00:23 1970 +0000
305 | | | | | date: Thu Jan 01 00:00:23 1970 +0000
306 | | | | | summary: (23) merge one known; immediate left
306 | | | | | summary: (23) merge one known; immediate left
307 | | | | |
307 | | | | |
308 +---o---+ changeset: 22:e0d9cccacb5d
308 +---o---+ changeset: 22:e0d9cccacb5d
309 | | | | parent: 18:1aa84d96232a
309 | | | | parent: 18:1aa84d96232a
310 | | / / parent: 21:d42a756af44d
310 | | / / parent: 21:d42a756af44d
311 | | | | user: test
311 | | | | user: test
312 | | | | date: Thu Jan 01 00:00:22 1970 +0000
312 | | | | date: Thu Jan 01 00:00:22 1970 +0000
313 | | | | summary: (22) merge two known; one far left, one far right
313 | | | | summary: (22) merge two known; one far left, one far right
314 | | | |
314 | | | |
315 o | | | changeset: 21:d42a756af44d
315 o | | | changeset: 21:d42a756af44d
316 |\ \ \ \ parent: 19:31ddc2c1573b
316 |\ \ \ \ parent: 19:31ddc2c1573b
317 | | | | | parent: 20:d30ed6450e32
317 | | | | | parent: 20:d30ed6450e32
318 | | | | | user: test
318 | | | | | user: test
319 | | | | | date: Thu Jan 01 00:00:21 1970 +0000
319 | | | | | date: Thu Jan 01 00:00:21 1970 +0000
320 | | | | | summary: (21) expand
320 | | | | | summary: (21) expand
321 | | | | |
321 | | | | |
322 | o---+-+ changeset: 20:d30ed6450e32
322 | o---+-+ changeset: 20:d30ed6450e32
323 | | | | parent: 0:e6eb3150255d
323 | | | | parent: 0:e6eb3150255d
324 | / / / parent: 18:1aa84d96232a
324 | / / / parent: 18:1aa84d96232a
325 | | | | user: test
325 | | | | user: test
326 | | | | date: Thu Jan 01 00:00:20 1970 +0000
326 | | | | date: Thu Jan 01 00:00:20 1970 +0000
327 | | | | summary: (20) merge two known; two far right
327 | | | | summary: (20) merge two known; two far right
328 | | | |
328 | | | |
329 o | | | changeset: 19:31ddc2c1573b
329 o | | | changeset: 19:31ddc2c1573b
330 |\ \ \ \ parent: 15:1dda3f72782d
330 |\ \ \ \ parent: 15:1dda3f72782d
331 | | | | | parent: 17:44765d7c06e0
331 | | | | | parent: 17:44765d7c06e0
332 | | | | | user: test
332 | | | | | user: test
333 | | | | | date: Thu Jan 01 00:00:19 1970 +0000
333 | | | | | date: Thu Jan 01 00:00:19 1970 +0000
334 | | | | | summary: (19) expand
334 | | | | | summary: (19) expand
335 | | | | |
335 | | | | |
336 +---+---o changeset: 18:1aa84d96232a
336 +---+---o changeset: 18:1aa84d96232a
337 | | | | parent: 1:6db2ef61d156
337 | | | | parent: 1:6db2ef61d156
338 | | | | parent: 15:1dda3f72782d
338 | | | | parent: 15:1dda3f72782d
339 | | | | user: test
339 | | | | user: test
340 | | | | date: Thu Jan 01 00:00:18 1970 +0000
340 | | | | date: Thu Jan 01 00:00:18 1970 +0000
341 | | | | summary: (18) merge two known; two far left
341 | | | | summary: (18) merge two known; two far left
342 | | | |
342 | | | |
343 | o | | changeset: 17:44765d7c06e0
343 | o | | changeset: 17:44765d7c06e0
344 | |\ \ \ parent: 12:86b91144a6e9
344 | |\ \ \ parent: 12:86b91144a6e9
345 | | | | | parent: 16:3677d192927d
345 | | | | | parent: 16:3677d192927d
346 | | | | | user: test
346 | | | | | user: test
347 | | | | | date: Thu Jan 01 00:00:17 1970 +0000
347 | | | | | date: Thu Jan 01 00:00:17 1970 +0000
348 | | | | | summary: (17) expand
348 | | | | | summary: (17) expand
349 | | | | |
349 | | | | |
350 | | o---+ changeset: 16:3677d192927d
350 | | o---+ changeset: 16:3677d192927d
351 | | | | | parent: 0:e6eb3150255d
351 | | | | | parent: 0:e6eb3150255d
352 | | |/ / parent: 1:6db2ef61d156
352 | | |/ / parent: 1:6db2ef61d156
353 | | | | user: test
353 | | | | user: test
354 | | | | date: Thu Jan 01 00:00:16 1970 +0000
354 | | | | date: Thu Jan 01 00:00:16 1970 +0000
355 | | | | summary: (16) merge two known; one immediate right, one near right
355 | | | | summary: (16) merge two known; one immediate right, one near right
356 | | | |
356 | | | |
357 o | | | changeset: 15:1dda3f72782d
357 o | | | changeset: 15:1dda3f72782d
358 |\ \ \ \ parent: 13:22d8966a97e3
358 |\ \ \ \ parent: 13:22d8966a97e3
359 | | | | | parent: 14:8eac370358ef
359 | | | | | parent: 14:8eac370358ef
360 | | | | | user: test
360 | | | | | user: test
361 | | | | | date: Thu Jan 01 00:00:15 1970 +0000
361 | | | | | date: Thu Jan 01 00:00:15 1970 +0000
362 | | | | | summary: (15) expand
362 | | | | | summary: (15) expand
363 | | | | |
363 | | | | |
364 | o-----+ changeset: 14:8eac370358ef
364 | o-----+ changeset: 14:8eac370358ef
365 | | | | | parent: 0:e6eb3150255d
365 | | | | | parent: 0:e6eb3150255d
366 | |/ / / parent: 12:86b91144a6e9
366 | |/ / / parent: 12:86b91144a6e9
367 | | | | user: test
367 | | | | user: test
368 | | | | date: Thu Jan 01 00:00:14 1970 +0000
368 | | | | date: Thu Jan 01 00:00:14 1970 +0000
369 | | | | summary: (14) merge two known; one immediate right, one far right
369 | | | | summary: (14) merge two known; one immediate right, one far right
370 | | | |
370 | | | |
371 o | | | changeset: 13:22d8966a97e3
371 o | | | changeset: 13:22d8966a97e3
372 |\ \ \ \ parent: 9:7010c0af0a35
372 |\ \ \ \ parent: 9:7010c0af0a35
373 | | | | | parent: 11:832d76e6bdf2
373 | | | | | parent: 11:832d76e6bdf2
374 | | | | | user: test
374 | | | | | user: test
375 | | | | | date: Thu Jan 01 00:00:13 1970 +0000
375 | | | | | date: Thu Jan 01 00:00:13 1970 +0000
376 | | | | | summary: (13) expand
376 | | | | | summary: (13) expand
377 | | | | |
377 | | | | |
378 +---o | | changeset: 12:86b91144a6e9
378 +---o | | changeset: 12:86b91144a6e9
379 | | |/ / parent: 1:6db2ef61d156
379 | | |/ / parent: 1:6db2ef61d156
380 | | | | parent: 9:7010c0af0a35
380 | | | | parent: 9:7010c0af0a35
381 | | | | user: test
381 | | | | user: test
382 | | | | date: Thu Jan 01 00:00:12 1970 +0000
382 | | | | date: Thu Jan 01 00:00:12 1970 +0000
383 | | | | summary: (12) merge two known; one immediate right, one far left
383 | | | | summary: (12) merge two known; one immediate right, one far left
384 | | | |
384 | | | |
385 | o | | changeset: 11:832d76e6bdf2
385 | o | | changeset: 11:832d76e6bdf2
386 | |\ \ \ parent: 6:b105a072e251
386 | |\ \ \ parent: 6:b105a072e251
387 | | | | | parent: 10:74c64d036d72
387 | | | | | parent: 10:74c64d036d72
388 | | | | | user: test
388 | | | | | user: test
389 | | | | | date: Thu Jan 01 00:00:11 1970 +0000
389 | | | | | date: Thu Jan 01 00:00:11 1970 +0000
390 | | | | | summary: (11) expand
390 | | | | | summary: (11) expand
391 | | | | |
391 | | | | |
392 | | o---+ changeset: 10:74c64d036d72
392 | | o---+ changeset: 10:74c64d036d72
393 | | | | | parent: 0:e6eb3150255d
393 | | | | | parent: 0:e6eb3150255d
394 | |/ / / parent: 6:b105a072e251
394 | |/ / / parent: 6:b105a072e251
395 | | | | user: test
395 | | | | user: test
396 | | | | date: Thu Jan 01 00:00:10 1970 +0000
396 | | | | date: Thu Jan 01 00:00:10 1970 +0000
397 | | | | summary: (10) merge two known; one immediate left, one near right
397 | | | | summary: (10) merge two known; one immediate left, one near right
398 | | | |
398 | | | |
399 o | | | changeset: 9:7010c0af0a35
399 o | | | changeset: 9:7010c0af0a35
400 |\ \ \ \ parent: 7:b632bb1b1224
400 |\ \ \ \ parent: 7:b632bb1b1224
401 | | | | | parent: 8:7a0b11f71937
401 | | | | | parent: 8:7a0b11f71937
402 | | | | | user: test
402 | | | | | user: test
403 | | | | | date: Thu Jan 01 00:00:09 1970 +0000
403 | | | | | date: Thu Jan 01 00:00:09 1970 +0000
404 | | | | | summary: (9) expand
404 | | | | | summary: (9) expand
405 | | | | |
405 | | | | |
406 | o-----+ changeset: 8:7a0b11f71937
406 | o-----+ changeset: 8:7a0b11f71937
407 | | | | | parent: 0:e6eb3150255d
407 | | | | | parent: 0:e6eb3150255d
408 |/ / / / parent: 7:b632bb1b1224
408 |/ / / / parent: 7:b632bb1b1224
409 | | | | user: test
409 | | | | user: test
410 | | | | date: Thu Jan 01 00:00:08 1970 +0000
410 | | | | date: Thu Jan 01 00:00:08 1970 +0000
411 | | | | summary: (8) merge two known; one immediate left, one far right
411 | | | | summary: (8) merge two known; one immediate left, one far right
412 | | | |
412 | | | |
413 o | | | changeset: 7:b632bb1b1224
413 o | | | changeset: 7:b632bb1b1224
414 |\ \ \ \ parent: 2:3d9a33b8d1e1
414 |\ \ \ \ parent: 2:3d9a33b8d1e1
415 | | | | | parent: 5:4409d547b708
415 | | | | | parent: 5:4409d547b708
416 | | | | | user: test
416 | | | | | user: test
417 | | | | | date: Thu Jan 01 00:00:07 1970 +0000
417 | | | | | date: Thu Jan 01 00:00:07 1970 +0000
418 | | | | | summary: (7) expand
418 | | | | | summary: (7) expand
419 | | | | |
419 | | | | |
420 +---o | | changeset: 6:b105a072e251
420 +---o | | changeset: 6:b105a072e251
421 | |/ / / parent: 2:3d9a33b8d1e1
421 | |/ / / parent: 2:3d9a33b8d1e1
422 | | | | parent: 5:4409d547b708
422 | | | | parent: 5:4409d547b708
423 | | | | user: test
423 | | | | user: test
424 | | | | date: Thu Jan 01 00:00:06 1970 +0000
424 | | | | date: Thu Jan 01 00:00:06 1970 +0000
425 | | | | summary: (6) merge two known; one immediate left, one far left
425 | | | | summary: (6) merge two known; one immediate left, one far left
426 | | | |
426 | | | |
427 | o | | changeset: 5:4409d547b708
427 | o | | changeset: 5:4409d547b708
428 | |\ \ \ parent: 3:27eef8ed80b4
428 | |\ \ \ parent: 3:27eef8ed80b4
429 | | | | | parent: 4:26a8bac39d9f
429 | | | | | parent: 4:26a8bac39d9f
430 | | | | | user: test
430 | | | | | user: test
431 | | | | | date: Thu Jan 01 00:00:05 1970 +0000
431 | | | | | date: Thu Jan 01 00:00:05 1970 +0000
432 | | | | | summary: (5) expand
432 | | | | | summary: (5) expand
433 | | | | |
433 | | | | |
434 | | o | | changeset: 4:26a8bac39d9f
434 | | o | | changeset: 4:26a8bac39d9f
435 | |/|/ / parent: 1:6db2ef61d156
435 | |/|/ / parent: 1:6db2ef61d156
436 | | | | parent: 3:27eef8ed80b4
436 | | | | parent: 3:27eef8ed80b4
437 | | | | user: test
437 | | | | user: test
438 | | | | date: Thu Jan 01 00:00:04 1970 +0000
438 | | | | date: Thu Jan 01 00:00:04 1970 +0000
439 | | | | summary: (4) merge two known; one immediate left, one immediate right
439 | | | | summary: (4) merge two known; one immediate left, one immediate right
440 | | | |
440 | | | |
441 | o | | changeset: 3:27eef8ed80b4
441 | o | | changeset: 3:27eef8ed80b4
442 |/ / / user: test
442 |/ / / user: test
443 | | | date: Thu Jan 01 00:00:03 1970 +0000
443 | | | date: Thu Jan 01 00:00:03 1970 +0000
444 | | | summary: (3) collapse
444 | | | summary: (3) collapse
445 | | |
445 | | |
446 o | | changeset: 2:3d9a33b8d1e1
446 o | | changeset: 2:3d9a33b8d1e1
447 |/ / user: test
447 |/ / user: test
448 | | date: Thu Jan 01 00:00:02 1970 +0000
448 | | date: Thu Jan 01 00:00:02 1970 +0000
449 | | summary: (2) collapse
449 | | summary: (2) collapse
450 | |
450 | |
451 o | changeset: 1:6db2ef61d156
451 o | changeset: 1:6db2ef61d156
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: (1) collapse
454 | summary: (1) collapse
455 |
455 |
456 o changeset: 0:e6eb3150255d
456 o changeset: 0:e6eb3150255d
457 user: test
457 user: test
458 date: Thu Jan 01 00:00:00 1970 +0000
458 date: Thu Jan 01 00:00:00 1970 +0000
459 summary: (0) root
459 summary: (0) root
460
460
461
461
462 File glog:
462 File glog:
463 $ hg log -G a
463 $ hg log -G a
464 @ changeset: 34:fea3ac5810e0
464 @ changeset: 34:fea3ac5810e0
465 | tag: tip
465 | tag: tip
466 | parent: 32:d06dffa21a31
466 | parent: 32:d06dffa21a31
467 | user: test
467 | user: test
468 | date: Thu Jan 01 00:00:34 1970 +0000
468 | date: Thu Jan 01 00:00:34 1970 +0000
469 | summary: (34) head
469 | summary: (34) head
470 |
470 |
471 | o changeset: 33:68608f5145f9
471 | o changeset: 33:68608f5145f9
472 | | parent: 18:1aa84d96232a
472 | | parent: 18:1aa84d96232a
473 | | user: test
473 | | user: test
474 | | date: Thu Jan 01 00:00:33 1970 +0000
474 | | date: Thu Jan 01 00:00:33 1970 +0000
475 | | summary: (33) head
475 | | summary: (33) head
476 | |
476 | |
477 o | changeset: 32:d06dffa21a31
477 o | changeset: 32:d06dffa21a31
478 |\ \ parent: 27:886ed638191b
478 |\ \ parent: 27:886ed638191b
479 | | | parent: 31:621d83e11f67
479 | | | parent: 31:621d83e11f67
480 | | | user: test
480 | | | user: test
481 | | | date: Thu Jan 01 00:00:32 1970 +0000
481 | | | date: Thu Jan 01 00:00:32 1970 +0000
482 | | | summary: (32) expand
482 | | | summary: (32) expand
483 | | |
483 | | |
484 | o | changeset: 31:621d83e11f67
484 | o | changeset: 31:621d83e11f67
485 | |\ \ parent: 21:d42a756af44d
485 | |\ \ parent: 21:d42a756af44d
486 | | | | parent: 30:6e11cd4b648f
486 | | | | parent: 30:6e11cd4b648f
487 | | | | user: test
487 | | | | user: test
488 | | | | date: Thu Jan 01 00:00:31 1970 +0000
488 | | | | date: Thu Jan 01 00:00:31 1970 +0000
489 | | | | summary: (31) expand
489 | | | | summary: (31) expand
490 | | | |
490 | | | |
491 | | o | changeset: 30:6e11cd4b648f
491 | | o | changeset: 30:6e11cd4b648f
492 | | |\ \ parent: 28:44ecd0b9ae99
492 | | |\ \ parent: 28:44ecd0b9ae99
493 | | | | | parent: 29:cd9bb2be7593
493 | | | | | parent: 29:cd9bb2be7593
494 | | | | | user: test
494 | | | | | user: test
495 | | | | | date: Thu Jan 01 00:00:30 1970 +0000
495 | | | | | date: Thu Jan 01 00:00:30 1970 +0000
496 | | | | | summary: (30) expand
496 | | | | | summary: (30) expand
497 | | | | |
497 | | | | |
498 | | | o | changeset: 29:cd9bb2be7593
498 | | | o | changeset: 29:cd9bb2be7593
499 | | | | | parent: 0:e6eb3150255d
499 | | | | | parent: 0:e6eb3150255d
500 | | | | | user: test
500 | | | | | user: test
501 | | | | | date: Thu Jan 01 00:00:29 1970 +0000
501 | | | | | date: Thu Jan 01 00:00:29 1970 +0000
502 | | | | | summary: (29) regular commit
502 | | | | | summary: (29) regular commit
503 | | | | |
503 | | | | |
504 | | o | | changeset: 28:44ecd0b9ae99
504 | | o | | changeset: 28:44ecd0b9ae99
505 | | |\ \ \ parent: 1:6db2ef61d156
505 | | |\ \ \ parent: 1:6db2ef61d156
506 | | | | | | parent: 26:7f25b6c2f0b9
506 | | | | | | parent: 26:7f25b6c2f0b9
507 | | | | | | user: test
507 | | | | | | user: test
508 | | | | | | date: Thu Jan 01 00:00:28 1970 +0000
508 | | | | | | date: Thu Jan 01 00:00:28 1970 +0000
509 | | | | | | summary: (28) merge zero known
509 | | | | | | summary: (28) merge zero known
510 | | | | | |
510 | | | | | |
511 o | | | | | changeset: 27:886ed638191b
511 o | | | | | changeset: 27:886ed638191b
512 |/ / / / / parent: 21:d42a756af44d
512 |/ / / / / parent: 21:d42a756af44d
513 | | | | | user: test
513 | | | | | user: test
514 | | | | | date: Thu Jan 01 00:00:27 1970 +0000
514 | | | | | date: Thu Jan 01 00:00:27 1970 +0000
515 | | | | | summary: (27) collapse
515 | | | | | summary: (27) collapse
516 | | | | |
516 | | | | |
517 | | o---+ changeset: 26:7f25b6c2f0b9
517 | | o---+ changeset: 26:7f25b6c2f0b9
518 | | | | | parent: 18:1aa84d96232a
518 | | | | | parent: 18:1aa84d96232a
519 | | | | | parent: 25:91da8ed57247
519 | | | | | parent: 25:91da8ed57247
520 | | | | | user: test
520 | | | | | user: test
521 | | | | | date: Thu Jan 01 00:00:26 1970 +0000
521 | | | | | date: Thu Jan 01 00:00:26 1970 +0000
522 | | | | | summary: (26) merge one known; far right
522 | | | | | summary: (26) merge one known; far right
523 | | | | |
523 | | | | |
524 +---o | | changeset: 25:91da8ed57247
524 +---o | | changeset: 25:91da8ed57247
525 | | | | | parent: 21:d42a756af44d
525 | | | | | parent: 21:d42a756af44d
526 | | | | | parent: 24:a9c19a3d96b7
526 | | | | | parent: 24:a9c19a3d96b7
527 | | | | | user: test
527 | | | | | user: test
528 | | | | | date: Thu Jan 01 00:00:25 1970 +0000
528 | | | | | date: Thu Jan 01 00:00:25 1970 +0000
529 | | | | | summary: (25) merge one known; far left
529 | | | | | summary: (25) merge one known; far left
530 | | | | |
530 | | | | |
531 | | o | | changeset: 24:a9c19a3d96b7
531 | | o | | changeset: 24:a9c19a3d96b7
532 | | |\| | parent: 0:e6eb3150255d
532 | | |\| | parent: 0:e6eb3150255d
533 | | | | | parent: 23:a01cddf0766d
533 | | | | | parent: 23:a01cddf0766d
534 | | | | | user: test
534 | | | | | user: test
535 | | | | | date: Thu Jan 01 00:00:24 1970 +0000
535 | | | | | date: Thu Jan 01 00:00:24 1970 +0000
536 | | | | | summary: (24) merge one known; immediate right
536 | | | | | summary: (24) merge one known; immediate right
537 | | | | |
537 | | | | |
538 | | o | | changeset: 23:a01cddf0766d
538 | | o | | changeset: 23:a01cddf0766d
539 | |/| | | parent: 1:6db2ef61d156
539 | |/| | | parent: 1:6db2ef61d156
540 | | | | | parent: 22:e0d9cccacb5d
540 | | | | | parent: 22:e0d9cccacb5d
541 | | | | | user: test
541 | | | | | user: test
542 | | | | | date: Thu Jan 01 00:00:23 1970 +0000
542 | | | | | date: Thu Jan 01 00:00:23 1970 +0000
543 | | | | | summary: (23) merge one known; immediate left
543 | | | | | summary: (23) merge one known; immediate left
544 | | | | |
544 | | | | |
545 +---o---+ changeset: 22:e0d9cccacb5d
545 +---o---+ changeset: 22:e0d9cccacb5d
546 | | | | parent: 18:1aa84d96232a
546 | | | | parent: 18:1aa84d96232a
547 | | / / parent: 21:d42a756af44d
547 | | / / parent: 21:d42a756af44d
548 | | | | user: test
548 | | | | user: test
549 | | | | date: Thu Jan 01 00:00:22 1970 +0000
549 | | | | date: Thu Jan 01 00:00:22 1970 +0000
550 | | | | summary: (22) merge two known; one far left, one far right
550 | | | | summary: (22) merge two known; one far left, one far right
551 | | | |
551 | | | |
552 o | | | changeset: 21:d42a756af44d
552 o | | | changeset: 21:d42a756af44d
553 |\ \ \ \ parent: 19:31ddc2c1573b
553 |\ \ \ \ parent: 19:31ddc2c1573b
554 | | | | | parent: 20:d30ed6450e32
554 | | | | | parent: 20:d30ed6450e32
555 | | | | | user: test
555 | | | | | user: test
556 | | | | | date: Thu Jan 01 00:00:21 1970 +0000
556 | | | | | date: Thu Jan 01 00:00:21 1970 +0000
557 | | | | | summary: (21) expand
557 | | | | | summary: (21) expand
558 | | | | |
558 | | | | |
559 | o---+-+ changeset: 20:d30ed6450e32
559 | o---+-+ changeset: 20:d30ed6450e32
560 | | | | parent: 0:e6eb3150255d
560 | | | | parent: 0:e6eb3150255d
561 | / / / parent: 18:1aa84d96232a
561 | / / / parent: 18:1aa84d96232a
562 | | | | user: test
562 | | | | user: test
563 | | | | date: Thu Jan 01 00:00:20 1970 +0000
563 | | | | date: Thu Jan 01 00:00:20 1970 +0000
564 | | | | summary: (20) merge two known; two far right
564 | | | | summary: (20) merge two known; two far right
565 | | | |
565 | | | |
566 o | | | changeset: 19:31ddc2c1573b
566 o | | | changeset: 19:31ddc2c1573b
567 |\ \ \ \ parent: 15:1dda3f72782d
567 |\ \ \ \ parent: 15:1dda3f72782d
568 | | | | | parent: 17:44765d7c06e0
568 | | | | | parent: 17:44765d7c06e0
569 | | | | | user: test
569 | | | | | user: test
570 | | | | | date: Thu Jan 01 00:00:19 1970 +0000
570 | | | | | date: Thu Jan 01 00:00:19 1970 +0000
571 | | | | | summary: (19) expand
571 | | | | | summary: (19) expand
572 | | | | |
572 | | | | |
573 +---+---o changeset: 18:1aa84d96232a
573 +---+---o changeset: 18:1aa84d96232a
574 | | | | parent: 1:6db2ef61d156
574 | | | | parent: 1:6db2ef61d156
575 | | | | parent: 15:1dda3f72782d
575 | | | | parent: 15:1dda3f72782d
576 | | | | user: test
576 | | | | user: test
577 | | | | date: Thu Jan 01 00:00:18 1970 +0000
577 | | | | date: Thu Jan 01 00:00:18 1970 +0000
578 | | | | summary: (18) merge two known; two far left
578 | | | | summary: (18) merge two known; two far left
579 | | | |
579 | | | |
580 | o | | changeset: 17:44765d7c06e0
580 | o | | changeset: 17:44765d7c06e0
581 | |\ \ \ parent: 12:86b91144a6e9
581 | |\ \ \ parent: 12:86b91144a6e9
582 | | | | | parent: 16:3677d192927d
582 | | | | | parent: 16:3677d192927d
583 | | | | | user: test
583 | | | | | user: test
584 | | | | | date: Thu Jan 01 00:00:17 1970 +0000
584 | | | | | date: Thu Jan 01 00:00:17 1970 +0000
585 | | | | | summary: (17) expand
585 | | | | | summary: (17) expand
586 | | | | |
586 | | | | |
587 | | o---+ changeset: 16:3677d192927d
587 | | o---+ changeset: 16:3677d192927d
588 | | | | | parent: 0:e6eb3150255d
588 | | | | | parent: 0:e6eb3150255d
589 | | |/ / parent: 1:6db2ef61d156
589 | | |/ / parent: 1:6db2ef61d156
590 | | | | user: test
590 | | | | user: test
591 | | | | date: Thu Jan 01 00:00:16 1970 +0000
591 | | | | date: Thu Jan 01 00:00:16 1970 +0000
592 | | | | summary: (16) merge two known; one immediate right, one near right
592 | | | | summary: (16) merge two known; one immediate right, one near right
593 | | | |
593 | | | |
594 o | | | changeset: 15:1dda3f72782d
594 o | | | changeset: 15:1dda3f72782d
595 |\ \ \ \ parent: 13:22d8966a97e3
595 |\ \ \ \ parent: 13:22d8966a97e3
596 | | | | | parent: 14:8eac370358ef
596 | | | | | parent: 14:8eac370358ef
597 | | | | | user: test
597 | | | | | user: test
598 | | | | | date: Thu Jan 01 00:00:15 1970 +0000
598 | | | | | date: Thu Jan 01 00:00:15 1970 +0000
599 | | | | | summary: (15) expand
599 | | | | | summary: (15) expand
600 | | | | |
600 | | | | |
601 | o-----+ changeset: 14:8eac370358ef
601 | o-----+ changeset: 14:8eac370358ef
602 | | | | | parent: 0:e6eb3150255d
602 | | | | | parent: 0:e6eb3150255d
603 | |/ / / parent: 12:86b91144a6e9
603 | |/ / / parent: 12:86b91144a6e9
604 | | | | user: test
604 | | | | user: test
605 | | | | date: Thu Jan 01 00:00:14 1970 +0000
605 | | | | date: Thu Jan 01 00:00:14 1970 +0000
606 | | | | summary: (14) merge two known; one immediate right, one far right
606 | | | | summary: (14) merge two known; one immediate right, one far right
607 | | | |
607 | | | |
608 o | | | changeset: 13:22d8966a97e3
608 o | | | changeset: 13:22d8966a97e3
609 |\ \ \ \ parent: 9:7010c0af0a35
609 |\ \ \ \ parent: 9:7010c0af0a35
610 | | | | | parent: 11:832d76e6bdf2
610 | | | | | parent: 11:832d76e6bdf2
611 | | | | | user: test
611 | | | | | user: test
612 | | | | | date: Thu Jan 01 00:00:13 1970 +0000
612 | | | | | date: Thu Jan 01 00:00:13 1970 +0000
613 | | | | | summary: (13) expand
613 | | | | | summary: (13) expand
614 | | | | |
614 | | | | |
615 +---o | | changeset: 12:86b91144a6e9
615 +---o | | changeset: 12:86b91144a6e9
616 | | |/ / parent: 1:6db2ef61d156
616 | | |/ / parent: 1:6db2ef61d156
617 | | | | parent: 9:7010c0af0a35
617 | | | | parent: 9:7010c0af0a35
618 | | | | user: test
618 | | | | user: test
619 | | | | date: Thu Jan 01 00:00:12 1970 +0000
619 | | | | date: Thu Jan 01 00:00:12 1970 +0000
620 | | | | summary: (12) merge two known; one immediate right, one far left
620 | | | | summary: (12) merge two known; one immediate right, one far left
621 | | | |
621 | | | |
622 | o | | changeset: 11:832d76e6bdf2
622 | o | | changeset: 11:832d76e6bdf2
623 | |\ \ \ parent: 6:b105a072e251
623 | |\ \ \ parent: 6:b105a072e251
624 | | | | | parent: 10:74c64d036d72
624 | | | | | parent: 10:74c64d036d72
625 | | | | | user: test
625 | | | | | user: test
626 | | | | | date: Thu Jan 01 00:00:11 1970 +0000
626 | | | | | date: Thu Jan 01 00:00:11 1970 +0000
627 | | | | | summary: (11) expand
627 | | | | | summary: (11) expand
628 | | | | |
628 | | | | |
629 | | o---+ changeset: 10:74c64d036d72
629 | | o---+ changeset: 10:74c64d036d72
630 | | | | | parent: 0:e6eb3150255d
630 | | | | | parent: 0:e6eb3150255d
631 | |/ / / parent: 6:b105a072e251
631 | |/ / / parent: 6:b105a072e251
632 | | | | user: test
632 | | | | user: test
633 | | | | date: Thu Jan 01 00:00:10 1970 +0000
633 | | | | date: Thu Jan 01 00:00:10 1970 +0000
634 | | | | summary: (10) merge two known; one immediate left, one near right
634 | | | | summary: (10) merge two known; one immediate left, one near right
635 | | | |
635 | | | |
636 o | | | changeset: 9:7010c0af0a35
636 o | | | changeset: 9:7010c0af0a35
637 |\ \ \ \ parent: 7:b632bb1b1224
637 |\ \ \ \ parent: 7:b632bb1b1224
638 | | | | | parent: 8:7a0b11f71937
638 | | | | | parent: 8:7a0b11f71937
639 | | | | | user: test
639 | | | | | user: test
640 | | | | | date: Thu Jan 01 00:00:09 1970 +0000
640 | | | | | date: Thu Jan 01 00:00:09 1970 +0000
641 | | | | | summary: (9) expand
641 | | | | | summary: (9) expand
642 | | | | |
642 | | | | |
643 | o-----+ changeset: 8:7a0b11f71937
643 | o-----+ changeset: 8:7a0b11f71937
644 | | | | | parent: 0:e6eb3150255d
644 | | | | | parent: 0:e6eb3150255d
645 |/ / / / parent: 7:b632bb1b1224
645 |/ / / / parent: 7:b632bb1b1224
646 | | | | user: test
646 | | | | user: test
647 | | | | date: Thu Jan 01 00:00:08 1970 +0000
647 | | | | date: Thu Jan 01 00:00:08 1970 +0000
648 | | | | summary: (8) merge two known; one immediate left, one far right
648 | | | | summary: (8) merge two known; one immediate left, one far right
649 | | | |
649 | | | |
650 o | | | changeset: 7:b632bb1b1224
650 o | | | changeset: 7:b632bb1b1224
651 |\ \ \ \ parent: 2:3d9a33b8d1e1
651 |\ \ \ \ parent: 2:3d9a33b8d1e1
652 | | | | | parent: 5:4409d547b708
652 | | | | | parent: 5:4409d547b708
653 | | | | | user: test
653 | | | | | user: test
654 | | | | | date: Thu Jan 01 00:00:07 1970 +0000
654 | | | | | date: Thu Jan 01 00:00:07 1970 +0000
655 | | | | | summary: (7) expand
655 | | | | | summary: (7) expand
656 | | | | |
656 | | | | |
657 +---o | | changeset: 6:b105a072e251
657 +---o | | changeset: 6:b105a072e251
658 | |/ / / parent: 2:3d9a33b8d1e1
658 | |/ / / parent: 2:3d9a33b8d1e1
659 | | | | parent: 5:4409d547b708
659 | | | | parent: 5:4409d547b708
660 | | | | user: test
660 | | | | user: test
661 | | | | date: Thu Jan 01 00:00:06 1970 +0000
661 | | | | date: Thu Jan 01 00:00:06 1970 +0000
662 | | | | summary: (6) merge two known; one immediate left, one far left
662 | | | | summary: (6) merge two known; one immediate left, one far left
663 | | | |
663 | | | |
664 | o | | changeset: 5:4409d547b708
664 | o | | changeset: 5:4409d547b708
665 | |\ \ \ parent: 3:27eef8ed80b4
665 | |\ \ \ parent: 3:27eef8ed80b4
666 | | | | | parent: 4:26a8bac39d9f
666 | | | | | parent: 4:26a8bac39d9f
667 | | | | | user: test
667 | | | | | user: test
668 | | | | | date: Thu Jan 01 00:00:05 1970 +0000
668 | | | | | date: Thu Jan 01 00:00:05 1970 +0000
669 | | | | | summary: (5) expand
669 | | | | | summary: (5) expand
670 | | | | |
670 | | | | |
671 | | o | | changeset: 4:26a8bac39d9f
671 | | o | | changeset: 4:26a8bac39d9f
672 | |/|/ / parent: 1:6db2ef61d156
672 | |/|/ / parent: 1:6db2ef61d156
673 | | | | parent: 3:27eef8ed80b4
673 | | | | parent: 3:27eef8ed80b4
674 | | | | user: test
674 | | | | user: test
675 | | | | date: Thu Jan 01 00:00:04 1970 +0000
675 | | | | date: Thu Jan 01 00:00:04 1970 +0000
676 | | | | summary: (4) merge two known; one immediate left, one immediate right
676 | | | | summary: (4) merge two known; one immediate left, one immediate right
677 | | | |
677 | | | |
678 | o | | changeset: 3:27eef8ed80b4
678 | o | | changeset: 3:27eef8ed80b4
679 |/ / / user: test
679 |/ / / user: test
680 | | | date: Thu Jan 01 00:00:03 1970 +0000
680 | | | date: Thu Jan 01 00:00:03 1970 +0000
681 | | | summary: (3) collapse
681 | | | summary: (3) collapse
682 | | |
682 | | |
683 o | | changeset: 2:3d9a33b8d1e1
683 o | | changeset: 2:3d9a33b8d1e1
684 |/ / user: test
684 |/ / user: test
685 | | date: Thu Jan 01 00:00:02 1970 +0000
685 | | date: Thu Jan 01 00:00:02 1970 +0000
686 | | summary: (2) collapse
686 | | summary: (2) collapse
687 | |
687 | |
688 o | changeset: 1:6db2ef61d156
688 o | changeset: 1:6db2ef61d156
689 |/ user: test
689 |/ user: test
690 | date: Thu Jan 01 00:00:01 1970 +0000
690 | date: Thu Jan 01 00:00:01 1970 +0000
691 | summary: (1) collapse
691 | summary: (1) collapse
692 |
692 |
693 o changeset: 0:e6eb3150255d
693 o changeset: 0:e6eb3150255d
694 user: test
694 user: test
695 date: Thu Jan 01 00:00:00 1970 +0000
695 date: Thu Jan 01 00:00:00 1970 +0000
696 summary: (0) root
696 summary: (0) root
697
697
698
698
699 File glog per revset:
699 File glog per revset:
700
700
701 $ hg log -G -r 'file("a")'
701 $ hg log -G -r 'file("a")'
702 @ changeset: 34:fea3ac5810e0
702 @ changeset: 34:fea3ac5810e0
703 | tag: tip
703 | tag: tip
704 | parent: 32:d06dffa21a31
704 | parent: 32:d06dffa21a31
705 | user: test
705 | user: test
706 | date: Thu Jan 01 00:00:34 1970 +0000
706 | date: Thu Jan 01 00:00:34 1970 +0000
707 | summary: (34) head
707 | summary: (34) head
708 |
708 |
709 | o changeset: 33:68608f5145f9
709 | o changeset: 33:68608f5145f9
710 | | parent: 18:1aa84d96232a
710 | | parent: 18:1aa84d96232a
711 | | user: test
711 | | user: test
712 | | date: Thu Jan 01 00:00:33 1970 +0000
712 | | date: Thu Jan 01 00:00:33 1970 +0000
713 | | summary: (33) head
713 | | summary: (33) head
714 | |
714 | |
715 o | changeset: 32:d06dffa21a31
715 o | changeset: 32:d06dffa21a31
716 |\ \ parent: 27:886ed638191b
716 |\ \ parent: 27:886ed638191b
717 | | | parent: 31:621d83e11f67
717 | | | parent: 31:621d83e11f67
718 | | | user: test
718 | | | user: test
719 | | | date: Thu Jan 01 00:00:32 1970 +0000
719 | | | date: Thu Jan 01 00:00:32 1970 +0000
720 | | | summary: (32) expand
720 | | | summary: (32) expand
721 | | |
721 | | |
722 | o | changeset: 31:621d83e11f67
722 | o | changeset: 31:621d83e11f67
723 | |\ \ parent: 21:d42a756af44d
723 | |\ \ parent: 21:d42a756af44d
724 | | | | parent: 30:6e11cd4b648f
724 | | | | parent: 30:6e11cd4b648f
725 | | | | user: test
725 | | | | user: test
726 | | | | date: Thu Jan 01 00:00:31 1970 +0000
726 | | | | date: Thu Jan 01 00:00:31 1970 +0000
727 | | | | summary: (31) expand
727 | | | | summary: (31) expand
728 | | | |
728 | | | |
729 | | o | changeset: 30:6e11cd4b648f
729 | | o | changeset: 30:6e11cd4b648f
730 | | |\ \ parent: 28:44ecd0b9ae99
730 | | |\ \ parent: 28:44ecd0b9ae99
731 | | | | | parent: 29:cd9bb2be7593
731 | | | | | parent: 29:cd9bb2be7593
732 | | | | | user: test
732 | | | | | user: test
733 | | | | | date: Thu Jan 01 00:00:30 1970 +0000
733 | | | | | date: Thu Jan 01 00:00:30 1970 +0000
734 | | | | | summary: (30) expand
734 | | | | | summary: (30) expand
735 | | | | |
735 | | | | |
736 | | | o | changeset: 29:cd9bb2be7593
736 | | | o | changeset: 29:cd9bb2be7593
737 | | | | | parent: 0:e6eb3150255d
737 | | | | | parent: 0:e6eb3150255d
738 | | | | | user: test
738 | | | | | user: test
739 | | | | | date: Thu Jan 01 00:00:29 1970 +0000
739 | | | | | date: Thu Jan 01 00:00:29 1970 +0000
740 | | | | | summary: (29) regular commit
740 | | | | | summary: (29) regular commit
741 | | | | |
741 | | | | |
742 | | o | | changeset: 28:44ecd0b9ae99
742 | | o | | changeset: 28:44ecd0b9ae99
743 | | |\ \ \ parent: 1:6db2ef61d156
743 | | |\ \ \ parent: 1:6db2ef61d156
744 | | | | | | parent: 26:7f25b6c2f0b9
744 | | | | | | parent: 26:7f25b6c2f0b9
745 | | | | | | user: test
745 | | | | | | user: test
746 | | | | | | date: Thu Jan 01 00:00:28 1970 +0000
746 | | | | | | date: Thu Jan 01 00:00:28 1970 +0000
747 | | | | | | summary: (28) merge zero known
747 | | | | | | summary: (28) merge zero known
748 | | | | | |
748 | | | | | |
749 o | | | | | changeset: 27:886ed638191b
749 o | | | | | changeset: 27:886ed638191b
750 |/ / / / / parent: 21:d42a756af44d
750 |/ / / / / parent: 21:d42a756af44d
751 | | | | | user: test
751 | | | | | user: test
752 | | | | | date: Thu Jan 01 00:00:27 1970 +0000
752 | | | | | date: Thu Jan 01 00:00:27 1970 +0000
753 | | | | | summary: (27) collapse
753 | | | | | summary: (27) collapse
754 | | | | |
754 | | | | |
755 | | o---+ changeset: 26:7f25b6c2f0b9
755 | | o---+ changeset: 26:7f25b6c2f0b9
756 | | | | | parent: 18:1aa84d96232a
756 | | | | | parent: 18:1aa84d96232a
757 | | | | | parent: 25:91da8ed57247
757 | | | | | parent: 25:91da8ed57247
758 | | | | | user: test
758 | | | | | user: test
759 | | | | | date: Thu Jan 01 00:00:26 1970 +0000
759 | | | | | date: Thu Jan 01 00:00:26 1970 +0000
760 | | | | | summary: (26) merge one known; far right
760 | | | | | summary: (26) merge one known; far right
761 | | | | |
761 | | | | |
762 +---o | | changeset: 25:91da8ed57247
762 +---o | | changeset: 25:91da8ed57247
763 | | | | | parent: 21:d42a756af44d
763 | | | | | parent: 21:d42a756af44d
764 | | | | | parent: 24:a9c19a3d96b7
764 | | | | | parent: 24:a9c19a3d96b7
765 | | | | | user: test
765 | | | | | user: test
766 | | | | | date: Thu Jan 01 00:00:25 1970 +0000
766 | | | | | date: Thu Jan 01 00:00:25 1970 +0000
767 | | | | | summary: (25) merge one known; far left
767 | | | | | summary: (25) merge one known; far left
768 | | | | |
768 | | | | |
769 | | o | | changeset: 24:a9c19a3d96b7
769 | | o | | changeset: 24:a9c19a3d96b7
770 | | |\| | parent: 0:e6eb3150255d
770 | | |\| | parent: 0:e6eb3150255d
771 | | | | | parent: 23:a01cddf0766d
771 | | | | | parent: 23:a01cddf0766d
772 | | | | | user: test
772 | | | | | user: test
773 | | | | | date: Thu Jan 01 00:00:24 1970 +0000
773 | | | | | date: Thu Jan 01 00:00:24 1970 +0000
774 | | | | | summary: (24) merge one known; immediate right
774 | | | | | summary: (24) merge one known; immediate right
775 | | | | |
775 | | | | |
776 | | o | | changeset: 23:a01cddf0766d
776 | | o | | changeset: 23:a01cddf0766d
777 | |/| | | parent: 1:6db2ef61d156
777 | |/| | | parent: 1:6db2ef61d156
778 | | | | | parent: 22:e0d9cccacb5d
778 | | | | | parent: 22:e0d9cccacb5d
779 | | | | | user: test
779 | | | | | user: test
780 | | | | | date: Thu Jan 01 00:00:23 1970 +0000
780 | | | | | date: Thu Jan 01 00:00:23 1970 +0000
781 | | | | | summary: (23) merge one known; immediate left
781 | | | | | summary: (23) merge one known; immediate left
782 | | | | |
782 | | | | |
783 +---o---+ changeset: 22:e0d9cccacb5d
783 +---o---+ changeset: 22:e0d9cccacb5d
784 | | | | parent: 18:1aa84d96232a
784 | | | | parent: 18:1aa84d96232a
785 | | / / parent: 21:d42a756af44d
785 | | / / parent: 21:d42a756af44d
786 | | | | user: test
786 | | | | user: test
787 | | | | date: Thu Jan 01 00:00:22 1970 +0000
787 | | | | date: Thu Jan 01 00:00:22 1970 +0000
788 | | | | summary: (22) merge two known; one far left, one far right
788 | | | | summary: (22) merge two known; one far left, one far right
789 | | | |
789 | | | |
790 o | | | changeset: 21:d42a756af44d
790 o | | | changeset: 21:d42a756af44d
791 |\ \ \ \ parent: 19:31ddc2c1573b
791 |\ \ \ \ parent: 19:31ddc2c1573b
792 | | | | | parent: 20:d30ed6450e32
792 | | | | | parent: 20:d30ed6450e32
793 | | | | | user: test
793 | | | | | user: test
794 | | | | | date: Thu Jan 01 00:00:21 1970 +0000
794 | | | | | date: Thu Jan 01 00:00:21 1970 +0000
795 | | | | | summary: (21) expand
795 | | | | | summary: (21) expand
796 | | | | |
796 | | | | |
797 | o---+-+ changeset: 20:d30ed6450e32
797 | o---+-+ changeset: 20:d30ed6450e32
798 | | | | parent: 0:e6eb3150255d
798 | | | | parent: 0:e6eb3150255d
799 | / / / parent: 18:1aa84d96232a
799 | / / / parent: 18:1aa84d96232a
800 | | | | user: test
800 | | | | user: test
801 | | | | date: Thu Jan 01 00:00:20 1970 +0000
801 | | | | date: Thu Jan 01 00:00:20 1970 +0000
802 | | | | summary: (20) merge two known; two far right
802 | | | | summary: (20) merge two known; two far right
803 | | | |
803 | | | |
804 o | | | changeset: 19:31ddc2c1573b
804 o | | | changeset: 19:31ddc2c1573b
805 |\ \ \ \ parent: 15:1dda3f72782d
805 |\ \ \ \ parent: 15:1dda3f72782d
806 | | | | | parent: 17:44765d7c06e0
806 | | | | | parent: 17:44765d7c06e0
807 | | | | | user: test
807 | | | | | user: test
808 | | | | | date: Thu Jan 01 00:00:19 1970 +0000
808 | | | | | date: Thu Jan 01 00:00:19 1970 +0000
809 | | | | | summary: (19) expand
809 | | | | | summary: (19) expand
810 | | | | |
810 | | | | |
811 +---+---o changeset: 18:1aa84d96232a
811 +---+---o changeset: 18:1aa84d96232a
812 | | | | parent: 1:6db2ef61d156
812 | | | | parent: 1:6db2ef61d156
813 | | | | parent: 15:1dda3f72782d
813 | | | | parent: 15:1dda3f72782d
814 | | | | user: test
814 | | | | user: test
815 | | | | date: Thu Jan 01 00:00:18 1970 +0000
815 | | | | date: Thu Jan 01 00:00:18 1970 +0000
816 | | | | summary: (18) merge two known; two far left
816 | | | | summary: (18) merge two known; two far left
817 | | | |
817 | | | |
818 | o | | changeset: 17:44765d7c06e0
818 | o | | changeset: 17:44765d7c06e0
819 | |\ \ \ parent: 12:86b91144a6e9
819 | |\ \ \ parent: 12:86b91144a6e9
820 | | | | | parent: 16:3677d192927d
820 | | | | | parent: 16:3677d192927d
821 | | | | | user: test
821 | | | | | user: test
822 | | | | | date: Thu Jan 01 00:00:17 1970 +0000
822 | | | | | date: Thu Jan 01 00:00:17 1970 +0000
823 | | | | | summary: (17) expand
823 | | | | | summary: (17) expand
824 | | | | |
824 | | | | |
825 | | o---+ changeset: 16:3677d192927d
825 | | o---+ changeset: 16:3677d192927d
826 | | | | | parent: 0:e6eb3150255d
826 | | | | | parent: 0:e6eb3150255d
827 | | |/ / parent: 1:6db2ef61d156
827 | | |/ / parent: 1:6db2ef61d156
828 | | | | user: test
828 | | | | user: test
829 | | | | date: Thu Jan 01 00:00:16 1970 +0000
829 | | | | date: Thu Jan 01 00:00:16 1970 +0000
830 | | | | summary: (16) merge two known; one immediate right, one near right
830 | | | | summary: (16) merge two known; one immediate right, one near right
831 | | | |
831 | | | |
832 o | | | changeset: 15:1dda3f72782d
832 o | | | changeset: 15:1dda3f72782d
833 |\ \ \ \ parent: 13:22d8966a97e3
833 |\ \ \ \ parent: 13:22d8966a97e3
834 | | | | | parent: 14:8eac370358ef
834 | | | | | parent: 14:8eac370358ef
835 | | | | | user: test
835 | | | | | user: test
836 | | | | | date: Thu Jan 01 00:00:15 1970 +0000
836 | | | | | date: Thu Jan 01 00:00:15 1970 +0000
837 | | | | | summary: (15) expand
837 | | | | | summary: (15) expand
838 | | | | |
838 | | | | |
839 | o-----+ changeset: 14:8eac370358ef
839 | o-----+ changeset: 14:8eac370358ef
840 | | | | | parent: 0:e6eb3150255d
840 | | | | | parent: 0:e6eb3150255d
841 | |/ / / parent: 12:86b91144a6e9
841 | |/ / / parent: 12:86b91144a6e9
842 | | | | user: test
842 | | | | user: test
843 | | | | date: Thu Jan 01 00:00:14 1970 +0000
843 | | | | date: Thu Jan 01 00:00:14 1970 +0000
844 | | | | summary: (14) merge two known; one immediate right, one far right
844 | | | | summary: (14) merge two known; one immediate right, one far right
845 | | | |
845 | | | |
846 o | | | changeset: 13:22d8966a97e3
846 o | | | changeset: 13:22d8966a97e3
847 |\ \ \ \ parent: 9:7010c0af0a35
847 |\ \ \ \ parent: 9:7010c0af0a35
848 | | | | | parent: 11:832d76e6bdf2
848 | | | | | parent: 11:832d76e6bdf2
849 | | | | | user: test
849 | | | | | user: test
850 | | | | | date: Thu Jan 01 00:00:13 1970 +0000
850 | | | | | date: Thu Jan 01 00:00:13 1970 +0000
851 | | | | | summary: (13) expand
851 | | | | | summary: (13) expand
852 | | | | |
852 | | | | |
853 +---o | | changeset: 12:86b91144a6e9
853 +---o | | changeset: 12:86b91144a6e9
854 | | |/ / parent: 1:6db2ef61d156
854 | | |/ / parent: 1:6db2ef61d156
855 | | | | parent: 9:7010c0af0a35
855 | | | | parent: 9:7010c0af0a35
856 | | | | user: test
856 | | | | user: test
857 | | | | date: Thu Jan 01 00:00:12 1970 +0000
857 | | | | date: Thu Jan 01 00:00:12 1970 +0000
858 | | | | summary: (12) merge two known; one immediate right, one far left
858 | | | | summary: (12) merge two known; one immediate right, one far left
859 | | | |
859 | | | |
860 | o | | changeset: 11:832d76e6bdf2
860 | o | | changeset: 11:832d76e6bdf2
861 | |\ \ \ parent: 6:b105a072e251
861 | |\ \ \ parent: 6:b105a072e251
862 | | | | | parent: 10:74c64d036d72
862 | | | | | parent: 10:74c64d036d72
863 | | | | | user: test
863 | | | | | user: test
864 | | | | | date: Thu Jan 01 00:00:11 1970 +0000
864 | | | | | date: Thu Jan 01 00:00:11 1970 +0000
865 | | | | | summary: (11) expand
865 | | | | | summary: (11) expand
866 | | | | |
866 | | | | |
867 | | o---+ changeset: 10:74c64d036d72
867 | | o---+ changeset: 10:74c64d036d72
868 | | | | | parent: 0:e6eb3150255d
868 | | | | | parent: 0:e6eb3150255d
869 | |/ / / parent: 6:b105a072e251
869 | |/ / / parent: 6:b105a072e251
870 | | | | user: test
870 | | | | user: test
871 | | | | date: Thu Jan 01 00:00:10 1970 +0000
871 | | | | date: Thu Jan 01 00:00:10 1970 +0000
872 | | | | summary: (10) merge two known; one immediate left, one near right
872 | | | | summary: (10) merge two known; one immediate left, one near right
873 | | | |
873 | | | |
874 o | | | changeset: 9:7010c0af0a35
874 o | | | changeset: 9:7010c0af0a35
875 |\ \ \ \ parent: 7:b632bb1b1224
875 |\ \ \ \ parent: 7:b632bb1b1224
876 | | | | | parent: 8:7a0b11f71937
876 | | | | | parent: 8:7a0b11f71937
877 | | | | | user: test
877 | | | | | user: test
878 | | | | | date: Thu Jan 01 00:00:09 1970 +0000
878 | | | | | date: Thu Jan 01 00:00:09 1970 +0000
879 | | | | | summary: (9) expand
879 | | | | | summary: (9) expand
880 | | | | |
880 | | | | |
881 | o-----+ changeset: 8:7a0b11f71937
881 | o-----+ changeset: 8:7a0b11f71937
882 | | | | | parent: 0:e6eb3150255d
882 | | | | | parent: 0:e6eb3150255d
883 |/ / / / parent: 7:b632bb1b1224
883 |/ / / / parent: 7:b632bb1b1224
884 | | | | user: test
884 | | | | user: test
885 | | | | date: Thu Jan 01 00:00:08 1970 +0000
885 | | | | date: Thu Jan 01 00:00:08 1970 +0000
886 | | | | summary: (8) merge two known; one immediate left, one far right
886 | | | | summary: (8) merge two known; one immediate left, one far right
887 | | | |
887 | | | |
888 o | | | changeset: 7:b632bb1b1224
888 o | | | changeset: 7:b632bb1b1224
889 |\ \ \ \ parent: 2:3d9a33b8d1e1
889 |\ \ \ \ parent: 2:3d9a33b8d1e1
890 | | | | | parent: 5:4409d547b708
890 | | | | | parent: 5:4409d547b708
891 | | | | | user: test
891 | | | | | user: test
892 | | | | | date: Thu Jan 01 00:00:07 1970 +0000
892 | | | | | date: Thu Jan 01 00:00:07 1970 +0000
893 | | | | | summary: (7) expand
893 | | | | | summary: (7) expand
894 | | | | |
894 | | | | |
895 +---o | | changeset: 6:b105a072e251
895 +---o | | changeset: 6:b105a072e251
896 | |/ / / parent: 2:3d9a33b8d1e1
896 | |/ / / parent: 2:3d9a33b8d1e1
897 | | | | parent: 5:4409d547b708
897 | | | | parent: 5:4409d547b708
898 | | | | user: test
898 | | | | user: test
899 | | | | date: Thu Jan 01 00:00:06 1970 +0000
899 | | | | date: Thu Jan 01 00:00:06 1970 +0000
900 | | | | summary: (6) merge two known; one immediate left, one far left
900 | | | | summary: (6) merge two known; one immediate left, one far left
901 | | | |
901 | | | |
902 | o | | changeset: 5:4409d547b708
902 | o | | changeset: 5:4409d547b708
903 | |\ \ \ parent: 3:27eef8ed80b4
903 | |\ \ \ parent: 3:27eef8ed80b4
904 | | | | | parent: 4:26a8bac39d9f
904 | | | | | parent: 4:26a8bac39d9f
905 | | | | | user: test
905 | | | | | user: test
906 | | | | | date: Thu Jan 01 00:00:05 1970 +0000
906 | | | | | date: Thu Jan 01 00:00:05 1970 +0000
907 | | | | | summary: (5) expand
907 | | | | | summary: (5) expand
908 | | | | |
908 | | | | |
909 | | o | | changeset: 4:26a8bac39d9f
909 | | o | | changeset: 4:26a8bac39d9f
910 | |/|/ / parent: 1:6db2ef61d156
910 | |/|/ / parent: 1:6db2ef61d156
911 | | | | parent: 3:27eef8ed80b4
911 | | | | parent: 3:27eef8ed80b4
912 | | | | user: test
912 | | | | user: test
913 | | | | date: Thu Jan 01 00:00:04 1970 +0000
913 | | | | date: Thu Jan 01 00:00:04 1970 +0000
914 | | | | summary: (4) merge two known; one immediate left, one immediate right
914 | | | | summary: (4) merge two known; one immediate left, one immediate right
915 | | | |
915 | | | |
916 | o | | changeset: 3:27eef8ed80b4
916 | o | | changeset: 3:27eef8ed80b4
917 |/ / / user: test
917 |/ / / user: test
918 | | | date: Thu Jan 01 00:00:03 1970 +0000
918 | | | date: Thu Jan 01 00:00:03 1970 +0000
919 | | | summary: (3) collapse
919 | | | summary: (3) collapse
920 | | |
920 | | |
921 o | | changeset: 2:3d9a33b8d1e1
921 o | | changeset: 2:3d9a33b8d1e1
922 |/ / user: test
922 |/ / user: test
923 | | date: Thu Jan 01 00:00:02 1970 +0000
923 | | date: Thu Jan 01 00:00:02 1970 +0000
924 | | summary: (2) collapse
924 | | summary: (2) collapse
925 | |
925 | |
926 o | changeset: 1:6db2ef61d156
926 o | changeset: 1:6db2ef61d156
927 |/ user: test
927 |/ user: test
928 | date: Thu Jan 01 00:00:01 1970 +0000
928 | date: Thu Jan 01 00:00:01 1970 +0000
929 | summary: (1) collapse
929 | summary: (1) collapse
930 |
930 |
931 o changeset: 0:e6eb3150255d
931 o changeset: 0:e6eb3150255d
932 user: test
932 user: test
933 date: Thu Jan 01 00:00:00 1970 +0000
933 date: Thu Jan 01 00:00:00 1970 +0000
934 summary: (0) root
934 summary: (0) root
935
935
936
936
937
937
938 File glog per revset (only merges):
938 File glog per revset (only merges):
939
939
940 $ hg log -G -r 'file("a")' -m
940 $ hg log -G -r 'file("a")' -m
941 o changeset: 32:d06dffa21a31
941 o changeset: 32:d06dffa21a31
942 |\ parent: 27:886ed638191b
942 |\ parent: 27:886ed638191b
943 | | parent: 31:621d83e11f67
943 | | parent: 31:621d83e11f67
944 | | user: test
944 | | user: test
945 | | date: Thu Jan 01 00:00:32 1970 +0000
945 | | date: Thu Jan 01 00:00:32 1970 +0000
946 | | summary: (32) expand
946 | | summary: (32) expand
947 | |
947 | |
948 o | changeset: 31:621d83e11f67
948 o | changeset: 31:621d83e11f67
949 |\| parent: 21:d42a756af44d
949 |\| parent: 21:d42a756af44d
950 | | parent: 30:6e11cd4b648f
950 | | parent: 30:6e11cd4b648f
951 | | user: test
951 | | user: test
952 | | date: Thu Jan 01 00:00:31 1970 +0000
952 | | date: Thu Jan 01 00:00:31 1970 +0000
953 | | summary: (31) expand
953 | | summary: (31) expand
954 | |
954 | |
955 o | changeset: 30:6e11cd4b648f
955 o | changeset: 30:6e11cd4b648f
956 |\ \ parent: 28:44ecd0b9ae99
956 |\ \ parent: 28:44ecd0b9ae99
957 | | | parent: 29:cd9bb2be7593
957 | | | parent: 29:cd9bb2be7593
958 | | | user: test
958 | | | user: test
959 | | | date: Thu Jan 01 00:00:30 1970 +0000
959 | | | date: Thu Jan 01 00:00:30 1970 +0000
960 | | | summary: (30) expand
960 | | | summary: (30) expand
961 | | |
961 | | |
962 o | | changeset: 28:44ecd0b9ae99
962 o | | changeset: 28:44ecd0b9ae99
963 |\ \ \ parent: 1:6db2ef61d156
963 |\ \ \ parent: 1:6db2ef61d156
964 | | | | parent: 26:7f25b6c2f0b9
964 | | | | parent: 26:7f25b6c2f0b9
965 | | | | user: test
965 | | | | user: test
966 | | | | date: Thu Jan 01 00:00:28 1970 +0000
966 | | | | date: Thu Jan 01 00:00:28 1970 +0000
967 | | | | summary: (28) merge zero known
967 | | | | summary: (28) merge zero known
968 | | | |
968 | | | |
969 o | | | changeset: 26:7f25b6c2f0b9
969 o | | | changeset: 26:7f25b6c2f0b9
970 |\ \ \ \ parent: 18:1aa84d96232a
970 |\ \ \ \ parent: 18:1aa84d96232a
971 | | | | | parent: 25:91da8ed57247
971 | | | | | parent: 25:91da8ed57247
972 | | | | | user: test
972 | | | | | user: test
973 | | | | | date: Thu Jan 01 00:00:26 1970 +0000
973 | | | | | date: Thu Jan 01 00:00:26 1970 +0000
974 | | | | | summary: (26) merge one known; far right
974 | | | | | summary: (26) merge one known; far right
975 | | | | |
975 | | | | |
976 | o-----+ changeset: 25:91da8ed57247
976 | o-----+ changeset: 25:91da8ed57247
977 | | | | | parent: 21:d42a756af44d
977 | | | | | parent: 21:d42a756af44d
978 | | | | | parent: 24:a9c19a3d96b7
978 | | | | | parent: 24:a9c19a3d96b7
979 | | | | | user: test
979 | | | | | user: test
980 | | | | | date: Thu Jan 01 00:00:25 1970 +0000
980 | | | | | date: Thu Jan 01 00:00:25 1970 +0000
981 | | | | | summary: (25) merge one known; far left
981 | | | | | summary: (25) merge one known; far left
982 | | | | |
982 | | | | |
983 | o | | | changeset: 24:a9c19a3d96b7
983 | o | | | changeset: 24:a9c19a3d96b7
984 | |\ \ \ \ parent: 0:e6eb3150255d
984 | |\ \ \ \ parent: 0:e6eb3150255d
985 | | | | | | parent: 23:a01cddf0766d
985 | | | | | | parent: 23:a01cddf0766d
986 | | | | | | user: test
986 | | | | | | user: test
987 | | | | | | date: Thu Jan 01 00:00:24 1970 +0000
987 | | | | | | date: Thu Jan 01 00:00:24 1970 +0000
988 | | | | | | summary: (24) merge one known; immediate right
988 | | | | | | summary: (24) merge one known; immediate right
989 | | | | | |
989 | | | | | |
990 | o---+ | | changeset: 23:a01cddf0766d
990 | o---+ | | changeset: 23:a01cddf0766d
991 | | | | | | parent: 1:6db2ef61d156
991 | | | | | | parent: 1:6db2ef61d156
992 | | | | | | parent: 22:e0d9cccacb5d
992 | | | | | | parent: 22:e0d9cccacb5d
993 | | | | | | user: test
993 | | | | | | user: test
994 | | | | | | date: Thu Jan 01 00:00:23 1970 +0000
994 | | | | | | date: Thu Jan 01 00:00:23 1970 +0000
995 | | | | | | summary: (23) merge one known; immediate left
995 | | | | | | summary: (23) merge one known; immediate left
996 | | | | | |
996 | | | | | |
997 | o-------+ changeset: 22:e0d9cccacb5d
997 | o-------+ changeset: 22:e0d9cccacb5d
998 | | | | | | parent: 18:1aa84d96232a
998 | | | | | | parent: 18:1aa84d96232a
999 |/ / / / / parent: 21:d42a756af44d
999 |/ / / / / parent: 21:d42a756af44d
1000 | | | | | user: test
1000 | | | | | user: test
1001 | | | | | date: Thu Jan 01 00:00:22 1970 +0000
1001 | | | | | date: Thu Jan 01 00:00:22 1970 +0000
1002 | | | | | summary: (22) merge two known; one far left, one far right
1002 | | | | | summary: (22) merge two known; one far left, one far right
1003 | | | | |
1003 | | | | |
1004 | | | | o changeset: 21:d42a756af44d
1004 | | | | o changeset: 21:d42a756af44d
1005 | | | | |\ parent: 19:31ddc2c1573b
1005 | | | | |\ parent: 19:31ddc2c1573b
1006 | | | | | | parent: 20:d30ed6450e32
1006 | | | | | | parent: 20:d30ed6450e32
1007 | | | | | | user: test
1007 | | | | | | user: test
1008 | | | | | | date: Thu Jan 01 00:00:21 1970 +0000
1008 | | | | | | date: Thu Jan 01 00:00:21 1970 +0000
1009 | | | | | | summary: (21) expand
1009 | | | | | | summary: (21) expand
1010 | | | | | |
1010 | | | | | |
1011 +-+-------o changeset: 20:d30ed6450e32
1011 +-+-------o changeset: 20:d30ed6450e32
1012 | | | | | parent: 0:e6eb3150255d
1012 | | | | | parent: 0:e6eb3150255d
1013 | | | | | parent: 18:1aa84d96232a
1013 | | | | | parent: 18:1aa84d96232a
1014 | | | | | user: test
1014 | | | | | user: test
1015 | | | | | date: Thu Jan 01 00:00:20 1970 +0000
1015 | | | | | date: Thu Jan 01 00:00:20 1970 +0000
1016 | | | | | summary: (20) merge two known; two far right
1016 | | | | | summary: (20) merge two known; two far right
1017 | | | | |
1017 | | | | |
1018 | | | | o changeset: 19:31ddc2c1573b
1018 | | | | o changeset: 19:31ddc2c1573b
1019 | | | | |\ parent: 15:1dda3f72782d
1019 | | | | |\ parent: 15:1dda3f72782d
1020 | | | | | | parent: 17:44765d7c06e0
1020 | | | | | | parent: 17:44765d7c06e0
1021 | | | | | | user: test
1021 | | | | | | user: test
1022 | | | | | | date: Thu Jan 01 00:00:19 1970 +0000
1022 | | | | | | date: Thu Jan 01 00:00:19 1970 +0000
1023 | | | | | | summary: (19) expand
1023 | | | | | | summary: (19) expand
1024 | | | | | |
1024 | | | | | |
1025 o---+---+ | changeset: 18:1aa84d96232a
1025 o---+---+ | changeset: 18:1aa84d96232a
1026 | | | | | parent: 1:6db2ef61d156
1026 | | | | | parent: 1:6db2ef61d156
1027 / / / / / parent: 15:1dda3f72782d
1027 / / / / / parent: 15:1dda3f72782d
1028 | | | | | user: test
1028 | | | | | user: test
1029 | | | | | date: Thu Jan 01 00:00:18 1970 +0000
1029 | | | | | date: Thu Jan 01 00:00:18 1970 +0000
1030 | | | | | summary: (18) merge two known; two far left
1030 | | | | | summary: (18) merge two known; two far left
1031 | | | | |
1031 | | | | |
1032 | | | | o changeset: 17:44765d7c06e0
1032 | | | | o changeset: 17:44765d7c06e0
1033 | | | | |\ parent: 12:86b91144a6e9
1033 | | | | |\ parent: 12:86b91144a6e9
1034 | | | | | | parent: 16:3677d192927d
1034 | | | | | | parent: 16:3677d192927d
1035 | | | | | | user: test
1035 | | | | | | user: test
1036 | | | | | | date: Thu Jan 01 00:00:17 1970 +0000
1036 | | | | | | date: Thu Jan 01 00:00:17 1970 +0000
1037 | | | | | | summary: (17) expand
1037 | | | | | | summary: (17) expand
1038 | | | | | |
1038 | | | | | |
1039 +-+-------o changeset: 16:3677d192927d
1039 +-+-------o changeset: 16:3677d192927d
1040 | | | | | parent: 0:e6eb3150255d
1040 | | | | | parent: 0:e6eb3150255d
1041 | | | | | parent: 1:6db2ef61d156
1041 | | | | | parent: 1:6db2ef61d156
1042 | | | | | user: test
1042 | | | | | user: test
1043 | | | | | date: Thu Jan 01 00:00:16 1970 +0000
1043 | | | | | date: Thu Jan 01 00:00:16 1970 +0000
1044 | | | | | summary: (16) merge two known; one immediate right, one near right
1044 | | | | | summary: (16) merge two known; one immediate right, one near right
1045 | | | | |
1045 | | | | |
1046 | | | o | changeset: 15:1dda3f72782d
1046 | | | o | changeset: 15:1dda3f72782d
1047 | | | |\ \ parent: 13:22d8966a97e3
1047 | | | |\ \ parent: 13:22d8966a97e3
1048 | | | | | | parent: 14:8eac370358ef
1048 | | | | | | parent: 14:8eac370358ef
1049 | | | | | | user: test
1049 | | | | | | user: test
1050 | | | | | | date: Thu Jan 01 00:00:15 1970 +0000
1050 | | | | | | date: Thu Jan 01 00:00:15 1970 +0000
1051 | | | | | | summary: (15) expand
1051 | | | | | | summary: (15) expand
1052 | | | | | |
1052 | | | | | |
1053 +-------o | changeset: 14:8eac370358ef
1053 +-------o | changeset: 14:8eac370358ef
1054 | | | | |/ parent: 0:e6eb3150255d
1054 | | | | |/ parent: 0:e6eb3150255d
1055 | | | | | parent: 12:86b91144a6e9
1055 | | | | | parent: 12:86b91144a6e9
1056 | | | | | user: test
1056 | | | | | user: test
1057 | | | | | date: Thu Jan 01 00:00:14 1970 +0000
1057 | | | | | date: Thu Jan 01 00:00:14 1970 +0000
1058 | | | | | summary: (14) merge two known; one immediate right, one far right
1058 | | | | | summary: (14) merge two known; one immediate right, one far right
1059 | | | | |
1059 | | | | |
1060 | | | o | changeset: 13:22d8966a97e3
1060 | | | o | changeset: 13:22d8966a97e3
1061 | | | |\ \ parent: 9:7010c0af0a35
1061 | | | |\ \ parent: 9:7010c0af0a35
1062 | | | | | | parent: 11:832d76e6bdf2
1062 | | | | | | parent: 11:832d76e6bdf2
1063 | | | | | | user: test
1063 | | | | | | user: test
1064 | | | | | | date: Thu Jan 01 00:00:13 1970 +0000
1064 | | | | | | date: Thu Jan 01 00:00:13 1970 +0000
1065 | | | | | | summary: (13) expand
1065 | | | | | | summary: (13) expand
1066 | | | | | |
1066 | | | | | |
1067 | +---+---o changeset: 12:86b91144a6e9
1067 | +---+---o changeset: 12:86b91144a6e9
1068 | | | | | parent: 1:6db2ef61d156
1068 | | | | | parent: 1:6db2ef61d156
1069 | | | | | parent: 9:7010c0af0a35
1069 | | | | | parent: 9:7010c0af0a35
1070 | | | | | user: test
1070 | | | | | user: test
1071 | | | | | date: Thu Jan 01 00:00:12 1970 +0000
1071 | | | | | date: Thu Jan 01 00:00:12 1970 +0000
1072 | | | | | summary: (12) merge two known; one immediate right, one far left
1072 | | | | | summary: (12) merge two known; one immediate right, one far left
1073 | | | | |
1073 | | | | |
1074 | | | | o changeset: 11:832d76e6bdf2
1074 | | | | o changeset: 11:832d76e6bdf2
1075 | | | | |\ parent: 6:b105a072e251
1075 | | | | |\ parent: 6:b105a072e251
1076 | | | | | | parent: 10:74c64d036d72
1076 | | | | | | parent: 10:74c64d036d72
1077 | | | | | | user: test
1077 | | | | | | user: test
1078 | | | | | | date: Thu Jan 01 00:00:11 1970 +0000
1078 | | | | | | date: Thu Jan 01 00:00:11 1970 +0000
1079 | | | | | | summary: (11) expand
1079 | | | | | | summary: (11) expand
1080 | | | | | |
1080 | | | | | |
1081 +---------o changeset: 10:74c64d036d72
1081 +---------o changeset: 10:74c64d036d72
1082 | | | | |/ parent: 0:e6eb3150255d
1082 | | | | |/ parent: 0:e6eb3150255d
1083 | | | | | parent: 6:b105a072e251
1083 | | | | | parent: 6:b105a072e251
1084 | | | | | user: test
1084 | | | | | user: test
1085 | | | | | date: Thu Jan 01 00:00:10 1970 +0000
1085 | | | | | date: Thu Jan 01 00:00:10 1970 +0000
1086 | | | | | summary: (10) merge two known; one immediate left, one near right
1086 | | | | | summary: (10) merge two known; one immediate left, one near right
1087 | | | | |
1087 | | | | |
1088 | | | o | changeset: 9:7010c0af0a35
1088 | | | o | changeset: 9:7010c0af0a35
1089 | | | |\ \ parent: 7:b632bb1b1224
1089 | | | |\ \ parent: 7:b632bb1b1224
1090 | | | | | | parent: 8:7a0b11f71937
1090 | | | | | | parent: 8:7a0b11f71937
1091 | | | | | | user: test
1091 | | | | | | user: test
1092 | | | | | | date: Thu Jan 01 00:00:09 1970 +0000
1092 | | | | | | date: Thu Jan 01 00:00:09 1970 +0000
1093 | | | | | | summary: (9) expand
1093 | | | | | | summary: (9) expand
1094 | | | | | |
1094 | | | | | |
1095 +-------o | changeset: 8:7a0b11f71937
1095 +-------o | changeset: 8:7a0b11f71937
1096 | | | |/ / parent: 0:e6eb3150255d
1096 | | | |/ / parent: 0:e6eb3150255d
1097 | | | | | parent: 7:b632bb1b1224
1097 | | | | | parent: 7:b632bb1b1224
1098 | | | | | user: test
1098 | | | | | user: test
1099 | | | | | date: Thu Jan 01 00:00:08 1970 +0000
1099 | | | | | date: Thu Jan 01 00:00:08 1970 +0000
1100 | | | | | summary: (8) merge two known; one immediate left, one far right
1100 | | | | | summary: (8) merge two known; one immediate left, one far right
1101 | | | | |
1101 | | | | |
1102 | | | o | changeset: 7:b632bb1b1224
1102 | | | o | changeset: 7:b632bb1b1224
1103 | | | |\ \ parent: 2:3d9a33b8d1e1
1103 | | | |\ \ parent: 2:3d9a33b8d1e1
1104 | | | | | | parent: 5:4409d547b708
1104 | | | | | | parent: 5:4409d547b708
1105 | | | | | | user: test
1105 | | | | | | user: test
1106 | | | | | | date: Thu Jan 01 00:00:07 1970 +0000
1106 | | | | | | date: Thu Jan 01 00:00:07 1970 +0000
1107 | | | | | | summary: (7) expand
1107 | | | | | | summary: (7) expand
1108 | | | | | |
1108 | | | | | |
1109 | | | +---o changeset: 6:b105a072e251
1109 | | | +---o changeset: 6:b105a072e251
1110 | | | | |/ parent: 2:3d9a33b8d1e1
1110 | | | | |/ parent: 2:3d9a33b8d1e1
1111 | | | | | parent: 5:4409d547b708
1111 | | | | | parent: 5:4409d547b708
1112 | | | | | user: test
1112 | | | | | user: test
1113 | | | | | date: Thu Jan 01 00:00:06 1970 +0000
1113 | | | | | date: Thu Jan 01 00:00:06 1970 +0000
1114 | | | | | summary: (6) merge two known; one immediate left, one far left
1114 | | | | | summary: (6) merge two known; one immediate left, one far left
1115 | | | | |
1115 | | | | |
1116 | | | o | changeset: 5:4409d547b708
1116 | | | o | changeset: 5:4409d547b708
1117 | | | |\ \ parent: 3:27eef8ed80b4
1117 | | | |\ \ parent: 3:27eef8ed80b4
1118 | | | | | | parent: 4:26a8bac39d9f
1118 | | | | | | parent: 4:26a8bac39d9f
1119 | | | | | | user: test
1119 | | | | | | user: test
1120 | | | | | | date: Thu Jan 01 00:00:05 1970 +0000
1120 | | | | | | date: Thu Jan 01 00:00:05 1970 +0000
1121 | | | | | | summary: (5) expand
1121 | | | | | | summary: (5) expand
1122 | | | | | |
1122 | | | | | |
1123 | +---o | | changeset: 4:26a8bac39d9f
1123 | +---o | | changeset: 4:26a8bac39d9f
1124 | | | |/ / parent: 1:6db2ef61d156
1124 | | | |/ / parent: 1:6db2ef61d156
1125 | | | | | parent: 3:27eef8ed80b4
1125 | | | | | parent: 3:27eef8ed80b4
1126 | | | | | user: test
1126 | | | | | user: test
1127 | | | | | date: Thu Jan 01 00:00:04 1970 +0000
1127 | | | | | date: Thu Jan 01 00:00:04 1970 +0000
1128 | | | | | summary: (4) merge two known; one immediate left, one immediate right
1128 | | | | | summary: (4) merge two known; one immediate left, one immediate right
1129 | | | | |
1129 | | | | |
1130
1130
1131
1131
1132 Empty revision range - display nothing:
1132 Empty revision range - display nothing:
1133 $ hg log -G -r 1..0
1133 $ hg log -G -r 1..0
1134
1134
1135 $ cd ..
1135 $ cd ..
1136
1136
1137 #if no-outer-repo
1137 #if no-outer-repo
1138
1138
1139 From outer space:
1139 From outer space:
1140 $ hg log -G -l1 repo
1140 $ hg log -G -l1 repo
1141 @ changeset: 34:fea3ac5810e0
1141 @ changeset: 34:fea3ac5810e0
1142 | tag: tip
1142 | tag: tip
1143 | parent: 32:d06dffa21a31
1143 | parent: 32:d06dffa21a31
1144 | user: test
1144 | user: test
1145 | date: Thu Jan 01 00:00:34 1970 +0000
1145 | date: Thu Jan 01 00:00:34 1970 +0000
1146 | summary: (34) head
1146 | summary: (34) head
1147 |
1147 |
1148 $ hg log -G -l1 repo/a
1148 $ hg log -G -l1 repo/a
1149 @ changeset: 34:fea3ac5810e0
1149 @ changeset: 34:fea3ac5810e0
1150 | tag: tip
1150 | tag: tip
1151 | parent: 32:d06dffa21a31
1151 | parent: 32:d06dffa21a31
1152 | user: test
1152 | user: test
1153 | date: Thu Jan 01 00:00:34 1970 +0000
1153 | date: Thu Jan 01 00:00:34 1970 +0000
1154 | summary: (34) head
1154 | summary: (34) head
1155 |
1155 |
1156 $ hg log -G -l1 repo/missing
1156 $ hg log -G -l1 repo/missing
1157
1157
1158 #endif
1158 #endif
1159
1159
1160 File log with revs != cset revs:
1160 File log with revs != cset revs:
1161 $ hg init flog
1161 $ hg init flog
1162 $ cd flog
1162 $ cd flog
1163 $ echo one >one
1163 $ echo one >one
1164 $ hg add one
1164 $ hg add one
1165 $ hg commit -mone
1165 $ hg commit -mone
1166 $ echo two >two
1166 $ echo two >two
1167 $ hg add two
1167 $ hg add two
1168 $ hg commit -mtwo
1168 $ hg commit -mtwo
1169 $ echo more >two
1169 $ echo more >two
1170 $ hg commit -mmore
1170 $ hg commit -mmore
1171 $ hg log -G two
1171 $ hg log -G two
1172 @ changeset: 2:12c28321755b
1172 @ changeset: 2:12c28321755b
1173 | tag: tip
1173 | tag: tip
1174 | user: test
1174 | user: test
1175 | date: Thu Jan 01 00:00:00 1970 +0000
1175 | date: Thu Jan 01 00:00:00 1970 +0000
1176 | summary: more
1176 | summary: more
1177 |
1177 |
1178 o changeset: 1:5ac72c0599bf
1178 o changeset: 1:5ac72c0599bf
1179 | user: test
1179 | user: test
1180 | date: Thu Jan 01 00:00:00 1970 +0000
1180 | date: Thu Jan 01 00:00:00 1970 +0000
1181 | summary: two
1181 | summary: two
1182 |
1182 |
1183
1183
1184 Issue1896: File log with explicit style
1184 Issue1896: File log with explicit style
1185 $ hg log -G --style=default one
1185 $ hg log -G --style=default one
1186 o changeset: 0:3d578b4a1f53
1186 o changeset: 0:3d578b4a1f53
1187 user: test
1187 user: test
1188 date: Thu Jan 01 00:00:00 1970 +0000
1188 date: Thu Jan 01 00:00:00 1970 +0000
1189 summary: one
1189 summary: one
1190
1190
1191 Issue2395: glog --style header and footer
1191 Issue2395: glog --style header and footer
1192 $ hg log -G --style=xml one
1192 $ hg log -G --style=xml one
1193 <?xml version="1.0"?>
1193 <?xml version="1.0"?>
1194 <log>
1194 <log>
1195 o <logentry revision="0" node="3d578b4a1f537d5fcf7301bfa9c0b97adfaa6fb1">
1195 o <logentry revision="0" node="3d578b4a1f537d5fcf7301bfa9c0b97adfaa6fb1">
1196 <author email="test">test</author>
1196 <author email="test">test</author>
1197 <date>1970-01-01T00:00:00+00:00</date>
1197 <date>1970-01-01T00:00:00+00:00</date>
1198 <msg xml:space="preserve">one</msg>
1198 <msg xml:space="preserve">one</msg>
1199 </logentry>
1199 </logentry>
1200 </log>
1200 </log>
1201
1201
1202 $ cd ..
1202 $ cd ..
1203
1203
1204 Incoming and outgoing:
1204 Incoming and outgoing:
1205
1205
1206 $ hg clone -U -r31 repo repo2
1206 $ hg clone -U -r31 repo repo2
1207 adding changesets
1207 adding changesets
1208 adding manifests
1208 adding manifests
1209 adding file changes
1209 adding file changes
1210 added 31 changesets with 31 changes to 1 files
1210 added 31 changesets with 31 changes to 1 files
1211 $ cd repo2
1211 $ cd repo2
1212
1212
1213 $ hg incoming --graph ../repo
1213 $ hg incoming --graph ../repo
1214 comparing with ../repo
1214 comparing with ../repo
1215 searching for changes
1215 searching for changes
1216 o changeset: 34:fea3ac5810e0
1216 o changeset: 34:fea3ac5810e0
1217 | tag: tip
1217 | tag: tip
1218 | parent: 32:d06dffa21a31
1218 | parent: 32:d06dffa21a31
1219 | user: test
1219 | user: test
1220 | date: Thu Jan 01 00:00:34 1970 +0000
1220 | date: Thu Jan 01 00:00:34 1970 +0000
1221 | summary: (34) head
1221 | summary: (34) head
1222 |
1222 |
1223 | o changeset: 33:68608f5145f9
1223 | o changeset: 33:68608f5145f9
1224 | parent: 18:1aa84d96232a
1224 | parent: 18:1aa84d96232a
1225 | user: test
1225 | user: test
1226 | date: Thu Jan 01 00:00:33 1970 +0000
1226 | date: Thu Jan 01 00:00:33 1970 +0000
1227 | summary: (33) head
1227 | summary: (33) head
1228 |
1228 |
1229 o changeset: 32:d06dffa21a31
1229 o changeset: 32:d06dffa21a31
1230 | parent: 27:886ed638191b
1230 | parent: 27:886ed638191b
1231 | parent: 31:621d83e11f67
1231 | parent: 31:621d83e11f67
1232 | user: test
1232 | user: test
1233 | date: Thu Jan 01 00:00:32 1970 +0000
1233 | date: Thu Jan 01 00:00:32 1970 +0000
1234 | summary: (32) expand
1234 | summary: (32) expand
1235 |
1235 |
1236 o changeset: 27:886ed638191b
1236 o changeset: 27:886ed638191b
1237 parent: 21:d42a756af44d
1237 parent: 21:d42a756af44d
1238 user: test
1238 user: test
1239 date: Thu Jan 01 00:00:27 1970 +0000
1239 date: Thu Jan 01 00:00:27 1970 +0000
1240 summary: (27) collapse
1240 summary: (27) collapse
1241
1241
1242 $ cd ..
1242 $ cd ..
1243
1243
1244 $ hg -R repo outgoing --graph repo2
1244 $ hg -R repo outgoing --graph repo2
1245 comparing with repo2
1245 comparing with repo2
1246 searching for changes
1246 searching for changes
1247 @ changeset: 34:fea3ac5810e0
1247 @ changeset: 34:fea3ac5810e0
1248 | tag: tip
1248 | tag: tip
1249 | parent: 32:d06dffa21a31
1249 | parent: 32:d06dffa21a31
1250 | user: test
1250 | user: test
1251 | date: Thu Jan 01 00:00:34 1970 +0000
1251 | date: Thu Jan 01 00:00:34 1970 +0000
1252 | summary: (34) head
1252 | summary: (34) head
1253 |
1253 |
1254 | o changeset: 33:68608f5145f9
1254 | o changeset: 33:68608f5145f9
1255 | parent: 18:1aa84d96232a
1255 | parent: 18:1aa84d96232a
1256 | user: test
1256 | user: test
1257 | date: Thu Jan 01 00:00:33 1970 +0000
1257 | date: Thu Jan 01 00:00:33 1970 +0000
1258 | summary: (33) head
1258 | summary: (33) head
1259 |
1259 |
1260 o changeset: 32:d06dffa21a31
1260 o changeset: 32:d06dffa21a31
1261 | parent: 27:886ed638191b
1261 | parent: 27:886ed638191b
1262 | parent: 31:621d83e11f67
1262 | parent: 31:621d83e11f67
1263 | user: test
1263 | user: test
1264 | date: Thu Jan 01 00:00:32 1970 +0000
1264 | date: Thu Jan 01 00:00:32 1970 +0000
1265 | summary: (32) expand
1265 | summary: (32) expand
1266 |
1266 |
1267 o changeset: 27:886ed638191b
1267 o changeset: 27:886ed638191b
1268 parent: 21:d42a756af44d
1268 parent: 21:d42a756af44d
1269 user: test
1269 user: test
1270 date: Thu Jan 01 00:00:27 1970 +0000
1270 date: Thu Jan 01 00:00:27 1970 +0000
1271 summary: (27) collapse
1271 summary: (27) collapse
1272
1272
1273
1273
1274 File + limit with revs != cset revs:
1274 File + limit with revs != cset revs:
1275 $ cd repo
1275 $ cd repo
1276 $ touch b
1276 $ touch b
1277 $ hg ci -Aqm0
1277 $ hg ci -Aqm0
1278 $ hg log -G -l2 a
1278 $ hg log -G -l2 a
1279 o changeset: 34:fea3ac5810e0
1279 o changeset: 34:fea3ac5810e0
1280 | parent: 32:d06dffa21a31
1280 | parent: 32:d06dffa21a31
1281 | user: test
1281 | user: test
1282 | date: Thu Jan 01 00:00:34 1970 +0000
1282 | date: Thu Jan 01 00:00:34 1970 +0000
1283 | summary: (34) head
1283 | summary: (34) head
1284 |
1284 |
1285 | o changeset: 33:68608f5145f9
1285 | o changeset: 33:68608f5145f9
1286 | | parent: 18:1aa84d96232a
1286 | | parent: 18:1aa84d96232a
1287 | | user: test
1287 | | user: test
1288 | | date: Thu Jan 01 00:00:33 1970 +0000
1288 | | date: Thu Jan 01 00:00:33 1970 +0000
1289 | | summary: (33) head
1289 | | summary: (33) head
1290 | |
1290 | |
1291
1291
1292 File + limit + -ra:b, (b - a) < limit:
1292 File + limit + -ra:b, (b - a) < limit:
1293 $ hg log -G -l3000 -r32:tip a
1293 $ hg log -G -l3000 -r32:tip a
1294 o changeset: 34:fea3ac5810e0
1294 o changeset: 34:fea3ac5810e0
1295 | parent: 32:d06dffa21a31
1295 | parent: 32:d06dffa21a31
1296 | user: test
1296 | user: test
1297 | date: Thu Jan 01 00:00:34 1970 +0000
1297 | date: Thu Jan 01 00:00:34 1970 +0000
1298 | summary: (34) head
1298 | summary: (34) head
1299 |
1299 |
1300 | o changeset: 33:68608f5145f9
1300 | o changeset: 33:68608f5145f9
1301 | | parent: 18:1aa84d96232a
1301 | | parent: 18:1aa84d96232a
1302 | | user: test
1302 | | user: test
1303 | | date: Thu Jan 01 00:00:33 1970 +0000
1303 | | date: Thu Jan 01 00:00:33 1970 +0000
1304 | | summary: (33) head
1304 | | summary: (33) head
1305 | |
1305 | |
1306 o | changeset: 32:d06dffa21a31
1306 o | changeset: 32:d06dffa21a31
1307 |\ \ parent: 27:886ed638191b
1307 |\ \ parent: 27:886ed638191b
1308 | | | parent: 31:621d83e11f67
1308 | | | parent: 31:621d83e11f67
1309 | | | user: test
1309 | | | user: test
1310 | | | date: Thu Jan 01 00:00:32 1970 +0000
1310 | | | date: Thu Jan 01 00:00:32 1970 +0000
1311 | | | summary: (32) expand
1311 | | | summary: (32) expand
1312 | | |
1312 | | |
1313
1313
1314 Point out a common and an uncommon unshown parent
1314 Point out a common and an uncommon unshown parent
1315
1315
1316 $ hg log -G -r 'rev(8) or rev(9)'
1316 $ hg log -G -r 'rev(8) or rev(9)'
1317 o changeset: 9:7010c0af0a35
1317 o changeset: 9:7010c0af0a35
1318 |\ parent: 7:b632bb1b1224
1318 |\ parent: 7:b632bb1b1224
1319 | | parent: 8:7a0b11f71937
1319 | | parent: 8:7a0b11f71937
1320 | | user: test
1320 | | user: test
1321 | | date: Thu Jan 01 00:00:09 1970 +0000
1321 | | date: Thu Jan 01 00:00:09 1970 +0000
1322 | | summary: (9) expand
1322 | | summary: (9) expand
1323 | |
1323 | |
1324 o | changeset: 8:7a0b11f71937
1324 o | changeset: 8:7a0b11f71937
1325 |\| parent: 0:e6eb3150255d
1325 |\| parent: 0:e6eb3150255d
1326 | | parent: 7:b632bb1b1224
1326 | | parent: 7:b632bb1b1224
1327 | | user: test
1327 | | user: test
1328 | | date: Thu Jan 01 00:00:08 1970 +0000
1328 | | date: Thu Jan 01 00:00:08 1970 +0000
1329 | | summary: (8) merge two known; one immediate left, one far right
1329 | | summary: (8) merge two known; one immediate left, one far right
1330 | |
1330 | |
1331
1331
1332 File + limit + -ra:b, b < tip:
1332 File + limit + -ra:b, b < tip:
1333
1333
1334 $ hg log -G -l1 -r32:34 a
1334 $ hg log -G -l1 -r32:34 a
1335 o changeset: 34:fea3ac5810e0
1335 o changeset: 34:fea3ac5810e0
1336 | parent: 32:d06dffa21a31
1336 | parent: 32:d06dffa21a31
1337 | user: test
1337 | user: test
1338 | date: Thu Jan 01 00:00:34 1970 +0000
1338 | date: Thu Jan 01 00:00:34 1970 +0000
1339 | summary: (34) head
1339 | summary: (34) head
1340 |
1340 |
1341
1341
1342 file(File) + limit + -ra:b, b < tip:
1342 file(File) + limit + -ra:b, b < tip:
1343
1343
1344 $ hg log -G -l1 -r32:34 -r 'file("a")'
1344 $ hg log -G -l1 -r32:34 -r 'file("a")'
1345 o changeset: 34:fea3ac5810e0
1345 o changeset: 34:fea3ac5810e0
1346 | parent: 32:d06dffa21a31
1346 | parent: 32:d06dffa21a31
1347 | user: test
1347 | user: test
1348 | date: Thu Jan 01 00:00:34 1970 +0000
1348 | date: Thu Jan 01 00:00:34 1970 +0000
1349 | summary: (34) head
1349 | summary: (34) head
1350 |
1350 |
1351
1351
1352 limit(file(File) and a::b), b < tip:
1352 limit(file(File) and a::b), b < tip:
1353
1353
1354 $ hg log -G -r 'limit(file("a") and 32::34, 1)'
1354 $ hg log -G -r 'limit(file("a") and 32::34, 1)'
1355 o changeset: 32:d06dffa21a31
1355 o changeset: 32:d06dffa21a31
1356 |\ parent: 27:886ed638191b
1356 |\ parent: 27:886ed638191b
1357 | | parent: 31:621d83e11f67
1357 | | parent: 31:621d83e11f67
1358 | | user: test
1358 | | user: test
1359 | | date: Thu Jan 01 00:00:32 1970 +0000
1359 | | date: Thu Jan 01 00:00:32 1970 +0000
1360 | | summary: (32) expand
1360 | | summary: (32) expand
1361 | |
1361 | |
1362
1362
1363 File + limit + -ra:b, b < tip:
1363 File + limit + -ra:b, b < tip:
1364
1364
1365 $ hg log -G -r 'limit(file("a") and 34::32, 1)'
1365 $ hg log -G -r 'limit(file("a") and 34::32, 1)'
1366
1366
1367 File + limit + -ra:b, b < tip, (b - a) < limit:
1367 File + limit + -ra:b, b < tip, (b - a) < limit:
1368
1368
1369 $ hg log -G -l10 -r33:34 a
1369 $ hg log -G -l10 -r33:34 a
1370 o changeset: 34:fea3ac5810e0
1370 o changeset: 34:fea3ac5810e0
1371 | parent: 32:d06dffa21a31
1371 | parent: 32:d06dffa21a31
1372 | user: test
1372 | user: test
1373 | date: Thu Jan 01 00:00:34 1970 +0000
1373 | date: Thu Jan 01 00:00:34 1970 +0000
1374 | summary: (34) head
1374 | summary: (34) head
1375 |
1375 |
1376 | o changeset: 33:68608f5145f9
1376 | o changeset: 33:68608f5145f9
1377 | | parent: 18:1aa84d96232a
1377 | | parent: 18:1aa84d96232a
1378 | | user: test
1378 | | user: test
1379 | | date: Thu Jan 01 00:00:33 1970 +0000
1379 | | date: Thu Jan 01 00:00:33 1970 +0000
1380 | | summary: (33) head
1380 | | summary: (33) head
1381 | |
1381 | |
1382
1382
1383 Do not crash or produce strange graphs if history is buggy
1383 Do not crash or produce strange graphs if history is buggy
1384
1384
1385 $ hg branch branch
1385 $ hg branch branch
1386 marked working directory as branch branch
1386 marked working directory as branch branch
1387 (branches are permanent and global, did you want a bookmark?)
1387 (branches are permanent and global, did you want a bookmark?)
1388 $ commit 36 "buggy merge: identical parents" 35 35
1388 $ commit 36 "buggy merge: identical parents" 35 35
1389 $ hg log -G -l5
1389 $ hg log -G -l5
1390 @ changeset: 36:08a19a744424
1390 @ changeset: 36:08a19a744424
1391 | branch: branch
1391 | branch: branch
1392 | tag: tip
1392 | tag: tip
1393 | parent: 35:9159c3644c5e
1393 | parent: 35:9159c3644c5e
1394 | parent: 35:9159c3644c5e
1394 | parent: 35:9159c3644c5e
1395 | user: test
1395 | user: test
1396 | date: Thu Jan 01 00:00:36 1970 +0000
1396 | date: Thu Jan 01 00:00:36 1970 +0000
1397 | summary: (36) buggy merge: identical parents
1397 | summary: (36) buggy merge: identical parents
1398 |
1398 |
1399 o changeset: 35:9159c3644c5e
1399 o changeset: 35:9159c3644c5e
1400 | user: test
1400 | user: test
1401 | date: Thu Jan 01 00:00:00 1970 +0000
1401 | date: Thu Jan 01 00:00:00 1970 +0000
1402 | summary: 0
1402 | summary: 0
1403 |
1403 |
1404 o changeset: 34:fea3ac5810e0
1404 o changeset: 34:fea3ac5810e0
1405 | parent: 32:d06dffa21a31
1405 | parent: 32:d06dffa21a31
1406 | user: test
1406 | user: test
1407 | date: Thu Jan 01 00:00:34 1970 +0000
1407 | date: Thu Jan 01 00:00:34 1970 +0000
1408 | summary: (34) head
1408 | summary: (34) head
1409 |
1409 |
1410 | o changeset: 33:68608f5145f9
1410 | o changeset: 33:68608f5145f9
1411 | | parent: 18:1aa84d96232a
1411 | | parent: 18:1aa84d96232a
1412 | | user: test
1412 | | user: test
1413 | | date: Thu Jan 01 00:00:33 1970 +0000
1413 | | date: Thu Jan 01 00:00:33 1970 +0000
1414 | | summary: (33) head
1414 | | summary: (33) head
1415 | |
1415 | |
1416 o | changeset: 32:d06dffa21a31
1416 o | changeset: 32:d06dffa21a31
1417 |\ \ parent: 27:886ed638191b
1417 |\ \ parent: 27:886ed638191b
1418 | | | parent: 31:621d83e11f67
1418 | | | parent: 31:621d83e11f67
1419 | | | user: test
1419 | | | user: test
1420 | | | date: Thu Jan 01 00:00:32 1970 +0000
1420 | | | date: Thu Jan 01 00:00:32 1970 +0000
1421 | | | summary: (32) expand
1421 | | | summary: (32) expand
1422 | | |
1422 | | |
1423
1423
1424 Test log -G options
1424 Test log -G options
1425
1425
1426 $ testlog() {
1426 $ testlog() {
1427 > hg log -G --print-revset "$@"
1427 > hg log -G --print-revset "$@"
1428 > hg log --template 'nodetag {rev}\n' "$@" | grep nodetag \
1428 > hg log --template 'nodetag {rev}\n' "$@" | grep nodetag \
1429 > | sed 's/.*nodetag/nodetag/' > log.nodes
1429 > | sed 's/.*nodetag/nodetag/' > log.nodes
1430 > hg log -G --template 'nodetag {rev}\n' "$@" | grep nodetag \
1430 > hg log -G --template 'nodetag {rev}\n' "$@" | grep nodetag \
1431 > | sed 's/.*nodetag/nodetag/' > glog.nodes
1431 > | sed 's/.*nodetag/nodetag/' > glog.nodes
1432 > diff -u log.nodes glog.nodes | grep '^[-+@ ]' || :
1432 > diff -u log.nodes glog.nodes | grep '^[-+@ ]' || :
1433 > }
1433 > }
1434
1434
1435 glog always reorders nodes which explains the difference with log
1435 glog always reorders nodes which explains the difference with log
1436
1436
1437 $ testlog -r 27 -r 25 -r 21 -r 34 -r 32 -r 31
1437 $ testlog -r 27 -r 25 -r 21 -r 34 -r 32 -r 31
1438 ['27', '25', '21', '34', '32', '31']
1438 ['27', '25', '21', '34', '32', '31']
1439 []
1439 []
1440 --- log.nodes * (glob)
1440 --- log.nodes * (glob)
1441 +++ glog.nodes * (glob)
1441 +++ glog.nodes * (glob)
1442 @@ -1,6 +1,6 @@
1442 @@ -1,6 +1,6 @@
1443 -nodetag 27
1443 -nodetag 27
1444 -nodetag 25
1444 -nodetag 25
1445 -nodetag 21
1445 -nodetag 21
1446 nodetag 34
1446 nodetag 34
1447 nodetag 32
1447 nodetag 32
1448 nodetag 31
1448 nodetag 31
1449 +nodetag 27
1449 +nodetag 27
1450 +nodetag 25
1450 +nodetag 25
1451 +nodetag 21
1451 +nodetag 21
1452 $ testlog -u test -u not-a-user
1452 $ testlog -u test -u not-a-user
1453 []
1453 []
1454 (group
1454 (group
1455 (group
1455 (group
1456 (or
1456 (or
1457 (func
1457 (func
1458 ('symbol', 'user')
1458 ('symbol', 'user')
1459 ('string', 'test'))
1459 ('string', 'test'))
1460 (func
1460 (func
1461 ('symbol', 'user')
1461 ('symbol', 'user')
1462 ('string', 'not-a-user')))))
1462 ('string', 'not-a-user')))))
1463 $ testlog -b not-a-branch
1463 $ testlog -b not-a-branch
1464 abort: unknown revision 'not-a-branch'!
1464 abort: unknown revision 'not-a-branch'!
1465 abort: unknown revision 'not-a-branch'!
1465 abort: unknown revision 'not-a-branch'!
1466 abort: unknown revision 'not-a-branch'!
1466 abort: unknown revision 'not-a-branch'!
1467 $ testlog -b 35 -b 36 --only-branch branch
1467 $ testlog -b 35 -b 36 --only-branch branch
1468 []
1468 []
1469 (group
1469 (group
1470 (group
1470 (group
1471 (or
1471 (or
1472 (or
1472 (or
1473 (func
1473 (func
1474 ('symbol', 'branch')
1474 ('symbol', 'branch')
1475 ('string', 'default'))
1475 ('string', 'default'))
1476 (func
1476 (func
1477 ('symbol', 'branch')
1477 ('symbol', 'branch')
1478 ('string', 'branch')))
1478 ('string', 'branch')))
1479 (func
1479 (func
1480 ('symbol', 'branch')
1480 ('symbol', 'branch')
1481 ('string', 'branch')))))
1481 ('string', 'branch')))))
1482 $ testlog -k expand -k merge
1482 $ testlog -k expand -k merge
1483 []
1483 []
1484 (group
1484 (group
1485 (group
1485 (group
1486 (or
1486 (or
1487 (func
1487 (func
1488 ('symbol', 'keyword')
1488 ('symbol', 'keyword')
1489 ('string', 'expand'))
1489 ('string', 'expand'))
1490 (func
1490 (func
1491 ('symbol', 'keyword')
1491 ('symbol', 'keyword')
1492 ('string', 'merge')))))
1492 ('string', 'merge')))))
1493 $ testlog --only-merges
1493 $ testlog --only-merges
1494 []
1494 []
1495 (group
1495 (group
1496 (func
1496 (func
1497 ('symbol', 'merge')
1497 ('symbol', 'merge')
1498 None))
1498 None))
1499 $ testlog --no-merges
1499 $ testlog --no-merges
1500 []
1500 []
1501 (group
1501 (group
1502 (not
1502 (not
1503 (func
1503 (func
1504 ('symbol', 'merge')
1504 ('symbol', 'merge')
1505 None)))
1505 None)))
1506 $ testlog --date '2 0 to 4 0'
1506 $ testlog --date '2 0 to 4 0'
1507 []
1507 []
1508 (group
1508 (group
1509 (func
1509 (func
1510 ('symbol', 'date')
1510 ('symbol', 'date')
1511 ('string', '2 0 to 4 0')))
1511 ('string', '2 0 to 4 0')))
1512 $ hg log -G -d 'brace ) in a date'
1512 $ hg log -G -d 'brace ) in a date'
1513 abort: invalid date: 'brace ) in a date'
1513 abort: invalid date: 'brace ) in a date'
1514 [255]
1514 [255]
1515 $ testlog --prune 31 --prune 32
1515 $ testlog --prune 31 --prune 32
1516 []
1516 []
1517 (group
1517 (group
1518 (group
1518 (group
1519 (and
1519 (and
1520 (not
1520 (not
1521 (group
1521 (group
1522 (or
1522 (or
1523 ('string', '31')
1523 ('string', '31')
1524 (func
1524 (func
1525 ('symbol', 'ancestors')
1525 ('symbol', 'ancestors')
1526 ('string', '31')))))
1526 ('string', '31')))))
1527 (not
1527 (not
1528 (group
1528 (group
1529 (or
1529 (or
1530 ('string', '32')
1530 ('string', '32')
1531 (func
1531 (func
1532 ('symbol', 'ancestors')
1532 ('symbol', 'ancestors')
1533 ('string', '32'))))))))
1533 ('string', '32'))))))))
1534
1534
1535 Dedicated repo for --follow and paths filtering. The g is crafted to
1535 Dedicated repo for --follow and paths filtering. The g is crafted to
1536 have 2 filelog topological heads in a linear changeset graph.
1536 have 2 filelog topological heads in a linear changeset graph.
1537
1537
1538 $ cd ..
1538 $ cd ..
1539 $ hg init follow
1539 $ hg init follow
1540 $ cd follow
1540 $ cd follow
1541 $ testlog --follow
1541 $ testlog --follow
1542 []
1542 []
1543 []
1543 []
1544 $ echo a > a
1544 $ echo a > a
1545 $ echo aa > aa
1545 $ echo aa > aa
1546 $ echo f > f
1546 $ echo f > f
1547 $ hg ci -Am "add a" a aa f
1547 $ hg ci -Am "add a" a aa f
1548 $ hg cp a b
1548 $ hg cp a b
1549 $ hg cp f g
1549 $ hg cp f g
1550 $ hg ci -m "copy a b"
1550 $ hg ci -m "copy a b"
1551 $ mkdir dir
1551 $ mkdir dir
1552 $ hg mv b dir
1552 $ hg mv b dir
1553 $ echo g >> g
1553 $ echo g >> g
1554 $ echo f >> f
1554 $ echo f >> f
1555 $ hg ci -m "mv b dir/b"
1555 $ hg ci -m "mv b dir/b"
1556 $ hg mv a b
1556 $ hg mv a b
1557 $ hg cp -f f g
1557 $ hg cp -f f g
1558 $ echo a > d
1558 $ echo a > d
1559 $ hg add d
1559 $ hg add d
1560 $ hg ci -m "mv a b; add d"
1560 $ hg ci -m "mv a b; add d"
1561 $ hg mv dir/b e
1561 $ hg mv dir/b e
1562 $ hg ci -m "mv dir/b e"
1562 $ hg ci -m "mv dir/b e"
1563 $ hg log -G --template '({rev}) {desc|firstline}\n'
1563 $ hg log -G --template '({rev}) {desc|firstline}\n'
1564 @ (4) mv dir/b e
1564 @ (4) mv dir/b e
1565 |
1565 |
1566 o (3) mv a b; add d
1566 o (3) mv a b; add d
1567 |
1567 |
1568 o (2) mv b dir/b
1568 o (2) mv b dir/b
1569 |
1569 |
1570 o (1) copy a b
1570 o (1) copy a b
1571 |
1571 |
1572 o (0) add a
1572 o (0) add a
1573
1573
1574
1574
1575 $ testlog a
1575 $ testlog a
1576 []
1576 []
1577 (group
1577 (group
1578 (group
1578 (group
1579 (func
1579 (func
1580 ('symbol', 'filelog')
1580 ('symbol', 'filelog')
1581 ('string', 'a'))))
1581 ('string', 'a'))))
1582 $ testlog a b
1582 $ testlog a b
1583 []
1583 []
1584 (group
1584 (group
1585 (group
1585 (group
1586 (or
1586 (or
1587 (func
1587 (func
1588 ('symbol', 'filelog')
1588 ('symbol', 'filelog')
1589 ('string', 'a'))
1589 ('string', 'a'))
1590 (func
1590 (func
1591 ('symbol', 'filelog')
1591 ('symbol', 'filelog')
1592 ('string', 'b')))))
1592 ('string', 'b')))))
1593
1593
1594 Test falling back to slow path for non-existing files
1594 Test falling back to slow path for non-existing files
1595
1595
1596 $ testlog a c
1596 $ testlog a c
1597 []
1597 []
1598 (group
1598 (group
1599 (func
1599 (func
1600 ('symbol', '_matchfiles')
1600 ('symbol', '_matchfiles')
1601 (list
1601 (list
1602 (list
1602 (list
1603 (list
1603 (list
1604 ('string', 'r:')
1604 ('string', 'r:')
1605 ('string', 'd:relpath'))
1605 ('string', 'd:relpath'))
1606 ('string', 'p:a'))
1606 ('string', 'p:a'))
1607 ('string', 'p:c'))))
1607 ('string', 'p:c'))))
1608
1608
1609 Test multiple --include/--exclude/paths
1609 Test multiple --include/--exclude/paths
1610
1610
1611 $ testlog --include a --include e --exclude b --exclude e a e
1611 $ testlog --include a --include e --exclude b --exclude e a e
1612 []
1612 []
1613 (group
1613 (group
1614 (func
1614 (func
1615 ('symbol', '_matchfiles')
1615 ('symbol', '_matchfiles')
1616 (list
1616 (list
1617 (list
1617 (list
1618 (list
1618 (list
1619 (list
1619 (list
1620 (list
1620 (list
1621 (list
1621 (list
1622 (list
1622 (list
1623 ('string', 'r:')
1623 ('string', 'r:')
1624 ('string', 'd:relpath'))
1624 ('string', 'd:relpath'))
1625 ('string', 'p:a'))
1625 ('string', 'p:a'))
1626 ('string', 'p:e'))
1626 ('string', 'p:e'))
1627 ('string', 'i:a'))
1627 ('string', 'i:a'))
1628 ('string', 'i:e'))
1628 ('string', 'i:e'))
1629 ('string', 'x:b'))
1629 ('string', 'x:b'))
1630 ('string', 'x:e'))))
1630 ('string', 'x:e'))))
1631
1631
1632 Test glob expansion of pats
1632 Test glob expansion of pats
1633
1633
1634 $ expandglobs=`python -c "import mercurial.util; \
1634 $ expandglobs=`python -c "import mercurial.util; \
1635 > print mercurial.util.expandglobs and 'true' or 'false'"`
1635 > print mercurial.util.expandglobs and 'true' or 'false'"`
1636 $ if [ $expandglobs = "true" ]; then
1636 $ if [ $expandglobs = "true" ]; then
1637 > testlog 'a*';
1637 > testlog 'a*';
1638 > else
1638 > else
1639 > testlog a*;
1639 > testlog a*;
1640 > fi;
1640 > fi;
1641 []
1641 []
1642 (group
1642 (group
1643 (group
1643 (group
1644 (func
1644 (func
1645 ('symbol', 'filelog')
1645 ('symbol', 'filelog')
1646 ('string', 'aa'))))
1646 ('string', 'aa'))))
1647
1647
1648 Test --follow on a directory
1648 Test --follow on a directory
1649
1649
1650 $ testlog -f dir
1650 $ testlog -f dir
1651 abort: cannot follow file not in parent revision: "dir"
1651 abort: cannot follow file not in parent revision: "dir"
1652 abort: cannot follow file not in parent revision: "dir"
1652 abort: cannot follow file not in parent revision: "dir"
1653 abort: cannot follow file not in parent revision: "dir"
1653 abort: cannot follow file not in parent revision: "dir"
1654
1654
1655 Test --follow on file not in parent revision
1655 Test --follow on file not in parent revision
1656
1656
1657 $ testlog -f a
1657 $ testlog -f a
1658 abort: cannot follow file not in parent revision: "a"
1658 abort: cannot follow file not in parent revision: "a"
1659 abort: cannot follow file not in parent revision: "a"
1659 abort: cannot follow file not in parent revision: "a"
1660 abort: cannot follow file not in parent revision: "a"
1660 abort: cannot follow file not in parent revision: "a"
1661
1661
1662 Test --follow and patterns
1662 Test --follow and patterns
1663
1663
1664 $ testlog -f 'glob:*'
1664 $ testlog -f 'glob:*'
1665 abort: can only follow copies/renames for explicit filenames
1665 abort: can only follow copies/renames for explicit filenames
1666 abort: can only follow copies/renames for explicit filenames
1666 abort: can only follow copies/renames for explicit filenames
1667 abort: can only follow copies/renames for explicit filenames
1667 abort: can only follow copies/renames for explicit filenames
1668
1668
1669 Test --follow on a single rename
1669 Test --follow on a single rename
1670
1670
1671 $ hg up -q 2
1671 $ hg up -q 2
1672 $ testlog -f a
1672 $ testlog -f a
1673 []
1673 []
1674 (group
1674 (group
1675 (group
1675 (group
1676 (func
1676 (func
1677 ('symbol', 'follow')
1677 ('symbol', 'follow')
1678 ('string', 'a'))))
1678 ('string', 'a'))))
1679
1679
1680 Test --follow and multiple renames
1680 Test --follow and multiple renames
1681
1681
1682 $ hg up -q tip
1682 $ hg up -q tip
1683 $ testlog -f e
1683 $ testlog -f e
1684 []
1684 []
1685 (group
1685 (group
1686 (group
1686 (group
1687 (func
1687 (func
1688 ('symbol', 'follow')
1688 ('symbol', 'follow')
1689 ('string', 'e'))))
1689 ('string', 'e'))))
1690
1690
1691 Test --follow and multiple filelog heads
1691 Test --follow and multiple filelog heads
1692
1692
1693 $ hg up -q 2
1693 $ hg up -q 2
1694 $ testlog -f g
1694 $ testlog -f g
1695 []
1695 []
1696 (group
1696 (group
1697 (group
1697 (group
1698 (func
1698 (func
1699 ('symbol', 'follow')
1699 ('symbol', 'follow')
1700 ('string', 'g'))))
1700 ('string', 'g'))))
1701 $ cat log.nodes
1701 $ cat log.nodes
1702 nodetag 2
1702 nodetag 2
1703 nodetag 1
1703 nodetag 1
1704 nodetag 0
1704 nodetag 0
1705 $ hg up -q tip
1705 $ hg up -q tip
1706 $ testlog -f g
1706 $ testlog -f g
1707 []
1707 []
1708 (group
1708 (group
1709 (group
1709 (group
1710 (func
1710 (func
1711 ('symbol', 'follow')
1711 ('symbol', 'follow')
1712 ('string', 'g'))))
1712 ('string', 'g'))))
1713 $ cat log.nodes
1713 $ cat log.nodes
1714 nodetag 3
1714 nodetag 3
1715 nodetag 2
1715 nodetag 2
1716 nodetag 0
1716 nodetag 0
1717
1717
1718 Test --follow and multiple files
1718 Test --follow and multiple files
1719
1719
1720 $ testlog -f g e
1720 $ testlog -f g e
1721 []
1721 []
1722 (group
1722 (group
1723 (group
1723 (group
1724 (or
1724 (or
1725 (func
1725 (func
1726 ('symbol', 'follow')
1726 ('symbol', 'follow')
1727 ('string', 'g'))
1727 ('string', 'g'))
1728 (func
1728 (func
1729 ('symbol', 'follow')
1729 ('symbol', 'follow')
1730 ('string', 'e')))))
1730 ('string', 'e')))))
1731 $ cat log.nodes
1731 $ cat log.nodes
1732 nodetag 4
1732 nodetag 4
1733 nodetag 3
1733 nodetag 3
1734 nodetag 2
1734 nodetag 2
1735 nodetag 1
1735 nodetag 1
1736 nodetag 0
1736 nodetag 0
1737
1737
1738 Test --follow-first
1738 Test --follow-first
1739
1739
1740 $ hg up -q 3
1740 $ hg up -q 3
1741 $ echo ee > e
1741 $ echo ee > e
1742 $ hg ci -Am "add another e" e
1742 $ hg ci -Am "add another e" e
1743 created new head
1743 created new head
1744 $ hg merge --tool internal:other 4
1744 $ hg merge --tool internal:other 4
1745 0 files updated, 1 files merged, 1 files removed, 0 files unresolved
1745 0 files updated, 1 files merged, 1 files removed, 0 files unresolved
1746 (branch merge, don't forget to commit)
1746 (branch merge, don't forget to commit)
1747 $ echo merge > e
1747 $ echo merge > e
1748 $ hg ci -m "merge 5 and 4"
1748 $ hg ci -m "merge 5 and 4"
1749 $ testlog --follow-first
1749 $ testlog --follow-first
1750 []
1750 []
1751 (group
1751 (group
1752 (func
1752 (func
1753 ('symbol', '_firstancestors')
1753 ('symbol', '_firstancestors')
1754 ('symbol', '6')))
1754 ('symbol', '6')))
1755
1755
1756 Cannot compare with log --follow-first FILE as it never worked
1756 Cannot compare with log --follow-first FILE as it never worked
1757
1757
1758 $ hg log -G --print-revset --follow-first e
1758 $ hg log -G --print-revset --follow-first e
1759 []
1759 []
1760 (group
1760 (group
1761 (group
1761 (group
1762 (func
1762 (func
1763 ('symbol', '_followfirst')
1763 ('symbol', '_followfirst')
1764 ('string', 'e'))))
1764 ('string', 'e'))))
1765 $ hg log -G --follow-first e --template '{rev} {desc|firstline}\n'
1765 $ hg log -G --follow-first e --template '{rev} {desc|firstline}\n'
1766 @ 6 merge 5 and 4
1766 @ 6 merge 5 and 4
1767 |\
1767 |\
1768 o | 5 add another e
1768 o | 5 add another e
1769 | |
1769 | |
1770
1770
1771 Test --copies
1771 Test --copies
1772
1772
1773 $ hg log -G --copies --template "{rev} {desc|firstline} \
1773 $ hg log -G --copies --template "{rev} {desc|firstline} \
1774 > copies: {file_copies_switch}\n"
1774 > copies: {file_copies_switch}\n"
1775 @ 6 merge 5 and 4 copies:
1775 @ 6 merge 5 and 4 copies:
1776 |\
1776 |\
1777 | o 5 add another e copies:
1777 | o 5 add another e copies:
1778 | |
1778 | |
1779 o | 4 mv dir/b e copies: e (dir/b)
1779 o | 4 mv dir/b e copies: e (dir/b)
1780 |/
1780 |/
1781 o 3 mv a b; add d copies: b (a)g (f)
1781 o 3 mv a b; add d copies: b (a)g (f)
1782 |
1782 |
1783 o 2 mv b dir/b copies: dir/b (b)
1783 o 2 mv b dir/b copies: dir/b (b)
1784 |
1784 |
1785 o 1 copy a b copies: b (a)g (f)
1785 o 1 copy a b copies: b (a)g (f)
1786 |
1786 |
1787 o 0 add a copies:
1787 o 0 add a copies:
1788
1788
1789 Test "set:..." and parent revision
1789 Test "set:..." and parent revision
1790
1790
1791 $ hg up -q 4
1791 $ hg up -q 4
1792 $ testlog "set:copied()"
1792 $ testlog "set:copied()"
1793 []
1793 []
1794 (group
1794 (group
1795 (func
1795 (func
1796 ('symbol', '_matchfiles')
1796 ('symbol', '_matchfiles')
1797 (list
1797 (list
1798 (list
1798 (list
1799 ('string', 'r:')
1799 ('string', 'r:')
1800 ('string', 'd:relpath'))
1800 ('string', 'd:relpath'))
1801 ('string', 'p:set:copied()'))))
1801 ('string', 'p:set:copied()'))))
1802 $ testlog --include "set:copied()"
1802 $ testlog --include "set:copied()"
1803 []
1803 []
1804 (group
1804 (group
1805 (func
1805 (func
1806 ('symbol', '_matchfiles')
1806 ('symbol', '_matchfiles')
1807 (list
1807 (list
1808 (list
1808 (list
1809 ('string', 'r:')
1809 ('string', 'r:')
1810 ('string', 'd:relpath'))
1810 ('string', 'd:relpath'))
1811 ('string', 'i:set:copied()'))))
1811 ('string', 'i:set:copied()'))))
1812 $ testlog -r "sort(file('set:copied()'), -rev)"
1812 $ testlog -r "sort(file('set:copied()'), -rev)"
1813 ["sort(file('set:copied()'), -rev)"]
1813 ["sort(file('set:copied()'), -rev)"]
1814 []
1814 []
1815
1815
1816 Test --removed
1816 Test --removed
1817
1817
1818 $ testlog --removed
1818 $ testlog --removed
1819 []
1819 []
1820 []
1820 []
1821 $ testlog --removed a
1821 $ testlog --removed a
1822 []
1822 []
1823 (group
1823 (group
1824 (func
1824 (func
1825 ('symbol', '_matchfiles')
1825 ('symbol', '_matchfiles')
1826 (list
1826 (list
1827 (list
1827 (list
1828 ('string', 'r:')
1828 ('string', 'r:')
1829 ('string', 'd:relpath'))
1829 ('string', 'd:relpath'))
1830 ('string', 'p:a'))))
1830 ('string', 'p:a'))))
1831 $ testlog --removed --follow a
1831 $ testlog --removed --follow a
1832 abort: can only follow copies/renames for explicit filenames
1832 abort: can only follow copies/renames for explicit filenames
1833 abort: can only follow copies/renames for explicit filenames
1833 abort: can only follow copies/renames for explicit filenames
1834 abort: can only follow copies/renames for explicit filenames
1834 abort: can only follow copies/renames for explicit filenames
1835
1835
1836 Test --patch and --stat with --follow and --follow-first
1836 Test --patch and --stat with --follow and --follow-first
1837
1837
1838 $ hg up -q 3
1838 $ hg up -q 3
1839 $ hg log -G --git --patch b
1839 $ hg log -G --git --patch b
1840 o changeset: 1:216d4c92cf98
1840 o changeset: 1:216d4c92cf98
1841 | user: test
1841 | user: test
1842 | date: Thu Jan 01 00:00:00 1970 +0000
1842 | date: Thu Jan 01 00:00:00 1970 +0000
1843 | summary: copy a b
1843 | summary: copy a b
1844 |
1844 |
1845 | diff --git a/a b/b
1845 | diff --git a/a b/b
1846 | copy from a
1846 | copy from a
1847 | copy to b
1847 | copy to b
1848 |
1848 |
1849
1849
1850 $ hg log -G --git --stat b
1850 $ hg log -G --git --stat b
1851 o changeset: 1:216d4c92cf98
1851 o changeset: 1:216d4c92cf98
1852 | user: test
1852 | user: test
1853 | date: Thu Jan 01 00:00:00 1970 +0000
1853 | date: Thu Jan 01 00:00:00 1970 +0000
1854 | summary: copy a b
1854 | summary: copy a b
1855 |
1855 |
1856 | a | 0
1856 | b | 0
1857 | 1 files changed, 0 insertions(+), 0 deletions(-)
1857 | 1 files changed, 0 insertions(+), 0 deletions(-)
1858 |
1858 |
1859
1859
1860 $ hg log -G --git --patch --follow b
1860 $ hg log -G --git --patch --follow b
1861 o changeset: 1:216d4c92cf98
1861 o changeset: 1:216d4c92cf98
1862 | user: test
1862 | user: test
1863 | date: Thu Jan 01 00:00:00 1970 +0000
1863 | date: Thu Jan 01 00:00:00 1970 +0000
1864 | summary: copy a b
1864 | summary: copy a b
1865 |
1865 |
1866 | diff --git a/a b/b
1866 | diff --git a/a b/b
1867 | copy from a
1867 | copy from a
1868 | copy to b
1868 | copy to b
1869 |
1869 |
1870 o changeset: 0:f8035bb17114
1870 o changeset: 0:f8035bb17114
1871 user: test
1871 user: test
1872 date: Thu Jan 01 00:00:00 1970 +0000
1872 date: Thu Jan 01 00:00:00 1970 +0000
1873 summary: add a
1873 summary: add a
1874
1874
1875 diff --git a/a b/a
1875 diff --git a/a b/a
1876 new file mode 100644
1876 new file mode 100644
1877 --- /dev/null
1877 --- /dev/null
1878 +++ b/a
1878 +++ b/a
1879 @@ -0,0 +1,1 @@
1879 @@ -0,0 +1,1 @@
1880 +a
1880 +a
1881
1881
1882
1882
1883 $ hg log -G --git --stat --follow b
1883 $ hg log -G --git --stat --follow b
1884 o changeset: 1:216d4c92cf98
1884 o changeset: 1:216d4c92cf98
1885 | user: test
1885 | user: test
1886 | date: Thu Jan 01 00:00:00 1970 +0000
1886 | date: Thu Jan 01 00:00:00 1970 +0000
1887 | summary: copy a b
1887 | summary: copy a b
1888 |
1888 |
1889 | a | 0
1889 | b | 0
1890 | 1 files changed, 0 insertions(+), 0 deletions(-)
1890 | 1 files changed, 0 insertions(+), 0 deletions(-)
1891 |
1891 |
1892 o changeset: 0:f8035bb17114
1892 o changeset: 0:f8035bb17114
1893 user: test
1893 user: test
1894 date: Thu Jan 01 00:00:00 1970 +0000
1894 date: Thu Jan 01 00:00:00 1970 +0000
1895 summary: add a
1895 summary: add a
1896
1896
1897 a | 1 +
1897 a | 1 +
1898 1 files changed, 1 insertions(+), 0 deletions(-)
1898 1 files changed, 1 insertions(+), 0 deletions(-)
1899
1899
1900
1900
1901 $ hg up -q 6
1901 $ hg up -q 6
1902 $ hg log -G --git --patch --follow-first e
1902 $ hg log -G --git --patch --follow-first e
1903 @ changeset: 6:fc281d8ff18d
1903 @ changeset: 6:fc281d8ff18d
1904 |\ tag: tip
1904 |\ tag: tip
1905 | | parent: 5:99b31f1c2782
1905 | | parent: 5:99b31f1c2782
1906 | | parent: 4:17d952250a9d
1906 | | parent: 4:17d952250a9d
1907 | | user: test
1907 | | user: test
1908 | | date: Thu Jan 01 00:00:00 1970 +0000
1908 | | date: Thu Jan 01 00:00:00 1970 +0000
1909 | | summary: merge 5 and 4
1909 | | summary: merge 5 and 4
1910 | |
1910 | |
1911 | | diff --git a/e b/e
1911 | | diff --git a/e b/e
1912 | | --- a/e
1912 | | --- a/e
1913 | | +++ b/e
1913 | | +++ b/e
1914 | | @@ -1,1 +1,1 @@
1914 | | @@ -1,1 +1,1 @@
1915 | | -ee
1915 | | -ee
1916 | | +merge
1916 | | +merge
1917 | |
1917 | |
1918 o | changeset: 5:99b31f1c2782
1918 o | changeset: 5:99b31f1c2782
1919 | | parent: 3:5918b8d165d1
1919 | | parent: 3:5918b8d165d1
1920 | | user: test
1920 | | user: test
1921 | | date: Thu Jan 01 00:00:00 1970 +0000
1921 | | date: Thu Jan 01 00:00:00 1970 +0000
1922 | | summary: add another e
1922 | | summary: add another e
1923 | |
1923 | |
1924 | | diff --git a/e b/e
1924 | | diff --git a/e b/e
1925 | | new file mode 100644
1925 | | new file mode 100644
1926 | | --- /dev/null
1926 | | --- /dev/null
1927 | | +++ b/e
1927 | | +++ b/e
1928 | | @@ -0,0 +1,1 @@
1928 | | @@ -0,0 +1,1 @@
1929 | | +ee
1929 | | +ee
1930 | |
1930 | |
1931
1931
1932 Test old-style --rev
1932 Test old-style --rev
1933
1933
1934 $ hg tag 'foo-bar'
1934 $ hg tag 'foo-bar'
1935 $ testlog -r 'foo-bar'
1935 $ testlog -r 'foo-bar'
1936 ['foo-bar']
1936 ['foo-bar']
1937 []
1937 []
1938
1938
1939 Test --follow and forward --rev
1939 Test --follow and forward --rev
1940
1940
1941 $ hg up -q 6
1941 $ hg up -q 6
1942 $ echo g > g
1942 $ echo g > g
1943 $ hg ci -Am 'add g' g
1943 $ hg ci -Am 'add g' g
1944 created new head
1944 created new head
1945 $ hg up -q 2
1945 $ hg up -q 2
1946 $ hg log -G --template "{rev} {desc|firstline}\n"
1946 $ hg log -G --template "{rev} {desc|firstline}\n"
1947 o 8 add g
1947 o 8 add g
1948 |
1948 |
1949 | o 7 Added tag foo-bar for changeset fc281d8ff18d
1949 | o 7 Added tag foo-bar for changeset fc281d8ff18d
1950 |/
1950 |/
1951 o 6 merge 5 and 4
1951 o 6 merge 5 and 4
1952 |\
1952 |\
1953 | o 5 add another e
1953 | o 5 add another e
1954 | |
1954 | |
1955 o | 4 mv dir/b e
1955 o | 4 mv dir/b e
1956 |/
1956 |/
1957 o 3 mv a b; add d
1957 o 3 mv a b; add d
1958 |
1958 |
1959 @ 2 mv b dir/b
1959 @ 2 mv b dir/b
1960 |
1960 |
1961 o 1 copy a b
1961 o 1 copy a b
1962 |
1962 |
1963 o 0 add a
1963 o 0 add a
1964
1964
1965 $ testlog --follow -r6 -r8 -r5 -r7 -r4
1965 $ testlog --follow -r6 -r8 -r5 -r7 -r4
1966 ['6', '8', '5', '7', '4']
1966 ['6', '8', '5', '7', '4']
1967 (group
1967 (group
1968 (func
1968 (func
1969 ('symbol', 'descendants')
1969 ('symbol', 'descendants')
1970 ('symbol', '6')))
1970 ('symbol', '6')))
1971 --- log.nodes * (glob)
1971 --- log.nodes * (glob)
1972 +++ glog.nodes * (glob)
1972 +++ glog.nodes * (glob)
1973 @@ -1,3 +1,3 @@
1973 @@ -1,3 +1,3 @@
1974 -nodetag 6
1974 -nodetag 6
1975 nodetag 8
1975 nodetag 8
1976 nodetag 7
1976 nodetag 7
1977 +nodetag 6
1977 +nodetag 6
1978
1978
1979 Test --follow-first and forward --rev
1979 Test --follow-first and forward --rev
1980
1980
1981 $ testlog --follow-first -r6 -r8 -r5 -r7 -r4
1981 $ testlog --follow-first -r6 -r8 -r5 -r7 -r4
1982 ['6', '8', '5', '7', '4']
1982 ['6', '8', '5', '7', '4']
1983 (group
1983 (group
1984 (func
1984 (func
1985 ('symbol', '_firstdescendants')
1985 ('symbol', '_firstdescendants')
1986 ('symbol', '6')))
1986 ('symbol', '6')))
1987 --- log.nodes * (glob)
1987 --- log.nodes * (glob)
1988 +++ glog.nodes * (glob)
1988 +++ glog.nodes * (glob)
1989 @@ -1,3 +1,3 @@
1989 @@ -1,3 +1,3 @@
1990 -nodetag 6
1990 -nodetag 6
1991 nodetag 8
1991 nodetag 8
1992 nodetag 7
1992 nodetag 7
1993 +nodetag 6
1993 +nodetag 6
1994
1994
1995 Test --follow and backward --rev
1995 Test --follow and backward --rev
1996
1996
1997 $ testlog --follow -r6 -r5 -r7 -r8 -r4
1997 $ testlog --follow -r6 -r5 -r7 -r8 -r4
1998 ['6', '5', '7', '8', '4']
1998 ['6', '5', '7', '8', '4']
1999 (group
1999 (group
2000 (func
2000 (func
2001 ('symbol', 'ancestors')
2001 ('symbol', 'ancestors')
2002 ('symbol', '6')))
2002 ('symbol', '6')))
2003
2003
2004 Test --follow-first and backward --rev
2004 Test --follow-first and backward --rev
2005
2005
2006 $ testlog --follow-first -r6 -r5 -r7 -r8 -r4
2006 $ testlog --follow-first -r6 -r5 -r7 -r8 -r4
2007 ['6', '5', '7', '8', '4']
2007 ['6', '5', '7', '8', '4']
2008 (group
2008 (group
2009 (func
2009 (func
2010 ('symbol', '_firstancestors')
2010 ('symbol', '_firstancestors')
2011 ('symbol', '6')))
2011 ('symbol', '6')))
2012
2012
2013 Test subdir
2013 Test subdir
2014
2014
2015 $ hg up -q 3
2015 $ hg up -q 3
2016 $ cd dir
2016 $ cd dir
2017 $ testlog .
2017 $ testlog .
2018 []
2018 []
2019 (group
2019 (group
2020 (func
2020 (func
2021 ('symbol', '_matchfiles')
2021 ('symbol', '_matchfiles')
2022 (list
2022 (list
2023 (list
2023 (list
2024 ('string', 'r:')
2024 ('string', 'r:')
2025 ('string', 'd:relpath'))
2025 ('string', 'd:relpath'))
2026 ('string', 'p:.'))))
2026 ('string', 'p:.'))))
2027 $ testlog ../b
2027 $ testlog ../b
2028 []
2028 []
2029 (group
2029 (group
2030 (group
2030 (group
2031 (func
2031 (func
2032 ('symbol', 'filelog')
2032 ('symbol', 'filelog')
2033 ('string', '../b'))))
2033 ('string', '../b'))))
2034 $ testlog -f ../b
2034 $ testlog -f ../b
2035 []
2035 []
2036 (group
2036 (group
2037 (group
2037 (group
2038 (func
2038 (func
2039 ('symbol', 'follow')
2039 ('symbol', 'follow')
2040 ('string', 'b'))))
2040 ('string', 'b'))))
2041 $ cd ..
2041 $ cd ..
2042
2042
2043 Test --hidden
2043 Test --hidden
2044 (enable obsolete)
2044 (enable obsolete)
2045
2045
2046 $ cat > ${TESTTMP}/obs.py << EOF
2046 $ cat > ${TESTTMP}/obs.py << EOF
2047 > import mercurial.obsolete
2047 > import mercurial.obsolete
2048 > mercurial.obsolete._enabled = True
2048 > mercurial.obsolete._enabled = True
2049 > EOF
2049 > EOF
2050 $ echo '[extensions]' >> $HGRCPATH
2050 $ echo '[extensions]' >> $HGRCPATH
2051 $ echo "obs=${TESTTMP}/obs.py" >> $HGRCPATH
2051 $ echo "obs=${TESTTMP}/obs.py" >> $HGRCPATH
2052
2052
2053 $ hg debugobsolete `hg id --debug -i -r 8`
2053 $ hg debugobsolete `hg id --debug -i -r 8`
2054 $ testlog
2054 $ testlog
2055 []
2055 []
2056 []
2056 []
2057 $ testlog --hidden
2057 $ testlog --hidden
2058 []
2058 []
2059 []
2059 []
2060 $ hg log -G --template '{rev} {desc}\n'
2060 $ hg log -G --template '{rev} {desc}\n'
2061 o 7 Added tag foo-bar for changeset fc281d8ff18d
2061 o 7 Added tag foo-bar for changeset fc281d8ff18d
2062 |
2062 |
2063 o 6 merge 5 and 4
2063 o 6 merge 5 and 4
2064 |\
2064 |\
2065 | o 5 add another e
2065 | o 5 add another e
2066 | |
2066 | |
2067 o | 4 mv dir/b e
2067 o | 4 mv dir/b e
2068 |/
2068 |/
2069 @ 3 mv a b; add d
2069 @ 3 mv a b; add d
2070 |
2070 |
2071 o 2 mv b dir/b
2071 o 2 mv b dir/b
2072 |
2072 |
2073 o 1 copy a b
2073 o 1 copy a b
2074 |
2074 |
2075 o 0 add a
2075 o 0 add a
2076
2076
2077
2077
2078 A template without trailing newline should do something sane
2078 A template without trailing newline should do something sane
2079
2079
2080 $ hg log -G -r ::2 --template '{rev} {desc}'
2080 $ hg log -G -r ::2 --template '{rev} {desc}'
2081 o 2 mv b dir/b
2081 o 2 mv b dir/b
2082 |
2082 |
2083 o 1 copy a b
2083 o 1 copy a b
2084 |
2084 |
2085 o 0 add a
2085 o 0 add a
2086
2086
2087
2087
2088 Extra newlines must be preserved
2088 Extra newlines must be preserved
2089
2089
2090 $ hg log -G -r ::2 --template '\n{rev} {desc}\n\n'
2090 $ hg log -G -r ::2 --template '\n{rev} {desc}\n\n'
2091 o
2091 o
2092 | 2 mv b dir/b
2092 | 2 mv b dir/b
2093 |
2093 |
2094 o
2094 o
2095 | 1 copy a b
2095 | 1 copy a b
2096 |
2096 |
2097 o
2097 o
2098 0 add a
2098 0 add a
2099
2099
2100
2100
2101 The almost-empty template should do something sane too ...
2101 The almost-empty template should do something sane too ...
2102
2102
2103 $ hg log -G -r ::2 --template '\n'
2103 $ hg log -G -r ::2 --template '\n'
2104 o
2104 o
2105 |
2105 |
2106 o
2106 o
2107 |
2107 |
2108 o
2108 o
2109
2109
2110
2110
2111 issue3772
2111 issue3772
2112
2112
2113 $ hg log -G -r :null
2113 $ hg log -G -r :null
2114 o changeset: 0:f8035bb17114
2114 o changeset: 0:f8035bb17114
2115 | user: test
2115 | user: test
2116 | date: Thu Jan 01 00:00:00 1970 +0000
2116 | date: Thu Jan 01 00:00:00 1970 +0000
2117 | summary: add a
2117 | summary: add a
2118 |
2118 |
2119 o changeset: -1:000000000000
2119 o changeset: -1:000000000000
2120 user:
2120 user:
2121 date: Thu Jan 01 00:00:00 1970 +0000
2121 date: Thu Jan 01 00:00:00 1970 +0000
2122
2122
2123 $ hg log -G -r null:null
2123 $ hg log -G -r null:null
2124 o changeset: -1:000000000000
2124 o changeset: -1:000000000000
2125 user:
2125 user:
2126 date: Thu Jan 01 00:00:00 1970 +0000
2126 date: Thu Jan 01 00:00:00 1970 +0000
2127
2127
2128
2128
2129 $ cd ..
2129 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now