cvs2git: new package

conversion from cvs to git using cvs2fossil
master
Thomas Klausner 2024-04-21 22:57:09 +02:00
parent f35cf16987
commit 1d719db65c
7 changed files with 275 additions and 2 deletions

View File

@ -595,6 +595,7 @@ SUBDIR+= cvc3
SUBDIR+= cvs-syncmail
SUBDIR+= cvs2darcs
SUBDIR+= cvs2fossil
SUBDIR+= cvs2git
SUBDIR+= cvs2hg
SUBDIR+= cvsanaly
SUBDIR+= cvsnt
@ -2008,7 +2009,8 @@ SUBDIR+= libraw1394
SUBDIR+= libre
SUBDIR+= librem
SUBDIR+= libreoffice-git
SUBDIR+= libreswan
SUBDIR+= libreswan-4
SUBDIR+= libreswan-5
SUBDIR+= libreswan-git
SUBDIR+= libretro-4do
SUBDIR+= libretro-beetle-lynx

1
cvs2git/DESCR Normal file
View File

@ -0,0 +1 @@
Convert a CVS repository to git via fossil.

36
cvs2git/Makefile Normal file
View File

@ -0,0 +1,36 @@
# $NetBSD$
PKGNAME= cvs2git-3.0
CATEGORIES= devel scm
MAINTAINER= wiz@NetBSD.org
#HOMEPAGE= https://www.pkgsrc.org/
COMMENT= Convert CVS repository to git
LICENSE= 2-clause-bsd
DEPENDS+= cvs2fossil-[0-9]*:../../wip/cvs2fossil
DEPENDS+= git-base-[0-9]*:../../devel/git-base
DEPENDS+= git-filter-repo-[0-9]*:../../devel/git-filter-repo
WRKSRC= ${WRKDIR}
USE_LANGUAGES= # empty
INSTALLATION_DIRS= bin ${PKGMANDIR}/man1
PYTHON_VERSIONS_INCOMPATIBLE= 27
REPLACE_PYTHON+= cvs2git
post-extract:
${CP} ${FILESDIR}/cvs2git ${WRKSRC}
${CP} ${FILESDIR}/cvs2git.1 ${WRKSRC}
do-configure:
do-build:
do-install:
${INSTALL_SCRIPT} ${WRKDIR}/cvs2git ${DESTDIR}${PREFIX}/bin
${INSTALL_MAN} ${FILESDIR}/cvs2git.1 ${DESTDIR}${PREFIX}/${PKGMANDIR}/man1
.include "../../lang/python/application.mk"
.include "../../lang/python/pyversion.mk"
.include "../../mk/bsd.pkg.mk"

3
cvs2git/PLIST Normal file
View File

@ -0,0 +1,3 @@
@comment $NetBSD$
bin/cvs2git
man/man1/cvs2git.1

114
cvs2git/files/cvs2git Normal file
View File

@ -0,0 +1,114 @@
#!/usr/bin/env python3
#
# $NetBSD$
#
# Copyright (c) 2024 The NetBSD Foundation, Inc.
# All rights reserved.
#
# This code is derived from software contributed to The NetBSD Foundation
# by Thomas Klausner.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
#
import argparse
import os
from pathlib import Path
import subprocess
def file_path(path):
'''Check if argument is a valid file path.'''
if Path(path).is_file():
return str(Path(path).resolve())
raise argparse.ArgumentTypeError(f"{path} is not a valid file path")
def dir_path(path):
'''Check if argument is a valid directory path.'''
if Path(path).is_dir():
return str(Path(path).resolve())
raise argparse.ArgumentTypeError(f"{path} is not a valid directory path")
parser = argparse.ArgumentParser(description='Convert CVS repository to git')
parser.add_argument('-m', dest='mailmap', type=file_path,
help='use %(dest)s to map UNIX logins to names ' +
'and email addresses')
parser.add_argument('-f', dest='fixup_sql_script', type=file_path,
help='run %(dest)s on fossil database to fix problems')
parser.add_argument('-s', dest='merge_limit', type=int,
help='pass %(dest)s to cvs2fossil ' +
'(merge window time in seconds)')
parser.add_argument('source', type=dir_path,
help='source CVS repository master')
# may not exist yet, can't use dir_path
parser.add_argument('destination', type=str,
help='base name of target git repository')
args = parser.parse_args()
run_args = ['cvs2fossil', '-m']
if args.fixup_sql_script:
run_args += ['-f', args.fixup_sql_script]
if args.merge_limit:
run_args += ['-s', str(args.merge_limit)]
run_args += [args.source, args.destination]
# convert to fossil using cvs2fossil
subprocess.run(run_args, check=True)
# cvs2fossil creates {args.destination} and {args.destination}.fossil,
# but we don't need the former
os.remove(args.destination)
# export from fossil
with open(f'{args.destination}.fossil.export', 'wb') as output:
subprocess.run(['fossil1', 'export', '-R', f'{args.destination}.fossil'],
stdout=output, check=True)
# import to git
subprocess.run(['git', 'init', f'{args.destination}.git'], check=True)
with open(f'{args.destination}.fossil.export', 'rb') as input:
subprocess.run(['git', 'fast-import'], check=True,
cwd=f'{args.destination}.git', stdin=input)
# HEAD is broken, fix it
with open(f'{args.destination}.git/.git/HEAD', 'w', encoding='ASCII') as conf:
conf.write('ref: refs/heads/trunk\n')
# rename 'trunk' to 'main'
subprocess.run(['git', 'branch', 'main', 'trunk'], check=True,
cwd=f'{args.destination}.git')
subprocess.run(['git', 'switch', 'main'], check=True,
cwd=f'{args.destination}.git')
subprocess.run(['git', 'branch', '-d', 'trunk'], check=True,
cwd=f'{args.destination}.git')
# fix author names
if args.mailmap:
run_args = ['git', 'clone', f'{args.destination}.git',
f'{args.destination}.rewrite.git']
subprocess.run(run_args, check=True)
run_args = ['git', 'filter-repo', '--force']
# and apply author fixes
if args.mailmap:
run_args.append('--mailmap')
run_args.append(f'{args.mailmap}')
subprocess.run(run_args, check=True,
cwd=f'{args.destination}.rewrite.git')
os.rename(f'{args.destination}.git', f'{args.destination}.git.old')
os.rename(f'{args.destination}.rewrite.git', f'{args.destination}.git')

