comparison mercurial/logcmdutil.py @ 51139:204af2aa4931

logcmdutil: return structured diffstat data for json
author zegervdv <zeger@vandevan.net>
date Thu, 07 Sep 2023 08:39:21 +0200
parents dcb2581e33be
children d6e5bec550f1
comparison
equal deleted inserted replaced
51138:c845479fc64d 51139:204af2aa4931
96 return wctx 96 return wctx
97 else: 97 else:
98 return ctx.p1() 98 return ctx.p1()
99 99
100 100
101 def get_diff_chunks(
102 ui,
103 repo,
104 diffopts,
105 ctx1,
106 ctx2,
107 match,
108 changes=None,
109 stat=False,
110 prefix=b'',
111 root=b'',
112 hunksfilterfn=None,
113 ):
114 if root:
115 relroot = pathutil.canonpath(repo.root, repo.getcwd(), root)
116 else:
117 relroot = b''
118 copysourcematch = None
119
120 def compose(f, g):
121 return lambda x: f(g(x))
122
123 def pathfn(f):
124 return posixpath.join(prefix, f)
125
126 if relroot != b'':
127 # XXX relative roots currently don't work if the root is within a
128 # subrepo
129 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
130 uirelroot = uipathfn(pathfn(relroot))
131 relroot += b'/'
132 for matchroot in match.files():
133 if not matchroot.startswith(relroot):
134 ui.warn(
135 _(b'warning: %s not inside relative root %s\n')
136 % (uipathfn(pathfn(matchroot)), uirelroot)
137 )
138
139 relrootmatch = scmutil.match(ctx2, pats=[relroot], default=b'path')
140 match = matchmod.intersectmatchers(match, relrootmatch)
141 copysourcematch = relrootmatch
142
143 checkroot = repo.ui.configbool(
144 b'devel', b'all-warnings'
145 ) or repo.ui.configbool(b'devel', b'check-relroot')
146
147 def relrootpathfn(f):
148 if checkroot and not f.startswith(relroot):
149 raise AssertionError(
150 b"file %s doesn't start with relroot %s" % (f, relroot)
151 )
152 return f[len(relroot) :]
153
154 pathfn = compose(relrootpathfn, pathfn)
155
156 if stat:
157 diffopts = diffopts.copy(context=0, noprefix=False)
158 # If an explicit --root was given, don't respect ui.relative-paths
159 if not relroot:
160 pathfn = compose(scmutil.getuipathfn(repo), pathfn)
161
162 return ctx2.diff(
163 ctx1,
164 match,
165 changes,
166 opts=diffopts,
167 pathfn=pathfn,
168 copysourcematch=copysourcematch,
169 hunksfilterfn=hunksfilterfn,
170 )
171
172
101 def diffordiffstat( 173 def diffordiffstat(
102 ui, 174 ui,
103 repo, 175 repo,
104 diffopts, 176 diffopts,
105 ctx1, 177 ctx1,
113 root=b'', 185 root=b'',
114 listsubrepos=False, 186 listsubrepos=False,
115 hunksfilterfn=None, 187 hunksfilterfn=None,
116 ): 188 ):
117 '''show diff or diffstat.''' 189 '''show diff or diffstat.'''
118 if root: 190
119 relroot = pathutil.canonpath(repo.root, repo.getcwd(), root) 191 chunks = get_diff_chunks(
120 else: 192 ui,
121 relroot = b'' 193 repo,
122 copysourcematch = None 194 diffopts,
123 195 ctx1,
124 def compose(f, g): 196 ctx2,
125 return lambda x: f(g(x)) 197 match,
126 198 changes=changes,
127 def pathfn(f): 199 stat=stat,
128 return posixpath.join(prefix, f) 200 prefix=prefix,
129 201 root=root,
130 if relroot != b'': 202 hunksfilterfn=hunksfilterfn,
131 # XXX relative roots currently don't work if the root is within a 203 )
132 # subrepo
133 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
134 uirelroot = uipathfn(pathfn(relroot))
135 relroot += b'/'
136 for matchroot in match.files():
137 if not matchroot.startswith(relroot):
138 ui.warn(
139 _(b'warning: %s not inside relative root %s\n')
140 % (uipathfn(pathfn(matchroot)), uirelroot)
141 )
142
143 relrootmatch = scmutil.match(ctx2, pats=[relroot], default=b'path')
144 match = matchmod.intersectmatchers(match, relrootmatch)
145 copysourcematch = relrootmatch
146
147 checkroot = repo.ui.configbool(
148 b'devel', b'all-warnings'
149 ) or repo.ui.configbool(b'devel', b'check-relroot')
150
151 def relrootpathfn(f):
152 if checkroot and not f.startswith(relroot):
153 raise AssertionError(
154 b"file %s doesn't start with relroot %s" % (f, relroot)
155 )
156 return f[len(relroot) :]
157
158 pathfn = compose(relrootpathfn, pathfn)
159 204
160 if stat: 205 if stat:
161 diffopts = diffopts.copy(context=0, noprefix=False) 206 diffopts = diffopts.copy(context=0, noprefix=False)
162 width = 80 207 width = 80
163 if not ui.plain(): 208 if not ui.plain():
164 width = ui.termwidth() - graphwidth 209 width = ui.termwidth() - graphwidth
165 # If an explicit --root was given, don't respect ui.relative-paths
166 if not relroot:
167 pathfn = compose(scmutil.getuipathfn(repo), pathfn)
168
169 chunks = ctx2.diff(
170 ctx1,
171 match,
172 changes,
173 opts=diffopts,
174 pathfn=pathfn,
175 copysourcematch=copysourcematch,
176 hunksfilterfn=hunksfilterfn,
177 )
178 210
179 if fp is not None or ui.canwritewithoutlabels(): 211 if fp is not None or ui.canwritewithoutlabels():
180 out = fp or ui 212 out = fp or ui
181 if stat: 213 if stat:
182 chunks = [patch.diffstat(util.iterlines(chunks), width=width)] 214 chunks = [patch.diffstat(util.iterlines(chunks), width=width)]
247 stat=stat, 279 stat=stat,
248 graphwidth=graphwidth, 280 graphwidth=graphwidth,
249 hunksfilterfn=self._makehunksfilter(ctx), 281 hunksfilterfn=self._makehunksfilter(ctx),
250 ) 282 )
251 283
284 def getdiffstats(self, ui, ctx, diffopts, stat=False):
285 chunks = get_diff_chunks(
286 ui,
287 ctx.repo(),
288 diffopts,
289 diff_parent(ctx),
290 ctx,
291 match=self._makefilematcher(ctx),
292 stat=stat,
293 hunksfilterfn=self._makehunksfilter(ctx),
294 )
295
296 diffdata = []
297 for filename, additions, removals, binary in patch.diffstatdata(
298 util.iterlines(chunks)
299 ):
300 diffdata.append(
301 {
302 b"name": filename,
303 b"additions": additions,
304 b"removals": removals,
305 b"binary": binary,
306 }
307 )
308
309 return diffdata
310
252 311
253 def changesetlabels(ctx): 312 def changesetlabels(ctx):
254 labels = [b'log.changeset', b'changeset.%s' % ctx.phasestr()] 313 labels = [b'log.changeset', b'changeset.%s' % ctx.phasestr()]
255 if ctx.obsolete(): 314 if ctx.obsolete():
256 labels.append(b'changeset.obsolete') 315 labels.append(b'changeset.obsolete')
523 fm.data( 582 fm.data(
524 copies=fm.formatdict(copies or {}, key=b'name', value=b'source') 583 copies=fm.formatdict(copies or {}, key=b'name', value=b'source')
525 ) 584 )
526 585
527 if self._includestat or b'diffstat' in datahint: 586 if self._includestat or b'diffstat' in datahint:
528 self.ui.pushbuffer() 587 data = self._differ.getdiffstats(
529 self._differ.showdiff(self.ui, ctx, self._diffopts, stat=True) 588 self.ui, ctx, self._diffopts, stat=True
530 fm.data(diffstat=self.ui.popbuffer()) 589 )
590 fm.data(diffstat=fm.formatlist(data, name=b'diffstat'))
531 if self._includediff or b'diff' in datahint: 591 if self._includediff or b'diff' in datahint:
532 self.ui.pushbuffer() 592 self.ui.pushbuffer()
533 self._differ.showdiff(self.ui, ctx, self._diffopts, stat=False) 593 self._differ.showdiff(self.ui, ctx, self._diffopts, stat=False)
534 fm.data(diff=self.ui.popbuffer()) 594 fm.data(diff=self.ui.popbuffer())
535 595