##// END OF EJS Templates
extdiff: support tools that can be run simultaneously
Ludovic Chabant -
r41724:a4cd77a4 default
parent child Browse files
Show More
@@ -59,6 +59,22 b' sections for diff tool arguments, when n'
59 59 [diff-tools]
60 60 kdiff3.diffargs=--L1 '$plabel1' --L2 '$clabel' $parent $child
61 61
62 If a program has a graphical interface, it might be interesting to tell
63 Mercurial about it. It will prevent the program from being mistakenly
64 used in a terminal-only environment (such as an SSH terminal session),
65 and will make :hg:`extdiff --per-file` open multiple file diffs at once
66 instead of one by one (if you still want to open file diffs one by one,
67 you can use the --confirm option).
68
69 Declaring that a tool has a graphical interface can be done with the
70 ``gui`` flag next to where ``diffargs`` are specified:
71
72 ::
73
74 [diff-tools]
75 kdiff3.diffargs=--L1 '$plabel1' --L2 '$clabel' $parent $child
76 kdiff3.gui = true
77
62 78 You can use -I/-X and list of file or directory names like normal
63 79 :hg:`diff` command. The extdiff extension makes snapshots of only
64 80 needed files, so running the external diff program will actually be
@@ -71,6 +87,7 b' import os'
71 87 import re
72 88 import shutil
73 89 import stat
90 import subprocess
74 91
75 92 from mercurial.i18n import _
76 93 from mercurial.node import (
@@ -105,11 +122,19 b" configitem('extdiff', br'opts\\..*',"
105 122 generic=True,
106 123 )
107 124
125 configitem('extdiff', br'gui\..*',
126 generic=True,
127 )
128
108 129 configitem('diff-tools', br'.*\.diffargs$',
109 130 default=None,
110 131 generic=True,
111 132 )
112 133
134 configitem('diff-tools', br'.*\.gui$',
135 generic=True,
136 )
137
113 138 # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for
114 139 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
115 140 # be specifying the version(s) of Mercurial they are tested with, or
@@ -176,13 +201,26 b' def formatcmdline(cmdline, repo_root, do'
176 201 cmdline += ' $parent1 $child'
177 202 return re.sub(regex, quote, cmdline)
178 203
179 def _runperfilediff(cmdline, repo_root, ui, do3way, confirm,
204 def _systembackground(cmd, environ=None, cwd=None):
205 ''' like 'procutil.system', but returns the Popen object directly
206 so we don't have to wait on it.
207 '''
208 cmd = procutil.quotecommand(cmd)
209 env = procutil.shellenviron(environ)
210 proc = subprocess.Popen(procutil.tonativestr(cmd),
211 shell=True, close_fds=procutil.closefds,
212 env=procutil.tonativeenv(env),
213 cwd=pycompat.rapply(procutil.tonativestr, cwd))
214 return proc
215
216 def _runperfilediff(cmdline, repo_root, ui, guitool, do3way, confirm,
180 217 commonfiles, tmproot, dir1a, dir1b,
181 218 dir2root, dir2,
182 219 rev1a, rev1b, rev2):
183 220 # Note that we need to sort the list of files because it was
184 221 # built in an "unstable" way and it's annoying to get files in a
185 222 # random order, especially when "confirm" mode is enabled.
223 waitprocs = []
186 224 totalfiles = len(commonfiles)
187 225 for idx, commonfile in enumerate(sorted(commonfiles)):
188 226 path1a = os.path.join(tmproot, dir1a, commonfile)
@@ -228,14 +266,32 b' def _runperfilediff(cmdline, repo_root, '
228 266 parent1=path1a, plabel1=label1a,
229 267 parent2=path1b, plabel2=label1b,
230 268 child=path2, clabel=label2)
231 ui.debug('running %r in %s\n' % (pycompat.bytestr(curcmdline),
232 tmproot))
233 269
270 if confirm or not guitool:
234 271 # Run the comparison program and wait for it to exit
235 272 # before we show the next file.
273 # This is because either we need to wait for confirmation
274 # from the user between each invocation, or because, as far
275 # as we know, the tool doesn't have a GUI, in which case
276 # we can't run multiple CLI programs at the same time.
277 ui.debug('running %r in %s\n' %
278 (pycompat.bytestr(curcmdline), tmproot))
236 279 ui.system(curcmdline, cwd=tmproot, blockedtag='extdiff')
280 else:
281 # Run the comparison program but don't wait, as we're
282 # going to rapid-fire each file diff and then wait on
283 # the whole group.
284 ui.debug('running %r in %s (backgrounded)\n' %
285 (pycompat.bytestr(curcmdline), tmproot))
286 proc = _systembackground(curcmdline, cwd=tmproot)
287 waitprocs.append(proc)
237 288
238 def dodiff(ui, repo, cmdline, pats, opts):
289 if waitprocs:
290 with ui.timeblockedsection('extdiff'):
291 for proc in waitprocs:
292 proc.wait()
293
294 def dodiff(ui, repo, cmdline, pats, opts, guitool=False):
239 295 '''Do the actual diff:
240 296
241 297 - copy to a temp structure if diffing 2 internal revisions
@@ -382,7 +438,8 b' def dodiff(ui, repo, cmdline, pats, opts'
382 438 else:
383 439 # Run the external tool once for each pair of files
384 440 _runperfilediff(
385 cmdline, repo.root, ui, do3way=do3way, confirm=confirm,
441 cmdline, repo.root, ui, guitool=guitool,
442 do3way=do3way, confirm=confirm,
386 443 commonfiles=common, tmproot=tmproot, dir1a=dir1a, dir1b=dir1b,
387 444 dir2root=dir2root, dir2=dir2,
388 445 rev1a=rev1a, rev1b=rev1b, rev2=rev2)
@@ -446,7 +503,13 b' def extdiff(ui, repo, *pats, **opts):'
446 503 to its parent.
447 504
448 505 The --per-file option runs the external program repeatedly on each
449 file to diff, instead of once on two directories.
506 file to diff, instead of once on two directories. By default,
507 this happens one by one, where the next file diff is open in the
508 external program only once the previous external program (for the
509 previous file diff) has exited. If the external program has a
510 graphical interface, it can open all the file diffs at once instead
511 of one by one. See :hg:`help -e extdiff` for information about how
512 to tell Mercurial that a given program has a graphical interface.
450 513
451 514 The --confirm option will prompt the user before each invocation of
452 515 the external program. It is ignored if --per-file isn't specified.
@@ -475,20 +538,22 b' class savedcmd(object):'
475 538 to its parent.
476 539 """
477 540
478 def __init__(self, path, cmdline):
541 def __init__(self, path, cmdline, isgui):
479 542 # We can't pass non-ASCII through docstrings (and path is
480 543 # in an unknown encoding anyway), but avoid double separators on
481 544 # Windows
482 545 docpath = stringutil.escapestr(path).replace(b'\\\\', b'\\')
483 546 self.__doc__ %= {r'path': pycompat.sysstr(stringutil.uirepr(docpath))}
484 547 self._cmdline = cmdline
548 self._isgui = isgui
485 549
486 550 def __call__(self, ui, repo, *pats, **opts):
487 551 opts = pycompat.byteskwargs(opts)
488 552 options = ' '.join(map(procutil.shellquote, opts['option']))
489 553 if options:
490 554 options = ' ' + options
491 return dodiff(ui, repo, self._cmdline + options, pats, opts)
555 return dodiff(ui, repo, self._cmdline + options, pats, opts,
556 guitool=self._isgui)
492 557
493 558 def uisetup(ui):
494 559 for cmd, path in ui.configitems('extdiff'):
@@ -503,7 +568,8 b' def uisetup(ui):'
503 568 cmdline = procutil.shellquote(path)
504 569 if diffopts:
505 570 cmdline += ' ' + diffopts
506 elif cmd.startswith('opts.'):
571 isgui = ui.configbool('extdiff', 'gui.' + cmd)
572 elif cmd.startswith('opts.') or cmd.startswith('gui.'):
507 573 continue
508 574 else:
509 575 if path:
@@ -517,15 +583,20 b' def uisetup(ui):'
517 583 path = filemerge.findexternaltool(ui, cmd) or cmd
518 584 cmdline = procutil.shellquote(path)
519 585 diffopts = False
586 isgui = ui.configbool('extdiff', 'gui.' + cmd)
520 587 # look for diff arguments in [diff-tools] then [merge-tools]
521 588 if not diffopts:
522 args = ui.config('diff-tools', cmd+'.diffargs') or \
523 ui.config('merge-tools', cmd+'.diffargs')
589 key = cmd + '.diffargs'
590 for section in ('diff-tools', 'merge-tools'):
591 args = ui.config(section, key)
524 592 if args:
525 593 cmdline += ' ' + args
594 if isgui is None:
595 isgui = ui.configbool(section, cmd + '.gui') or False
596 break
526 597 command(cmd, extdiffopts[:], _('hg %s [OPTION]... [FILE]...') % cmd,
527 598 helpcategory=command.CATEGORY_FILE_CONTENTS,
528 inferrepo=True)(savedcmd(path, cmdline))
599 inferrepo=True)(savedcmd(path, cmdline, isgui))
529 600
530 601 # tell hggettext to extract docstrings from these functions:
531 602 i18nfunctions = [savedcmd]
@@ -22,6 +22,10 b' Should diff cloned directories:'
22 22 > opts.falabala = diffing
23 23 > cmd.edspace = echo
24 24 > opts.edspace = "name <user@example.com>"
25 > alabalaf =
26 > [merge-tools]
27 > alabalaf.executable = echo
28 > alabalaf.diffargs = diffing
25 29 > EOF
26 30
27 31 $ hg falabala
@@ -144,6 +148,42 b' Test --per-file option:'
144 148 diffing */extdiff.*/a.46c0e4daeb72/b a.81906f2b98ac/b (glob) (no-windows !)
145 149 [1]
146 150
151 Test --per-file option for gui tool:
152
153 $ hg --config extdiff.gui.alabalaf=True alabalaf -c 6 --per-file --debug
154 diffing "*\\extdiff.*\\a.46c0e4daeb72\\a" "a.81906f2b98ac\\a" (glob) (windows !)
155 diffing */extdiff.*/a.46c0e4daeb72/a a.81906f2b98ac/a (glob) (no-windows !)
156 diffing "*\\extdiff.*\\a.46c0e4daeb72\\b" "a.81906f2b98ac\\b" (glob) (windows !)
157 diffing */extdiff.*/a.46c0e4daeb72/b a.81906f2b98ac/b (glob) (no-windows !)
158 making snapshot of 2 files from rev 46c0e4daeb72
159 a
160 b
161 making snapshot of 2 files from rev 81906f2b98ac
162 a
163 b
164 running '* diffing * *' in * (backgrounded) (glob)
165 running '* diffing * *' in * (backgrounded) (glob)
166 cleaning up temp directory
167 [1]
168
169 Test --per-file option for gui tool again:
170
171 $ hg --config merge-tools.alabalaf.gui=True alabalaf -c 6 --per-file --debug
172 diffing "*\\extdiff.*\\a.46c0e4daeb72\\a" "a.81906f2b98ac\\a" (glob) (windows !)
173 diffing */extdiff.*/a.46c0e4daeb72/a a.81906f2b98ac/a (glob) (no-windows !)
174 diffing "*\\extdiff.*\\a.46c0e4daeb72\\b" "a.81906f2b98ac\\b" (glob) (windows !)
175 diffing */extdiff.*/a.46c0e4daeb72/b a.81906f2b98ac/b (glob) (no-windows !)
176 making snapshot of 2 files from rev 46c0e4daeb72
177 a
178 b
179 making snapshot of 2 files from rev 81906f2b98ac
180 a
181 b
182 running '* diffing * *' in * (backgrounded) (glob)
183 running '* diffing * *' in * (backgrounded) (glob)
184 cleaning up temp directory
185 [1]
186
147 187 Test --per-file and --confirm options:
148 188
149 189 $ hg --config ui.interactive=True falabala -c 6 --per-file --confirm <<EOF
@@ -823,7 +823,13 b' Extension module help vs command help:'
823 823 the working directory files are compared to its parent.
824 824
825 825 The --per-file option runs the external program repeatedly on each file to
826 diff, instead of once on two directories.
826 diff, instead of once on two directories. By default, this happens one by
827 one, where the next file diff is open in the external program only once
828 the previous external program (for the previous file diff) has exited. If
829 the external program has a graphical interface, it can open all the file
830 diffs at once instead of one by one. See 'hg help -e extdiff' for
831 information about how to tell Mercurial that a given program has a
832 graphical interface.
827 833
828 834 The --confirm option will prompt the user before each invocation of the
829 835 external program. It is ignored if --per-file isn't specified.
@@ -905,6 +911,20 b' Extension module help vs command help:'
905 911 [diff-tools]
906 912 kdiff3.diffargs=--L1 '$plabel1' --L2 '$clabel' $parent $child
907 913
914 If a program has a graphical interface, it might be interesting to tell
915 Mercurial about it. It will prevent the program from being mistakenly used in
916 a terminal-only environment (such as an SSH terminal session), and will make
917 'hg extdiff --per-file' open multiple file diffs at once instead of one by one
918 (if you still want to open file diffs one by one, you can use the --confirm
919 option).
920
921 Declaring that a tool has a graphical interface can be done with the "gui"
922 flag next to where "diffargs" are specified:
923
924 [diff-tools]
925 kdiff3.diffargs=--L1 '$plabel1' --L2 '$clabel' $parent $child
926 kdiff3.gui = true
927
908 928 You can use -I/-X and list of file or directory names like normal 'hg diff'
909 929 command. The extdiff extension makes snapshots of only needed files, so
910 930 running the external diff program will actually be pretty fast (at least
General Comments 0
You need to be logged in to leave comments. Login now