117
cvs2git/files/cvs2git.1 Normal file
View File

@ -0,0 +1,117 @@
.\" $NetBSD$
.\"
.\" Copyright (c) 2024 The NetBSD Foundation, Inc.
.\" All rights reserved.
.\"
.\" This code is derived from software contributed to The NetBSD Foundation
.\" by Thomas Klausner.
.\"
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions
.\" are met:
.\" 1. Redistributions of source code must retain the above copyright
.\" notice, this list of conditions and the following disclaimer.
.\" 2. Redistributions in binary form must reproduce the above copyright
.\" notice, this list of conditions and the following disclaimer in the
.\" documentation and/or other materials provided with the distribution.
.\"
.\" THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
.\" ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
.\" TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
.\" PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
.\" BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
.\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
.\" POSSIBILITY OF SUCH DAMAGE.
.\"
.Dd April 21, 2024
.Dt CVS2GIT 1
.Os
.Sh NAME
.Nm cvs2git
.Nd convert CVS repository to git
.Sh SYNOPSIS
.Nm
.Op Fl f Ar fixup-sql-script
.Op Fl m Ar mailmap
.Op Fl s Ar merge-limit-seconds
.Ar source
.Ar destination
.Sh DESCRIPTION
.Nm
is a script to convert a
.Xr cvs 1
repository to
.Xr git 1
using
.Xr cvs2fossil 1
and
.Xr git 1 .
.Pp
.Ar source
must be a CVS repository (the server side, i.e. the RCS files, not a
checkout).
.Nm
will create
.Ar destination Ns Pa .fossil
(the fossil conversion created by
.Xr cvs2fossil 1 ) .
.Xr fossil 1
will create a
.Xr git 1
fast-import file
.Ar destination Ns Pa .fossil.export
which will be converted into the git repository
.Ar destination Ns Pa .git
by
.Xr git 1.
.Xr git 1
will also be used to update the author information using a mailmap
file, if provided.
.Pp
.Nm
supports the following flags:
.Bl -tag -width 10n
.It Fl m Ar mailmap
Pass a
.Xr git 1
mailmap file for creating proper author information using
.Xr git-filter-repo 1 .
The mailmap format is described in
.Xr gitmailmap 5 .
.It Fl f Ar fixup-sql-script
Pass an SQL fixup script for
.Xr cvs2fossil 1
for its
.Fl f
flag.
.It Fl s Ar merge-limit-seconds
Pass a merge time limit in seconds to
.Xr cvs2fossil 1 .
.El
.Sh EXAMPLES
To convert the
.Nx
src repository, if you have developer access, do:
.Bd -literal -offset indent
export REPO=src
export LOGIN=username
rsync -aS --delete -e ssh "$LOGIN"@cvs.NetBSD.org::cvsroot/"$REPO" "$REPO"-rsync
cvs2git -m mailmap -s 300 "$REPO"-rsync/"$REPO" "$REPO"
.Ed
.Pp
The conversion output is in
.Pa src.git .
The intermediate fossil directory is
.Pa src.fossil
and the corresponsing fast-import file
.Pa src.fossil.export .
If you use
.Fl a
there will be another
.Pa src.git.old
directory, respectively.
You can delete them after the conversion.

View File

@ -1 +1 @@
Convert a CVS repository to Mercurial (hg) via fossil and git.
Convert a CVS repository to Mercurial (hg) via fossil.