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)
|