File: commands.py

package info (click to toggle)
hg-git 1.2.0-1
  • links: PTS, VCS
  • area: main
  • in suites: sid, trixie
  • size: 1,244 kB
  • sloc: python: 8,702; sh: 185; makefile: 23
file content (220 lines) | stat: -rw-r--r-- 6,641 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
# git.py - git server bridge
#
# Copyright 2008 Scott Chacon <schacon at gmail dot com>
#   also some code (and help) borrowed from durin42
#
# This software may be used and distributed according to the terms
# of the GNU General Public License, incorporated herein by reference.

# global modules
import os

from dulwich import porcelain

from mercurial.node import hex, nullhex
from mercurial.i18n import _
from mercurial import (
    cmdutil,
    error,
    exthelper,
    pycompat,
    registrar,
    scmutil,
)

# local modules
from . import verify

eh = exthelper.exthelper()


@eh.command(
    b'git-import|gimport',
    helpcategory=registrar.command.CATEGORY_IMPORT_EXPORT,
)
def gimport(ui, repo, remote_name=None):
    '''import commits from Git to Mercurial (ADVANCED)

    This command is equivalent to pulling from a Git source, but
    without actually accessing the network. Internally, hg-git relies
    on a local, cached git repository containing changes equivalent to
    the Mercurial repository. If you modify that Git repository
    somehow, use this command to import those changes.

    '''
    with repo.wlock():
        repo.githandler.import_commits(remote_name)


@eh.command(
    b'git-export|gexport',
    helpcategory=registrar.command.CATEGORY_IMPORT_EXPORT,
)
def gexport(ui, repo):
    '''export commits from Mercurial to Git (ADVANCED)

    This command is equivalent to pushing to a Git source, but without
    actually access the network. Internally, hg-git relies on a local,
    cached git repository containing changes equivalent to the
    Mercurial repository. If you wish to see what the Git commits
    would be, use this command to export those changes. As an example,
    it ensures that all changesets have a corresponding Git node.

    '''
    repo.githandler.export_commits()


@eh.command(
    b'git-verify|gverify',
    [
        (b'r', b'rev', b'', _(b'revision to verify'), _(b'REV')),
        (b'c', b'fsck', False, _(b'verify repository integrity as well')),
    ],
    _(b'[-r REV]'),
    helpcategory=registrar.command.CATEGORY_MAINTENANCE,
)
def gverify(ui, repo, **opts):
    '''verify that a Mercurial rev matches the corresponding Git rev

    Given a Mercurial revision that has a corresponding Git revision in the
    map, this attempts to answer whether that revision has the same contents as
    the corresponding Git revision.

    '''

    if opts.get('fsck'):
        for badsha, e in porcelain.fsck(repo.githandler.git):
            raise error.Abort(b'git repository is corrupt!')

    ctx = scmutil.revsingle(repo, opts.get('rev'), b'.')
    return verify.verify(ui, repo, ctx)


@eh.command(b'git-cleanup', helpcategory=registrar.command.CATEGORY_MAINTENANCE)
def git_cleanup(ui, repo):
    '''clean up Git commit map after history editing'''
    new_map = []
    gh = repo.githandler
    for line in gh.vfs(gh.map_file):
        gitsha, hgsha = line.strip().split(b' ', 1)
        if hgsha in repo:
            ui.debug(b'keeping GIT:%s -> HG:%s\n' % (gitsha, hgsha))
            new_map.append(b'%s %s\n' % (gitsha, hgsha))
        else:
            ui.note(b'dropping GIT:%s -> HG:%s\n' % (gitsha, hgsha))
    with repo.githandler.store_repo.wlock():
        f = gh.vfs(gh.map_file, b'wb')
        f.writelines(new_map)

    for ref, gitsha in gh.git.refs.as_dict().items():
        if gitsha in gh.git:
            ui.debug(b'keeping %s -> GIT:%s\n' % (ref, gitsha))
        else:
            ui.note(b'dropping %s -> GIT:%s\n' % (ref, gitsha))
            del gh.git.refs[ref]

    ui.status(_(b'git commit map cleaned\n'))


@eh.wrapcommand(
    b'tag',
    opts=[
        (
            b'',
            b'git',
            False,
            b'''create a lightweight Git tag; this requires an explicit -r/--rev,
        and does not support any of the other flags''',
        )
    ],
)
def tag(orig, ui, repo, *names, **opts):
    if not opts.get('git'):
        return orig(ui, repo, *names, **opts)

    opts = pycompat.byteskwargs(opts)

    # check for various unimplemented arguments
    cmdutil.check_incompatible_arguments(
        opts,
        b'git',
        [
            # conflict
            b'local',
            # we currently don't convert or expose this metadata, so
            # disallow setting it on creation
            b'edit',
            b'message',
            b'date',
            b'user',
        ],
    )
    cmdutil.check_at_most_one_arg(opts, b'rev', b'remove')

    if opts[b'remove']:
        opts[b'rev'] = b'null'

    if not opts.get(b'rev'):
        raise error.Abort(
            _(b'git tags require an explicit revision'),
            hint=b'please specify -r/--rev',
        )

    # the semantics of git tag editing are quite confusing, so we
    # don't bother; if you _really_ want to, use another tool to do
    # this, and ensure all contributors prune their tags -- otherwise,
    # it'll reappear next time someone pushes tags (ah, the wonders of
    # nonversioned markers!)
    #
    # see also https://git-scm.com/docs/git-tag#_discussion
    if opts[b'force']:
        raise error.Abort(
            b'cannot move git tags',
            hint=b'the git documentation heavily discourages editing tags',
        )

    names = [t.strip() for t in names]

    if len(names) != len(set(names)):
        raise error.Abort(_('tag names must be unique'))

    with repo.wlock(), repo.lock():
        target = hex(repo.lookup(opts[b'rev']))

        # see above
        if target == nullhex:
            raise error.Abort(
                b'cannot remove git tags',
                hint=b'the git documentation heavily discourages editing tags',
            )

        repo.githandler.add_tag(target, *names)


@eh.wrapcommand(b'annotate')
def annotate(orig, ui, repo, *pats, **opts):
    skiprevs = opts.get(b'skip', [])
    ignorerevscfg = ui.config(b'git', b'blame.ignoreRevsFile')

    if ignorerevscfg and repo.githandler:
        ignorerevsfile = repo.wjoin(ignorerevscfg)

        if os.path.isfile(ignorerevsfile):
            with open(ignorerevsfile, 'rb') as fp:
                for line in fp:
                    git_sha = line.strip().split(b'#', 1)[0]

                    if not git_sha:
                        continue

                    hg_sha = repo.githandler.map_hg_get(git_sha)

                    if hg_sha is not None:
                        ui.debug(
                            b'skipping %s -> %s\n' % (git_sha[:12], hg_sha[:12])
                        )
                        skiprevs.append(hg_sha)

            opts['skip'] = skiprevs

    return orig(ui, repo, *pats, **opts)