1# githelp.py - Try to map Git commands to Mercurial equivalents.
2#
3# Copyright 2013 Facebook, Inc.
4#
5# This software may be used and distributed according to the terms of the
6# GNU General Public License version 2 or any later version.
7"""try mapping git commands to Mercurial commands
8
9Tries to map a given git command to a Mercurial command:
10
11  $ hg githelp -- git checkout master
12  hg update master
13
14If an unknown command or parameter combination is detected, an error is
15produced.
16"""
17
18from __future__ import absolute_import
19
20import getopt
21import re
22
23from mercurial.i18n import _
24from mercurial import (
25    encoding,
26    error,
27    fancyopts,
28    pycompat,
29    registrar,
30    scmutil,
31)
32from mercurial.utils import procutil
33
34# Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for
35# extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
36# be specifying the version(s) of Mercurial they are tested with, or
37# leave the attribute unspecified.
38testedwith = b'ships-with-hg-core'
39
40cmdtable = {}
41command = registrar.command(cmdtable)
42
43
44def convert(s):
45    if s.startswith(b"origin/"):
46        return s[7:]
47    if b'HEAD' in s:
48        s = s.replace(b'HEAD', b'.')
49    # HEAD~ in git is .~1 in mercurial
50    s = re.sub(b'~$', b'~1', s)
51    return s
52
53
54@command(
55    b'githelp|git',
56    [],
57    _(b'hg githelp'),
58    helpcategory=command.CATEGORY_HELP,
59    helpbasic=True,
60)
61def githelp(ui, repo, *args, **kwargs):
62    """suggests the Mercurial equivalent of the given git command
63
64    Usage: hg githelp -- <git command>
65    """
66
67    if len(args) == 0 or (len(args) == 1 and args[0] == b'git'):
68        raise error.Abort(
69            _(b'missing git command - usage: hg githelp -- <git command>')
70        )
71
72    if args[0] == b'git':
73        args = args[1:]
74
75    cmd = args[0]
76    if not cmd in gitcommands:
77        raise error.Abort(_(b"error: unknown git command %s") % cmd)
78
79    ui.pager(b'githelp')
80    args = args[1:]
81    return gitcommands[cmd](ui, repo, *args, **kwargs)
82
83
84def parseoptions(ui, cmdoptions, args):
85    cmdoptions = list(cmdoptions)
86    opts = {}
87    args = list(args)
88    while True:
89        try:
90            args = fancyopts.fancyopts(list(args), cmdoptions, opts, True)
91            break
92        except getopt.GetoptError as ex:
93            if "requires argument" in ex.msg:
94                raise
95            if ('--' + ex.opt) in ex.msg:
96                flag = b'--' + pycompat.bytestr(ex.opt)
97            elif ('-' + ex.opt) in ex.msg:
98                flag = b'-' + pycompat.bytestr(ex.opt)
99            else:
100                raise error.Abort(
101                    _(b"unknown option %s") % pycompat.bytestr(ex.opt)
102                )
103            try:
104                args.remove(flag)
105            except Exception:
106                msg = _(b"unknown option '%s' packed with other options")
107                hint = _(b"please try passing the option as its own flag: -%s")
108                raise error.Abort(
109                    msg % pycompat.bytestr(ex.opt),
110                    hint=hint % pycompat.bytestr(ex.opt),
111                )
112
113            ui.warn(_(b"ignoring unknown option %s\n") % flag)
114
115    args = list([convert(x) for x in args])
116    opts = dict(
117        [
118            (k, convert(v)) if isinstance(v, bytes) else (k, v)
119            for k, v in pycompat.iteritems(opts)
120        ]
121    )
122
123    return args, opts
124
125
126class Command(object):
127    def __init__(self, name):
128        self.name = name
129        self.args = []
130        self.opts = {}
131
132    def __bytes__(self):
133        cmd = b"hg " + self.name
134        if self.opts:
135            for k, values in sorted(pycompat.iteritems(self.opts)):
136                for v in values:
137                    if v:
138                        if isinstance(v, int):
139                            fmt = b' %s %d'
140                        else:
141                            fmt = b' %s %s'
142
143                        cmd += fmt % (k, v)
144                    else:
145                        cmd += b" %s" % (k,)
146        if self.args:
147            cmd += b" "
148            cmd += b" ".join(self.args)
149        return cmd
150
151    __str__ = encoding.strmethod(__bytes__)
152
153    def append(self, value):
154        self.args.append(value)
155
156    def extend(self, values):
157        self.args.extend(values)
158
159    def __setitem__(self, key, value):
160        values = self.opts.setdefault(key, [])
161        values.append(value)
162
163    def __and__(self, other):
164        return AndCommand(self, other)
165
166
167class AndCommand(object):
168    def __init__(self, left, right):
169        self.left = left
170        self.right = right
171
172    def __str__(self):
173        return b"%s && %s" % (self.left, self.right)
174
175    def __and__(self, other):
176        return AndCommand(self, other)
177
178
179def add(ui, repo, *args, **kwargs):
180    cmdoptions = [
181        (b'A', b'all', None, b''),
182        (b'p', b'patch', None, b''),
183    ]
184    args, opts = parseoptions(ui, cmdoptions, args)
185
186    if opts.get(b'patch'):
187        ui.status(
188            _(
189                b"note: Mercurial will commit when complete, "
190                b"as there is no staging area in Mercurial\n\n"
191            )
192        )
193        cmd = Command(b'commit --interactive')
194    else:
195        cmd = Command(b"add")
196
197        if not opts.get(b'all'):
198            cmd.extend(args)
199        else:
200            ui.status(
201                _(
202                    b"note: use hg addremove to remove files that have "
203                    b"been deleted\n\n"
204                )
205            )
206
207    ui.status((bytes(cmd)), b"\n")
208
209
210def am(ui, repo, *args, **kwargs):
211    cmdoptions = []
212    parseoptions(ui, cmdoptions, args)
213    cmd = Command(b'import')
214    ui.status(bytes(cmd), b"\n")
215
216
217def apply(ui, repo, *args, **kwargs):
218    cmdoptions = [
219        (b'p', b'p', int, b''),
220        (b'', b'directory', b'', b''),
221    ]
222    args, opts = parseoptions(ui, cmdoptions, args)
223
224    cmd = Command(b'import --no-commit')
225    if opts.get(b'p'):
226        cmd[b'-p'] = opts.get(b'p')
227    if opts.get(b'directory'):
228        cmd[b'--prefix'] = opts.get(b'directory')
229    cmd.extend(args)
230
231    ui.status((bytes(cmd)), b"\n")
232
233
234def bisect(ui, repo, *args, **kwargs):
235    ui.status(_(b"see 'hg help bisect' for how to use bisect\n\n"))
236
237
238def blame(ui, repo, *args, **kwargs):
239    cmdoptions = []
240    args, opts = parseoptions(ui, cmdoptions, args)
241    cmd = Command(b'annotate -udl')
242    cmd.extend([convert(v) for v in args])
243    ui.status((bytes(cmd)), b"\n")
244
245
246def branch(ui, repo, *args, **kwargs):
247    cmdoptions = [
248        (b'', b'set-upstream', None, b''),
249        (b'', b'set-upstream-to', b'', b''),
250        (b'd', b'delete', None, b''),
251        (b'D', b'delete', None, b''),
252        (b'm', b'move', None, b''),
253        (b'M', b'move', None, b''),
254    ]
255    args, opts = parseoptions(ui, cmdoptions, args)
256
257    cmd = Command(b"bookmark")
258
259    if opts.get(b'set_upstream') or opts.get(b'set_upstream_to'):
260        ui.status(_(b"Mercurial has no concept of upstream branches\n"))
261        return
262    elif opts.get(b'delete'):
263        cmd = Command(b"strip")
264        for branch in args:
265            cmd[b'-B'] = branch
266        else:
267            cmd[b'-B'] = None
268    elif opts.get(b'move'):
269        if len(args) > 0:
270            if len(args) > 1:
271                old = args.pop(0)
272            else:
273                # shell command to output the active bookmark for the active
274                # revision
275                old = b'`hg log -T"{activebookmark}" -r .`'
276        else:
277            raise error.Abort(_(b'missing newbranch argument'))
278        new = args[0]
279        cmd[b'-m'] = old
280        cmd.append(new)
281    else:
282        if len(args) > 1:
283            cmd[b'-r'] = args[1]
284            cmd.append(args[0])
285        elif len(args) == 1:
286            cmd.append(args[0])
287    ui.status((bytes(cmd)), b"\n")
288
289
290def ispath(repo, string):
291    """
292    The first argument to git checkout can either be a revision or a path. Let's
293    generally assume it's a revision, unless it's obviously a path. There are
294    too many ways to spell revisions in git for us to reasonably catch all of
295    them, so let's be conservative.
296    """
297    if scmutil.isrevsymbol(repo, string):
298        # if it's definitely a revision let's not even check if a file of the
299        # same name exists.
300        return False
301
302    cwd = repo.getcwd()
303    if cwd == b'':
304        repopath = string
305    else:
306        repopath = cwd + b'/' + string
307
308    exists = repo.wvfs.exists(repopath)
309    if exists:
310        return True
311
312    manifest = repo[b'.'].manifest()
313
314    didexist = (repopath in manifest) or manifest.hasdir(repopath)
315
316    return didexist
317
318
319def checkout(ui, repo, *args, **kwargs):
320    cmdoptions = [
321        (b'b', b'branch', b'', b''),
322        (b'B', b'branch', b'', b''),
323        (b'f', b'force', None, b''),
324        (b'p', b'patch', None, b''),
325    ]
326    paths = []
327    if b'--' in args:
328        sepindex = args.index(b'--')
329        paths.extend(args[sepindex + 1 :])
330        args = args[:sepindex]
331
332    args, opts = parseoptions(ui, cmdoptions, args)
333
334    rev = None
335    if args and ispath(repo, args[0]):
336        paths = args + paths
337    elif args:
338        rev = args[0]
339        paths = args[1:] + paths
340
341    cmd = Command(b'update')
342
343    if opts.get(b'force'):
344        if paths or rev:
345            cmd[b'-C'] = None
346
347    if opts.get(b'patch'):
348        cmd = Command(b'revert')
349        cmd[b'-i'] = None
350
351    if opts.get(b'branch'):
352        if len(args) == 0:
353            cmd = Command(b'bookmark')
354            cmd.append(opts.get(b'branch'))
355        else:
356            cmd.append(args[0])
357            bookcmd = Command(b'bookmark')
358            bookcmd.append(opts.get(b'branch'))
359            cmd = cmd & bookcmd
360    # if there is any path argument supplied, use revert instead of update
361    elif len(paths) > 0:
362        ui.status(_(b"note: use --no-backup to avoid creating .orig files\n\n"))
363        cmd = Command(b'revert')
364        if opts.get(b'patch'):
365            cmd[b'-i'] = None
366        if rev:
367            cmd[b'-r'] = rev
368        cmd.extend(paths)
369    elif rev:
370        if opts.get(b'patch'):
371            cmd[b'-r'] = rev
372        else:
373            cmd.append(rev)
374    elif opts.get(b'force'):
375        cmd = Command(b'revert')
376        cmd[b'--all'] = None
377    else:
378        raise error.Abort(_(b"a commit must be specified"))
379
380    ui.status((bytes(cmd)), b"\n")
381
382
383def cherrypick(ui, repo, *args, **kwargs):
384    cmdoptions = [
385        (b'', b'continue', None, b''),
386        (b'', b'abort', None, b''),
387        (b'e', b'edit', None, b''),
388    ]
389    args, opts = parseoptions(ui, cmdoptions, args)
390
391    cmd = Command(b'graft')
392
393    if opts.get(b'edit'):
394        cmd[b'--edit'] = None
395    if opts.get(b'continue'):
396        cmd[b'--continue'] = None
397    elif opts.get(b'abort'):
398        ui.status(_(b"note: hg graft does not have --abort\n\n"))
399        return
400    else:
401        cmd.extend(args)
402
403    ui.status((bytes(cmd)), b"\n")
404
405
406def clean(ui, repo, *args, **kwargs):
407    cmdoptions = [
408        (b'd', b'd', None, b''),
409        (b'f', b'force', None, b''),
410        (b'x', b'x', None, b''),
411    ]
412    args, opts = parseoptions(ui, cmdoptions, args)
413
414    cmd = Command(b'purge')
415    if opts.get(b'x'):
416        cmd[b'--all'] = None
417    cmd.extend(args)
418
419    ui.status((bytes(cmd)), b"\n")
420
421
422def clone(ui, repo, *args, **kwargs):
423    cmdoptions = [
424        (b'', b'bare', None, b''),
425        (b'n', b'no-checkout', None, b''),
426        (b'b', b'branch', b'', b''),
427    ]
428    args, opts = parseoptions(ui, cmdoptions, args)
429
430    if len(args) == 0:
431        raise error.Abort(_(b"a repository to clone must be specified"))
432
433    cmd = Command(b'clone')
434    cmd.append(args[0])
435    if len(args) > 1:
436        cmd.append(args[1])
437
438    if opts.get(b'bare'):
439        cmd[b'-U'] = None
440        ui.status(
441            _(
442                b"note: Mercurial does not have bare clones. "
443                b"-U will clone the repo without checking out a commit\n\n"
444            )
445        )
446    elif opts.get(b'no_checkout'):
447        cmd[b'-U'] = None
448
449    if opts.get(b'branch'):
450        cocmd = Command(b"update")
451        cocmd.append(opts.get(b'branch'))
452        cmd = cmd & cocmd
453
454    ui.status((bytes(cmd)), b"\n")
455
456
457def commit(ui, repo, *args, **kwargs):
458    cmdoptions = [
459        (b'a', b'all', None, b''),
460        (b'm', b'message', b'', b''),
461        (b'p', b'patch', None, b''),
462        (b'C', b'reuse-message', b'', b''),
463        (b'F', b'file', b'', b''),
464        (b'', b'author', b'', b''),
465        (b'', b'date', b'', b''),
466        (b'', b'amend', None, b''),
467        (b'', b'no-edit', None, b''),
468    ]
469    args, opts = parseoptions(ui, cmdoptions, args)
470
471    cmd = Command(b'commit')
472    if opts.get(b'patch'):
473        cmd = Command(b'commit --interactive')
474
475    if opts.get(b'amend'):
476        if opts.get(b'no_edit'):
477            cmd = Command(b'amend')
478        else:
479            cmd[b'--amend'] = None
480
481    if opts.get(b'reuse_message'):
482        cmd[b'-M'] = opts.get(b'reuse_message')
483
484    if opts.get(b'message'):
485        cmd[b'-m'] = b"'%s'" % (opts.get(b'message'),)
486
487    if opts.get(b'all'):
488        ui.status(
489            _(
490                b"note: Mercurial doesn't have a staging area, "
491                b"so there is no --all. -A will add and remove files "
492                b"for you though.\n\n"
493            )
494        )
495
496    if opts.get(b'file'):
497        cmd[b'-l'] = opts.get(b'file')
498
499    if opts.get(b'author'):
500        cmd[b'-u'] = opts.get(b'author')
501
502    if opts.get(b'date'):
503        cmd[b'-d'] = opts.get(b'date')
504
505    cmd.extend(args)
506
507    ui.status((bytes(cmd)), b"\n")
508
509
510def deprecated(ui, repo, *args, **kwargs):
511    ui.warn(
512        _(
513            b'this command has been deprecated in the git project, '
514            b'thus isn\'t supported by this tool\n\n'
515        )
516    )
517
518
519def diff(ui, repo, *args, **kwargs):
520    cmdoptions = [
521        (b'a', b'all', None, b''),
522        (b'', b'cached', None, b''),
523        (b'R', b'reverse', None, b''),
524    ]
525    args, opts = parseoptions(ui, cmdoptions, args)
526
527    cmd = Command(b'diff')
528
529    if opts.get(b'cached'):
530        ui.status(
531            _(
532                b'note: Mercurial has no concept of a staging area, '
533                b'so --cached does nothing\n\n'
534            )
535        )
536
537    if opts.get(b'reverse'):
538        cmd[b'--reverse'] = None
539
540    for a in list(args):
541        args.remove(a)
542        try:
543            repo.revs(a)
544            cmd[b'-r'] = a
545        except Exception:
546            cmd.append(a)
547
548    ui.status((bytes(cmd)), b"\n")
549
550
551def difftool(ui, repo, *args, **kwargs):
552    ui.status(
553        _(
554            b'Mercurial does not enable external difftool by default. You '
555            b'need to enable the extdiff extension in your .hgrc file by adding\n'
556            b'extdiff =\n'
557            b'to the [extensions] section and then running\n\n'
558            b'hg extdiff -p <program>\n\n'
559            b'See \'hg help extdiff\' and \'hg help -e extdiff\' for more '
560            b'information.\n'
561        )
562    )
563
564
565def fetch(ui, repo, *args, **kwargs):
566    cmdoptions = [
567        (b'', b'all', None, b''),
568        (b'f', b'force', None, b''),
569    ]
570    args, opts = parseoptions(ui, cmdoptions, args)
571
572    cmd = Command(b'pull')
573
574    if len(args) > 0:
575        cmd.append(args[0])
576        if len(args) > 1:
577            ui.status(
578                _(
579                    b"note: Mercurial doesn't have refspecs. "
580                    b"-r can be used to specify which commits you want to "
581                    b"pull. -B can be used to specify which bookmark you "
582                    b"want to pull.\n\n"
583                )
584            )
585            for v in args[1:]:
586                if v in repo._bookmarks:
587                    cmd[b'-B'] = v
588                else:
589                    cmd[b'-r'] = v
590
591    ui.status((bytes(cmd)), b"\n")
592
593
594def grep(ui, repo, *args, **kwargs):
595    cmdoptions = []
596    args, opts = parseoptions(ui, cmdoptions, args)
597
598    cmd = Command(b'grep')
599
600    # For basic usage, git grep and hg grep are the same. They both have the
601    # pattern first, followed by paths.
602    cmd.extend(args)
603
604    ui.status((bytes(cmd)), b"\n")
605
606
607def init(ui, repo, *args, **kwargs):
608    cmdoptions = []
609    args, opts = parseoptions(ui, cmdoptions, args)
610
611    cmd = Command(b'init')
612
613    if len(args) > 0:
614        cmd.append(args[0])
615
616    ui.status((bytes(cmd)), b"\n")
617
618
619def log(ui, repo, *args, **kwargs):
620    cmdoptions = [
621        (b'', b'follow', None, b''),
622        (b'', b'decorate', None, b''),
623        (b'n', b'number', b'', b''),
624        (b'1', b'1', None, b''),
625        (b'', b'pretty', b'', b''),
626        (b'', b'format', b'', b''),
627        (b'', b'oneline', None, b''),
628        (b'', b'stat', None, b''),
629        (b'', b'graph', None, b''),
630        (b'p', b'patch', None, b''),
631        (b'G', b'grep-diff', b'', b''),
632        (b'S', b'pickaxe-regex', b'', b''),
633    ]
634    args, opts = parseoptions(ui, cmdoptions, args)
635    grep_pat = opts.get(b'grep_diff') or opts.get(b'pickaxe_regex')
636    if grep_pat:
637        cmd = Command(b'grep')
638        cmd[b'--diff'] = grep_pat
639        ui.status(b'%s\n' % bytes(cmd))
640        return
641
642    ui.status(
643        _(
644            b'note: -v prints the entire commit message like Git does. To '
645            b'print just the first line, drop the -v.\n\n'
646        )
647    )
648    ui.status(
649        _(
650            b"note: see hg help revset for information on how to filter "
651            b"log output\n\n"
652        )
653    )
654
655    cmd = Command(b'log')
656    cmd[b'-v'] = None
657
658    if opts.get(b'number'):
659        cmd[b'-l'] = opts.get(b'number')
660    if opts.get(b'1'):
661        cmd[b'-l'] = b'1'
662    if opts.get(b'stat'):
663        cmd[b'--stat'] = None
664    if opts.get(b'graph'):
665        cmd[b'-G'] = None
666    if opts.get(b'patch'):
667        cmd[b'-p'] = None
668
669    if opts.get(b'pretty') or opts.get(b'format') or opts.get(b'oneline'):
670        format = opts.get(b'format', b'')
671        if b'format:' in format:
672            ui.status(
673                _(
674                    b"note: --format format:??? equates to Mercurial's "
675                    b"--template. See hg help templates for more info.\n\n"
676                )
677            )
678            cmd[b'--template'] = b'???'
679        else:
680            ui.status(
681                _(
682                    b"note: --pretty/format/oneline equate to Mercurial's "
683                    b"--style or --template. See hg help templates for "
684                    b"more info.\n\n"
685                )
686            )
687            cmd[b'--style'] = b'???'
688
689    if len(args) > 0:
690        if b'..' in args[0]:
691            since, until = args[0].split(b'..')
692            cmd[b'-r'] = b"'%s::%s'" % (since, until)
693            del args[0]
694        cmd.extend(args)
695
696    ui.status((bytes(cmd)), b"\n")
697
698
699def lsfiles(ui, repo, *args, **kwargs):
700    cmdoptions = [
701        (b'c', b'cached', None, b''),
702        (b'd', b'deleted', None, b''),
703        (b'm', b'modified', None, b''),
704        (b'o', b'others', None, b''),
705        (b'i', b'ignored', None, b''),
706        (b's', b'stage', None, b''),
707        (b'z', b'_zero', None, b''),
708    ]
709    args, opts = parseoptions(ui, cmdoptions, args)
710
711    if (
712        opts.get(b'modified')
713        or opts.get(b'deleted')
714        or opts.get(b'others')
715        or opts.get(b'ignored')
716    ):
717        cmd = Command(b'status')
718        if opts.get(b'deleted'):
719            cmd[b'-d'] = None
720        if opts.get(b'modified'):
721            cmd[b'-m'] = None
722        if opts.get(b'others'):
723            cmd[b'-o'] = None
724        if opts.get(b'ignored'):
725            cmd[b'-i'] = None
726    else:
727        cmd = Command(b'files')
728    if opts.get(b'stage'):
729        ui.status(
730            _(
731                b"note: Mercurial doesn't have a staging area, ignoring "
732                b"--stage\n"
733            )
734        )
735    if opts.get(b'_zero'):
736        cmd[b'-0'] = None
737    cmd.append(b'.')
738    for include in args:
739        cmd[b'-I'] = procutil.shellquote(include)
740
741    ui.status((bytes(cmd)), b"\n")
742
743
744def merge(ui, repo, *args, **kwargs):
745    cmdoptions = []
746    args, opts = parseoptions(ui, cmdoptions, args)
747
748    cmd = Command(b'merge')
749
750    if len(args) > 0:
751        cmd.append(args[len(args) - 1])
752
753    ui.status((bytes(cmd)), b"\n")
754
755
756def mergebase(ui, repo, *args, **kwargs):
757    cmdoptions = []
758    args, opts = parseoptions(ui, cmdoptions, args)
759
760    if len(args) != 2:
761        args = [b'A', b'B']
762
763    cmd = Command(
764        b"log -T '{node}\\n' -r 'ancestor(%s,%s)'" % (args[0], args[1])
765    )
766
767    ui.status(
768        _(b'note: ancestors() is part of the revset language\n'),
769        _(b"(learn more about revsets with 'hg help revsets')\n\n"),
770    )
771    ui.status((bytes(cmd)), b"\n")
772
773
774def mergetool(ui, repo, *args, **kwargs):
775    cmdoptions = []
776    args, opts = parseoptions(ui, cmdoptions, args)
777
778    cmd = Command(b"resolve")
779
780    if len(args) == 0:
781        cmd[b'--all'] = None
782    cmd.extend(args)
783    ui.status((bytes(cmd)), b"\n")
784
785
786def mv(ui, repo, *args, **kwargs):
787    cmdoptions = [
788        (b'f', b'force', None, b''),
789        (b'n', b'dry-run', None, b''),
790    ]
791    args, opts = parseoptions(ui, cmdoptions, args)
792
793    cmd = Command(b'mv')
794    cmd.extend(args)
795
796    if opts.get(b'force'):
797        cmd[b'-f'] = None
798    if opts.get(b'dry_run'):
799        cmd[b'-n'] = None
800
801    ui.status((bytes(cmd)), b"\n")
802
803
804def pull(ui, repo, *args, **kwargs):
805    cmdoptions = [
806        (b'', b'all', None, b''),
807        (b'f', b'force', None, b''),
808        (b'r', b'rebase', None, b''),
809    ]
810    args, opts = parseoptions(ui, cmdoptions, args)
811
812    cmd = Command(b'pull')
813    cmd[b'--rebase'] = None
814
815    if len(args) > 0:
816        cmd.append(args[0])
817        if len(args) > 1:
818            ui.status(
819                _(
820                    b"note: Mercurial doesn't have refspecs. "
821                    b"-r can be used to specify which commits you want to "
822                    b"pull. -B can be used to specify which bookmark you "
823                    b"want to pull.\n\n"
824                )
825            )
826            for v in args[1:]:
827                if v in repo._bookmarks:
828                    cmd[b'-B'] = v
829                else:
830                    cmd[b'-r'] = v
831
832    ui.status((bytes(cmd)), b"\n")
833
834
835def push(ui, repo, *args, **kwargs):
836    cmdoptions = [
837        (b'', b'all', None, b''),
838        (b'f', b'force', None, b''),
839    ]
840    args, opts = parseoptions(ui, cmdoptions, args)
841
842    cmd = Command(b'push')
843
844    if len(args) > 0:
845        cmd.append(args[0])
846        if len(args) > 1:
847            ui.status(
848                _(
849                    b"note: Mercurial doesn't have refspecs. "
850                    b"-r can be used to specify which commits you want "
851                    b"to push. -B can be used to specify which bookmark "
852                    b"you want to push.\n\n"
853                )
854            )
855            for v in args[1:]:
856                if v in repo._bookmarks:
857                    cmd[b'-B'] = v
858                else:
859                    cmd[b'-r'] = v
860
861    if opts.get(b'force'):
862        cmd[b'-f'] = None
863
864    ui.status((bytes(cmd)), b"\n")
865
866
867def rebase(ui, repo, *args, **kwargs):
868    cmdoptions = [
869        (b'', b'all', None, b''),
870        (b'i', b'interactive', None, b''),
871        (b'', b'onto', b'', b''),
872        (b'', b'abort', None, b''),
873        (b'', b'continue', None, b''),
874        (b'', b'skip', None, b''),
875    ]
876    args, opts = parseoptions(ui, cmdoptions, args)
877
878    if opts.get(b'interactive'):
879        ui.status(
880            _(
881                b"note: hg histedit does not perform a rebase. "
882                b"It just edits history.\n\n"
883            )
884        )
885        cmd = Command(b'histedit')
886        if len(args) > 0:
887            ui.status(
888                _(
889                    b"also note: 'hg histedit' will automatically detect"
890                    b" your stack, so no second argument is necessary\n\n"
891                )
892            )
893        ui.status((bytes(cmd)), b"\n")
894        return
895
896    if opts.get(b'skip'):
897        cmd = Command(b'revert --all -r .')
898        ui.status((bytes(cmd)), b"\n")
899
900    cmd = Command(b'rebase')
901
902    if opts.get(b'continue') or opts.get(b'skip'):
903        cmd[b'--continue'] = None
904    if opts.get(b'abort'):
905        cmd[b'--abort'] = None
906
907    if opts.get(b'onto'):
908        ui.status(
909            _(
910                b"note: if you're trying to lift a commit off one branch, "
911                b"try hg rebase -d <destination commit> -s <commit to be "
912                b"lifted>\n\n"
913            )
914        )
915        cmd[b'-d'] = convert(opts.get(b'onto'))
916        if len(args) < 2:
917            raise error.Abort(_(b"expected format: git rebase --onto X Y Z"))
918        cmd[b'-s'] = b"'::%s - ::%s'" % (convert(args[1]), convert(args[0]))
919    else:
920        if len(args) == 1:
921            cmd[b'-d'] = convert(args[0])
922        elif len(args) == 2:
923            cmd[b'-d'] = convert(args[0])
924            cmd[b'-b'] = convert(args[1])
925
926    ui.status((bytes(cmd)), b"\n")
927
928
929def reflog(ui, repo, *args, **kwargs):
930    cmdoptions = [
931        (b'', b'all', None, b''),
932    ]
933    args, opts = parseoptions(ui, cmdoptions, args)
934
935    cmd = Command(b'journal')
936    if opts.get(b'all'):
937        cmd[b'--all'] = None
938    if len(args) > 0:
939        cmd.append(args[0])
940
941    ui.status(bytes(cmd), b"\n\n")
942    ui.status(
943        _(
944            b"note: in hg commits can be deleted from repo but we always"
945            b" have backups\n"
946        )
947    )
948
949
950def reset(ui, repo, *args, **kwargs):
951    cmdoptions = [
952        (b'', b'soft', None, b''),
953        (b'', b'hard', None, b''),
954        (b'', b'mixed', None, b''),
955    ]
956    args, opts = parseoptions(ui, cmdoptions, args)
957
958    commit = convert(args[0] if len(args) > 0 else b'.')
959    hard = opts.get(b'hard')
960
961    if opts.get(b'mixed'):
962        ui.status(
963            _(
964                b'note: --mixed has no meaning since Mercurial has no '
965                b'staging area\n\n'
966            )
967        )
968    if opts.get(b'soft'):
969        ui.status(
970            _(
971                b'note: --soft has no meaning since Mercurial has no '
972                b'staging area\n\n'
973            )
974        )
975
976    cmd = Command(b'update')
977    if hard:
978        cmd.append(b'--clean')
979
980    cmd.append(commit)
981
982    ui.status((bytes(cmd)), b"\n")
983
984
985def revert(ui, repo, *args, **kwargs):
986    cmdoptions = []
987    args, opts = parseoptions(ui, cmdoptions, args)
988
989    if len(args) > 1:
990        ui.status(
991            _(
992                b"note: hg backout doesn't support multiple commits at "
993                b"once\n\n"
994            )
995        )
996
997    cmd = Command(b'backout')
998    if args:
999        cmd.append(args[0])
1000
1001    ui.status((bytes(cmd)), b"\n")
1002
1003
1004def revparse(ui, repo, *args, **kwargs):
1005    cmdoptions = [
1006        (b'', b'show-cdup', None, b''),
1007        (b'', b'show-toplevel', None, b''),
1008    ]
1009    args, opts = parseoptions(ui, cmdoptions, args)
1010
1011    if opts.get(b'show_cdup') or opts.get(b'show_toplevel'):
1012        cmd = Command(b'root')
1013        if opts.get(b'show_cdup'):
1014            ui.status(_(b"note: hg root prints the root of the repository\n\n"))
1015        ui.status((bytes(cmd)), b"\n")
1016    else:
1017        ui.status(_(b"note: see hg help revset for how to refer to commits\n"))
1018
1019
1020def rm(ui, repo, *args, **kwargs):
1021    cmdoptions = [
1022        (b'f', b'force', None, b''),
1023        (b'n', b'dry-run', None, b''),
1024    ]
1025    args, opts = parseoptions(ui, cmdoptions, args)
1026
1027    cmd = Command(b'rm')
1028    cmd.extend(args)
1029
1030    if opts.get(b'force'):
1031        cmd[b'-f'] = None
1032    if opts.get(b'dry_run'):
1033        cmd[b'-n'] = None
1034
1035    ui.status((bytes(cmd)), b"\n")
1036
1037
1038def show(ui, repo, *args, **kwargs):
1039    cmdoptions = [
1040        (b'', b'name-status', None, b''),
1041        (b'', b'pretty', b'', b''),
1042        (b'U', b'unified', int, b''),
1043    ]
1044    args, opts = parseoptions(ui, cmdoptions, args)
1045
1046    if opts.get(b'name_status'):
1047        if opts.get(b'pretty') == b'format:':
1048            cmd = Command(b'status')
1049            cmd[b'--change'] = b'.'
1050        else:
1051            cmd = Command(b'log')
1052            cmd.append(b'--style status')
1053            cmd.append(b'-r .')
1054    elif len(args) > 0:
1055        if ispath(repo, args[0]):
1056            cmd = Command(b'cat')
1057        else:
1058            cmd = Command(b'export')
1059        cmd.extend(args)
1060        if opts.get(b'unified'):
1061            cmd.append(b'--config diff.unified=%d' % (opts[b'unified'],))
1062    elif opts.get(b'unified'):
1063        cmd = Command(b'export')
1064        cmd.append(b'--config diff.unified=%d' % (opts[b'unified'],))
1065    else:
1066        cmd = Command(b'export')
1067
1068    ui.status((bytes(cmd)), b"\n")
1069
1070
1071def stash(ui, repo, *args, **kwargs):
1072    cmdoptions = [
1073        (b'p', b'patch', None, b''),
1074    ]
1075    args, opts = parseoptions(ui, cmdoptions, args)
1076
1077    cmd = Command(b'shelve')
1078    action = args[0] if len(args) > 0 else None
1079
1080    if action == b'list':
1081        cmd[b'-l'] = None
1082        if opts.get(b'patch'):
1083            cmd[b'-p'] = None
1084    elif action == b'show':
1085        if opts.get(b'patch'):
1086            cmd[b'-p'] = None
1087        else:
1088            cmd[b'--stat'] = None
1089        if len(args) > 1:
1090            cmd.append(args[1])
1091    elif action == b'clear':
1092        cmd[b'--cleanup'] = None
1093    elif action == b'drop':
1094        cmd[b'-d'] = None
1095        if len(args) > 1:
1096            cmd.append(args[1])
1097        else:
1098            cmd.append(b'<shelve name>')
1099    elif action == b'pop' or action == b'apply':
1100        cmd = Command(b'unshelve')
1101        if len(args) > 1:
1102            cmd.append(args[1])
1103        if action == b'apply':
1104            cmd[b'--keep'] = None
1105    elif action == b'branch' or action == b'create':
1106        ui.status(
1107            _(
1108                b"note: Mercurial doesn't have equivalents to the "
1109                b"git stash branch or create actions\n\n"
1110            )
1111        )
1112        return
1113    else:
1114        if len(args) > 0:
1115            if args[0] != b'save':
1116                cmd[b'--name'] = args[0]
1117            elif len(args) > 1:
1118                cmd[b'--name'] = args[1]
1119
1120    ui.status((bytes(cmd)), b"\n")
1121
1122
1123def status(ui, repo, *args, **kwargs):
1124    cmdoptions = [
1125        (b'', b'ignored', None, b''),
1126    ]
1127    args, opts = parseoptions(ui, cmdoptions, args)
1128
1129    cmd = Command(b'status')
1130    cmd.extend(args)
1131
1132    if opts.get(b'ignored'):
1133        cmd[b'-i'] = None
1134
1135    ui.status((bytes(cmd)), b"\n")
1136
1137
1138def svn(ui, repo, *args, **kwargs):
1139    if not args:
1140        raise error.Abort(_(b'missing svn command'))
1141    svncmd = args[0]
1142    if svncmd not in gitsvncommands:
1143        raise error.Abort(_(b'unknown git svn command "%s"') % svncmd)
1144
1145    args = args[1:]
1146    return gitsvncommands[svncmd](ui, repo, *args, **kwargs)
1147
1148
1149def svndcommit(ui, repo, *args, **kwargs):
1150    cmdoptions = []
1151    parseoptions(ui, cmdoptions, args)
1152
1153    cmd = Command(b'push')
1154
1155    ui.status((bytes(cmd)), b"\n")
1156
1157
1158def svnfetch(ui, repo, *args, **kwargs):
1159    cmdoptions = []
1160    parseoptions(ui, cmdoptions, args)
1161
1162    cmd = Command(b'pull')
1163    cmd.append(b'default-push')
1164
1165    ui.status((bytes(cmd)), b"\n")
1166
1167
1168def svnfindrev(ui, repo, *args, **kwargs):
1169    cmdoptions = []
1170    args, opts = parseoptions(ui, cmdoptions, args)
1171
1172    if not args:
1173        raise error.Abort(_(b'missing find-rev argument'))
1174
1175    cmd = Command(b'log')
1176    cmd[b'-r'] = args[0]
1177
1178    ui.status((bytes(cmd)), b"\n")
1179
1180
1181def svnrebase(ui, repo, *args, **kwargs):
1182    cmdoptions = [
1183        (b'l', b'local', None, b''),
1184    ]
1185    parseoptions(ui, cmdoptions, args)
1186
1187    pullcmd = Command(b'pull')
1188    pullcmd.append(b'default-push')
1189    rebasecmd = Command(b'rebase')
1190    rebasecmd.append(b'tip')
1191
1192    cmd = pullcmd & rebasecmd
1193
1194    ui.status((bytes(cmd)), b"\n")
1195
1196
1197def tag(ui, repo, *args, **kwargs):
1198    cmdoptions = [
1199        (b'f', b'force', None, b''),
1200        (b'l', b'list', None, b''),
1201        (b'd', b'delete', None, b''),
1202    ]
1203    args, opts = parseoptions(ui, cmdoptions, args)
1204
1205    if opts.get(b'list'):
1206        cmd = Command(b'tags')
1207    else:
1208        cmd = Command(b'tag')
1209
1210        if not args:
1211            raise error.Abort(_(b'missing tag argument'))
1212
1213        cmd.append(args[0])
1214        if len(args) > 1:
1215            cmd[b'-r'] = args[1]
1216
1217        if opts.get(b'delete'):
1218            cmd[b'--remove'] = None
1219
1220        if opts.get(b'force'):
1221            cmd[b'-f'] = None
1222
1223    ui.status((bytes(cmd)), b"\n")
1224
1225
1226gitcommands = {
1227    b'add': add,
1228    b'am': am,
1229    b'apply': apply,
1230    b'bisect': bisect,
1231    b'blame': blame,
1232    b'branch': branch,
1233    b'checkout': checkout,
1234    b'cherry-pick': cherrypick,
1235    b'clean': clean,
1236    b'clone': clone,
1237    b'commit': commit,
1238    b'diff': diff,
1239    b'difftool': difftool,
1240    b'fetch': fetch,
1241    b'grep': grep,
1242    b'init': init,
1243    b'log': log,
1244    b'ls-files': lsfiles,
1245    b'merge': merge,
1246    b'merge-base': mergebase,
1247    b'mergetool': mergetool,
1248    b'mv': mv,
1249    b'pull': pull,
1250    b'push': push,
1251    b'rebase': rebase,
1252    b'reflog': reflog,
1253    b'reset': reset,
1254    b'revert': revert,
1255    b'rev-parse': revparse,
1256    b'rm': rm,
1257    b'show': show,
1258    b'stash': stash,
1259    b'status': status,
1260    b'svn': svn,
1261    b'tag': tag,
1262    b'whatchanged': deprecated,
1263}
1264
1265gitsvncommands = {
1266    b'dcommit': svndcommit,
1267    b'fetch': svnfetch,
1268    b'find-rev': svnfindrev,
1269    b'rebase': svnrebase,
1270}
1271