PageRenderTime 151ms CodeModel.GetById 7ms app.highlight 120ms RepoModel.GetById 14ms app.codeStats 0ms

/tortoisehg/hgtk/hgtk.py

https://bitbucket.org/tortoisehg/hgtk/
Python | 832 lines | 824 code | 1 blank | 7 comment | 10 complexity | ecb6846f382e24ae5e8e3e013cf0da5d MD5 | raw file
  1# hgtk.py - front-end script for TortoiseHg dialogs
  2#
  3# Copyright 2008 Steve Borho <steve@borho.org>
  4# Copyright 2008 TK Soh <teekaysoh@gmail.com>
  5#
  6# This software may be used and distributed according to the terms of the
  7# GNU General Public License version 2, incorporated herein by reference.
  8
  9shortlicense = '''
 10Copyright (C) 2008-2010 Steve Borho <steve@borho.org> and others.
 11This is free software; see the source for copying conditions.  There is NO
 12warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 13'''
 14
 15import os
 16import pdb
 17import sys
 18import subprocess
 19import traceback
 20import gtk
 21import gobject
 22
 23import mercurial.ui as _ui
 24from mercurial import hg, util, fancyopts, cmdutil, extensions, error
 25
 26from tortoisehg.util.i18n import agettext as _
 27from tortoisehg.util import hglib, paths, shlib, i18n
 28from tortoisehg.util import version as thgversion
 29try:
 30    from tortoisehg.util.config import nofork as config_nofork
 31except ImportError:
 32    config_nofork = None
 33
 34from tortoisehg.hgtk import textview
 35
 36try:
 37    import win32con
 38    openflags = win32con.CREATE_NO_WINDOW
 39except ImportError:
 40    openflags = 0
 41
 42nonrepo_commands = '''userconfig shellconfig clone debugcomplete init
 43about help version thgstatus serve'''
 44
 45# Add TortoiseHg signals, hooked to key accelerators in gtklib
 46for sig in ('copy-clipboard', 'thg-diff', 'thg-parent', 'thg-rename',
 47            'thg-revision', 'mq-move-up', 'mq-move-down', 'mq-move-top',
 48            'mq-move-bottom', 'mq-pop', 'mq-push'):
 49    gobject.signal_new(sig, gtk.TreeView,
 50        gobject.SIGNAL_ACTION, gobject.TYPE_NONE, ())
 51for sig in ('thg-exit', 'thg-close', 'thg-refresh', 'thg-accept',
 52            'thg-reflow', 'status-scroll-down', 'status-scroll-up', 
 53            'status-next-file', 'status-previous-file', 
 54            'status-select-all', 'status-next-page', 
 55            'status-previous-page'):
 56    gobject.signal_new(sig, gtk.Window,
 57            gobject.SIGNAL_ACTION, gobject.TYPE_NONE, ())
 58for sig in ('thg-close', 'thg-new'):
 59    gobject.signal_new(sig, gtk.Notebook,
 60        gobject.SIGNAL_ACTION, gobject.TYPE_NONE, ())
 61for sig in ('thg-undo', 'thg-redo'):
 62    gobject.signal_new(sig, textview.UndoableTextView,
 63        gobject.SIGNAL_ACTION, gobject.TYPE_NONE, ())
 64
 65gtkmainalive = False
 66def dispatch(args):
 67    "run the command specified in args"
 68    try:
 69        u = _ui.ui()
 70        if '--traceback' in args:
 71            u.setconfig('ui', 'traceback', 'on')
 72        if '--debugger' in args:
 73            pdb.set_trace()
 74        return _runcatch(u, args)
 75    except SystemExit:
 76        pass
 77    except KeyboardInterrupt:
 78        print _('\nCaught keyboard interrupt, aborting.\n')
 79    except:
 80        from tortoisehg.hgtk.bugreport import run
 81        if '--debugger' in args:
 82            pdb.post_mortem(sys.exc_info()[2])
 83        error = traceback.format_exc()
 84        opts = {}
 85        opts['cmd'] = ' '.join(sys.argv[1:])
 86        opts['error'] = error
 87        opts['nofork'] = True
 88        if gtkmainalive:
 89            dlg = run(u, **opts)
 90            dlg.display()
 91            dlg.show_all()
 92        else:
 93            gtkrun(run, u, **opts)
 94
 95origwdir = os.getcwd()
 96def portable_fork(ui, opts):
 97    if 'THG_HGTK_SPAWN' in os.environ or (
 98        not opts.get('fork') and opts.get('nofork')):
 99        return
