##// END OF EJS Templates
Attempt to make diff deal with null sources properly...
mpm@selenic.com -
r264:4c1d7072 default
parent child Browse files
Show More
@@ -1,604 +1,604 b''
1 # commands.py - command processing for mercurial
1 # commands.py - command processing for mercurial
2 #
2 #
3 # Copyright 2005 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms
5 # This software may be used and distributed according to the terms
6 # of the GNU General Public License, incorporated herein by reference.
6 # of the GNU General Public License, incorporated herein by reference.
7
7
8 import os, re, sys, signal
8 import os, re, sys, signal
9 import fancyopts, ui, hg
9 import fancyopts, ui, hg
10 from demandload import *
10 from demandload import *
11 demandload(globals(), "mdiff time hgweb traceback")
11 demandload(globals(), "mdiff time hgweb traceback")
12
12
13 class UnknownCommand(Exception): pass
13 class UnknownCommand(Exception): pass
14
14
15 def filterfiles(filters, files):
15 def filterfiles(filters, files):
16 l = [ x for x in files if x in filters ]
16 l = [ x for x in files if x in filters ]
17
17
18 for t in filters:
18 for t in filters:
19 if t and t[-1] != os.sep: t += os.sep
19 if t and t[-1] != os.sep: t += os.sep
20 l += [ x for x in files if x.startswith(t) ]
20 l += [ x for x in files if x.startswith(t) ]
21 return l
21 return l
22
22
23 def relfilter(repo, files):
23 def relfilter(repo, files):
24 if os.getcwd() != repo.root:
24 if os.getcwd() != repo.root:
25 p = os.getcwd()[len(repo.root) + 1: ]
25 p = os.getcwd()[len(repo.root) + 1: ]
26 return filterfiles(p, files)
26 return filterfiles(p, files)
27 return files
27 return files
28
28
29 def relpath(repo, args):
29 def relpath(repo, args):
30 if os.getcwd() != repo.root:
30 if os.getcwd() != repo.root:
31 p = os.getcwd()[len(repo.root) + 1: ]
31 p = os.getcwd()[len(repo.root) + 1: ]
32 return [ os.path.normpath(os.path.join(p, x)) for x in args ]
32 return [ os.path.normpath(os.path.join(p, x)) for x in args ]
33 return args
33 return args
34
34
35 def dodiff(repo, files = None, node1 = None, node2 = None):
35 def dodiff(repo, files = None, node1 = None, node2 = None):
36 def date(c):
36 def date(c):
37 return time.asctime(time.gmtime(float(c[2].split(' ')[0])))
37 return time.asctime(time.gmtime(float(c[2].split(' ')[0])))
38
38
39 if node2:
39 if node2:
40 change = repo.changelog.read(node2)
40 change = repo.changelog.read(node2)
41 mmap2 = repo.manifest.read(change[0])
41 mmap2 = repo.manifest.read(change[0])
42 (c, a, d) = repo.diffrevs(node1, node2)
42 (c, a, d) = repo.diffrevs(node1, node2)
43 def read(f): return repo.file(f).read(mmap2[f])
43 def read(f): return repo.file(f).read(mmap2[f])
44 date2 = date(change)
44 date2 = date(change)
45 else:
45 else:
46 date2 = time.asctime()
46 date2 = time.asctime()
47 (c, a, d, u) = repo.diffdir(repo.root, node1)
47 (c, a, d, u) = repo.diffdir(repo.root, node1)
48 if not node1:
48 if not node1:
49 node1 = repo.dirstate.parents()[0]
49 node1 = repo.dirstate.parents()[0]
50 def read(f): return file(os.path.join(repo.root, f)).read()
50 def read(f): return file(os.path.join(repo.root, f)).read()
51
51
52 change = repo.changelog.read(node1)
52 change = repo.changelog.read(node1)
53 mmap = repo.manifest.read(change[0])
53 mmap = repo.manifest.read(change[0])
54 date1 = date(change)
54 date1 = date(change)
55
55
56 if files:
56 if files:
57 c, a, d = map(lambda x: filterfiles(files, x), (c, a, d))
57 c, a, d = map(lambda x: filterfiles(files, x), (c, a, d))
58
58
59 for f in c:
59 for f in c:
60 to = repo.file(f).read(mmap[f])
60 to = repo.file(f).read(mmap[f])
61 tn = read(f)
61 tn = read(f)
62 sys.stdout.write(mdiff.unidiff(to, date1, tn, date2, f))
62 sys.stdout.write(mdiff.unidiff(to, date1, tn, date2, f))
63 for f in a:
63 for f in a:
64 to = ""
64 to = None
65 tn = read(f)
65 tn = read(f)
66 sys.stdout.write(mdiff.unidiff(to, date1, tn, date2, f))
66 sys.stdout.write(mdiff.unidiff(to, date1, tn, date2, f))
67 for f in d:
67 for f in d:
68 to = repo.file(f).read(mmap[f])
68 to = repo.file(f).read(mmap[f])
69 tn = ""
69 tn = None
70 sys.stdout.write(mdiff.unidiff(to, date1, tn, date2, f))
70 sys.stdout.write(mdiff.unidiff(to, date1, tn, date2, f))
71
71
72 def help(ui, cmd=None):
72 def help(ui, cmd=None):
73 '''show help for a given command or all commands'''
73 '''show help for a given command or all commands'''
74 if cmd:
74 if cmd:
75 try:
75 try:
76 i = find(cmd)
76 i = find(cmd)
77 ui.write("%s\n\n" % i[2])
77 ui.write("%s\n\n" % i[2])
78 ui.write(i[0].__doc__, "\n")
78 ui.write(i[0].__doc__, "\n")
79 except UnknownCommand:
79 except UnknownCommand:
80 ui.warn("unknown command %s" % cmd)
80 ui.warn("unknown command %s" % cmd)
81 sys.exit(0)
81 sys.exit(0)
82 else:
82 else:
83 ui.status('hg commands:\n\n')
83 ui.status('hg commands:\n\n')
84
84
85 h = {}
85 h = {}
86 for e in table.values():
86 for e in table.values():
87 f = e[0]
87 f = e[0]
88 if f.__name__.startswith("debug"): continue
88 if f.__name__.startswith("debug"): continue
89 d = ""
89 d = ""
90 if f.__doc__:
90 if f.__doc__:
91 d = f.__doc__.splitlines(0)[0].rstrip()
91 d = f.__doc__.splitlines(0)[0].rstrip()
92 h[f.__name__] = d
92 h[f.__name__] = d
93
93
94 fns = h.keys()
94 fns = h.keys()
95 fns.sort()
95 fns.sort()
96 m = max(map(len, fns))
96 m = max(map(len, fns))
97 for f in fns:
97 for f in fns:
98 ui.status(' %-*s %s\n' % (m, f, h[f]))
98 ui.status(' %-*s %s\n' % (m, f, h[f]))
99
99
100 # Commands start here, listed alphabetically
100 # Commands start here, listed alphabetically
101
101
102 def add(ui, repo, file, *files):
102 def add(ui, repo, file, *files):
103 '''add the specified files on the next commit'''
103 '''add the specified files on the next commit'''
104 repo.add(relpath(repo, (file,) + files))
104 repo.add(relpath(repo, (file,) + files))
105
105
106 def addremove(ui, repo):
106 def addremove(ui, repo):
107 """add all new files, delete all missing files"""
107 """add all new files, delete all missing files"""
108 (c, a, d, u) = repo.diffdir(repo.root)
108 (c, a, d, u) = repo.diffdir(repo.root)
109 repo.add(u)
109 repo.add(u)
110 repo.remove(d)
110 repo.remove(d)
111
111
112 def annotate(u, repo, file, *files, **ops):
112 def annotate(u, repo, file, *files, **ops):
113 """show changeset information per file line"""
113 """show changeset information per file line"""
114 def getnode(rev):
114 def getnode(rev):
115 return hg.short(repo.changelog.node(rev))
115 return hg.short(repo.changelog.node(rev))
116
116
117 def getname(rev):
117 def getname(rev):
118 try:
118 try:
119 return bcache[rev]
119 return bcache[rev]
120 except KeyError:
120 except KeyError:
121 cl = repo.changelog.read(repo.changelog.node(rev))
121 cl = repo.changelog.read(repo.changelog.node(rev))
122 name = cl[1]
122 name = cl[1]
123 f = name.find('@')
123 f = name.find('@')
124 if f >= 0:
124 if f >= 0:
125 name = name[:f]
125 name = name[:f]
126 bcache[rev] = name
126 bcache[rev] = name
127 return name
127 return name
128
128
129 bcache = {}
129 bcache = {}
130 opmap = [['user', getname], ['number', str], ['changeset', getnode]]
130 opmap = [['user', getname], ['number', str], ['changeset', getnode]]
131 if not ops['user'] and not ops['changeset']:
131 if not ops['user'] and not ops['changeset']:
132 ops['number'] = 1
132 ops['number'] = 1
133
133
134 node = repo.dirstate.parents()[0]
134 node = repo.dirstate.parents()[0]
135 if ops['revision']:
135 if ops['revision']:
136 node = repo.changelog.lookup(ops['revision'])
136 node = repo.changelog.lookup(ops['revision'])
137 change = repo.changelog.read(node)
137 change = repo.changelog.read(node)
138 mmap = repo.manifest.read(change[0])
138 mmap = repo.manifest.read(change[0])
139 maxuserlen = 0
139 maxuserlen = 0
140 maxchangelen = 0
140 maxchangelen = 0
141 for f in relpath(repo, (file,) + files):
141 for f in relpath(repo, (file,) + files):
142 lines = repo.file(f).annotate(mmap[f])
142 lines = repo.file(f).annotate(mmap[f])
143 pieces = []
143 pieces = []
144
144
145 for o, f in opmap:
145 for o, f in opmap:
146 if ops[o]:
146 if ops[o]:
147 l = [ f(n) for n,t in lines ]
147 l = [ f(n) for n,t in lines ]
148 m = max(map(len, l))
148 m = max(map(len, l))
149 pieces.append([ "%*s" % (m, x) for x in l])
149 pieces.append([ "%*s" % (m, x) for x in l])
150
150
151 for p,l in zip(zip(*pieces), lines):
151 for p,l in zip(zip(*pieces), lines):
152 u.write(" ".join(p) + ": " + l[1])
152 u.write(" ".join(p) + ": " + l[1])
153
153
154 def branch(ui, path):
154 def branch(ui, path):
155 '''branch from a local repository'''
155 '''branch from a local repository'''
156 # this should eventually support remote repos
156 # this should eventually support remote repos
157 os.system("cp -al %s/.hg .hg" % path)
157 os.system("cp -al %s/.hg .hg" % path)
158
158
159 def cat(ui, repo, file, rev = []):
159 def cat(ui, repo, file, rev = []):
160 """output the latest or given revision of a file"""
160 """output the latest or given revision of a file"""
161 r = repo.file(file)
161 r = repo.file(file)
162 n = r.tip()
162 n = r.tip()
163 if rev: n = r.lookup(rev)
163 if rev: n = r.lookup(rev)
164 sys.stdout.write(r.read(n))
164 sys.stdout.write(r.read(n))
165
165
166 def commit(ui, repo, *files):
166 def commit(ui, repo, *files):
167 """commit the specified files or all outstanding changes"""
167 """commit the specified files or all outstanding changes"""
168 repo.commit(relpath(repo, files))
168 repo.commit(relpath(repo, files))
169
169
170 def debugaddchangegroup(ui, repo):
170 def debugaddchangegroup(ui, repo):
171 data = sys.stdin.read()
171 data = sys.stdin.read()
172 repo.addchangegroup(data)
172 repo.addchangegroup(data)
173
173
174 def debugchangegroup(ui, repo, roots):
174 def debugchangegroup(ui, repo, roots):
175 newer = repo.newer(map(repo.lookup, roots))
175 newer = repo.newer(map(repo.lookup, roots))
176 for chunk in repo.changegroup(newer):
176 for chunk in repo.changegroup(newer):
177 sys.stdout.write(chunk)
177 sys.stdout.write(chunk)
178
178
179 def debugindex(ui, file):
179 def debugindex(ui, file):
180 r = hg.revlog(open, file, "")
180 r = hg.revlog(open, file, "")
181 print " rev offset length base linkrev"+\
181 print " rev offset length base linkrev"+\
182 " p1 p2 nodeid"
182 " p1 p2 nodeid"
183 for i in range(r.count()):
183 for i in range(r.count()):
184 e = r.index[i]
184 e = r.index[i]
185 print "% 6d % 9d % 7d % 6d % 7d %s.. %s.. %s.." % (
185 print "% 6d % 9d % 7d % 6d % 7d %s.. %s.. %s.." % (
186 i, e[0], e[1], e[2], e[3],
186 i, e[0], e[1], e[2], e[3],
187 hg.hex(e[4][:5]), hg.hex(e[5][:5]), hg.hex(e[6][:5]))
187 hg.hex(e[4][:5]), hg.hex(e[5][:5]), hg.hex(e[6][:5]))
188
188
189 def debugindexdot(ui, file):
189 def debugindexdot(ui, file):
190 r = hg.revlog(open, file, "")
190 r = hg.revlog(open, file, "")
191 print "digraph G {"
191 print "digraph G {"
192 for i in range(r.count()):
192 for i in range(r.count()):
193 e = r.index[i]
193 e = r.index[i]
194 print "\t%d -> %d" % (r.rev(e[4]), i)
194 print "\t%d -> %d" % (r.rev(e[4]), i)
195 if e[5] != hg.nullid:
195 if e[5] != hg.nullid:
196 print "\t%d -> %d" % (r.rev(e[5]), i)
196 print "\t%d -> %d" % (r.rev(e[5]), i)
197 print "}"
197 print "}"
198
198
199 def diff(ui, repo, *files, **opts):
199 def diff(ui, repo, *files, **opts):
200 """diff working directory (or selected files)"""
200 """diff working directory (or selected files)"""
201 revs = []
201 revs = []
202 if opts['rev']:
202 if opts['rev']:
203 revs = map(lambda x: repo.lookup(x), opts['rev'])
203 revs = map(lambda x: repo.lookup(x), opts['rev'])
204
204
205 if len(revs) > 2:
205 if len(revs) > 2:
206 self.ui.warn("too many revisions to diff\n")
206 self.ui.warn("too many revisions to diff\n")
207 sys.exit(1)
207 sys.exit(1)
208
208
209 if files:
209 if files:
210 files = relpath(repo, files)
210 files = relpath(repo, files)
211 else:
211 else:
212 files = relpath(repo, [""])
212 files = relpath(repo, [""])
213
213
214 dodiff(repo, files, *revs)
214 dodiff(repo, files, *revs)
215
215
216 def export(ui, repo, changeset):
216 def export(ui, repo, changeset):
217 """dump the changeset header and diffs for a revision"""
217 """dump the changeset header and diffs for a revision"""
218 node = repo.lookup(changeset)
218 node = repo.lookup(changeset)
219 prev, other = repo.changelog.parents(node)
219 prev, other = repo.changelog.parents(node)
220 change = repo.changelog.read(node)
220 change = repo.changelog.read(node)
221 print "# HG changeset patch"
221 print "# HG changeset patch"
222 print "# User %s" % change[1]
222 print "# User %s" % change[1]
223 print "# Node ID %s" % hg.hex(node)
223 print "# Node ID %s" % hg.hex(node)
224 print "# Parent %s" % hg.hex(prev)
224 print "# Parent %s" % hg.hex(prev)
225 print
225 print
226 if other != hg.nullid:
226 if other != hg.nullid:
227 print "# Parent %s" % hg.hex(other)
227 print "# Parent %s" % hg.hex(other)
228 print change[4].rstrip()
228 print change[4].rstrip()
229 print
229 print
230
230
231 dodiff(repo, None, prev, node)
231 dodiff(repo, None, prev, node)
232
232
233 def forget(ui, repo, file, *files):
233 def forget(ui, repo, file, *files):
234 """don't add the specified files on the next commit"""
234 """don't add the specified files on the next commit"""
235 repo.forget(relpath(repo, (file,) + files))
235 repo.forget(relpath(repo, (file,) + files))
236
236
237 def heads(ui, repo):
237 def heads(ui, repo):
238 '''show current repository heads'''
238 '''show current repository heads'''
239 for n in repo.changelog.heads():
239 for n in repo.changelog.heads():
240 i = repo.changelog.rev(n)
240 i = repo.changelog.rev(n)
241 changes = repo.changelog.read(n)
241 changes = repo.changelog.read(n)
242 (p1, p2) = repo.changelog.parents(n)
242 (p1, p2) = repo.changelog.parents(n)
243 (h, h1, h2) = map(hg.hex, (n, p1, p2))
243 (h, h1, h2) = map(hg.hex, (n, p1, p2))
244 (i1, i2) = map(repo.changelog.rev, (p1, p2))
244 (i1, i2) = map(repo.changelog.rev, (p1, p2))
245 print "rev: %4d:%s" % (i, h)
245 print "rev: %4d:%s" % (i, h)
246 print "parents: %4d:%s" % (i1, h1)
246 print "parents: %4d:%s" % (i1, h1)
247 if i2: print " %4d:%s" % (i2, h2)
247 if i2: print " %4d:%s" % (i2, h2)
248 print "manifest: %4d:%s" % (repo.manifest.rev(changes[0]),
248 print "manifest: %4d:%s" % (repo.manifest.rev(changes[0]),
249 hg.hex(changes[0]))
249 hg.hex(changes[0]))
250 print "user:", changes[1]
250 print "user:", changes[1]
251 print "date:", time.asctime(
251 print "date:", time.asctime(
252 time.localtime(float(changes[2].split(' ')[0])))
252 time.localtime(float(changes[2].split(' ')[0])))
253 if ui.verbose: print "files:", " ".join(changes[3])
253 if ui.verbose: print "files:", " ".join(changes[3])
254 print "description:"
254 print "description:"
255 print changes[4]
255 print changes[4]
256
256
257 def history(ui, repo):
257 def history(ui, repo):
258 """show the changelog history"""
258 """show the changelog history"""
259 for i in range(repo.changelog.count()):
259 for i in range(repo.changelog.count()):
260 n = repo.changelog.node(i)
260 n = repo.changelog.node(i)
261 changes = repo.changelog.read(n)
261 changes = repo.changelog.read(n)
262 (p1, p2) = repo.changelog.parents(n)
262 (p1, p2) = repo.changelog.parents(n)
263 (h, h1, h2) = map(hg.hex, (n, p1, p2))
263 (h, h1, h2) = map(hg.hex, (n, p1, p2))
264 (i1, i2) = map(repo.changelog.rev, (p1, p2))
264 (i1, i2) = map(repo.changelog.rev, (p1, p2))
265 print "rev: %4d:%s" % (i, h)
265 print "rev: %4d:%s" % (i, h)
266 print "parents: %4d:%s" % (i1, h1)
266 print "parents: %4d:%s" % (i1, h1)
267 if i2: print " %4d:%s" % (i2, h2)
267 if i2: print " %4d:%s" % (i2, h2)
268 print "manifest: %4d:%s" % (repo.manifest.rev(changes[0]),
268 print "manifest: %4d:%s" % (repo.manifest.rev(changes[0]),
269 hg.hex(changes[0]))
269 hg.hex(changes[0]))
270 print "user:", changes[1]
270 print "user:", changes[1]
271 print "date:", time.asctime(
271 print "date:", time.asctime(
272 time.localtime(float(changes[2].split(' ')[0])))
272 time.localtime(float(changes[2].split(' ')[0])))
273 if ui.verbose: print "files:", " ".join(changes[3])
273 if ui.verbose: print "files:", " ".join(changes[3])
274 print "description:"
274 print "description:"
275 print changes[4]
275 print changes[4]
276
276
277 def init(ui):
277 def init(ui):
278 """create a repository"""
278 """create a repository"""
279 hg.repository(ui, ".", create=1)
279 hg.repository(ui, ".", create=1)
280
280
281 def log(ui, repo, f):
281 def log(ui, repo, f):
282 """show the revision history of a single file"""
282 """show the revision history of a single file"""
283 f = relpath(repo, [f])[0]
283 f = relpath(repo, [f])[0]
284
284
285 r = repo.file(f)
285 r = repo.file(f)
286 for i in range(r.count()):
286 for i in range(r.count()):
287 n = r.node(i)
287 n = r.node(i)
288 (p1, p2) = r.parents(n)
288 (p1, p2) = r.parents(n)
289 (h, h1, h2) = map(hg.hex, (n, p1, p2))
289 (h, h1, h2) = map(hg.hex, (n, p1, p2))
290 (i1, i2) = map(r.rev, (p1, p2))
290 (i1, i2) = map(r.rev, (p1, p2))
291 cr = r.linkrev(n)
291 cr = r.linkrev(n)
292 cn = hg.hex(repo.changelog.node(cr))
292 cn = hg.hex(repo.changelog.node(cr))
293 print "rev: %4d:%s" % (i, h)
293 print "rev: %4d:%s" % (i, h)
294 print "changeset: %4d:%s" % (cr, cn)
294 print "changeset: %4d:%s" % (cr, cn)
295 print "parents: %4d:%s" % (i1, h1)
295 print "parents: %4d:%s" % (i1, h1)
296 if i2: print " %4d:%s" % (i2, h2)
296 if i2: print " %4d:%s" % (i2, h2)
297 changes = repo.changelog.read(repo.changelog.node(cr))
297 changes = repo.changelog.read(repo.changelog.node(cr))
298 print "user: %s" % changes[1]
298 print "user: %s" % changes[1]
299 print "date: %s" % time.asctime(
299 print "date: %s" % time.asctime(
300 time.localtime(float(changes[2].split(' ')[0])))
300 time.localtime(float(changes[2].split(' ')[0])))
301 print "description:"
301 print "description:"
302 print changes[4].rstrip()
302 print changes[4].rstrip()
303 print
303 print
304
304
305 def manifest(ui, repo, rev = []):
305 def manifest(ui, repo, rev = []):
306 """output the latest or given revision of the project manifest"""
306 """output the latest or given revision of the project manifest"""
307 n = repo.manifest.tip()
307 n = repo.manifest.tip()
308 if rev:
308 if rev:
309 n = repo.manifest.lookup(rev)
309 n = repo.manifest.lookup(rev)
310 m = repo.manifest.read(n)
310 m = repo.manifest.read(n)
311 files = m.keys()
311 files = m.keys()
312 files.sort()
312 files.sort()
313
313
314 for f in files:
314 for f in files:
315 print hg.hex(m[f]), f
315 print hg.hex(m[f]), f
316
316
317 def parents(ui, repo, node = None):
317 def parents(ui, repo, node = None):
318 '''show the parents of the current working dir'''
318 '''show the parents of the current working dir'''
319 if node:
319 if node:
320 p = repo.changelog.parents(repo.lookup(hg.bin(node)))
320 p = repo.changelog.parents(repo.lookup(hg.bin(node)))
321 else:
321 else:
322 p = repo.dirstate.parents()
322 p = repo.dirstate.parents()
323
323
324 for n in p:
324 for n in p:
325 if n != hg.nullid:
325 if n != hg.nullid:
326 ui.write("%d:%s\n" % (repo.changelog.rev(n), hg.hex(n)))
326 ui.write("%d:%s\n" % (repo.changelog.rev(n), hg.hex(n)))
327
327
328 def patch(ui, repo, patches, opts):
328 def patch(ui, repo, patches, opts):
329 """import an ordered set of patches"""
329 """import an ordered set of patches"""
330 try:
330 try:
331 import psyco
331 import psyco
332 psyco.full()
332 psyco.full()
333 except:
333 except:
334 pass
334 pass
335
335
336 d = opts["base"]
336 d = opts["base"]
337 strip = opts["strip"]
337 strip = opts["strip"]
338 quiet = opts["quiet"] and "> /dev/null" or ""
338 quiet = opts["quiet"] and "> /dev/null" or ""
339
339
340 for patch in patches:
340 for patch in patches:
341 ui.status("applying %s\n" % patch)
341 ui.status("applying %s\n" % patch)
342 pf = os.path.join(d, patch)
342 pf = os.path.join(d, patch)
343
343
344 text = ""
344 text = ""
345 for l in file(pf):
345 for l in file(pf):
346 if l[:4] == "--- ": break
346 if l[:4] == "--- ": break
347 text += l
347 text += l
348
348
349 f = os.popen("lsdiff --strip %d %s" % (strip, pf))
349 f = os.popen("lsdiff --strip %d %s" % (strip, pf))
350 files = filter(None, map(lambda x: x.rstrip(), f.read().splitlines()))
350 files = filter(None, map(lambda x: x.rstrip(), f.read().splitlines()))
351 f.close()
351 f.close()
352
352
353 if files:
353 if files:
354 if os.system("patch -p%d < %s %s" % (strip, pf, quiet)):
354 if os.system("patch -p%d < %s %s" % (strip, pf, quiet)):
355 raise "patch failed!"
355 raise "patch failed!"
356 repo.commit(files, text)
356 repo.commit(files, text)
357
357
358 def pull(ui, repo, source):
358 def pull(ui, repo, source):
359 """pull changes from the specified source"""
359 """pull changes from the specified source"""
360 paths = {}
360 paths = {}
361 try:
361 try:
362 pf = os.path.expanduser("~/.hgpaths")
362 pf = os.path.expanduser("~/.hgpaths")
363 for l in file(pf):
363 for l in file(pf):
364 name, path = l.split()
364 name, path = l.split()
365 paths[name] = path
365 paths[name] = path
366 except IOError:
366 except IOError:
367 pass
367 pass
368
368
369 if source in paths: source = paths[source]
369 if source in paths: source = paths[source]
370
370
371 other = hg.repository(ui, source)
371 other = hg.repository(ui, source)
372 cg = repo.getchangegroup(other)
372 cg = repo.getchangegroup(other)
373 repo.addchangegroup(cg)
373 repo.addchangegroup(cg)
374
374
375 def rawcommit(ui, repo, files, rc):
375 def rawcommit(ui, repo, files, rc):
376 "raw commit interface"
376 "raw commit interface"
377
377
378 text = rc['text']
378 text = rc['text']
379 if not text and rc['logfile']:
379 if not text and rc['logfile']:
380 try: text = open(rc['logfile']).read()
380 try: text = open(rc['logfile']).read()
381 except IOError: pass
381 except IOError: pass
382 if not text and not rc['logfile']:
382 if not text and not rc['logfile']:
383 print "missing commit text"
383 print "missing commit text"
384 return 1
384 return 1
385
385
386 files = relpath(repo, files)
386 files = relpath(repo, files)
387 if rc['files']:
387 if rc['files']:
388 files += open(rc['files']).read().splitlines()
388 files += open(rc['files']).read().splitlines()
389
389
390 repo.rawcommit(files, text, rc['user'], rc['date'], *rc['parent'])
390 repo.rawcommit(files, text, rc['user'], rc['date'], *rc['parent'])
391
391
392 def recover(ui, repo):
392 def recover(ui, repo):
393 """roll back an interrupted transaction"""
393 """roll back an interrupted transaction"""
394 repo.recover()
394 repo.recover()
395
395
396 def remove(ui, repo, file, *files):
396 def remove(ui, repo, file, *files):
397 """remove the specified files on the next commit"""
397 """remove the specified files on the next commit"""
398 repo.remove(relpath(repo, (file,) + files))
398 repo.remove(relpath(repo, (file,) + files))
399
399
400 def serve(ui, repo, **opts):
400 def serve(ui, repo, **opts):
401 """export the repository via HTTP"""
401 """export the repository via HTTP"""
402 hgweb.server(repo.root, opts["name"], opts["templates"],
402 hgweb.server(repo.root, opts["name"], opts["templates"],
403 opts["address"], opts["port"])
403 opts["address"], opts["port"])
404
404
405 def status(ui, repo):
405 def status(ui, repo):
406 '''show changed files in the working directory
406 '''show changed files in the working directory
407
407
408 C = changed
408 C = changed
409 A = added
409 A = added
410 R = removed
410 R = removed
411 ? = not tracked'''
411 ? = not tracked'''
412
412
413 (c, a, d, u) = repo.diffdir(repo.root)
413 (c, a, d, u) = repo.diffdir(repo.root)
414 (c, a, d, u) = map(lambda x: relfilter(repo, x), (c, a, d, u))
414 (c, a, d, u) = map(lambda x: relfilter(repo, x), (c, a, d, u))
415
415
416 for f in c: print "C", f
416 for f in c: print "C", f
417 for f in a: print "A", f
417 for f in a: print "A", f
418 for f in d: print "R", f
418 for f in d: print "R", f
419 for f in u: print "?", f
419 for f in u: print "?", f
420
420
421 def tags(ui, repo):
421 def tags(ui, repo):
422 """list repository tags"""
422 """list repository tags"""
423 repo.lookup(0) # prime the cache
423 repo.lookup(0) # prime the cache
424 i = repo.tags.items()
424 i = repo.tags.items()
425 n = []
425 n = []
426 for e in i:
426 for e in i:
427 try:
427 try:
428 l = repo.changelog.rev(e[1])
428 l = repo.changelog.rev(e[1])
429 except KeyError:
429 except KeyError:
430 l = -2
430 l = -2
431 n.append((l, e))
431 n.append((l, e))
432
432
433 n.sort()
433 n.sort()
434 n.reverse()
434 n.reverse()
435 i = [ e[1] for e in n ]
435 i = [ e[1] for e in n ]
436 for k, n in i:
436 for k, n in i:
437 try:
437 try:
438 r = repo.changelog.rev(n)
438 r = repo.changelog.rev(n)
439 except KeyError:
439 except KeyError:
440 r = "?"
440 r = "?"
441 print "%-30s %5d:%s" % (k, repo.changelog.rev(n), hg.hex(n))
441 print "%-30s %5d:%s" % (k, repo.changelog.rev(n), hg.hex(n))
442
442
443 def tip(ui, repo):
443 def tip(ui, repo):
444 """show the tip revision"""
444 """show the tip revision"""
445 n = repo.changelog.tip()
445 n = repo.changelog.tip()
446 t = repo.changelog.rev(n)
446 t = repo.changelog.rev(n)
447 ui.status("%d:%s\n" % (t, hg.hex(n)))
447 ui.status("%d:%s\n" % (t, hg.hex(n)))
448
448
449 def undo(ui, repo):
449 def undo(ui, repo):
450 """undo the last transaction"""
450 """undo the last transaction"""
451 repo.undo()
451 repo.undo()
452
452
453 def update(ui, repo, node=None):
453 def update(ui, repo, node=None):
454 '''update or merge working directory
454 '''update or merge working directory
455
455
456 If there are no outstanding changes in the working directory and
456 If there are no outstanding changes in the working directory and
457 there is a linear relationship between the current version and the
457 there is a linear relationship between the current version and the
458 requested version, the result is the requested version.
458 requested version, the result is the requested version.
459
459
460 Otherwise the result is a merge between the contents of the
460 Otherwise the result is a merge between the contents of the
461 current working directory and the requested version. Files that
461 current working directory and the requested version. Files that
462 changed between either parent are marked as changed for the next
462 changed between either parent are marked as changed for the next
463 commit and a commit must be performed before any further updates
463 commit and a commit must be performed before any further updates
464 are allowed.
464 are allowed.
465 '''
465 '''
466 node = node and repo.lookup(node) or repo.changelog.tip()
466 node = node and repo.lookup(node) or repo.changelog.tip()
467 repo.update(node)
467 repo.update(node)
468
468
469 def verify(ui, repo):
469 def verify(ui, repo):
470 """verify the integrity of the repository"""
470 """verify the integrity of the repository"""
471 return repo.verify()
471 return repo.verify()
472
472
473 # Command options and aliases are listed here, alphabetically
473 # Command options and aliases are listed here, alphabetically
474
474
475 table = {
475 table = {
476 "add": (add, [], "hg add [files]"),
476 "add": (add, [], "hg add [files]"),
477 "addremove": (addremove, [], "hg addremove"),
477 "addremove": (addremove, [], "hg addremove"),
478 "ann|annotate": (annotate,
478 "ann|annotate": (annotate,
479 [('r', 'revision', '', 'revision'),
479 [('r', 'revision', '', 'revision'),
480 ('u', 'user', None, 'show user'),
480 ('u', 'user', None, 'show user'),
481 ('n', 'number', None, 'show revision number'),
481 ('n', 'number', None, 'show revision number'),
482 ('c', 'changeset', None, 'show changeset')],
482 ('c', 'changeset', None, 'show changeset')],
483 'hg annotate [-u] [-c] [-n] [-r id] [files]'),
483 'hg annotate [-u] [-c] [-n] [-r id] [files]'),
484 "branch|clone": (branch, [], 'hg branch [path]'),
484 "branch|clone": (branch, [], 'hg branch [path]'),
485 "cat|dump": (cat, [], 'hg cat <file> [rev]'),
485 "cat|dump": (cat, [], 'hg cat <file> [rev]'),
486 "commit|ci": (commit, [], 'hg commit [files]'),
486 "commit|ci": (commit, [], 'hg commit [files]'),
487 "debugaddchangegroup": (debugaddchangegroup, [], 'debugaddchangegroup'),
487 "debugaddchangegroup": (debugaddchangegroup, [], 'debugaddchangegroup'),
488 "debugchangegroup": (debugchangegroup, [], 'debugchangegroup [roots]'),
488 "debugchangegroup": (debugchangegroup, [], 'debugchangegroup [roots]'),
489 "debugindex": (debugindex, [], 'debugindex <file>'),
489 "debugindex": (debugindex, [], 'debugindex <file>'),
490 "debugindexdot": (debugindexdot, [], 'debugindexdot <file>'),
490 "debugindexdot": (debugindexdot, [], 'debugindexdot <file>'),
491 "diff": (diff, [('r', 'rev', [], 'revision')],
491 "diff": (diff, [('r', 'rev', [], 'revision')],
492 'hg diff [-r A] [-r B] [files]'),
492 'hg diff [-r A] [-r B] [files]'),
493 "export": (export, [], "hg export <changeset>"),
493 "export": (export, [], "hg export <changeset>"),
494 "forget": (forget, [], "hg forget [files]"),
494 "forget": (forget, [], "hg forget [files]"),
495 "heads": (heads, [], 'hg heads'),
495 "heads": (heads, [], 'hg heads'),
496 "history": (history, [], 'hg history'),
496 "history": (history, [], 'hg history'),
497 "help": (help, [], 'hg help [command]'),
497 "help": (help, [], 'hg help [command]'),
498 "init": (init, [], 'hg init'),
498 "init": (init, [], 'hg init'),
499 "log": (log, [], 'hg log <file>'),
499 "log": (log, [], 'hg log <file>'),
500 "manifest|dumpmanifest": (manifest, [], 'hg manifest [rev]'),
500 "manifest|dumpmanifest": (manifest, [], 'hg manifest [rev]'),
501 "parents": (parents, [], 'hg parents [node]'),
501 "parents": (parents, [], 'hg parents [node]'),
502 "patch|import": (patch,
502 "patch|import": (patch,
503 [('p', 'strip', 1, 'path strip'),
503 [('p', 'strip', 1, 'path strip'),
504 ('b', 'base', "", 'base path'),
504 ('b', 'base', "", 'base path'),
505 ('q', 'quiet', "", 'silence diff')],
505 ('q', 'quiet', "", 'silence diff')],
506 "hg import [options] patches"),
506 "hg import [options] patches"),
507 "pull|merge": (pull, [], 'hg pull [source]'),
507 "pull|merge": (pull, [], 'hg pull [source]'),
508 "rawcommit": (rawcommit,
508 "rawcommit": (rawcommit,
509 [('p', 'parent', [], 'parent'),
509 [('p', 'parent', [], 'parent'),
510 ('d', 'date', "", 'data'),
510 ('d', 'date', "", 'data'),
511 ('u', 'user', "", 'user'),
511 ('u', 'user', "", 'user'),
512 ('F', 'files', "", 'file list'),
512 ('F', 'files', "", 'file list'),
513 ('t', 'text', "", 'commit text'),
513 ('t', 'text', "", 'commit text'),
514 ('l', 'logfile', "", 'commit text file')],
514 ('l', 'logfile', "", 'commit text file')],
515 'hg rawcommit [options] [files]'),
515 'hg rawcommit [options] [files]'),
516 "recover": (recover, [], "hg recover"),
516 "recover": (recover, [], "hg recover"),
517 "remove": (remove, [], "hg remove [files]"),
517 "remove": (remove, [], "hg remove [files]"),
518 "serve": (serve, [('p', 'port', 8000, 'listen port'),
518 "serve": (serve, [('p', 'port', 8000, 'listen port'),
519 ('a', 'address', '', 'interface address'),
519 ('a', 'address', '', 'interface address'),
520 ('n', 'name', os.getcwd(), 'repository name'),
520 ('n', 'name', os.getcwd(), 'repository name'),
521 ('t', 'templates', "", 'template map')],
521 ('t', 'templates', "", 'template map')],
522 "hg serve [options]"),
522 "hg serve [options]"),
523 "status": (status, [], 'hg status'),
523 "status": (status, [], 'hg status'),
524 "tags": (tags, [], 'hg tags'),
524 "tags": (tags, [], 'hg tags'),
525 "tip": (tip, [], 'hg tip'),
525 "tip": (tip, [], 'hg tip'),
526 "undo": (undo, [], 'hg undo'),
526 "undo": (undo, [], 'hg undo'),
527 "update|up|checkout|co|resolve": (update, [], 'hg update [node]'),
527 "update|up|checkout|co|resolve": (update, [], 'hg update [node]'),
528 "verify": (verify, [], 'hg verify'),
528 "verify": (verify, [], 'hg verify'),
529 }
529 }
530
530
531 norepo = "init branch help debugindex debugindexdot"
531 norepo = "init branch help debugindex debugindexdot"
532
532
533 def find(cmd):
533 def find(cmd):
534 i = None
534 i = None
535 for e in table.keys():
535 for e in table.keys():
536 if re.match(e + "$", cmd):
536 if re.match(e + "$", cmd):
537 return table[e]
537 return table[e]
538
538
539 raise UnknownCommand(cmd)
539 raise UnknownCommand(cmd)
540
540
541 class SignalInterrupt(Exception): pass
541 class SignalInterrupt(Exception): pass
542
542
543 def catchterm(*args):
543 def catchterm(*args):
544 raise SignalInterrupt
544 raise SignalInterrupt
545
545
546 def run():
546 def run():
547 sys.exit(dispatch(sys.argv[1:]))
547 sys.exit(dispatch(sys.argv[1:]))
548
548
549 def dispatch(args):
549 def dispatch(args):
550 options = {}
550 options = {}
551 opts = [('v', 'verbose', None, 'verbose'),
551 opts = [('v', 'verbose', None, 'verbose'),
552 ('d', 'debug', None, 'debug'),
552 ('d', 'debug', None, 'debug'),
553 ('q', 'quiet', None, 'quiet'),
553 ('q', 'quiet', None, 'quiet'),
554 ('y', 'noninteractive', None, 'run non-interactively'),
554 ('y', 'noninteractive', None, 'run non-interactively'),
555 ]
555 ]
556
556
557 args = fancyopts.fancyopts(args, opts, options,
557 args = fancyopts.fancyopts(args, opts, options,
558 'hg [options] <command> [options] [files]')
558 'hg [options] <command> [options] [files]')
559
559
560 if not args:
560 if not args:
561 cmd = "help"
561 cmd = "help"
562 else:
562 else:
563 cmd, args = args[0], args[1:]
563 cmd, args = args[0], args[1:]
564
564
565 u = ui.ui(options["verbose"], options["debug"], options["quiet"],
565 u = ui.ui(options["verbose"], options["debug"], options["quiet"],
566 not options["noninteractive"])
566 not options["noninteractive"])
567
567
568 try:
568 try:
569 i = find(cmd)
569 i = find(cmd)
570 except UnknownCommand:
570 except UnknownCommand:
571 u.warn("unknown command '%s'\n" % cmd)
571 u.warn("unknown command '%s'\n" % cmd)
572 help(u)
572 help(u)
573 sys.exit(1)
573 sys.exit(1)
574
574
575 signal.signal(signal.SIGTERM, catchterm)
575 signal.signal(signal.SIGTERM, catchterm)
576
576
577 cmdoptions = {}
577 cmdoptions = {}
578 args = fancyopts.fancyopts(args, i[1], cmdoptions, i[2])
578 args = fancyopts.fancyopts(args, i[1], cmdoptions, i[2])
579
579
580 if cmd not in norepo.split():
580 if cmd not in norepo.split():
581 repo = hg.repository(ui = u)
581 repo = hg.repository(ui = u)
582 d = lambda: i[0](u, repo, *args, **cmdoptions)
582 d = lambda: i[0](u, repo, *args, **cmdoptions)
583 else:
583 else:
584 d = lambda: i[0](u, *args, **cmdoptions)
584 d = lambda: i[0](u, *args, **cmdoptions)
585
585
586 try:
586 try:
587 return d()
587 return d()
588 except SignalInterrupt:
588 except SignalInterrupt:
589 u.warn("killed!\n")
589 u.warn("killed!\n")
590 except KeyboardInterrupt:
590 except KeyboardInterrupt:
591 u.warn("interrupted!\n")
591 u.warn("interrupted!\n")
592 except IOError, inst:
592 except IOError, inst:
593 if inst.errno == 32:
593 if inst.errno == 32:
594 u.warn("broken pipe\n")
594 u.warn("broken pipe\n")
595 else:
595 else:
596 raise
596 raise
597 except TypeError, inst:
597 except TypeError, inst:
598 # was this an argument error?
598 # was this an argument error?
599 tb = traceback.extract_tb(sys.exc_info()[2])
599 tb = traceback.extract_tb(sys.exc_info()[2])
600 if len(tb) > 2: # no
600 if len(tb) > 2: # no
601 raise
601 raise
602 u.warn("%s: invalid arguments\n" % i[0].__name__)
602 u.warn("%s: invalid arguments\n" % i[0].__name__)
603 u.warn("syntax: %s\n" % i[2])
603 u.warn("syntax: %s\n" % i[2])
604 sys.exit(-1)
604 sys.exit(-1)
@@ -1,77 +1,91 b''
1 # mdiff.py - diff and patch routines for mercurial
1 # mdiff.py - diff and patch routines for mercurial
2 #
2 #
3 # Copyright 2005 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms
5 # This software may be used and distributed according to the terms
6 # of the GNU General Public License, incorporated herein by reference.
6 # of the GNU General Public License, incorporated herein by reference.
7
7
8 import difflib, struct
8 import difflib, struct
9 from mercurial.mpatch import *
9 from mercurial.mpatch import *
10
10
11 def unidiff(a, ad, b, bd, fn):
11 def unidiff(a, ad, b, bd, fn):
12 if not a and not b: return ""
12 if not a and not b: return ""
13 a = a.splitlines(1)
13
14 b = b.splitlines(1)
14 if a == None:
15 l = list(difflib.unified_diff(a, b, "a/" + fn, "b/" + fn, ad, bd))
15 b = b.splitlines(1)
16 l1 = "--- %s\t%s\n" % ("/dev/null", ad)
17 l2 = "+++ %s\t%s\n" % ("b/" + fn, bd)
18 l3 = "@@ -0,0 +1,%d @@\n" % len(b)
19 l = [l1, l2, l3] + ["+" + e for e in b]
20 elif b == None:
21 a = a.splitlines(1)
22 l1 = "--- %s\t%s\n" % ("a/" + fn, ad)
23 l2 = "+++ %s\t%s\n" % ("/dev/null", bd)
24 l3 = "@@ -1,%d +0,0 @@\n" % len(a)
25 l = [l1, l2, l3] + ["-" + e for e in a]
26 else:
27 a = a.splitlines(1)
28 b = b.splitlines(1)
29 l = list(difflib.unified_diff(a, b, "a/" + fn, "b/" + fn, ad, bd))
16
30
17 for ln in xrange(len(l)):
31 for ln in xrange(len(l)):
18 if l[ln][-1] != '\n':
32 if l[ln][-1] != '\n':
19 l[ln] += "\n\ No newline at end of file\n"
33 l[ln] += "\n\ No newline at end of file\n"
20
34
21 return "".join(l)
35 return "".join(l)
22
36
23 def textdiff(a, b):
37 def textdiff(a, b):
24 return diff(a.splitlines(1), b.splitlines(1))
38 return diff(a.splitlines(1), b.splitlines(1))
25
39
26 def sortdiff(a, b):
40 def sortdiff(a, b):
27 la = lb = 0
41 la = lb = 0
28 lena = len(a)
42 lena = len(a)
29 lenb = len(b)
43 lenb = len(b)
30 while 1:
44 while 1:
31 am, bm, = la, lb
45 am, bm, = la, lb
32 while lb < lenb and la < len and a[la] == b[lb] :
46 while lb < lenb and la < len and a[la] == b[lb] :
33 la += 1
47 la += 1
34 lb += 1
48 lb += 1
35 if la>am: yield (am, bm, la-am)
49 if la>am: yield (am, bm, la-am)
36 while lb < lenb and b[lb] < a[la]: lb += 1
50 while lb < lenb and b[lb] < a[la]: lb += 1
37 if lb>=lenb: break
51 if lb>=lenb: break
38 while la < lena and b[lb] > a[la]: la += 1
52 while la < lena and b[lb] > a[la]: la += 1
39 if la>=lena: break
53 if la>=lena: break
40 yield (lena, lenb, 0)
54 yield (lena, lenb, 0)
41
55
42 def diff(a, b, sorted=0):
56 def diff(a, b, sorted=0):
43 if not a:
57 if not a:
44 s = "".join(b)
58 s = "".join(b)
45 return s and (struct.pack(">lll", 0, 0, len(s)) + s)
59 return s and (struct.pack(">lll", 0, 0, len(s)) + s)
46
60
47 bin = []
61 bin = []
48 p = [0]
62 p = [0]
49 for i in a: p.append(p[-1] + len(i))
63 for i in a: p.append(p[-1] + len(i))
50
64
51 if sorted:
65 if sorted:
52 d = sortdiff(a, b)
66 d = sortdiff(a, b)
53 else:
67 else:
54 d = difflib.SequenceMatcher(None, a, b).get_matching_blocks()
68 d = difflib.SequenceMatcher(None, a, b).get_matching_blocks()
55 la = 0
69 la = 0
56 lb = 0
70 lb = 0
57 for am, bm, size in d:
71 for am, bm, size in d:
58 s = "".join(b[lb:bm])
72 s = "".join(b[lb:bm])
59 if am > la or s:
73 if am > la or s:
60 bin.append(struct.pack(">lll", p[la], p[am], len(s)) + s)
74 bin.append(struct.pack(">lll", p[la], p[am], len(s)) + s)
61 la = am + size
75 la = am + size
62 lb = bm + size
76 lb = bm + size
63
77
64 return "".join(bin)
78 return "".join(bin)
65
79
66 def patchtext(bin):
80 def patchtext(bin):
67 pos = 0
81 pos = 0
68 t = []
82 t = []
69 while pos < len(bin):
83 while pos < len(bin):
70 p1, p2, l = struct.unpack(">lll", bin[pos:pos + 12])
84 p1, p2, l = struct.unpack(">lll", bin[pos:pos + 12])
71 pos += 12
85 pos += 12
72 t.append(bin[pos:pos + l])
86 t.append(bin[pos:pos + l])
73 pos += l
87 pos += l
74 return "".join(t)
88 return "".join(t)
75
89
76 def patch(a, bin):
90 def patch(a, bin):
77 return patches(a, [bin])
91 return patches(a, [bin])
General Comments 0
You need to be logged in to leave comments. Login now