##// END OF EJS Templates
Show revisions in diffs like CVS, based on a patch from Goffredo Baroncelli....
Thomas Arendsen Hein -
r396:8f8bb77d default
parent child Browse files
Show More
@@ -1,258 +1,258 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 #
2 #
3 # Minimal support for git commands on an hg repository
3 # Minimal support for git commands on an hg repository
4 #
4 #
5 # Copyright 2005 Chris Mason <mason@suse.com>
5 # Copyright 2005 Chris Mason <mason@suse.com>
6 #
6 #
7 # This software may be used and distributed according to the terms
7 # This software may be used and distributed according to the terms
8 # of the GNU General Public License, incorporated herein by reference.
8 # of the GNU General Public License, incorporated herein by reference.
9
9
10 import time, sys, signal
10 import time, sys, signal
11 from mercurial import hg, mdiff, fancyopts, commands, ui
11 from mercurial import hg, mdiff, fancyopts, commands, ui
12
12
13 def difftree(args, repo):
13 def difftree(args, ui, repo):
14 def __difftree(repo, files = None, node1 = None, node2 = None):
14 def __difftree(repo, files = None, node1 = None, node2 = None):
15 def date(c):
15 def date(c):
16 return time.asctime(time.gmtime(float(c[2].split(' ')[0])))
16 return time.asctime(time.gmtime(float(c[2].split(' ')[0])))
17
17
18 if node2:
18 if node2:
19 change = repo.changelog.read(node2)
19 change = repo.changelog.read(node2)
20 mmap2 = repo.manifest.read(change[0])
20 mmap2 = repo.manifest.read(change[0])
21 (c, a, d) = repo.diffrevs(node1, node2)
21 (c, a, d) = repo.diffrevs(node1, node2)
22 def read(f): return repo.file(f).read(mmap2[f])
22 def read(f): return repo.file(f).read(mmap2[f])
23 date2 = date(change)
23 date2 = date(change)
24 else:
24 else:
25 date2 = time.asctime()
25 date2 = time.asctime()
26 (c, a, d, u) = repo.diffdir(repo.root, node1)
26 (c, a, d, u) = repo.diffdir(repo.root, node1)
27 if not node1:
27 if not node1:
28 node1 = repo.dirstate.parents()[0]
28 node1 = repo.dirstate.parents()[0]
29 def read(f): return file(os.path.join(repo.root, f)).read()
29 def read(f): return file(os.path.join(repo.root, f)).read()
30
30
31 change = repo.changelog.read(node1)
31 change = repo.changelog.read(node1)
32 mmap = repo.manifest.read(change[0])
32 mmap = repo.manifest.read(change[0])
33 date1 = date(change)
33 date1 = date(change)
34 empty = "0" * 40;
34 empty = "0" * 40;
35
35
36 if files:
36 if files:
37 c, a, d = map(lambda x: filterfiles(files, x), (c, a, d))
37 c, a, d = map(lambda x: filterfiles(files, x), (c, a, d))
38
38
39 for f in c:
39 for f in c:
40 # TODO get file permissions
40 # TODO get file permissions
41 print ":100664 100664 %s %s %s %s" % (hg.hex(mmap[f]),
41 print ":100664 100664 %s %s %s %s" % (hg.hex(mmap[f]),
42 hg.hex(mmap2[f]), f, f)
42 hg.hex(mmap2[f]), f, f)
43 for f in a:
43 for f in a:
44 print ":000000 100664 %s %s %s %s" % (empty, hg.hex(mmap2[f]), f, f)
44 print ":000000 100664 %s %s %s %s" % (empty, hg.hex(mmap2[f]), f, f)
45 for f in d:
45 for f in d:
46 print ":100664 000000 %s %s %s %s" % (hg.hex(mmap[f]), empty, f, f)
46 print ":100664 000000 %s %s %s %s" % (hg.hex(mmap[f]), empty, f, f)
47 ##
47 ##
48
48
49 revs = []
49 revs = []
50 if args:
50 if args:
51 doptions = {}
51 doptions = {}
52 opts = [('p', 'patch', None, 'patch'),
52 opts = [('p', 'patch', None, 'patch'),
53 ('r', 'recursive', None, 'recursive')]
53 ('r', 'recursive', None, 'recursive')]
54 args = fancyopts.fancyopts(args, opts, doptions,
54 args = fancyopts.fancyopts(args, opts, doptions,
55 'hg diff-tree [options] sha1 sha1')
55 'hg diff-tree [options] sha1 sha1')
56
56
57 if len(args) < 2:
57 if len(args) < 2:
58 help()
58 help()
59 sys.exit(1)
59 sys.exit(1)
60 revs.append(repo.lookup(args[0]))
60 revs.append(repo.lookup(args[0]))
61 revs.append(repo.lookup(args[1]))
61 revs.append(repo.lookup(args[1]))
62 args = args[2:]
62 args = args[2:]
63 if doptions['patch']:
63 if doptions['patch']:
64 commands.dodiff(repo, "", args, *revs)
64 commands.dodiff(ui, repo, "", args, *revs)
65 else:
65 else:
66 __difftree(repo, args, *revs)
66 __difftree(repo, args, *revs)
67
67
68 def catcommit(repo, n, prefix):
68 def catcommit(repo, n, prefix):
69 nlprefix = '\n' + prefix;
69 nlprefix = '\n' + prefix;
70 changes = repo.changelog.read(n)
70 changes = repo.changelog.read(n)
71 (p1, p2) = repo.changelog.parents(n)
71 (p1, p2) = repo.changelog.parents(n)
72 (h, h1, h2) = map(hg.hex, (n, p1, p2))
72 (h, h1, h2) = map(hg.hex, (n, p1, p2))
73 (i1, i2) = map(repo.changelog.rev, (p1, p2))
73 (i1, i2) = map(repo.changelog.rev, (p1, p2))
74 print "tree %s" % (h)
74 print "tree %s" % (h)
75 if i1 != -1: print "%sparent %s" % (prefix, h1)
75 if i1 != -1: print "%sparent %s" % (prefix, h1)
76 if i2 != -1: print "%sparent %s" % (prefix, h2)
76 if i2 != -1: print "%sparent %s" % (prefix, h2)
77 date_ar = changes[2].split(' ')
77 date_ar = changes[2].split(' ')
78 date = int(float(date_ar[0]))
78 date = int(float(date_ar[0]))
79 print "%sauthor <%s> %s %s" % (prefix, changes[1], date, date_ar[1])
79 print "%sauthor <%s> %s %s" % (prefix, changes[1], date, date_ar[1])
80 print "%scommitter <%s> %s %s" % (prefix, changes[1], date, date_ar[1])
80 print "%scommitter <%s> %s %s" % (prefix, changes[1], date, date_ar[1])
81 print prefix
81 print prefix
82 if prefix != "":
82 if prefix != "":
83 print "%s%s" % (prefix, changes[4].replace('\n', nlprefix).strip())
83 print "%s%s" % (prefix, changes[4].replace('\n', nlprefix).strip())
84 else:
84 else:
85 print changes[4]
85 print changes[4]
86
86
87 def catfile(args, ui, repo):
87 def catfile(args, ui, repo):
88 doptions = {}
88 doptions = {}
89 opts = [('s', 'stdin', None, 'stdin')]
89 opts = [('s', 'stdin', None, 'stdin')]
90 args = fancyopts.fancyopts(args, opts, doptions,
90 args = fancyopts.fancyopts(args, opts, doptions,
91 'hg cat-file type sha1')
91 'hg cat-file type sha1')
92
92
93 # in stdin mode, every line except the commit is prefixed with two
93 # in stdin mode, every line except the commit is prefixed with two
94 # spaces. This way the our caller can find the commit without magic
94 # spaces. This way the our caller can find the commit without magic
95 # strings
95 # strings
96 #
96 #
97 prefix = ""
97 prefix = ""
98 if doptions['stdin']:
98 if doptions['stdin']:
99 try:
99 try:
100 (type, r) = raw_input().split(' ');
100 (type, r) = raw_input().split(' ');
101 prefix = " "
101 prefix = " "
102 except EOFError:
102 except EOFError:
103 return
103 return
104
104
105 else:
105 else:
106 if len(args) < 2:
106 if len(args) < 2:
107 help()
107 help()
108 sys.exit(1)
108 sys.exit(1)
109 type = args[0]
109 type = args[0]
110 r = args[1]
110 r = args[1]
111
111
112 while r:
112 while r:
113 if type != "commit":
113 if type != "commit":
114 sys.stderr.write("aborting hg cat-file only understands commits\n")
114 sys.stderr.write("aborting hg cat-file only understands commits\n")
115 sys.exit(1);
115 sys.exit(1);
116 n = repo.changelog.lookup(r)
116 n = repo.changelog.lookup(r)
117 catcommit(repo, n, prefix)
117 catcommit(repo, n, prefix)
118 if doptions['stdin']:
118 if doptions['stdin']:
119 try:
119 try:
120 (type, r) = raw_input().split(' ');
120 (type, r) = raw_input().split(' ');
121 except EOFError:
121 except EOFError:
122 break
122 break
123 else:
123 else:
124 break
124 break
125
125
126 # git rev-tree is a confusing thing. You can supply a number of
126 # git rev-tree is a confusing thing. You can supply a number of
127 # commit sha1s on the command line, and it walks the commit history
127 # commit sha1s on the command line, and it walks the commit history
128 # telling you which commits are reachable from the supplied ones via
128 # telling you which commits are reachable from the supplied ones via
129 # a bitmask based on arg position.
129 # a bitmask based on arg position.
130 # you can specify a commit to stop at by starting the sha1 with ^
130 # you can specify a commit to stop at by starting the sha1 with ^
131 def revtree(args, repo, full="tree", maxnr=0):
131 def revtree(args, repo, full="tree", maxnr=0):
132 # calculate and return the reachability bitmask for sha
132 # calculate and return the reachability bitmask for sha
133 def is_reachable(ar, reachable, sha):
133 def is_reachable(ar, reachable, sha):
134 if len(ar) == 0:
134 if len(ar) == 0:
135 return 1
135 return 1
136 mask = 0
136 mask = 0
137 for i in range(len(ar)):
137 for i in range(len(ar)):
138 if sha in reachable[i]:
138 if sha in reachable[i]:
139 mask |= 1 << i
139 mask |= 1 << i
140
140
141 return mask
141 return mask
142
142
143 reachable = []
143 reachable = []
144 stop_sha1 = []
144 stop_sha1 = []
145 want_sha1 = []
145 want_sha1 = []
146 count = 0
146 count = 0
147
147
148 # figure out which commits they are asking for and which ones they
148 # figure out which commits they are asking for and which ones they
149 # want us to stop on
149 # want us to stop on
150 for i in range(len(args)):
150 for i in range(len(args)):
151 if args[i].count('^'):
151 if args[i].count('^'):
152 s = args[i].split('^')[1]
152 s = args[i].split('^')[1]
153 stop_sha1.append(repo.changelog.lookup(s))
153 stop_sha1.append(repo.changelog.lookup(s))
154 want_sha1.append(s)
154 want_sha1.append(s)
155 elif args[i] != 'HEAD':
155 elif args[i] != 'HEAD':
156 want_sha1.append(args[i])
156 want_sha1.append(args[i])
157
157
158 # calculate the graph for the supplied commits
158 # calculate the graph for the supplied commits
159 for i in range(len(want_sha1)):
159 for i in range(len(want_sha1)):
160 reachable.append({});
160 reachable.append({});
161 n = repo.changelog.lookup(want_sha1[i]);
161 n = repo.changelog.lookup(want_sha1[i]);
162 visit = [n];
162 visit = [n];
163 reachable[i][n] = 1
163 reachable[i][n] = 1
164 while visit:
164 while visit:
165 n = visit.pop(0)
165 n = visit.pop(0)
166 if n in stop_sha1:
166 if n in stop_sha1:
167 break
167 break
168 for p in repo.changelog.parents(n):
168 for p in repo.changelog.parents(n):
169 if p not in reachable[i]:
169 if p not in reachable[i]:
170 reachable[i][p] = 1
170 reachable[i][p] = 1
171 visit.append(p)
171 visit.append(p)
172 if p in stop_sha1:
172 if p in stop_sha1:
173 break
173 break
174
174
175 # walk the repository looking for commits that are in our
175 # walk the repository looking for commits that are in our
176 # reachability graph
176 # reachability graph
177 for i in range(repo.changelog.count()-1, -1, -1):
177 for i in range(repo.changelog.count()-1, -1, -1):
178 n = repo.changelog.node(i)
178 n = repo.changelog.node(i)
179 mask = is_reachable(want_sha1, reachable, n)
179 mask = is_reachable(want_sha1, reachable, n)
180 if mask:
180 if mask:
181 if not full:
181 if not full:
182 print hg.hex(n)
182 print hg.hex(n)
183 elif full is "commit":
183 elif full is "commit":
184 print hg.hex(n)
184 print hg.hex(n)
185 catcommit(repo, n, ' ')
185 catcommit(repo, n, ' ')
186 else:
186 else:
187 changes = repo.changelog.read(n)
187 changes = repo.changelog.read(n)
188 (p1, p2) = repo.changelog.parents(n)
188 (p1, p2) = repo.changelog.parents(n)
189 (h, h1, h2) = map(hg.hex, (n, p1, p2))
189 (h, h1, h2) = map(hg.hex, (n, p1, p2))
190 (i1, i2) = map(repo.changelog.rev, (p1, p2))
190 (i1, i2) = map(repo.changelog.rev, (p1, p2))
191
191
192 date = changes[2].split(' ')[0]
192 date = changes[2].split(' ')[0]
193 print "%s %s:%s" % (date, h, mask),
193 print "%s %s:%s" % (date, h, mask),
194 mask = is_reachable(want_sha1, reachable, p1)
194 mask = is_reachable(want_sha1, reachable, p1)
195 if i1 != -1 and mask > 0:
195 if i1 != -1 and mask > 0:
196 print "%s:%s " % (h1, mask),
196 print "%s:%s " % (h1, mask),
197 mask = is_reachable(want_sha1, reachable, p2)
197 mask = is_reachable(want_sha1, reachable, p2)
198 if i2 != -1 and mask > 0:
198 if i2 != -1 and mask > 0:
199 print "%s:%s " % (h2, mask),
199 print "%s:%s " % (h2, mask),
200 print ""
200 print ""
201 if maxnr and count >= maxnr:
201 if maxnr and count >= maxnr:
202 break
202 break
203 count += 1
203 count += 1
204
204
205 # git rev-list tries to order things by date, and has the ability to stop
205 # git rev-list tries to order things by date, and has the ability to stop
206 # at a given commit without walking the whole repo. TODO add the stop
206 # at a given commit without walking the whole repo. TODO add the stop
207 # parameter
207 # parameter
208 def revlist(args, repo):
208 def revlist(args, repo):
209 doptions = {}
209 doptions = {}
210 opts = [('c', 'commit', None, 'commit'),
210 opts = [('c', 'commit', None, 'commit'),
211 ('n', 'max-nr', 0, 'max-nr')]
211 ('n', 'max-nr', 0, 'max-nr')]
212 args = fancyopts.fancyopts(args, opts, doptions,
212 args = fancyopts.fancyopts(args, opts, doptions,
213 'hg rev-list')
213 'hg rev-list')
214 if doptions['commit']:
214 if doptions['commit']:
215 full = "commit"
215 full = "commit"
216 else:
216 else:
217 full = None
217 full = None
218 for i in range(1, len(args)):
218 for i in range(1, len(args)):
219 args[i] = '^' + args[i]
219 args[i] = '^' + args[i]
220 revtree(args, repo, full, doptions['max-nr'])
220 revtree(args, repo, full, doptions['max-nr'])
221
221
222 def catchterm(*args):
222 def catchterm(*args):
223 raise SignalInterrupt
223 raise SignalInterrupt
224
224
225 def help():
225 def help():
226 sys.stderr.write("commands:\n")
226 sys.stderr.write("commands:\n")
227 sys.stderr.write(" hgit cat-file [type] sha1\n")
227 sys.stderr.write(" hgit cat-file [type] sha1\n")
228 sys.stderr.write(" hgit diff-tree [-p] [-r] sha1 sha1\n")
228 sys.stderr.write(" hgit diff-tree [-p] [-r] sha1 sha1\n")
229 sys.stderr.write(" hgit rev-tree [sha1 ... [^stop sha1]]\n")
229 sys.stderr.write(" hgit rev-tree [sha1 ... [^stop sha1]]\n")
230 sys.stderr.write(" hgit rev-list [-c]\n")
230 sys.stderr.write(" hgit rev-list [-c]\n")
231
231
232 cmd = sys.argv[1]
232 cmd = sys.argv[1]
233 args = sys.argv[2:]
233 args = sys.argv[2:]
234 u = ui.ui()
234 u = ui.ui()
235 signal.signal(signal.SIGTERM, catchterm)
235 signal.signal(signal.SIGTERM, catchterm)
236 repo = hg.repository(ui = u)
236 repo = hg.repository(ui = u)
237
237
238 if cmd == "diff-tree":
238 if cmd == "diff-tree":
239 difftree(args, repo)
239 difftree(args, u, repo)
240
240
241 elif cmd == "cat-file":
241 elif cmd == "cat-file":
242 catfile(args, ui, repo)
242 catfile(args, u, repo)
243
243
244 elif cmd == "rev-tree":
244 elif cmd == "rev-tree":
245 revtree(args, repo)
245 revtree(args, repo)
246
246
247 elif cmd == "rev-list":
247 elif cmd == "rev-list":
248 revlist(args, repo)
248 revlist(args, repo)
249
249
250 elif cmd == "help":
250 elif cmd == "help":
251 help()
251 help()
252
252
253 else:
253 else:
254 if cmd: sys.stderr.write("unknown command\n\n")
254 if cmd: sys.stderr.write("unknown command\n\n")
255 help()
255 help()
256 sys.exit(1)
256 sys.exit(1)
257
257
258 sys.exit(0)
258 sys.exit(0)
@@ -1,772 +1,778 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 random signal errno")
11 demandload(globals(), "mdiff time hgweb traceback random signal errno")
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, path, files = None, node1 = None, node2 = None):
35 def dodiff(ui, repo, path, 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(path, node1)
47 (c, a, d, u) = repo.diffdir(path, 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 if ui.quiet:
53 r = None
54 else:
55 hexfunc = ui.verbose and hg.hex or hg.short
56 r = [hexfunc(node) for node in [node1, node2] if node]
57
52 change = repo.changelog.read(node1)
58 change = repo.changelog.read(node1)
53 mmap = repo.manifest.read(change[0])
59 mmap = repo.manifest.read(change[0])
54 date1 = date(change)
60 date1 = date(change)
55
61
56 if files:
62 if files:
57 c, a, d = map(lambda x: filterfiles(files, x), (c, a, d))
63 c, a, d = map(lambda x: filterfiles(files, x), (c, a, d))
58
64
59 for f in c:
65 for f in c:
60 to = None
66 to = None
61 if f in mmap:
67 if f in mmap:
62 to = repo.file(f).read(mmap[f])
68 to = repo.file(f).read(mmap[f])
63 tn = read(f)
69 tn = read(f)
64 sys.stdout.write(mdiff.unidiff(to, date1, tn, date2, f))
70 sys.stdout.write(mdiff.unidiff(to, date1, tn, date2, f, r))
65 for f in a:
71 for f in a:
66 to = None
72 to = None
67 tn = read(f)
73 tn = read(f)
68 sys.stdout.write(mdiff.unidiff(to, date1, tn, date2, f))
74 sys.stdout.write(mdiff.unidiff(to, date1, tn, date2, f, r))
69 for f in d:
75 for f in d:
70 to = repo.file(f).read(mmap[f])
76 to = repo.file(f).read(mmap[f])
71 tn = None
77 tn = None
72 sys.stdout.write(mdiff.unidiff(to, date1, tn, date2, f))
78 sys.stdout.write(mdiff.unidiff(to, date1, tn, date2, f, r))
73
79
74 def show_changeset(ui, repo, rev=0, changenode=None, filelog=None):
80 def show_changeset(ui, repo, rev=0, changenode=None, filelog=None):
75 """show a single changeset or file revision"""
81 """show a single changeset or file revision"""
76 changelog = repo.changelog
82 changelog = repo.changelog
77 if filelog:
83 if filelog:
78 log = filelog
84 log = filelog
79 filerev = rev
85 filerev = rev
80 node = filenode = filelog.node(filerev)
86 node = filenode = filelog.node(filerev)
81 changerev = filelog.linkrev(filenode)
87 changerev = filelog.linkrev(filenode)
82 changenode = changenode or changelog.node(changerev)
88 changenode = changenode or changelog.node(changerev)
83 else:
89 else:
84 log = changelog
90 log = changelog
85 changerev = rev
91 changerev = rev
86 if changenode is None:
92 if changenode is None:
87 changenode = changelog.node(changerev)
93 changenode = changelog.node(changerev)
88 elif not changerev:
94 elif not changerev:
89 rev = changerev = changelog.rev(changenode)
95 rev = changerev = changelog.rev(changenode)
90 node = changenode
96 node = changenode
91
97
92 if ui.quiet:
98 if ui.quiet:
93 ui.write("%d:%s\n" % (rev, hg.hex(node)))
99 ui.write("%d:%s\n" % (rev, hg.hex(node)))
94 return
100 return
95
101
96 changes = changelog.read(changenode)
102 changes = changelog.read(changenode)
97
103
98 parents = [(log.rev(parent), hg.hex(parent))
104 parents = [(log.rev(parent), hg.hex(parent))
99 for parent in log.parents(node)
105 for parent in log.parents(node)
100 if ui.debugflag or parent != hg.nullid]
106 if ui.debugflag or parent != hg.nullid]
101 if not ui.debugflag and len(parents) == 1 and parents[0][0] == rev-1:
107 if not ui.debugflag and len(parents) == 1 and parents[0][0] == rev-1:
102 parents = []
108 parents = []
103
109
104 if filelog:
110 if filelog:
105 ui.write("revision: %d:%s\n" % (filerev, hg.hex(filenode)))
111 ui.write("revision: %d:%s\n" % (filerev, hg.hex(filenode)))
106 for parent in parents:
112 for parent in parents:
107 ui.write("parent: %d:%s\n" % parent)
113 ui.write("parent: %d:%s\n" % parent)
108 ui.status("changeset: %d:%s\n" % (changerev, hg.hex(changenode)))
114 ui.status("changeset: %d:%s\n" % (changerev, hg.hex(changenode)))
109 else:
115 else:
110 ui.write("changeset: %d:%s\n" % (changerev, hg.hex(changenode)))
116 ui.write("changeset: %d:%s\n" % (changerev, hg.hex(changenode)))
111 for tag in repo.nodetags(changenode):
117 for tag in repo.nodetags(changenode):
112 ui.status("tag: %s\n" % tag)
118 ui.status("tag: %s\n" % tag)
113 for parent in parents:
119 for parent in parents:
114 ui.write("parent: %d:%s\n" % parent)
120 ui.write("parent: %d:%s\n" % parent)
115 ui.note("manifest: %d:%s\n" % (repo.manifest.rev(changes[0]),
121 ui.note("manifest: %d:%s\n" % (repo.manifest.rev(changes[0]),
116 hg.hex(changes[0])))
122 hg.hex(changes[0])))
117 ui.status("user: %s\n" % changes[1])
123 ui.status("user: %s\n" % changes[1])
118 ui.status("date: %s\n" % time.asctime(
124 ui.status("date: %s\n" % time.asctime(
119 time.localtime(float(changes[2].split(' ')[0]))))
125 time.localtime(float(changes[2].split(' ')[0]))))
120 ui.note("files: %s\n" % " ".join(changes[3]))
126 ui.note("files: %s\n" % " ".join(changes[3]))
121 description = changes[4].strip()
127 description = changes[4].strip()
122 if description:
128 if description:
123 if ui.verbose:
129 if ui.verbose:
124 ui.status("description:\n")
130 ui.status("description:\n")
125 ui.status(description)
131 ui.status(description)
126 ui.status("\n")
132 ui.status("\n")
127 else:
133 else:
128 ui.status("summary: %s\n" % description.splitlines()[0])
134 ui.status("summary: %s\n" % description.splitlines()[0])
129 ui.status("\n")
135 ui.status("\n")
130
136
131 def help(ui, cmd=None):
137 def help(ui, cmd=None):
132 '''show help for a given command or all commands'''
138 '''show help for a given command or all commands'''
133 if cmd:
139 if cmd:
134 try:
140 try:
135 i = find(cmd)
141 i = find(cmd)
136 ui.write("%s\n\n" % i[2])
142 ui.write("%s\n\n" % i[2])
137
143
138 if i[1]:
144 if i[1]:
139 for s, l, d, c in i[1]:
145 for s, l, d, c in i[1]:
140 opt=' '
146 opt=' '
141 if s: opt = opt + '-' + s + ' '
147 if s: opt = opt + '-' + s + ' '
142 if l: opt = opt + '--' + l + ' '
148 if l: opt = opt + '--' + l + ' '
143 if d: opt = opt + '(' + str(d) + ')'
149 if d: opt = opt + '(' + str(d) + ')'
144 ui.write(opt, "\n")
150 ui.write(opt, "\n")
145 if c: ui.write(' %s\n' % c)
151 if c: ui.write(' %s\n' % c)
146 ui.write("\n")
152 ui.write("\n")
147
153
148 ui.write(i[0].__doc__, "\n")
154 ui.write(i[0].__doc__, "\n")
149 except UnknownCommand:
155 except UnknownCommand:
150 ui.warn("hg: unknown command %s\n" % cmd)
156 ui.warn("hg: unknown command %s\n" % cmd)
151 sys.exit(0)
157 sys.exit(0)
152 else:
158 else:
153 ui.status('hg commands:\n\n')
159 ui.status('hg commands:\n\n')
154
160
155 h = {}
161 h = {}
156 for e in table.values():
162 for e in table.values():
157 f = e[0]
163 f = e[0]
158 if f.__name__.startswith("debug"): continue
164 if f.__name__.startswith("debug"): continue
159 d = ""
165 d = ""
160 if f.__doc__:
166 if f.__doc__:
161 d = f.__doc__.splitlines(0)[0].rstrip()
167 d = f.__doc__.splitlines(0)[0].rstrip()
162 h[f.__name__] = d
168 h[f.__name__] = d
163
169
164 fns = h.keys()
170 fns = h.keys()
165 fns.sort()
171 fns.sort()
166 m = max(map(len, fns))
172 m = max(map(len, fns))
167 for f in fns:
173 for f in fns:
168 ui.status(' %-*s %s\n' % (m, f, h[f]))
174 ui.status(' %-*s %s\n' % (m, f, h[f]))
169
175
170 # Commands start here, listed alphabetically
176 # Commands start here, listed alphabetically
171
177
172 def add(ui, repo, file, *files):
178 def add(ui, repo, file, *files):
173 '''add the specified files on the next commit'''
179 '''add the specified files on the next commit'''
174 repo.add(relpath(repo, (file,) + files))
180 repo.add(relpath(repo, (file,) + files))
175
181
176 def addremove(ui, repo, *files):
182 def addremove(ui, repo, *files):
177 """add all new files, delete all missing files"""
183 """add all new files, delete all missing files"""
178 if files:
184 if files:
179 files = relpath(repo, files)
185 files = relpath(repo, files)
180 d = []
186 d = []
181 u = []
187 u = []
182 for f in files:
188 for f in files:
183 p = repo.wjoin(f)
189 p = repo.wjoin(f)
184 s = repo.dirstate.state(f)
190 s = repo.dirstate.state(f)
185 isfile = os.path.isfile(p)
191 isfile = os.path.isfile(p)
186 if s != 'r' and not isfile:
192 if s != 'r' and not isfile:
187 d.append(f)
193 d.append(f)
188 elif s not in 'nmai' and isfile:
194 elif s not in 'nmai' and isfile:
189 u.append(f)
195 u.append(f)
190 else:
196 else:
191 (c, a, d, u) = repo.diffdir(repo.root)
197 (c, a, d, u) = repo.diffdir(repo.root)
192 repo.add(u)
198 repo.add(u)
193 repo.remove(d)
199 repo.remove(d)
194
200
195 def annotate(u, repo, file, *files, **ops):
201 def annotate(u, repo, file, *files, **ops):
196 """show changeset information per file line"""
202 """show changeset information per file line"""
197 def getnode(rev):
203 def getnode(rev):
198 return hg.short(repo.changelog.node(rev))
204 return hg.short(repo.changelog.node(rev))
199
205
200 def getname(rev):
206 def getname(rev):
201 try:
207 try:
202 return bcache[rev]
208 return bcache[rev]
203 except KeyError:
209 except KeyError:
204 cl = repo.changelog.read(repo.changelog.node(rev))
210 cl = repo.changelog.read(repo.changelog.node(rev))
205 name = cl[1]
211 name = cl[1]
206 f = name.find('@')
212 f = name.find('@')
207 if f >= 0:
213 if f >= 0:
208 name = name[:f]
214 name = name[:f]
209 bcache[rev] = name
215 bcache[rev] = name
210 return name
216 return name
211
217
212 bcache = {}
218 bcache = {}
213 opmap = [['user', getname], ['number', str], ['changeset', getnode]]
219 opmap = [['user', getname], ['number', str], ['changeset', getnode]]
214 if not ops['user'] and not ops['changeset']:
220 if not ops['user'] and not ops['changeset']:
215 ops['number'] = 1
221 ops['number'] = 1
216
222
217 node = repo.dirstate.parents()[0]
223 node = repo.dirstate.parents()[0]
218 if ops['revision']:
224 if ops['revision']:
219 node = repo.changelog.lookup(ops['revision'])
225 node = repo.changelog.lookup(ops['revision'])
220 change = repo.changelog.read(node)
226 change = repo.changelog.read(node)
221 mmap = repo.manifest.read(change[0])
227 mmap = repo.manifest.read(change[0])
222 maxuserlen = 0
228 maxuserlen = 0
223 maxchangelen = 0
229 maxchangelen = 0
224 for f in relpath(repo, (file,) + files):
230 for f in relpath(repo, (file,) + files):
225 lines = repo.file(f).annotate(mmap[f])
231 lines = repo.file(f).annotate(mmap[f])
226 pieces = []
232 pieces = []
227
233
228 for o, f in opmap:
234 for o, f in opmap:
229 if ops[o]:
235 if ops[o]:
230 l = [ f(n) for n,t in lines ]
236 l = [ f(n) for n,t in lines ]
231 m = max(map(len, l))
237 m = max(map(len, l))
232 pieces.append([ "%*s" % (m, x) for x in l])
238 pieces.append([ "%*s" % (m, x) for x in l])
233
239
234 for p,l in zip(zip(*pieces), lines):
240 for p,l in zip(zip(*pieces), lines):
235 u.write(" ".join(p) + ": " + l[1])
241 u.write(" ".join(p) + ": " + l[1])
236
242
237 def cat(ui, repo, file, rev = []):
243 def cat(ui, repo, file, rev = []):
238 """output the latest or given revision of a file"""
244 """output the latest or given revision of a file"""
239 r = repo.file(relpath(repo, [file])[0])
245 r = repo.file(relpath(repo, [file])[0])
240 n = r.tip()
246 n = r.tip()
241 if rev: n = r.lookup(rev)
247 if rev: n = r.lookup(rev)
242 sys.stdout.write(r.read(n))
248 sys.stdout.write(r.read(n))
243
249
244 def commit(ui, repo, *files, **opts):
250 def commit(ui, repo, *files, **opts):
245 """commit the specified files or all outstanding changes"""
251 """commit the specified files or all outstanding changes"""
246 text = opts['text']
252 text = opts['text']
247 if not text and opts['logfile']:
253 if not text and opts['logfile']:
248 try: text = open(opts['logfile']).read()
254 try: text = open(opts['logfile']).read()
249 except IOError: pass
255 except IOError: pass
250
256
251 if opts['addremove']:
257 if opts['addremove']:
252 addremove(ui, repo, *files)
258 addremove(ui, repo, *files)
253 repo.commit(relpath(repo, files), text, opts['user'], opts['date'])
259 repo.commit(relpath(repo, files), text, opts['user'], opts['date'])
254
260
255 def copy(ui, repo, source, dest):
261 def copy(ui, repo, source, dest):
256 """mark a file as copied or renamed for the next commit"""
262 """mark a file as copied or renamed for the next commit"""
257 return repo.copy(*relpath(repo, (source, dest)))
263 return repo.copy(*relpath(repo, (source, dest)))
258
264
259 def debugaddchangegroup(ui, repo):
265 def debugaddchangegroup(ui, repo):
260 data = sys.stdin.read()
266 data = sys.stdin.read()
261 repo.addchangegroup(data)
267 repo.addchangegroup(data)
262
268
263 def debugchangegroup(ui, repo, roots):
269 def debugchangegroup(ui, repo, roots):
264 newer = repo.newer(map(repo.lookup, roots))
270 newer = repo.newer(map(repo.lookup, roots))
265 for chunk in repo.changegroup(newer):
271 for chunk in repo.changegroup(newer):
266 sys.stdout.write(chunk)
272 sys.stdout.write(chunk)
267
273
268 def debugindex(ui, file):
274 def debugindex(ui, file):
269 r = hg.revlog(open, file, "")
275 r = hg.revlog(open, file, "")
270 print " rev offset length base linkrev"+\
276 print " rev offset length base linkrev"+\
271 " p1 p2 nodeid"
277 " p1 p2 nodeid"
272 for i in range(r.count()):
278 for i in range(r.count()):
273 e = r.index[i]
279 e = r.index[i]
274 print "% 6d % 9d % 7d % 6d % 7d %s.. %s.. %s.." % (
280 print "% 6d % 9d % 7d % 6d % 7d %s.. %s.. %s.." % (
275 i, e[0], e[1], e[2], e[3],
281 i, e[0], e[1], e[2], e[3],
276 hg.hex(e[4][:5]), hg.hex(e[5][:5]), hg.hex(e[6][:5]))
282 hg.hex(e[4][:5]), hg.hex(e[5][:5]), hg.hex(e[6][:5]))
277
283
278 def debugindexdot(ui, file):
284 def debugindexdot(ui, file):
279 r = hg.revlog(open, file, "")
285 r = hg.revlog(open, file, "")
280 print "digraph G {"
286 print "digraph G {"
281 for i in range(r.count()):
287 for i in range(r.count()):
282 e = r.index[i]
288 e = r.index[i]
283 print "\t%d -> %d" % (r.rev(e[4]), i)
289 print "\t%d -> %d" % (r.rev(e[4]), i)
284 if e[5] != hg.nullid:
290 if e[5] != hg.nullid:
285 print "\t%d -> %d" % (r.rev(e[5]), i)
291 print "\t%d -> %d" % (r.rev(e[5]), i)
286 print "}"
292 print "}"
287
293
288 def diff(ui, repo, *files, **opts):
294 def diff(ui, repo, *files, **opts):
289 """diff working directory (or selected files)"""
295 """diff working directory (or selected files)"""
290 revs = []
296 revs = []
291 if opts['rev']:
297 if opts['rev']:
292 revs = map(lambda x: repo.lookup(x), opts['rev'])
298 revs = map(lambda x: repo.lookup(x), opts['rev'])
293
299
294 if len(revs) > 2:
300 if len(revs) > 2:
295 self.ui.warn("too many revisions to diff\n")
301 self.ui.warn("too many revisions to diff\n")
296 sys.exit(1)
302 sys.exit(1)
297
303
298 if files:
304 if files:
299 files = relpath(repo, files)
305 files = relpath(repo, files)
300 else:
306 else:
301 files = relpath(repo, [""])
307 files = relpath(repo, [""])
302
308
303 dodiff(repo, os.getcwd(), files, *revs)
309 dodiff(ui, repo, os.getcwd(), files, *revs)
304
310
305 def export(ui, repo, changeset):
311 def export(ui, repo, changeset):
306 """dump the changeset header and diffs for a revision"""
312 """dump the changeset header and diffs for a revision"""
307 node = repo.lookup(changeset)
313 node = repo.lookup(changeset)
308 prev, other = repo.changelog.parents(node)
314 prev, other = repo.changelog.parents(node)
309 change = repo.changelog.read(node)
315 change = repo.changelog.read(node)
310 print "# HG changeset patch"
316 print "# HG changeset patch"
311 print "# User %s" % change[1]
317 print "# User %s" % change[1]
312 print "# Node ID %s" % hg.hex(node)
318 print "# Node ID %s" % hg.hex(node)
313 print "# Parent %s" % hg.hex(prev)
319 print "# Parent %s" % hg.hex(prev)
314 print
320 print
315 if other != hg.nullid:
321 if other != hg.nullid:
316 print "# Parent %s" % hg.hex(other)
322 print "# Parent %s" % hg.hex(other)
317 print change[4].rstrip()
323 print change[4].rstrip()
318 print
324 print
319
325
320 dodiff(repo, "", None, prev, node)
326 dodiff(ui, repo, "", None, prev, node)
321
327
322 def forget(ui, repo, file, *files):
328 def forget(ui, repo, file, *files):
323 """don't add the specified files on the next commit"""
329 """don't add the specified files on the next commit"""
324 repo.forget(relpath(repo, (file,) + files))
330 repo.forget(relpath(repo, (file,) + files))
325
331
326 def heads(ui, repo):
332 def heads(ui, repo):
327 """show current repository heads"""
333 """show current repository heads"""
328 for n in repo.changelog.heads():
334 for n in repo.changelog.heads():
329 show_changeset(ui, repo, changenode=n)
335 show_changeset(ui, repo, changenode=n)
330
336
331 def history(ui, repo):
337 def history(ui, repo):
332 """show the changelog history"""
338 """show the changelog history"""
333 for i in range(repo.changelog.count() - 1, -1, -1):
339 for i in range(repo.changelog.count() - 1, -1, -1):
334 show_changeset(ui, repo, rev=i)
340 show_changeset(ui, repo, rev=i)
335
341
336 def identify(ui, repo):
342 def identify(ui, repo):
337 """print information about the working copy"""
343 """print information about the working copy"""
338 parents = [p for p in repo.dirstate.parents() if p != hg.nullid]
344 parents = [p for p in repo.dirstate.parents() if p != hg.nullid]
339 if not parents:
345 if not parents:
340 ui.write("unknown\n")
346 ui.write("unknown\n")
341 return
347 return
342
348
343 hexfunc = ui.verbose and hg.hex or hg.short
349 hexfunc = ui.verbose and hg.hex or hg.short
344 (c, a, d, u) = repo.diffdir(repo.root)
350 (c, a, d, u) = repo.diffdir(repo.root)
345 output = ["%s%s" % ('+'.join([hexfunc(parent) for parent in parents]),
351 output = ["%s%s" % ('+'.join([hexfunc(parent) for parent in parents]),
346 (c or a or d) and "+" or "")]
352 (c or a or d) and "+" or "")]
347
353
348 if not ui.quiet:
354 if not ui.quiet:
349 # multiple tags for a single parent separated by '/'
355 # multiple tags for a single parent separated by '/'
350 parenttags = ['/'.join(tags)
356 parenttags = ['/'.join(tags)
351 for tags in map(repo.nodetags, parents) if tags]
357 for tags in map(repo.nodetags, parents) if tags]
352 # tags for multiple parents separated by ' + '
358 # tags for multiple parents separated by ' + '
353 output.append(' + '.join(parenttags))
359 output.append(' + '.join(parenttags))
354
360
355 ui.write("%s\n" % ' '.join(output))
361 ui.write("%s\n" % ' '.join(output))
356
362
357 def init(ui, source=None, **opts):
363 def init(ui, source=None, **opts):
358 """create a new repository or copy an existing one"""
364 """create a new repository or copy an existing one"""
359
365
360 if source:
366 if source:
361 paths = {}
367 paths = {}
362 for name, path in ui.configitems("paths"):
368 for name, path in ui.configitems("paths"):
363 paths[name] = path
369 paths[name] = path
364
370
365 if source in paths: source = paths[source]
371 if source in paths: source = paths[source]
366
372
367 link = 0
373 link = 0
368 if not source.startswith("http://"):
374 if not source.startswith("http://"):
369 d1 = os.stat(os.getcwd()).st_dev
375 d1 = os.stat(os.getcwd()).st_dev
370 d2 = os.stat(source).st_dev
376 d2 = os.stat(source).st_dev
371 if d1 == d2: link = 1
377 if d1 == d2: link = 1
372
378
373 if link:
379 if link:
374 ui.debug("copying by hardlink\n")
380 ui.debug("copying by hardlink\n")
375 os.system("cp -al %s/.hg .hg" % source)
381 os.system("cp -al %s/.hg .hg" % source)
376 try:
382 try:
377 os.remove(".hg/dirstate")
383 os.remove(".hg/dirstate")
378 except: pass
384 except: pass
379
385
380 repo = hg.repository(ui, ".")
386 repo = hg.repository(ui, ".")
381
387
382 else:
388 else:
383 repo = hg.repository(ui, ".", create=1)
389 repo = hg.repository(ui, ".", create=1)
384 other = hg.repository(ui, source)
390 other = hg.repository(ui, source)
385 cg = repo.getchangegroup(other)
391 cg = repo.getchangegroup(other)
386 repo.addchangegroup(cg)
392 repo.addchangegroup(cg)
387
393
388 f = repo.opener("hgrc", "w")
394 f = repo.opener("hgrc", "w")
389 f.write("[paths]\n")
395 f.write("[paths]\n")
390 f.write("default = %s\n" % source)
396 f.write("default = %s\n" % source)
391
397
392 if opts['update']:
398 if opts['update']:
393 update(ui, repo)
399 update(ui, repo)
394 else:
400 else:
395 repo = hg.repository(ui, ".", create=1)
401 repo = hg.repository(ui, ".", create=1)
396
402
397 def log(ui, repo, f):
403 def log(ui, repo, f):
398 """show the revision history of a single file"""
404 """show the revision history of a single file"""
399 f = relpath(repo, [f])[0]
405 f = relpath(repo, [f])[0]
400
406
401 r = repo.file(f)
407 r = repo.file(f)
402 for i in range(r.count() - 1, -1, -1):
408 for i in range(r.count() - 1, -1, -1):
403 show_changeset(ui, repo, filelog=r, rev=i)
409 show_changeset(ui, repo, filelog=r, rev=i)
404
410
405 def manifest(ui, repo, rev = []):
411 def manifest(ui, repo, rev = []):
406 """output the latest or given revision of the project manifest"""
412 """output the latest or given revision of the project manifest"""
407 n = repo.manifest.tip()
413 n = repo.manifest.tip()
408 if rev:
414 if rev:
409 n = repo.manifest.lookup(rev)
415 n = repo.manifest.lookup(rev)
410 m = repo.manifest.read(n)
416 m = repo.manifest.read(n)
411 mf = repo.manifest.readflags(n)
417 mf = repo.manifest.readflags(n)
412 files = m.keys()
418 files = m.keys()
413 files.sort()
419 files.sort()
414
420
415 for f in files:
421 for f in files:
416 ui.write("%40s %3s %s\n" % (hg.hex(m[f]), mf[f] and "755" or "644", f))
422 ui.write("%40s %3s %s\n" % (hg.hex(m[f]), mf[f] and "755" or "644", f))
417
423
418 def parents(ui, repo, node = None):
424 def parents(ui, repo, node = None):
419 '''show the parents of the current working dir'''
425 '''show the parents of the current working dir'''
420 if node:
426 if node:
421 p = repo.changelog.parents(repo.lookup(hg.bin(node)))
427 p = repo.changelog.parents(repo.lookup(hg.bin(node)))
422 else:
428 else:
423 p = repo.dirstate.parents()
429 p = repo.dirstate.parents()
424
430
425 for n in p:
431 for n in p:
426 if n != hg.nullid:
432 if n != hg.nullid:
427 show_changeset(ui, repo, changenode=n)
433 show_changeset(ui, repo, changenode=n)
428
434
429 def patch(ui, repo, patch1, *patches, **opts):
435 def patch(ui, repo, patch1, *patches, **opts):
430 """import an ordered set of patches"""
436 """import an ordered set of patches"""
431 try:
437 try:
432 import psyco
438 import psyco
433 psyco.full()
439 psyco.full()
434 except:
440 except:
435 pass
441 pass
436
442
437 patches = (patch1,) + patches
443 patches = (patch1,) + patches
438
444
439 d = opts["base"]
445 d = opts["base"]
440 strip = opts["strip"]
446 strip = opts["strip"]
441 quiet = opts["quiet"] and "> /dev/null" or ""
447 quiet = opts["quiet"] and "> /dev/null" or ""
442
448
443 for patch in patches:
449 for patch in patches:
444 ui.status("applying %s\n" % patch)
450 ui.status("applying %s\n" % patch)
445 pf = os.path.join(d, patch)
451 pf = os.path.join(d, patch)
446
452
447 text = ""
453 text = ""
448 for l in file(pf):
454 for l in file(pf):
449 if l[:4] == "--- ": break
455 if l[:4] == "--- ": break
450 text += l
456 text += l
451
457
452 # make sure text isn't empty
458 # make sure text isn't empty
453 if not text: text = "imported patch %s\n" % patch
459 if not text: text = "imported patch %s\n" % patch
454
460
455 f = os.popen("patch -p%d < %s" % (strip, pf))
461 f = os.popen("patch -p%d < %s" % (strip, pf))
456 files = []
462 files = []
457 for l in f.read().splitlines():
463 for l in f.read().splitlines():
458 l.rstrip('\r\n');
464 l.rstrip('\r\n');
459 if not quiet:
465 if not quiet:
460 print l
466 print l
461 if l[:14] == 'patching file ':
467 if l[:14] == 'patching file ':
462 files.append(l[14:])
468 files.append(l[14:])
463 f.close()
469 f.close()
464
470
465 if len(files) > 0:
471 if len(files) > 0:
466 addremove(ui, repo, *files)
472 addremove(ui, repo, *files)
467 repo.commit(files, text)
473 repo.commit(files, text)
468
474
469 def pull(ui, repo, source="default"):
475 def pull(ui, repo, source="default"):
470 """pull changes from the specified source"""
476 """pull changes from the specified source"""
471 paths = {}
477 paths = {}
472 for name, path in ui.configitems("paths"):
478 for name, path in ui.configitems("paths"):
473 paths[name] = path
479 paths[name] = path
474
480
475 if source in paths: source = paths[source]
481 if source in paths: source = paths[source]
476
482
477 other = hg.repository(ui, source)
483 other = hg.repository(ui, source)
478 cg = repo.getchangegroup(other)
484 cg = repo.getchangegroup(other)
479 repo.addchangegroup(cg)
485 repo.addchangegroup(cg)
480
486
481 def push(ui, repo, dest="default-push"):
487 def push(ui, repo, dest="default-push"):
482 """push changes to the specified destination"""
488 """push changes to the specified destination"""
483 paths = {}
489 paths = {}
484 for name, path in ui.configitems("paths"):
490 for name, path in ui.configitems("paths"):
485 paths[name] = path
491 paths[name] = path
486
492
487 if dest in paths: dest = paths[dest]
493 if dest in paths: dest = paths[dest]
488
494
489 if not dest.startswith("ssh://"):
495 if not dest.startswith("ssh://"):
490 ui.warn("abort: can only push to ssh:// destinations currently\n")
496 ui.warn("abort: can only push to ssh:// destinations currently\n")
491 return 1
497 return 1
492
498
493 m = re.match(r'ssh://(([^@]+)@)?([^:/]+)(:(\d+))?(/(.*))?', dest)
499 m = re.match(r'ssh://(([^@]+)@)?([^:/]+)(:(\d+))?(/(.*))?', dest)
494 if not m:
500 if not m:
495 ui.warn("abort: couldn't parse destination %s\n" % dest)
501 ui.warn("abort: couldn't parse destination %s\n" % dest)
496 return 1
502 return 1
497
503
498 user, host, port, path = map(m.group, (2, 3, 5, 7))
504 user, host, port, path = map(m.group, (2, 3, 5, 7))
499 host = user and ("%s@%s" % (user, host)) or host
505 host = user and ("%s@%s" % (user, host)) or host
500 port = port and (" -p %s") % port or ""
506 port = port and (" -p %s") % port or ""
501 path = path or ""
507 path = path or ""
502
508
503 sport = random.randrange(30000, 60000)
509 sport = random.randrange(30000, 60000)
504 cmd = "ssh %s%s -R %d:localhost:%d 'cd %s; hg pull http://localhost:%d/'"
510 cmd = "ssh %s%s -R %d:localhost:%d 'cd %s; hg pull http://localhost:%d/'"
505 cmd = cmd % (host, port, sport+1, sport, path, sport+1)
511 cmd = cmd % (host, port, sport+1, sport, path, sport+1)
506
512
507 child = os.fork()
513 child = os.fork()
508 if not child:
514 if not child:
509 sys.stdout = file("/dev/null", "w")
515 sys.stdout = file("/dev/null", "w")
510 sys.stderr = sys.stdout
516 sys.stderr = sys.stdout
511 hgweb.server(repo.root, "pull", "", "localhost", sport)
517 hgweb.server(repo.root, "pull", "", "localhost", sport)
512 else:
518 else:
513 r = os.system(cmd)
519 r = os.system(cmd)
514 os.kill(child, signal.SIGTERM)
520 os.kill(child, signal.SIGTERM)
515 return r
521 return r
516
522
517 def rawcommit(ui, repo, flist, **rc):
523 def rawcommit(ui, repo, flist, **rc):
518 "raw commit interface"
524 "raw commit interface"
519
525
520 text = rc['text']
526 text = rc['text']
521 if not text and rc['logfile']:
527 if not text and rc['logfile']:
522 try: text = open(rc['logfile']).read()
528 try: text = open(rc['logfile']).read()
523 except IOError: pass
529 except IOError: pass
524 if not text and not rc['logfile']:
530 if not text and not rc['logfile']:
525 print "missing commit text"
531 print "missing commit text"
526 return 1
532 return 1
527
533
528 files = relpath(repo, flist)
534 files = relpath(repo, flist)
529 if rc['files']:
535 if rc['files']:
530 files += open(rc['files']).read().splitlines()
536 files += open(rc['files']).read().splitlines()
531
537
532 repo.rawcommit(files, text, rc['user'], rc['date'], *rc['parent'])
538 repo.rawcommit(files, text, rc['user'], rc['date'], *rc['parent'])
533
539
534 def recover(ui, repo):
540 def recover(ui, repo):
535 """roll back an interrupted transaction"""
541 """roll back an interrupted transaction"""
536 repo.recover()
542 repo.recover()
537
543
538 def remove(ui, repo, file, *files):
544 def remove(ui, repo, file, *files):
539 """remove the specified files on the next commit"""
545 """remove the specified files on the next commit"""
540 repo.remove(relpath(repo, (file,) + files))
546 repo.remove(relpath(repo, (file,) + files))
541
547
542 def serve(ui, repo, **opts):
548 def serve(ui, repo, **opts):
543 """export the repository via HTTP"""
549 """export the repository via HTTP"""
544 hgweb.server(repo.root, opts["name"], opts["templates"],
550 hgweb.server(repo.root, opts["name"], opts["templates"],
545 opts["address"], opts["port"])
551 opts["address"], opts["port"])
546
552
547 def status(ui, repo):
553 def status(ui, repo):
548 '''show changed files in the working directory
554 '''show changed files in the working directory
549
555
550 C = changed
556 C = changed
551 A = added
557 A = added
552 R = removed
558 R = removed
553 ? = not tracked'''
559 ? = not tracked'''
554
560
555 (c, a, d, u) = repo.diffdir(os.getcwd())
561 (c, a, d, u) = repo.diffdir(os.getcwd())
556 (c, a, d, u) = map(lambda x: relfilter(repo, x), (c, a, d, u))
562 (c, a, d, u) = map(lambda x: relfilter(repo, x), (c, a, d, u))
557
563
558 for f in c: print "C", f
564 for f in c: print "C", f
559 for f in a: print "A", f
565 for f in a: print "A", f
560 for f in d: print "R", f
566 for f in d: print "R", f
561 for f in u: print "?", f
567 for f in u: print "?", f
562
568
563 def tags(ui, repo):
569 def tags(ui, repo):
564 """list repository tags"""
570 """list repository tags"""
565
571
566 l = repo.tagslist()
572 l = repo.tagslist()
567 l.reverse()
573 l.reverse()
568 for t,n in l:
574 for t,n in l:
569 try:
575 try:
570 r = repo.changelog.rev(n)
576 r = repo.changelog.rev(n)
571 except KeyError:
577 except KeyError:
572 r = "?"
578 r = "?"
573 print "%-30s %5d:%s" % (t, repo.changelog.rev(n), hg.hex(n))
579 print "%-30s %5d:%s" % (t, repo.changelog.rev(n), hg.hex(n))
574
580
575 def tip(ui, repo):
581 def tip(ui, repo):
576 """show the tip revision"""
582 """show the tip revision"""
577 n = repo.changelog.tip()
583 n = repo.changelog.tip()
578 show_changeset(ui, repo, changenode=n)
584 show_changeset(ui, repo, changenode=n)
579
585
580 def undo(ui, repo):
586 def undo(ui, repo):
581 """undo the last transaction"""
587 """undo the last transaction"""
582 repo.undo()
588 repo.undo()
583
589
584 def update(ui, repo, node=None, merge=False, clean=False):
590 def update(ui, repo, node=None, merge=False, clean=False):
585 '''update or merge working directory
591 '''update or merge working directory
586
592
587 If there are no outstanding changes in the working directory and
593 If there are no outstanding changes in the working directory and
588 there is a linear relationship between the current version and the
594 there is a linear relationship between the current version and the
589 requested version, the result is the requested version.
595 requested version, the result is the requested version.
590
596
591 Otherwise the result is a merge between the contents of the
597 Otherwise the result is a merge between the contents of the
592 current working directory and the requested version. Files that
598 current working directory and the requested version. Files that
593 changed between either parent are marked as changed for the next
599 changed between either parent are marked as changed for the next
594 commit and a commit must be performed before any further updates
600 commit and a commit must be performed before any further updates
595 are allowed.
601 are allowed.
596 '''
602 '''
597 node = node and repo.lookup(node) or repo.changelog.tip()
603 node = node and repo.lookup(node) or repo.changelog.tip()
598 return repo.update(node, allow=merge, force=clean)
604 return repo.update(node, allow=merge, force=clean)
599
605
600 def verify(ui, repo):
606 def verify(ui, repo):
601 """verify the integrity of the repository"""
607 """verify the integrity of the repository"""
602 return repo.verify()
608 return repo.verify()
603
609
604 # Command options and aliases are listed here, alphabetically
610 # Command options and aliases are listed here, alphabetically
605
611
606 table = {
612 table = {
607 "add": (add, [], "hg add [files]"),
613 "add": (add, [], "hg add [files]"),
608 "addremove": (addremove, [], "hg addremove [files]"),
614 "addremove": (addremove, [], "hg addremove [files]"),
609 "ann|annotate": (annotate,
615 "ann|annotate": (annotate,
610 [('r', 'revision', '', 'revision'),
616 [('r', 'revision', '', 'revision'),
611 ('u', 'user', None, 'show user'),
617 ('u', 'user', None, 'show user'),
612 ('n', 'number', None, 'show revision number'),
618 ('n', 'number', None, 'show revision number'),
613 ('c', 'changeset', None, 'show changeset')],
619 ('c', 'changeset', None, 'show changeset')],
614 'hg annotate [-u] [-c] [-n] [-r id] [files]'),
620 'hg annotate [-u] [-c] [-n] [-r id] [files]'),
615 "cat|dump": (cat, [], 'hg cat <file> [rev]'),
621 "cat|dump": (cat, [], 'hg cat <file> [rev]'),
616 "commit|ci": (commit,
622 "commit|ci": (commit,
617 [('t', 'text', "", 'commit text'),
623 [('t', 'text', "", 'commit text'),
618 ('A', 'addremove', None, 'run add/remove during commit'),
624 ('A', 'addremove', None, 'run add/remove during commit'),
619 ('l', 'logfile', "", 'commit text file'),
625 ('l', 'logfile', "", 'commit text file'),
620 ('d', 'date', "", 'data'),
626 ('d', 'date', "", 'data'),
621 ('u', 'user', "", 'user')],
627 ('u', 'user', "", 'user')],
622 'hg commit [files]'),
628 'hg commit [files]'),
623 "copy": (copy, [], 'hg copy <source> <dest>'),
629 "copy": (copy, [], 'hg copy <source> <dest>'),
624 "debugaddchangegroup": (debugaddchangegroup, [], 'debugaddchangegroup'),
630 "debugaddchangegroup": (debugaddchangegroup, [], 'debugaddchangegroup'),
625 "debugchangegroup": (debugchangegroup, [], 'debugchangegroup [roots]'),
631 "debugchangegroup": (debugchangegroup, [], 'debugchangegroup [roots]'),
626 "debugindex": (debugindex, [], 'debugindex <file>'),
632 "debugindex": (debugindex, [], 'debugindex <file>'),
627 "debugindexdot": (debugindexdot, [], 'debugindexdot <file>'),
633 "debugindexdot": (debugindexdot, [], 'debugindexdot <file>'),
628 "diff": (diff, [('r', 'rev', [], 'revision')],
634 "diff": (diff, [('r', 'rev', [], 'revision')],
629 'hg diff [-r A] [-r B] [files]'),
635 'hg diff [-r A] [-r B] [files]'),
630 "export": (export, [], "hg export <changeset>"),
636 "export": (export, [], "hg export <changeset>"),
631 "forget": (forget, [], "hg forget [files]"),
637 "forget": (forget, [], "hg forget [files]"),
632 "heads": (heads, [], 'hg heads'),
638 "heads": (heads, [], 'hg heads'),
633 "history": (history, [], 'hg history'),
639 "history": (history, [], 'hg history'),
634 "help": (help, [], 'hg help [command]'),
640 "help": (help, [], 'hg help [command]'),
635 "identify|id": (identify, [], 'hg identify'),
641 "identify|id": (identify, [], 'hg identify'),
636 "init": (init, [('u', 'update', None, 'update after init')],
642 "init": (init, [('u', 'update', None, 'update after init')],
637 'hg init [options] [url]'),
643 'hg init [options] [url]'),
638 "log": (log, [], 'hg log <file>'),
644 "log": (log, [], 'hg log <file>'),
639 "manifest|dumpmanifest": (manifest, [], 'hg manifest [rev]'),
645 "manifest|dumpmanifest": (manifest, [], 'hg manifest [rev]'),
640 "parents": (parents, [], 'hg parents [node]'),
646 "parents": (parents, [], 'hg parents [node]'),
641 "patch|import": (patch,
647 "patch|import": (patch,
642 [('p', 'strip', 1, 'path strip'),
648 [('p', 'strip', 1, 'path strip'),
643 ('b', 'base', "", 'base path'),
649 ('b', 'base', "", 'base path'),
644 ('q', 'quiet', "", 'silence diff')],
650 ('q', 'quiet', "", 'silence diff')],
645 "hg import [options] patches"),
651 "hg import [options] patches"),
646 "pull|merge": (pull, [], 'hg pull [source]'),
652 "pull|merge": (pull, [], 'hg pull [source]'),
647 "push": (push, [], 'hg push <destination>'),
653 "push": (push, [], 'hg push <destination>'),
648 "rawcommit": (rawcommit,
654 "rawcommit": (rawcommit,
649 [('p', 'parent', [], 'parent'),
655 [('p', 'parent', [], 'parent'),
650 ('d', 'date', "", 'data'),
656 ('d', 'date', "", 'data'),
651 ('u', 'user', "", 'user'),
657 ('u', 'user', "", 'user'),
652 ('F', 'files', "", 'file list'),
658 ('F', 'files', "", 'file list'),
653 ('t', 'text', "", 'commit text'),
659 ('t', 'text', "", 'commit text'),
654 ('l', 'logfile', "", 'commit text file')],
660 ('l', 'logfile', "", 'commit text file')],
655 'hg rawcommit [options] [files]'),
661 'hg rawcommit [options] [files]'),
656 "recover": (recover, [], "hg recover"),
662 "recover": (recover, [], "hg recover"),
657 "remove": (remove, [], "hg remove [files]"),
663 "remove": (remove, [], "hg remove [files]"),
658 "serve": (serve, [('p', 'port', 8000, 'listen port'),
664 "serve": (serve, [('p', 'port', 8000, 'listen port'),
659 ('a', 'address', '', 'interface address'),
665 ('a', 'address', '', 'interface address'),
660 ('n', 'name', os.getcwd(), 'repository name'),
666 ('n', 'name', os.getcwd(), 'repository name'),
661 ('t', 'templates', "", 'template map')],
667 ('t', 'templates', "", 'template map')],
662 "hg serve [options]"),
668 "hg serve [options]"),
663 "status": (status, [], 'hg status'),
669 "status": (status, [], 'hg status'),
664 "tags": (tags, [], 'hg tags'),
670 "tags": (tags, [], 'hg tags'),
665 "tip": (tip, [], 'hg tip'),
671 "tip": (tip, [], 'hg tip'),
666 "undo": (undo, [], 'hg undo'),
672 "undo": (undo, [], 'hg undo'),
667 "update|up|checkout|co|resolve": (update,
673 "update|up|checkout|co|resolve": (update,
668 [('m', 'merge', None,
674 [('m', 'merge', None,
669 'allow merging of conflicts'),
675 'allow merging of conflicts'),
670 ('C', 'clean', None,
676 ('C', 'clean', None,
671 'overwrite locally modified files')],
677 'overwrite locally modified files')],
672 'hg update [options] [node]'),
678 'hg update [options] [node]'),
673 "verify": (verify, [], 'hg verify'),
679 "verify": (verify, [], 'hg verify'),
674 }
680 }
675
681
676 norepo = "init branch help debugindex debugindexdot"
682 norepo = "init branch help debugindex debugindexdot"
677
683
678 def find(cmd):
684 def find(cmd):
679 i = None
685 i = None
680 for e in table.keys():
686 for e in table.keys():
681 if re.match("(%s)$" % e, cmd):
687 if re.match("(%s)$" % e, cmd):
682 return table[e]
688 return table[e]
683
689
684 raise UnknownCommand(cmd)
690 raise UnknownCommand(cmd)
685
691
686 class SignalInterrupt(Exception): pass
692 class SignalInterrupt(Exception): pass
687
693
688 def catchterm(*args):
694 def catchterm(*args):
689 raise SignalInterrupt
695 raise SignalInterrupt
690
696
691 def run():
697 def run():
692 sys.exit(dispatch(sys.argv[1:]))
698 sys.exit(dispatch(sys.argv[1:]))
693
699
694 def dispatch(args):
700 def dispatch(args):
695 options = {}
701 options = {}
696 opts = [('v', 'verbose', None, 'verbose'),
702 opts = [('v', 'verbose', None, 'verbose'),
697 ('d', 'debug', None, 'debug'),
703 ('d', 'debug', None, 'debug'),
698 ('q', 'quiet', None, 'quiet'),
704 ('q', 'quiet', None, 'quiet'),
699 ('p', 'profile', None, 'profile'),
705 ('p', 'profile', None, 'profile'),
700 ('y', 'noninteractive', None, 'run non-interactively'),
706 ('y', 'noninteractive', None, 'run non-interactively'),
701 ]
707 ]
702
708
703 args = fancyopts.fancyopts(args, opts, options,
709 args = fancyopts.fancyopts(args, opts, options,
704 'hg [options] <command> [options] [files]')
710 'hg [options] <command> [options] [files]')
705
711
706 if not args:
712 if not args:
707 cmd = "help"
713 cmd = "help"
708 else:
714 else:
709 cmd, args = args[0], args[1:]
715 cmd, args = args[0], args[1:]
710
716
711 u = ui.ui(options["verbose"], options["debug"], options["quiet"],
717 u = ui.ui(options["verbose"], options["debug"], options["quiet"],
712 not options["noninteractive"])
718 not options["noninteractive"])
713
719
714 try:
720 try:
715 i = find(cmd)
721 i = find(cmd)
716 except UnknownCommand:
722 except UnknownCommand:
717 u.warn("hg: unknown command '%s'\n" % cmd)
723 u.warn("hg: unknown command '%s'\n" % cmd)
718 help(u)
724 help(u)
719 sys.exit(1)
725 sys.exit(1)
720
726
721 signal.signal(signal.SIGTERM, catchterm)
727 signal.signal(signal.SIGTERM, catchterm)
722
728
723 cmdoptions = {}
729 cmdoptions = {}
724 try:
730 try:
725 args = fancyopts.fancyopts(args, i[1], cmdoptions, i[2])
731 args = fancyopts.fancyopts(args, i[1], cmdoptions, i[2])
726 except fancyopts.getopt.GetoptError, inst:
732 except fancyopts.getopt.GetoptError, inst:
727 u.warn("hg %s: %s\n" % (cmd, inst))
733 u.warn("hg %s: %s\n" % (cmd, inst))
728 help(u, cmd)
734 help(u, cmd)
729 sys.exit(-1)
735 sys.exit(-1)
730
736
731 if cmd not in norepo.split():
737 if cmd not in norepo.split():
732 repo = hg.repository(ui = u)
738 repo = hg.repository(ui = u)
733 d = lambda: i[0](u, repo, *args, **cmdoptions)
739 d = lambda: i[0](u, repo, *args, **cmdoptions)
734 else:
740 else:
735 d = lambda: i[0](u, *args, **cmdoptions)
741 d = lambda: i[0](u, *args, **cmdoptions)
736
742
737 try:
743 try:
738 if options['profile']:
744 if options['profile']:
739 import hotshot, hotshot.stats
745 import hotshot, hotshot.stats
740 prof = hotshot.Profile("hg.prof")
746 prof = hotshot.Profile("hg.prof")
741 r = prof.runcall(d)
747 r = prof.runcall(d)
742 prof.close()
748 prof.close()
743 stats = hotshot.stats.load("hg.prof")
749 stats = hotshot.stats.load("hg.prof")
744 stats.strip_dirs()
750 stats.strip_dirs()
745 stats.sort_stats('time', 'calls')
751 stats.sort_stats('time', 'calls')
746 stats.print_stats(40)
752 stats.print_stats(40)
747 return r
753 return r
748 else:
754 else:
749 return d()
755 return d()
750 except SignalInterrupt:
756 except SignalInterrupt:
751 u.warn("killed!\n")
757 u.warn("killed!\n")
752 except KeyboardInterrupt:
758 except KeyboardInterrupt:
753 u.warn("interrupted!\n")
759 u.warn("interrupted!\n")
754 except IOError, inst:
760 except IOError, inst:
755 if hasattr(inst, "code"):
761 if hasattr(inst, "code"):
756 u.warn("abort: %s\n" % inst)
762 u.warn("abort: %s\n" % inst)
757 elif hasattr(inst, "reason"):
763 elif hasattr(inst, "reason"):
758 u.warn("abort: error %d: %s\n" % (inst.reason[0], inst.reason[1]))
764 u.warn("abort: error %d: %s\n" % (inst.reason[0], inst.reason[1]))
759 elif hasattr(inst, "args") and inst[0] == errno.EPIPE:
765 elif hasattr(inst, "args") and inst[0] == errno.EPIPE:
760 u.warn("broken pipe\n")
766 u.warn("broken pipe\n")
761 else:
767 else:
762 raise
768 raise
763 except TypeError, inst:
769 except TypeError, inst:
764 # was this an argument error?
770 # was this an argument error?
765 tb = traceback.extract_tb(sys.exc_info()[2])
771 tb = traceback.extract_tb(sys.exc_info()[2])
766 if len(tb) > 2: # no
772 if len(tb) > 2: # no
767 raise
773 raise
768 u.debug(inst, "\n")
774 u.debug(inst, "\n")
769 u.warn("%s: invalid arguments\n" % i[0].__name__)
775 u.warn("%s: invalid arguments\n" % i[0].__name__)
770 help(u, cmd)
776 help(u, cmd)
771 sys.exit(-1)
777 sys.exit(-1)
772
778
@@ -1,115 +1,120 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, r=None):
12
12 if not a and not b: return ""
13 if not a and not b: return ""
13
14
14 if a == None:
15 if a == None:
15 b = b.splitlines(1)
16 b = b.splitlines(1)
16 l1 = "--- %s\t%s\n" % ("/dev/null", ad)
17 l1 = "--- %s\t%s\n" % ("/dev/null", ad)
17 l2 = "+++ %s\t%s\n" % ("b/" + fn, bd)
18 l2 = "+++ %s\t%s\n" % ("b/" + fn, bd)
18 l3 = "@@ -0,0 +1,%d @@\n" % len(b)
19 l3 = "@@ -0,0 +1,%d @@\n" % len(b)
19 l = [l1, l2, l3] + ["+" + e for e in b]
20 l = [l1, l2, l3] + ["+" + e for e in b]
20 elif b == None:
21 elif b == None:
21 a = a.splitlines(1)
22 a = a.splitlines(1)
22 l1 = "--- %s\t%s\n" % ("a/" + fn, ad)
23 l1 = "--- %s\t%s\n" % ("a/" + fn, ad)
23 l2 = "+++ %s\t%s\n" % ("/dev/null", bd)
24 l2 = "+++ %s\t%s\n" % ("/dev/null", bd)
24 l3 = "@@ -1,%d +0,0 @@\n" % len(a)
25 l3 = "@@ -1,%d +0,0 @@\n" % len(a)
25 l = [l1, l2, l3] + ["-" + e for e in a]
26 l = [l1, l2, l3] + ["-" + e for e in a]
26 else:
27 else:
27 a = a.splitlines(1)
28 a = a.splitlines(1)
28 b = b.splitlines(1)
29 b = b.splitlines(1)
29 l = list(difflib.unified_diff(a, b, "a/" + fn, "b/" + fn))
30 l = list(difflib.unified_diff(a, b, "a/" + fn, "b/" + fn))
30 if not l: return ""
31 if not l: return ""
31 # difflib uses a space, rather than a tab
32 # difflib uses a space, rather than a tab
32 l[0] = l[0][:-2] + "\t" + ad + "\n"
33 l[0] = l[0][:-2] + "\t" + ad + "\n"
33 l[1] = l[1][:-2] + "\t" + bd + "\n"
34 l[1] = l[1][:-2] + "\t" + bd + "\n"
34
35
35 for ln in xrange(len(l)):
36 for ln in xrange(len(l)):
36 if l[ln][-1] != '\n':
37 if l[ln][-1] != '\n':
37 l[ln] += "\n\ No newline at end of file\n"
38 l[ln] += "\n\ No newline at end of file\n"
38
39
40 if r:
41 l.insert(0, "diff %s %s\n" %
42 (' '.join(["-r %s" % rev for rev in r]), fn))
43
39 return "".join(l)
44 return "".join(l)
40
45
41 def textdiff(a, b):
46 def textdiff(a, b):
42 return diff(a.splitlines(1), b.splitlines(1))
47 return diff(a.splitlines(1), b.splitlines(1))
43
48
44 def sortdiff(a, b):
49 def sortdiff(a, b):
45 la = lb = 0
50 la = lb = 0
46 lena = len(a)
51 lena = len(a)
47 lenb = len(b)
52 lenb = len(b)
48
53
49 while 1:
54 while 1:
50 am, bm, = la, lb
55 am, bm, = la, lb
51
56
52 # walk over matching lines
57 # walk over matching lines
53 while lb < lenb and la < lena and a[la] == b[lb] :
58 while lb < lenb and la < lena and a[la] == b[lb] :
54 la += 1
59 la += 1
55 lb += 1
60 lb += 1
56
61
57 if la > am:
62 if la > am:
58 yield (am, bm, la - am) # return a match
63 yield (am, bm, la - am) # return a match
59
64
60 # skip mismatched lines from b
65 # skip mismatched lines from b
61 while la < lena and lb < lenb and b[lb] < a[la]:
66 while la < lena and lb < lenb and b[lb] < a[la]:
62 lb += 1
67 lb += 1
63
68
64 if lb >= lenb:
69 if lb >= lenb:
65 break
70 break
66
71
67 # skip mismatched lines from a
72 # skip mismatched lines from a
68 while la < lena and lb < lenb and b[lb] > a[la]:
73 while la < lena and lb < lenb and b[lb] > a[la]:
69 la += 1
74 la += 1
70
75
71 if la >= lena:
76 if la >= lena:
72 break
77 break
73
78
74 yield (lena, lenb, 0)
79 yield (lena, lenb, 0)
75
80
76 def diff(a, b, sorted=0):
81 def diff(a, b, sorted=0):
77 if not a:
82 if not a:
78 s = "".join(b)
83 s = "".join(b)
79 return s and (struct.pack(">lll", 0, 0, len(s)) + s)
84 return s and (struct.pack(">lll", 0, 0, len(s)) + s)
80
85
81 bin = []
86 bin = []
82 p = [0]
87 p = [0]
83 for i in a: p.append(p[-1] + len(i))
88 for i in a: p.append(p[-1] + len(i))
84
89
85 if sorted:
90 if sorted:
86 try:
91 try:
87 d = sortdiff(a, b)
92 d = sortdiff(a, b)
88 except:
93 except:
89 print a, b
94 print a, b
90 raise
95 raise
91 else:
96 else:
92 d = difflib.SequenceMatcher(None, a, b).get_matching_blocks()
97 d = difflib.SequenceMatcher(None, a, b).get_matching_blocks()
93 la = 0
98 la = 0
94 lb = 0
99 lb = 0
95 for am, bm, size in d:
100 for am, bm, size in d:
96 s = "".join(b[lb:bm])
101 s = "".join(b[lb:bm])
97 if am > la or s:
102 if am > la or s:
98 bin.append(struct.pack(">lll", p[la], p[am], len(s)) + s)
103 bin.append(struct.pack(">lll", p[la], p[am], len(s)) + s)
99 la = am + size
104 la = am + size
100 lb = bm + size
105 lb = bm + size
101
106
102 return "".join(bin)
107 return "".join(bin)
103
108
104 def patchtext(bin):
109 def patchtext(bin):
105 pos = 0
110 pos = 0
106 t = []
111 t = []
107 while pos < len(bin):
112 while pos < len(bin):
108 p1, p2, l = struct.unpack(">lll", bin[pos:pos + 12])
113 p1, p2, l = struct.unpack(">lll", bin[pos:pos + 12])
109 pos += 12
114 pos += 12
110 t.append(bin[pos:pos + l])
115 t.append(bin[pos:pos + l])
111 pos += l
116 pos += l
112 return "".join(t)
117 return "".join(t)
113
118
114 def patch(a, bin):
119 def patch(a, bin):
115 return patches(a, [bin])
120 return patches(a, [bin])
@@ -1,72 +1,74 b''
1 + mkdir r1
1 + mkdir r1
2 + cd r1
2 + cd r1
3 + hg init
3 + hg init
4 + echo a
4 + echo a
5 + hg addremove
5 + hg addremove
6 + hg commit -t 1 -u test -d '0 0'
6 + hg commit -t 1 -u test -d '0 0'
7 + cd ..
7 + cd ..
8 + mkdir r2
8 + mkdir r2
9 + cd r2
9 + cd r2
10 + hg init ../r1
10 + hg init ../r1
11 + hg up
11 + hg up
12 + echo abc
12 + echo abc
13 + hg diff
13 + hg diff
14 + sed 's/\(\(---\|+++\).*\)\t.*/\1/'
14 + sed 's/\(\(---\|+++\).*\)\t.*/\1/'
15 diff -r c19d34741b0a a
15 --- a/a
16 --- a/a
16 +++ b/a
17 +++ b/a
17 @@ -1,1 +1,1 @@
18 @@ -1,1 +1,1 @@
18 -a
19 -a
19 +abc
20 +abc
20 + cd ../r1
21 + cd ../r1
21 + echo b
22 + echo b
22 + echo a2
23 + echo a2
23 + hg addremove
24 + hg addremove
24 + hg commit -t 2 -u test -d '0 0'
25 + hg commit -t 2 -u test -d '0 0'
25 + cd ../r2
26 + cd ../r2
26 + hg -q pull ../r1
27 + hg -q pull ../r1
27 + hg status
28 + hg status
28 C a
29 C a
29 + hg -d up
30 + hg -d up
30 resolving manifests
31 resolving manifests
31 ancestor a0c8bcbbb45c local a0c8bcbbb45c remote 1165e8bd193e
32 ancestor a0c8bcbbb45c local a0c8bcbbb45c remote 1165e8bd193e
32 a versions differ, resolve
33 a versions differ, resolve
33 remote created b
34 remote created b
34 getting b
35 getting b
35 merging a
36 merging a
36 resolving a
37 resolving a
37 file a: other d730145abbf9 ancestor b789fdd96dc2
38 file a: other d730145abbf9 ancestor b789fdd96dc2
38 + hg -d up -m
39 + hg -d up -m
39 resolving manifests
40 resolving manifests
40 ancestor 1165e8bd193e local 1165e8bd193e remote 1165e8bd193e
41 ancestor 1165e8bd193e local 1165e8bd193e remote 1165e8bd193e
41 + hg parents
42 + hg parents
42 changeset: 1:1e71731e6fbb5b35fae293120dea6964371c13c6
43 changeset: 1:1e71731e6fbb5b35fae293120dea6964371c13c6
43 tag: tip
44 tag: tip
44 user: test
45 user: test
45 date: Thu Jan 1 00:00:00 1970
46 date: Thu Jan 1 00:00:00 1970
46 summary: 2
47 summary: 2
47
48
48 + hg -v history
49 + hg -v history
49 changeset: 1:1e71731e6fbb5b35fae293120dea6964371c13c6
50 changeset: 1:1e71731e6fbb5b35fae293120dea6964371c13c6
50 tag: tip
51 tag: tip
51 manifest: 1:1165e8bd193e17ad7d321d846fcf27ff3f412758
52 manifest: 1:1165e8bd193e17ad7d321d846fcf27ff3f412758
52 user: test
53 user: test
53 date: Thu Jan 1 00:00:00 1970
54 date: Thu Jan 1 00:00:00 1970
54 files: a b
55 files: a b
55 description:
56 description:
56 2
57 2
57
58
58 changeset: 0:c19d34741b0a4ced8e4ba74bb834597d5193851e
59 changeset: 0:c19d34741b0a4ced8e4ba74bb834597d5193851e
59 manifest: 0:a0c8bcbbb45c63b90b70ad007bf38961f64f2af0
60 manifest: 0:a0c8bcbbb45c63b90b70ad007bf38961f64f2af0
60 user: test
61 user: test
61 date: Thu Jan 1 00:00:00 1970
62 date: Thu Jan 1 00:00:00 1970
62 files: a
63 files: a
63 description:
64 description:
64 1
65 1
65
66
66 + hg diff
67 + hg diff
67 + sed 's/\(\(---\|+++\).*\)\t.*/\1/'
68 + sed 's/\(\(---\|+++\).*\)\t.*/\1/'
69 diff -r 1e71731e6fbb a
68 --- a/a
70 --- a/a
69 +++ b/a
71 +++ b/a
70 @@ -1,1 +1,1 @@
72 @@ -1,1 +1,1 @@
71 -a2
73 -a2
72 +abc
74 +abc
General Comments 0
You need to be logged in to leave comments. Login now