12 # accompanied this code).
13 #
14 # You should have received a copy of the GNU General Public License version
15 # 2 along with this work; if not, write to the Free Software Foundation,
16 # Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
17 #
18 # Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
19 # or visit www.oracle.com if you need additional information or have any
20 # questions.
21
22 import mercurial
23 import mercurial.patch
24 import mercurial.mdiff
25 import mercurial.util
26 import mercurial.hg
27 import mercurial.node
28 import difflib
29 import sys
30
31 # space separated version list
32 testedwith = '4.9.2 5.0.2'
33
34 def mode(fctx):
35 flags = fctx.flags()
36 if flags == '': return '100644'
37 if flags == 'x': return '100755'
38 if flags == 'l': return '120000'
39
40 def ratio(a, b, threshold):
41 s = difflib.SequenceMatcher(None, a, b)
42 if s.real_quick_ratio() < threshold:
43 return 0
44 if s.quick_ratio() < threshold:
45 return 0
46 ratio = s.ratio()
47 if ratio < threshold:
48 return 0
49 return ratio
50
51 def encode(s):
52 return s.decode('utf-8').encode('utf-8')
53
54 def write(s):
55 sys.stdout.write(encode(s))
56
57 def writeln(s):
58 write(s)
59 sys.stdout.write(encode('\n'))
60
61 def _match_exact(root, cwd, files, badfn=None):
62 """
63 Wrapper for mercurial.match.exact that ignores some arguments based on the used version
64 """
65 if mercurial.util.version().startswith("5"):
66 return mercurial.match.exact(files, badfn)
67 else:
68 return mercurial.match.exact(root, cwd, files, badfn)
69
70 def _diff_git_raw(repo, ctx1, ctx2, modified, added, removed, showPatch):
71 nullHash = '0' * 40
72 removed_copy = set(removed)
73
74 for path in added:
75 fctx = ctx2.filectx(path)
76 if fctx.renamed():
77 parent = fctx.p1()
78 old_path, _ = fctx.renamed()
79 if old_path in removed:
80 removed_copy.discard(old_path)
81
82 for path in sorted(modified | added | removed_copy):
83 if path in modified:
84 fctx = ctx2.filectx(path)
85 writeln(':{} {} {} {} M\t{}'.format(mode(ctx1.filectx(path)), mode(fctx), nullHash, nullHash, fctx.path()))
86 elif path in added:
87 fctx = ctx2.filectx(path)
88 if not fctx.renamed():
89 writeln(':000000 {} {} {} A\t{}'.format(mode(fctx), nullHash, nullHash, fctx.path()))
90 else:
91 parent = fctx.p1()
92 score = int(ratio(parent.data(), fctx.data(), 0.5) * 100)
93 old_path, _ = fctx.renamed()
94
95 if old_path in removed:
96 operation = 'R'
97 else:
98 operation = 'C'
99
100 writeln(':{} {} {} {} {}{}\t{}\t{}'.format(mode(parent), mode(fctx), nullHash, nullHash, operation, score, old_path, path))
101 elif path in removed_copy:
102 fctx = ctx1.filectx(path)
103 writeln(':{} 000000 {} {} D\t{}'.format(mode(fctx), nullHash, nullHash, path))
104
105 if showPatch:
106 writeln('')
107
108 match = _match_exact(repo.root, repo.getcwd(), list(modified) + list(added) + list(removed_copy))
109 opts = mercurial.mdiff.diffopts(git=True, nodates=True, context=0, showfunc=True)
110 for d in mercurial.patch.diff(repo, ctx1.node(), ctx2.node(), match=match, opts=opts):
111 sys.stdout.write(d)
112
113 def really_differs(repo, p1, p2, ctx, files):
114 # workaround bug in hg (present since forever):
115 # `hg status` can, for merge commits, report a file as modififed between one parent
116 # and the merge even though it isn't. `hg diff` works correctly, so remove any "modified"
117 # that has an empty diff against one of its parents
118 differs = set()
119 for path in files:
120 match = _match_exact(repo.root, repo.getcwd(), [path])
121 opts = mercurial.mdiff.diffopts(git=True, nodates=True, context=0, showfunc=True)
122
123 diff1 = mercurial.patch.diff(repo, p1.node(), ctx.node(), match=match, opts=opts)
124 diff2 = mercurial.patch.diff(repo, p2.node(), ctx.node(), match=match, opts=opts)
125 if len(list(diff1)) > 0 and len(list(diff2)) > 0:
126 differs.add(path)
127
128 return differs
129
130 cmdtable = {}
131 if hasattr(mercurial, 'registrar') and hasattr(mercurial.registrar, 'command'):
134 command = mercurial.cmdutil.command(cmdtable)
135 else:
136 def command(name, options, synopsis):
137 def decorator(func):
138 cmdtable[name] = func, list(options), synopsis
139 return func
140 return decorator
141
142 if hasattr(mercurial, 'utils') and hasattr(mercurial.utils, 'dateutil'):
143 datestr = mercurial.utils.dateutil.datestr
144 else:
145 datestr = mercurial.util.datestr
146
147 if hasattr(mercurial, 'scmutil'):
148 revsingle = mercurial.scmutil.revsingle
149 revrange = mercurial.scmutil.revrange
150 else:
151 revsingle = mercurial.cmdutil.revsingle
152 revrange = mercurial.cmdutil.revrange
153
154 @command('diff-git-raw', [('', 'patch', False, '')], 'hg diff-git-raw rev1 [rev2]')
155 def diff_git_raw(ui, repo, rev1, rev2=None, **opts):
156 ctx1 = revsingle(repo, rev1)
157
158 if rev2 != None:
159 ctx2 = revsingle(repo, rev2)
160 status = repo.status(ctx1, ctx2)
161 else:
162 ctx2 = mercurial.context.workingctx(repo)
163 status = repo.status(ctx1)
164
165 modified, added, removed = [set(l) for l in status[:3]]
166 _diff_git_raw(repo, ctx1, ctx2, modified, added, removed, opts['patch'])
167
168 @command('log-git', [('', 'reverse', False, ''), ('l', 'limit', -1, '')], 'hg log-git <revisions>')
169 def log_git(ui, repo, revs=None, **opts):
170 if len(repo) == 0:
171 return
172
173 if revs == None:
174 if opts['reverse']:
175 revs = '0:tip'
176 else:
177 revs = 'tip:0'
178
179 limit = opts['limit']
180 i = 0
181 for r in revrange(repo, [revs]):
182 ctx = repo[r]
183
184 __dump_metadata(ctx)
185 parents = ctx.parents()
186
187 if len(parents) == 1:
188 modified, added, removed = [set(l) for l in repo.status(parents[0], ctx)[:3]]
189 _diff_git_raw(repo, parents[0], ctx, modified, added, removed, True)
190 else:
191 p1 = parents[0]
192 p2 = parents[1]
193
194 modified_p1, added_p1, removed_p1 = [set(l) for l in repo.status(p1, ctx)[:3]]
195 modified_p2, added_p2, removed_p2 = [set(l) for l in repo.status(p2, ctx)[:3]]
196
197 added_both = added_p1 & added_p2
198 modified_both = modified_p1 & modified_p2
199 removed_both = removed_p1 & removed_p2
200
201 combined_modified_p1 = modified_both | (modified_p1 & added_p2)
202 combined_added_p1 = added_both | (added_p1 & modified_p2)
203 combined_modified_p2 = modified_both | (modified_p2 & added_p1)
204 combined_added_p2 = added_both | (added_p2 & modified_p1)
205
206 combined_modified_p1 = really_differs(repo, p1, p2, ctx, combined_modified_p1)
207 combined_added_p1 = really_differs(repo, p1, p2, ctx, combined_added_p1)
208 combined_modified_p2 = really_differs(repo, p1, p2, ctx, combined_modified_p2)
209 combined_added_p2 = really_differs(repo, p1, p2, ctx, combined_added_p2)
210
211 _diff_git_raw(repo, p1, ctx, combined_modified_p1, combined_added_p1, removed_both, True)
212 writeln('#@!_-=&')
213 _diff_git_raw(repo, p2, ctx, combined_modified_p2, combined_added_p2, removed_both, True)
214
215 i += 1
216 if i == limit:
217 break
218
219 def __dump_metadata(ctx):
220 writeln('#@!_-=&')
221 writeln(ctx.hex())
222 writeln(str(ctx.rev()))
223 writeln(ctx.branch())
224
225 parents = ctx.parents()
226 writeln(' '.join([str(p.hex()) for p in parents]))
227 writeln(' '.join([str(p.rev()) for p in parents]))
228
229 writeln(ctx.user())
230 date = datestr(ctx.date(), format='%Y-%m-%d %H:%M:%S%z')
231 writeln(date)
232
233 description = encode(ctx.description())
234 writeln(str(len(description)))
235 write(description)
236
237 def __dump(repo, start, end):
238 for rev in xrange(start, end):
239 ctx = revsingle(repo, rev)
240
241 __dump_metadata(ctx)
242 parents = ctx.parents()
243
244 modified, added, removed = repo.status(parents[0], ctx)[:3]
245 writeln(str(len(modified)))
246 writeln(str(len(added)))
247 writeln(str(len(removed)))
248
249 for filename in added + modified:
250 fctx = ctx.filectx(filename)
251
252 writeln(filename)
253 writeln(' '.join(fctx.flags()))
254
255 content = fctx.data()
256 writeln(str(len(content)))
257 sys.stdout.write(content)
258
259 for filename in removed:
260 writeln(filename)
261
262 def pretxnclose(ui, repo, **kwargs):
263 start = revsingle(repo, kwargs['node'])
264 end = revsingle(repo, kwargs['node_last'])
265 __dump(repo, start.rev(), end.rev() + 1)
266
267 @command('dump', [], 'hg dump')
268 def dump(ui, repo, **opts):
269 __dump(repo, 0, len(repo))
270
271 @command('metadata', [], 'hg metadata')
272 def dump(ui, repo, revs=None, **opts):
273 if revs == None:
274 revs = "0:tip"
275
276 for r in revrange(repo, [revs]):
277 ctx = repo[r]
278 __dump_metadata(ctx)
279
280 @command('ls-tree', [], 'hg ls-tree')
281 def ls_tree(ui, repo, rev, **opts):
282 nullHash = '0' * 40
283 ctx = revsingle(repo, rev)
284 for filename in ctx.manifest():
285 fctx = ctx.filectx(filename)
286 if 'x' in fctx.flags():
287 write('100755 blob ')
288 else:
289 write('100644 blob ')
290 write(nullHash)
291 write('\t')
292 writeln(filename)
293
294 @command('ls-remote', [], 'hg ls-remote PATH')
295 def ls_remote(ui, repo, path, **opts):
296 peer = mercurial.hg.peer(ui or repo, opts, ui.expandpath(path))
297 for branch, heads in peer.branchmap().iteritems():
298 for head in heads:
299 write(mercurial.node.hex(head))
300 write("\t")
301 writeln(branch)
|
12 # accompanied this code).
13 #
14 # You should have received a copy of the GNU General Public License version
15 # 2 along with this work; if not, write to the Free Software Foundation,
16 # Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
17 #
18 # Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
19 # or visit www.oracle.com if you need additional information or have any
20 # questions.
21
22 import mercurial
23 import mercurial.patch
24 import mercurial.mdiff
25 import mercurial.util
26 import mercurial.hg
27 import mercurial.node
28 import difflib
29 import sys
30
31 # space separated version list
32 testedwith = '4.9.2 5.0.2 5.2.1'
33
34 def mode(fctx):
35 flags = fctx.flags()
36 if flags == b'': return b'100644'
37 if flags == b'x': return b'100755'
38 if flags == b'l': return b'120000'
39
40 def ratio(a, b, threshold):
41 s = difflib.SequenceMatcher(None, a, b)
42 if s.real_quick_ratio() < threshold:
43 return 0
44 if s.quick_ratio() < threshold:
45 return 0
46 ratio = s.ratio()
47 if ratio < threshold:
48 return 0
49 return ratio
50
51 def write(s):
52 if sys.version_info >= (3, 0):
53 sys.stdout.buffer.write(s)
54 else:
55 sys.stdout.write(s)
56
57 def writeln(s):
58 write(s)
59 write(b'\n')
60
61 def int_to_str(i):
62 return str(i).encode('ascii')
63
64 def _match_exact(root, cwd, files, badfn=None):
65 """
66 Wrapper for mercurial.match.exact that ignores some arguments based on the used version
67 """
68 if mercurial.util.version().startswith(b"5"):
69 return mercurial.match.exact(files, badfn)
70 else:
71 return mercurial.match.exact(root, cwd, files, badfn)
72
73 def _diff_git_raw(repo, ctx1, ctx2, modified, added, removed, showPatch):
74 nullHash = b'0' * 40
75 removed_copy = set(removed)
76
77 for path in added:
78 fctx = ctx2.filectx(path)
79 if fctx.renamed():
80 parent = fctx.p1()
81 old_path, _ = fctx.renamed()
82 if old_path in removed:
83 removed_copy.discard(old_path)
84
85 for path in sorted(modified | added | removed_copy):
86 if path in modified:
87 fctx = ctx2.filectx(path)
88 writeln(b':' + mode(ctx1.filectx(path)) + b' ' + mode(fctx) + b' ' + nullHash + b' ' + nullHash + b' M\t' + fctx.path())
89 elif path in added:
90 fctx = ctx2.filectx(path)
91 if not fctx.renamed():
92 writeln(b':000000 ' + mode(fctx) + b' ' + nullHash + b' ' + nullHash + b' A\t' + fctx.path())
93 else:
94 parent = fctx.p1()
95 score = int_to_str(int(ratio(parent.data(), fctx.data(), 0.5) * 100))
96 old_path, _ = fctx.renamed()
97
98 if old_path in removed:
99 operation = b'R'
100 else:
101 operation = b'C'
102
103 write(b':' + mode(parent) + b' ' + mode(fctx) + b' ' + nullHash + b' ' + nullHash + b' ')
104 writeln(operation + score + b'\t' + old_path + b'\t' + path)
105 elif path in removed_copy:
106 fctx = ctx1.filectx(path)
107 writeln(b':' + mode(fctx) + b' 000000 ' + nullHash + b' ' + nullHash + b' D\t' + path)
108
109 if showPatch:
110 writeln(b'')
111
112 match = _match_exact(repo.root, repo.getcwd(), list(modified) + list(added) + list(removed_copy))
113 opts = mercurial.mdiff.diffopts(git=True, nodates=True, context=0, showfunc=True)
114 for d in mercurial.patch.diff(repo, ctx1.node(), ctx2.node(), match=match, opts=opts):
115 write(d)
116
117 def really_differs(repo, p1, p2, ctx, files):
118 # workaround bug in hg (present since forever):
119 # `hg status` can, for merge commits, report a file as modififed between one parent
120 # and the merge even though it isn't. `hg diff` works correctly, so remove any "modified"
121 # that has an empty diff against one of its parents
122 differs = set()
123 for path in files:
124 match = _match_exact(repo.root, repo.getcwd(), [path])
125 opts = mercurial.mdiff.diffopts(git=True, nodates=True, context=0, showfunc=True)
126
127 diff1 = mercurial.patch.diff(repo, p1.node(), ctx.node(), match=match, opts=opts)
128 diff2 = mercurial.patch.diff(repo, p2.node(), ctx.node(), match=match, opts=opts)
129 if len(list(diff1)) > 0 and len(list(diff2)) > 0:
130 differs.add(path)
131
132 return differs
133
134 cmdtable = {}
135 if hasattr(mercurial, 'registrar') and hasattr(mercurial.registrar, 'command'):
138 command = mercurial.cmdutil.command(cmdtable)
139 else:
140 def command(name, options, synopsis):
141 def decorator(func):
142 cmdtable[name] = func, list(options), synopsis
143 return func
144 return decorator
145
146 if hasattr(mercurial, 'utils') and hasattr(mercurial.utils, 'dateutil'):
147 datestr = mercurial.utils.dateutil.datestr
148 else:
149 datestr = mercurial.util.datestr
150
151 if hasattr(mercurial, 'scmutil'):
152 revsingle = mercurial.scmutil.revsingle
153 revrange = mercurial.scmutil.revrange
154 else:
155 revsingle = mercurial.cmdutil.revsingle
156 revrange = mercurial.cmdutil.revrange
157
158 @command(b'diff-git-raw', [(b'', b'patch', False, b'')], b'hg diff-git-raw rev1 [rev2]')
159 def diff_git_raw(ui, repo, rev1, rev2=None, **opts):
160 ctx1 = revsingle(repo, rev1)
161
162 if rev2 != None:
163 ctx2 = revsingle(repo, rev2)
164 status = repo.status(ctx1, ctx2)
165 else:
166 ctx2 = mercurial.context.workingctx(repo)
167 status = repo.status(ctx1)
168
169 modified, added, removed = [set(l) for l in status[:3]]
170 _diff_git_raw(repo, ctx1, ctx2, modified, added, removed, opts['patch'])
171
172 @command(b'log-git', [(b'', b'reverse', False, b''), (b'l', b'limit', -1, b'')], b'hg log-git <revisions>')
173 def log_git(ui, repo, revs=None, **opts):
174 if len(repo) == 0:
175 return
176
177 if revs == None:
178 if opts['reverse']:
179 revs = b'0:tip'
180 else:
181 revs = b'tip:0'
182
183 limit = opts['limit']
184 i = 0
185 for r in revrange(repo, [revs]):
186 ctx = repo[r]
187
188 __dump_metadata(ctx)
189 parents = ctx.parents()
190
191 if len(parents) == 1:
192 modified, added, removed = [set(l) for l in repo.status(parents[0], ctx)[:3]]
193 _diff_git_raw(repo, parents[0], ctx, modified, added, removed, True)
194 else:
195 p1 = parents[0]
196 p2 = parents[1]
197
198 modified_p1, added_p1, removed_p1 = [set(l) for l in repo.status(p1, ctx)[:3]]
199 modified_p2, added_p2, removed_p2 = [set(l) for l in repo.status(p2, ctx)[:3]]
200
201 added_both = added_p1 & added_p2
202 modified_both = modified_p1 & modified_p2
203 removed_both = removed_p1 & removed_p2
204
205 combined_modified_p1 = modified_both | (modified_p1 & added_p2)
206 combined_added_p1 = added_both | (added_p1 & modified_p2)
207 combined_modified_p2 = modified_both | (modified_p2 & added_p1)
208 combined_added_p2 = added_both | (added_p2 & modified_p1)
209
210 combined_modified_p1 = really_differs(repo, p1, p2, ctx, combined_modified_p1)
211 combined_added_p1 = really_differs(repo, p1, p2, ctx, combined_added_p1)
212 combined_modified_p2 = really_differs(repo, p1, p2, ctx, combined_modified_p2)
213 combined_added_p2 = really_differs(repo, p1, p2, ctx, combined_added_p2)
214
215 _diff_git_raw(repo, p1, ctx, combined_modified_p1, combined_added_p1, removed_both, True)
216 writeln(b'#@!_-=&')
217 _diff_git_raw(repo, p2, ctx, combined_modified_p2, combined_added_p2, removed_both, True)
218
219 i += 1
220 if i == limit:
221 break
222
223 def __dump_metadata(ctx):
224 writeln(b'#@!_-=&')
225 writeln(ctx.hex())
226 writeln(int_to_str(ctx.rev()))
227 writeln(ctx.branch())
228
229 parents = ctx.parents()
230 writeln(b' '.join([p.hex() for p in parents]))
231 writeln(b' '.join([int_to_str(p.rev()) for p in parents]))
232
233 writeln(ctx.user())
234 date = datestr(ctx.date(), format=b'%Y-%m-%d %H:%M:%S%z')
235 writeln(date)
236
237 description = ctx.description()
238 writeln(int_to_str(len(description)))
239 write(description)
240
241 def __dump(repo, start, end):
242 for rev in range(start, end):
243 ctx = revsingle(repo, rev)
244
245 __dump_metadata(ctx)
246 parents = ctx.parents()
247
248 modified, added, removed = repo.status(parents[0], ctx)[:3]
249 writeln(int_to_str(len(modified)))
250 writeln(int_to_str(len(added)))
251 writeln(int_to_str(len(removed)))
252
253 for filename in added + modified:
254 fctx = ctx.filectx(filename)
255
256 writeln(filename)
257 writeln(b' '.join(fctx.flags()))
258
259 content = fctx.data()
260 writeln(int_to_str(len(content)))
261 write(content)
262
263 for filename in removed:
264 writeln(filename)
265
266 def pretxnclose(ui, repo, **kwargs):
267 start = revsingle(repo, kwargs['node'])
268 end = revsingle(repo, kwargs['node_last'])
269 __dump(repo, start.rev(), end.rev() + 1)
270
271 @command(b'dump', [], b'hg dump')
272 def dump(ui, repo, **opts):
273 __dump(repo, 0, len(repo))
274
275 @command(b'metadata', [], b'hg metadata')
276 def dump(ui, repo, revs=None, **opts):
277 if revs == None:
278 revs = b"0:tip"
279
280 for r in revrange(repo, [revs]):
281 ctx = repo[r]
282 __dump_metadata(ctx)
283
284 @command(b'ls-tree', [], b'hg ls-tree')
285 def ls_tree(ui, repo, rev, **opts):
286 nullHash = b'0' * 40
287 ctx = revsingle(repo, rev)
288 for filename in ctx.manifest():
289 fctx = ctx.filectx(filename)
290 if b'x' in fctx.flags():
291 write(b'100755 blob ')
292 else:
293 write(b'100644 blob ')
294 write(nullHash)
295 write(b'\t')
296 writeln(filename)
297
298 @command(b'ls-remote', [], b'hg ls-remote PATH')
299 def ls_remote(ui, repo, path, **opts):
300 peer = mercurial.hg.peer(ui or repo, opts, ui.expandpath(path))
301 for branch, heads in peer.branchmap().iteritems():
302 for head in heads:
303 write(mercurial.node.hex(head))
304 write(b"\t")
305 writeln(branch)
|