100    elif ui.configbool('tortoisehg', 'hgtkfork', None) is not None:
101        if not ui.configbool('tortoisehg', 'hgtkfork'):
102            return
103    elif config_nofork:
104        return
105    # Spawn background process and exit
106    if hasattr(sys, "frozen"):
107        args = sys.argv
108    else:
109        args = [sys.executable] + sys.argv
110    os.environ['THG_HGTK_SPAWN'] = '1'
111    cmdline = subprocess.list2cmdline(args)
112    os.chdir(origwdir)
113    subprocess.Popen(cmdline,
114                     creationflags=openflags,
115                     shell=True)
116    sys.exit(0)
117
118def get_list_from_file(filename):
119    try:
120        if filename == '-':
121            lines = [ x.replace("\n", "") for x in sys.stdin.readlines() ]
122        else:
123            fd = open(filename, "r")
124            lines = [ x.replace("\n", "") for x in fd.readlines() ]
125            fd.close()
126            os.unlink(filename)
127    except IOError, e:
128        sys.stderr.write(_('can not read file "%s". Ignored.\n') % filename)
129        return []
130
131    # Convert absolute file paths to repo/cwd canonical
132    cwd = os.getcwd()
133    root = paths.find_root(cwd)
134    if not root:
135        return lines
136    if cwd == root:
137        cwd_rel = ''
138    else:
139        cwd_rel = cwd[len(root+os.sep):] + os.sep
140    files = []
141    for f in lines:
142        try:
143            cpath = util.canonpath(root, cwd, f)
144            # canonpath will abort on .hg/ paths
145        except util.Abort:
146            continue
147        if cpath.startswith(cwd_rel):
148            cpath = cpath[len(cwd_rel):]
149            files.append(cpath)
150        else:
151            files.append(f)
152    return files
153
154def _parse(ui, args):
155    options = {}
156    cmdoptions = {}
157
158    try:
159        args = fancyopts.fancyopts(args, globalopts, options)
160    except fancyopts.getopt.GetoptError, inst:
161        raise error.ParseError(None, inst)
162
163    if args:
164        alias, args = args[0], args[1:]
165        aliases, i = cmdutil.findcmd(alias, table, ui.config("ui", "strict"))
166        for a in aliases:
167            if a.startswith(alias):
168                alias = a
169                break
170        cmd = aliases[0]
171        c = list(i[1])
172    else:
173        alias = None
174        cmd = None
175        c = []
176
177    # combine global options into local
178    for o in globalopts:
179        c.append((o[0], o[1], options[o[1]], o[3]))
180
181    try:
182        args = fancyopts.fancyopts(args, c, cmdoptions)
183    except fancyopts.getopt.GetoptError, inst:
184        raise error.ParseError(cmd, inst)
185
186    # separate global options back out
187    for o in globalopts:
188        n = o[1]
189        options[n] = cmdoptions[n]
190        del cmdoptions[n]
191
192    listfile = options.get('listfile')
193    if listfile:
194        del options['listfile']
195        args += get_list_from_file(listfile)
196
197    return (cmd, cmd and i[0] or None, args, options, cmdoptions, alias)
198
199def _runcatch(ui, args):
200    try:
201        try:
202            return runcommand(ui, args)
203        finally:
204            ui.flush()
205    except error.ParseError, inst:
206        if inst.args[0]:
207            ui.status(_("hgtk %s: %s\n") % (inst.args[0], inst.args[1]))
208            help_(ui, inst.args[0])
209        else:
210            ui.status(_("hgtk: %s\n") % inst.args[1])
211            help_(ui, 'shortlist')
212    except error.AmbiguousCommand, inst:
213        ui.status(_("hgtk: command '%s' is ambiguous:\n    %s\n") %
214                (inst.args[0], " ".join(inst.args[1])))
215    except error.UnknownCommand, inst:
216        ui.status(_("hgtk: unknown command '%s'\n") % inst.args[0])
217        help_(ui, 'shortlist')
218    except error.RepoError, inst:
219        ui.status(_("abort: %s!\n") % inst)
220
221    return -1
222
223def runcommand(ui, args):
224    cmd, func, args, options, cmdoptions, alias = _parse(ui, args)
225    cmdoptions['alias'] = alias
226    ui.setconfig("ui", "verbose", str(bool(options["verbose"])))
227    i18n.setlanguage(ui.config('tortoisehg', 'ui.language'))
228
229    if options['help']:
230        return help_(ui, cmd)
231    elif not cmd:
232        return help_(ui, 'shortlist')
233
234    path = options['repository']
235    if path:
236        if path.startswith('bundle:'):
237            s = path[7:].split('+', 1)
238            if len(s) == 1:
239                path, bundle = os.getcwd(), s[0]
240            else:
241                path, bundle = s
242            cmdoptions['bundle'] = os.path.abspath(bundle)
243        path = ui.expandpath(path)
244        cmdoptions['repository'] = path
245        os.chdir(path)
246    if options['fork']:
247        cmdoptions['fork'] = True
248    if options['nofork'] or options['profile']:
249        cmdoptions['nofork'] = True
250    path = paths.find_root(os.getcwd())
251    if path:
252        try:
253            lui = ui.copy()
254            lui.readconfig(os.path.join(path, ".hg", "hgrc"))
255        except IOError:
256            pass
257    else:
258        lui = ui
259
260    hglib.wrapextensionsloader()  # enable blacklist of extensions
261    extensions.loadall(ui)
262
263    if options['quiet']:
264        ui.quiet = True
265
266    if cmd not in nonrepo_commands.split() and not path:
267        raise error.RepoError(_("There is no Mercurial repository here"
268                    " (.hg not found)"))
269
270    cmdoptions['mainapp'] = True
271    d = lambda: util.checksignature(func)(ui, *args, **cmdoptions)
272    return _runcommand(lui, options, cmd, d)
273
274def _runcommand(ui, options, cmd, cmdfunc):
275    def checkargs():
276        try:
277            return cmdfunc()
278        except error.SignatureError:
279            raise error.ParseError(cmd, _("invalid arguments"))
280
281    if options['profile']:
282        format = ui.config('profiling', 'format', default='text')
283
284        if not format in ['text', 'kcachegrind']:
285            ui.warn(_("unrecognized profiling format '%s'"
286                        " - Ignored\n") % format)
287            format = 'text'
288
289        output = ui.config('profiling', 'output')
290
291        if output:
292            path = ui.expandpath(output)
293            ostream = open(path, 'wb')
294        else:
295            ostream = sys.stderr
296
297        try:
298            from mercurial import lsprof
299        except ImportError:
300            raise util.Abort(_(
301                'lsprof not available - install from '
302                'http://codespeak.net/svn/user/arigo/hack/misc/lsprof/'))
303        p = lsprof.Profiler()
304        p.enable(subcalls=True)
305        try:
306            return checkargs()
307        finally:
308            p.disable()
309
310            if format == 'kcachegrind':
311                import lsprofcalltree
312                calltree = lsprofcalltree.KCacheGrind(p)
313                calltree.output(ostream)
314            else:
315                # format == 'text'
316                stats = lsprof.Stats(p.getstats())
317                stats.sort()
318                stats.pprint(top=10, file=ostream, climit=5)
319
320            if output:
321                ostream.close()
322    else:
323        return checkargs()
324
325mainwindow = None
326def thgexit(win):
327    if hasattr(mainwindow, 'should_live'):
328        if mainwindow.should_live(): return
329    mainwindow.destroy()
330
331def gtkrun(dlgfunc, ui, *args, **opts):
332    portable_fork(ui, opts)
333    win = dlgfunc(ui, *args, **opts)
334    if not win:
335        return
336    global mainwindow, gtkmainalive
337    mainwindow = win
338    if hasattr(win, 'display'):
339        win.display()
340    win.show_all()
341    if 'response' in gobject.signal_list_names(win):
342        win.connect('response', gtk.main_quit)
343    win.connect('destroy', gtk.main_quit)
344    gtk.gdk.threads_init()
345    gtk.gdk.threads_enter()
346    gtkmainalive = True
347    gtk.main()
348    gtkmainalive = False
349    gtk.gdk.threads_leave()
350
351def about(ui, *pats, **opts):
352    """about TortoiseHg"""
353    from tortoisehg.hgtk.about import run
354    gtkrun(run, ui, *pats, **opts)
355
356def add(ui, *pats, **opts):
357    """add files"""
358    from tortoisehg.hgtk.quickop import run
359    gtkrun(run, ui, *pats, **opts)
360
361def thgstatus(ui, *pats, **opts):
362    """update TortoiseHg status cache"""
363    from tortoisehg.util.thgstatus import run
364    run(ui, *pats, **opts)
365
366def clone(ui, *pats, **opts):
367    """clone tool"""
368    from tortoisehg.hgtk.clone import run
369    gtkrun(run, ui, *pats, **opts)
370
371def commit(ui, *pats, **opts):
372    """commit tool"""
373    # move cwd to repo root if repo is merged, so we can show
374    # all the changed files
375    repo = hg.repository(ui, path=paths.find_root())
376    if len(repo.parents()) > 1:
377        os.chdir(repo.root)
378        pats = []
379    from tortoisehg.hgtk.commit import run
380    gtkrun(run, ui, *pats, **opts)
381
382def shelve(ui, *pats, **opts):
383    """shelve/unshelve tool"""
384    from tortoisehg.hgtk.thgshelve import run
385    gtkrun(run, ui, *pats, **opts)
386
387def userconfig(ui, *pats, **opts):
388    """user configuration editor"""
389    from tortoisehg.hgtk.thgconfig import run
390    gtkrun(run, ui, *pats, **opts)
391
392def repoconfig(ui, *pats, **opts):
393    """repository configuration editor"""
394    from tortoisehg.hgtk.thgconfig import run
395    gtkrun(run, ui, *pats, **opts)
396
397def shellconfig(ui, *pats, **opts):
398    """Explorer extension configuration editor"""
399    from tortoisehg.hgtk.shellconf import run
400    gtkrun(run, ui, *pats, **opts)
401
402def rename(ui, *pats, **opts):
403    """rename a single file or directory"""
404    if not pats or len(pats) > 2:
405        from tortoisehg.hgtk import gdialog
406        gdialog.Prompt(_('Rename error'),
407                       _('rename takes one or two path arguments'), None).run()        
408        return 
409    from tortoisehg.hgtk.rename import run
410    gtkrun(run, ui, *pats, **opts)
411
412def guess(ui, *pats, **opts):
413    """guess previous renames or copies"""
414    from tortoisehg.hgtk.guess import run
415    gtkrun(run, ui, *pats, **opts)
416
417def datamine(ui, *pats, **opts):
418    """repository search and annotate tool"""
419    from tortoisehg.hgtk.datamine import run
420    gtkrun(run, ui, *pats, **opts)
421
422def hgignore(ui, *pats, **opts):
423    """ignore filter editor"""
424    from tortoisehg.hgtk.hgignore import run
425    gtkrun(run, ui, *pats, **opts)
426
427def hginit(ui, *pats, **opts):
428    """repository initialization tool"""
429    from tortoisehg.hgtk.hginit import run
430    gtkrun(run, ui, *pats, **opts)
431
432def log(ui, *pats, **opts):
433    """Repository Explorer (changelog viewer)"""
434    from tortoisehg.hgtk.history import run
435    gtkrun(run, ui, *pats, **opts)
436
437def merge(ui, *pats, **opts):
438    """merge tool"""
439    from tortoisehg.hgtk.merge import run
440    gtkrun(run, ui, *pats, **opts)
441
442def recovery(ui, *pats, **opts):
443    """recover, rollback & verify"""
444    from tortoisehg.hgtk.recovery import run
445    gtkrun(run, ui, *pats, **opts)
446
447def remove(ui, *pats, **opts):
448    """file status viewer in remove mode"""
449    from tortoisehg.hgtk.quickop import run
450    gtkrun(run, ui, *pats, **opts)
451
452def revert(ui, *pats, **opts):
453    """file status viewer in revert mode"""
454    from tortoisehg.hgtk.quickop import run
455    gtkrun(run, ui, *pats, **opts)
456
457def forget(ui, *pats, **opts):
458    """file status viewer in forget mode"""
459    from tortoisehg.hgtk.quickop import run
460    gtkrun(run, ui, *pats, **opts)
461
462def serve(ui, *pats, **opts):
463    """web server"""
464    from tortoisehg.hgtk.serve import run
465    if paths.find_root() == None and not (opts['web_conf'] or opts['webdir_conf']):
466        raise error.RepoError(_("There is no Mercurial repository here"
467                    " (.hg not found)"))
468    gtkrun(run, ui, *pats, **opts)
469
470def status(ui, *pats, **opts):
471    """file status & diff viewer"""
472    from tortoisehg.hgtk.status import run
473    gtkrun(run, ui, *pats, **opts)
474
475def strip(ui, *pats, **opts):
476    """strip changesets"""
477    from tortoisehg.hgtk.thgstrip import run
478    gtkrun(run, ui, *pats, **opts)
479
480def synch(ui, *pats, **opts):
481    """repository synchronization tool"""
482    from tortoisehg.hgtk.synch import run
483    cmd = opts['alias']
484    if cmd in ('push', 'outgoing', 'email'):
485        opts['pushmode'] = True
486    else:
487        opts['pushmode'] = False
488    gtkrun(run, ui, *pats, **opts)
489
490def update(ui, *pats, **opts):
491    """update/checkout tool"""
492    from tortoisehg.hgtk.update import run
493    gtkrun(run, ui, *pats, **opts)
494
495def browse(ui, *pats, **opts):
496    """browse repository state"""
497    from tortoisehg.hgtk.browse import run
498    gtkrun(run, ui, *pats, **opts)
499
500def vdiff(ui, *pats, **opts):
501    """launch configured visual diff tool"""
502    from tortoisehg.hgtk.visdiff import run
503    gtkrun(run, ui, *pats, **opts)
504
505def thgimport(ui, *pats, **opts):
506    """import patches to repository/patch queue"""
507    from tortoisehg.hgtk.thgimport import run
508    gtkrun(run, ui, *pats, **opts)
509
510def mpatch(ui, rejfile, *pats, **opts):
511    """Attempt to resolve conflicts in a .rej file"""
512    def abort(err):
513        from tortoisehg.hgtk import gdialog
514        gdialog.Prompt(_('mpatch error'), err, None).run()        
515        return None
516    if not rejfile or pats or not rejfile.endswith('.rej'):
517        return abort(_('mpatch expects *.rej file argument\n'))
518    if not os.path.exists(rejfile):
519        return abort(_('%s does not exist\n') % rejfile)
520    # Assume patch was made from repo root, and arrange ourselves thusly
521    repo = hg.repository(ui, path=paths.find_root())
522    rejfile = util.canonpath(repo.root, repo.getcwd(), rejfile)
523    os.chdir(repo.root)
524    source = rejfile[:-4]
525    if not os.path.exists(source):
526        return abort(_('%s does not exist\n') % source)
527    from tortoisehg.util import prej
528    from tortoisehg.hgtk import visdiff
529    prej.run(ui, rejfile, source, visdiff.filemerge)
530
531### help management, adapted from mercurial.commands.help_()
532def help_(ui, name=None, with_version=False, **opts):
533    """show help for a command, extension, or list of commands
534
535    With no arguments, print a list of commands and short help.
536
537    Given a command name, print help for that command.
538
539    Given an extension name, print help for that extension, and the
540    commands it provides."""
541    option_lists = []
542    textwidth = ui.termwidth() - 2
543
544    def addglobalopts(aliases):
545        if ui.verbose:
546            option_lists.append((_("global options:"), globalopts))
547            if name == 'shortlist':
548                option_lists.append((_('use "hgtk help" for the full list '
549                                       'of commands'), ()))
550        else:
551            if name == 'shortlist':
552                msg = _('use "hgtk help" for the full list of commands '
553                        'or "hgtk -v" for details')
554            elif aliases:
555                msg = _('use "hgtk -v help%s" to show aliases and '
556                        'global options') % (name and " " + name or "")
557            else:
558                msg = _('use "hgtk -v help %s" to show global options') % name
559            option_lists.append((msg, ()))
560
561    def helpcmd(name):
562        if with_version:
563            version(ui)
564            ui.write('\n')
565
566        try:
567            aliases, i = cmdutil.findcmd(name, table, False)
568        except error.AmbiguousCommand, inst:
569            select = lambda c: c.lstrip('^').startswith(inst.args[0])
570            helplist(_('list of commands:\n\n'), select)
571            return
572
573        # synopsis
574        ui.write("%s\n" % i[2])
575
576        # aliases
577        if not ui.quiet and len(aliases) > 1:
578            ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
579
580        # description
581        doc = i[0].__doc__
582        if not doc:
583            doc = _("(No help text available)")
584        if ui.quiet:
585            doc = doc.splitlines(0)[0]
586        ui.write("\n%s\n" % doc.rstrip())
587
588        if not ui.quiet:
589            # options
590            if i[1]:
591                option_lists.append((_("options:\n"), i[1]))
592
593            addglobalopts(False)
594
595    def helplist(header, select=None):
596        h = {}
597        cmds = {}
598        for c, e in table.iteritems():
599            f = c.split("|", 1)[0]
600            if select and not select(f):
601                continue
602            if (not select and name != 'shortlist' and
603                e[0].__module__ != __name__):
604                continue
605            if name == "shortlist" and not f.startswith("^"):
606                continue
607            f = f.lstrip("^")
608            if not ui.debugflag and f.startswith("debug"):
609                continue
610            doc = e[0].__doc__
611            if doc and 'DEPRECATED' in doc and not ui.verbose:
612                continue
613            #doc = gettext(doc)
614            if not doc:
615                doc = _("(no help text available)")
616            h[f] = doc.splitlines()[0].rstrip()
617            cmds[f] = c.lstrip("^")
618
619        if not h:
620            ui.status(_('no commands defined\n'))
621            return
622
623        ui.status(header)
624        fns = sorted(h)
625        m = max(map(len, fns))
626        for f in fns:
627            if ui.verbose:
628                commands = cmds[f].replace("|",", ")
629                ui.write(" %s:\n      %s\n"%(commands, h[f]))
630            else:
631                ui.write('%s\n' % (util.wrap(h[f], textwidth,
632                                             initindent=' %-*s   ' % (m, f),
633                                             hangindent=' ' * (m + 4))))
634
635        if not ui.quiet:
636            addglobalopts(True)
637
638    def helptopic(name):
639        from mercurial import help
640        for names, header, doc in help.helptable:
641            if name in names:
642                break
643        else:
644            raise error.UnknownCommand(name)
645
646        # description
647        if not doc:
648            doc = _("(No help text available)")
649        if hasattr(doc, '__call__'):
650            doc = doc()
651
652        ui.write("%s\n" % header)
653        ui.write("%s\n" % doc.rstrip())
654
655    if name and name != 'shortlist':
656        i = None
657        for f in (helpcmd, helptopic):
658            try:
659                f(name)
660                i = None
661                break
662            except error.UnknownCommand, inst:
663                i = inst
664        if i:
665            raise i
666
667    else:
668        # program name
669        if ui.verbose or with_version:
670            version(ui)
671        else:
672            ui.status(_("Hgtk - TortoiseHg's GUI tools for Mercurial SCM (Hg)\n"))
673        ui.status('\n')
674
675        # list of commands
676        if name == "shortlist":
677            header = _('basic commands:\n\n')
678        else:
679            header = _('list of commands:\n\n')
680
681        helplist(header)
682
683    # list all option lists
684    opt_output = []
685    for title, options in option_lists:
686        opt_output.append(("\n%s" % title, None))
687        for shortopt, longopt, default, desc in options:
688            if "DEPRECATED" in desc and not ui.verbose: continue
689            opt_output.append(("%2s%s" % (shortopt and "-%s" % shortopt,
690                                          longopt and " --%s" % longopt),
691                               "%s%s" % (desc,
692                                         default
693                                         and _(" (default: %s)") % default
694                                         or "")))
695
696    if opt_output:
697        opts_len = max([len(line[0]) for line in opt_output if line[1]] or [0])
698        for first, second in opt_output:
699            if second:
700                initindent = ' %-*s  ' % (opts_len, first)
701                hangindent = ' ' * (opts_len + 3)
702                ui.write('%s\n' % (util.wrap(second, textwidth,
703                                             initindent=initindent,
704                                             hangindent=hangindent)))
705            else:
706                ui.write("%s\n" % first)
707
708def version(ui, **opts):
709    """output version and copyright information"""
710    ui.write(_('TortoiseHg Dialogs (version %s), '
711               'Mercurial (version %s)\n') %
712               (hglib.fromutf(thgversion.version()), hglib.hgversion))
713    if not ui.quiet:
714        ui.write(shortlicense)
715
716def debugcomplete(ui, cmd='', **opts):
717    """output list of possible commands"""
718    if opts.get('options'):
719        options = []
720        otables = [globalopts]
721        if cmd:
722            aliases, entry = cmdutil.findcmd(cmd, table, False)
723            otables.append(entry[1])
724        for t in otables:
725            for o in t:
726                if o[0]:
727                    options.append('-%s' % o[0])
728                options.append('--%s' % o[1])
729        ui.write("%s\n" % "\n".join(options))
730        return
731
732    cmdlist = cmdutil.findpossible(cmd, table)
733    if ui.verbose:
734        cmdlist = [' '.join(c[0]) for c in cmdlist.values()]
735    ui.write("%s\n" % "\n".join(sorted(cmdlist)))
736
737def archive(ui, *pats, **opts):
738    """create an unversioned archive of a repository revision"""
739    from tortoisehg.hgtk.archive import run
740    gtkrun(run, ui, *pats, **opts)
741
742globalopts = [
743    ('R', 'repository', '',
744     _('repository root directory or symbolic path name')),
745    ('v', 'verbose', None, _('enable additional output')),
746    ('q', 'quiet', None, _('suppress output')),
747    ('h', 'help', None, _('display help and exit')),
748    ('', 'debugger', None, _('start debugger')),
749    ('', 'profile', None, _('print command execution profile')),
750    ('', 'nofork', None, _('do not fork GUI process')),
751    ('', 'fork', None, _('always fork GUI process')),
752    ('', 'listfile', '', _('read file list from file')),
753]
754
755table = {
756    "about": (about, [], _('hgtk about')),
757    "add": (add, [], _('hgtk add [FILE]...')),
758    "^clone": (clone, [],  _('hgtk clone SOURCE [DEST]')),
759    "^commit|ci": (commit,
760        [('u', 'user', '', _('record user as committer')),
761         ('d', 'date', '', _('record datecode as commit date'))],
762        _('hgtk commit [OPTIONS] [FILE]...')),
763    "^datamine|annotate|blame|grep": (datamine, [], _('hgtk datamine')),
764    "^hgignore|ignore|filter": (hgignore, [], _('hgtk hgignore [FILE]')),
765    "^init": (hginit, [], _('hgtk init [DEST]')),
766    "^log|history|explorer": (log,
767        [('l', 'limit', '', _('limit number of changes displayed'))],
768        _('hgtk log [OPTIONS] [FILE]')),
769    "merge": (merge, 
770        [('r', 'rev', '', _('revision to merge with'))],
771        _('hgtk merge')),
772    "^recovery|rollback|verify": (recovery, [], _('hgtk recovery')),
773    "^shelve|unshelve": (shelve, [], _('hgtk shelve')),
774    "synch|pull|push|incoming|outgoing|email": (synch, [], _('hgtk synch')),
775    "^status|st|diff": (status,
776        [('r', 'rev', [], _('revisions to compare'))],
777        _('hgtk status [FILE]...')),
778    "^userconfig": (userconfig,
779        [('', 'focus', '', _('field to give initial focus'))],
780        _('hgtk userconfig')),
781    "^repoconfig": (repoconfig,
782        [('', 'focus', '', _('field to give initial focus'))],
783        _('hgtk repoconfig')),
784    "^guess": (guess, [], _('hgtk guess')),
785    "remove|rm": (revert, [], _('hgtk remove [FILE]...')),
786    "rename|mv": (rename, [], _('hgtk rename SOURCE [DEST]')),
787    "revert": (revert, [], _('hgtk revert [FILE]...')),
788    "forget": (forget, [], _('hgtk forget [FILE]...')),
789    "^serve":
790        (serve,
791         [('', 'web-conf', '',
792           _('name of the hgweb config file (serve more than one repository)')),
793          ('', 'webdir-conf', '',
794           _('name of the hgweb config file (DEPRECATED)'))],
795         _('hgtk serve [OPTION]...')),
796    "thgstatus": (thgstatus,
797        [('',  'delay', None, _('wait until the second ticks over')),
798         ('n', 'notify', [], _('notify the shell for paths given')),
799         ('',  'remove', None, _('remove the status cache')),
800         ('s', 'show', None, _('show the contents of the'
801                               ' status cache (no update)')),
802         ('',  'all', None, _('udpate all repos in current dir')) ],
803        _('hgtk thgstatus [OPTION]')),
804    "^update|checkout|co": (update,
805        [('r', 'rev', [], _('revision to update'))],
806        ('hgtk update')),
807    "^vdiff": (vdiff,
808        [('c', 'change', '', _('changeset to view in diff tool')),
809         ('r', 'rev', [], _('revisions to view in diff tool')),
810         ('b', 'bundle', '', _('bundle file to preview'))],
811            _('launch visual diff tool')),
812    "^version": (version,
813        [('v', 'verbose', None, _('print license'))],
814        _('hgtk version [OPTION]')),
815    "debugcomplete": (debugcomplete,
816         [('o', 'options', None, _('show the command options'))],
817         _('[-o] CMD')),
818    "help": (help_, [], _('hgtk help [COMMAND]')),
819    "archive": (archive,
820        [('r', 'rev', '', _('revision to update'))],
821        ('hgtk archive')),
822    "strip": (strip, [], ('hgtk strip [REV]')),
823    "^mpatch": (mpatch, [], ('hgtk mpatch file.rej')),
824    "import": (thgimport,
825        [('', 'repo', False, _('import to the repository')),
826         ('', 'mq', False, _('import to the patch queue (MQ)'))],
827        _('hgtk import [OPTION] [SOURCE]...')),
828}
829
830if os.name == 'nt':
831    # TODO: extra detection to determine if shell extension is installed
832    table['shellconfig'] = (shellconfig, [], _('hgtk shellconfig'))