= repocutter(1) =
:doctype: manpage

== NAME ==
repocutter - surgical and filtering operations on Subversion dump files 

== SYNOPSIS ==

*repocutter* [-q] [-d n] [-i 'filename'] [-r 'selection'] 'subcommand'

[[description]]
== DESCRIPTION ==

This program does surgical and filtering operations on Subversion dump
files.  While it is is not as flexible as reposurgeon(1), it can
perform Subversion-specific transformations that reposurgeon cannot,
and can be useful for processing Subversion repositories into a form
suitable for conversion. Also, it supports the version 3 dumpfile
format, which reposurgeon does not.

The "help" subcommand lists help topics.  With a following argument
that is a topic name it lists the help for that topic.

In most commands, the -r (or --range) option limits the selection of
revisions over which an operation will be performed. Usually other
revisions will be passed through unaltered, except in the select and
deselect commands for which the option controls which revisions will be
passed through. A selection consists of one or more comma-separated
ranges. A range may consist of an integer revision number or the
special name HEAD for the head revision. Or it may be a
colon-separated pair of integers, or an integer followed by a colon
followed by HEAD.

If the output stream contains copyfrom references to missing revisions,
repocutter silently patch each copysources by stepping it backwards to
the most recent previous version that exists.

(Older versions of this tool, before 4.30, treated -r as an implied
selection filter rather than passing through unselected revisions
unaltered. If you have old scripts using repocutter they may need
modification.)

Normally, each subcommand produces a progress spinner on standard
error; each turn means another revision has been filtered. The -q (or
--quiet) option suppresses this. Quiet mode is set automatically when
output is redirected to a file or pipe.

The -d option enables debug messages on standard error. It takes an
integer debug level. These messages are probably only of interest to
repocutter developers.

The -i option sets the input source to a specified filename.
This is primarily useful when running the program under a debugger.
When this option is not present the program expects to read a 
stream from standard input.

Generally, if you need to use this program at all, you will find that
you need to pipe your dump file through multiple instances of it doing
one kind of operation each.  This is not as expensive as it sounds;
with the exception of the reduce subcommand, the working set of this
program is bounded by the size of the the largest single blob plus its
metadata.  It does not need to hold the entire repo metadata in
memory.

The -f/-fixed option disables regexp compilation of PATTERN arguments,
treating them as literal strings.

The -t option sets a tag to be included in error and warning messages.
This will be useful for determining which stage of a multistage
repocutter pipeline failed.

There are a few other command-specific options described under
individual commands.

In the command descriptions, PATTERN arguments are regular expressions
to match pathnames, constrained so that each match must be a path
segment or a sequence of path segments; that is, the left end must be
either at the start of path or immediately following a /, and the
right end must precede a / or be at end of string.  With a leading ^
the match is constrained to be a leading sequence of the pathname;
with a trailing $, a trailing one.

The following subcommands are available:

include::cuttercommands.inc[]

[[history]]
== HISTORY ==

Under the name "svncutter", an ancestor of this program traveled in
the 'contrib/' director of the Subversion
distribution. It had functional overlap with reposurgeon(1) because it
was directly ancestral to that code. It was moved to the
reposurgeon(1) distribution in January 2016.  This program was ported
from Python to Go in August 2018, at which time the obsolete "squash"
command was retired.  The syntax of regular expressions in the
pathrename command changed at that time.

The reason for the partial functional overlap between repocutter and
reposurgeon is that repocutter was first written earlier and became a
testbed for some of the design concepts in reposurgeon. After
reposurgeon was written, the author learned that it could not
naturally support some useful operations very specific to Subversion,
and enhanced repocutter to do those.

[[return-values]]
== RETURN VALUES ==
Normally 0. Can be 1 if repocutter sees an ill-formed dump, or if the
output stream contains any copyfrom references to missing revisions.

[[bugs]]
== BUGS ==

There is one regression since the Python version: repocutter no
longer recognizes Macintosh-style line endings consisting of a carriage
return only. This may be addressed in a future version.

[[see_also]]
== SEE ALSO ==

reposurgeon(1).

[[example]]
== EXAMPLE ==

Suppose you have a Subversion repository with the following
semi-pathological structure:

----
Directory1/ (with unrelated content)
Directory2/ (with unrelated content)
TheDirIWantToMigrate/
                branches/
                               crazy-feature/
                                               UnrelatedApp1/
                                               TheAppIWantToMigrate/
                tags/
                               v1.001/
                                               UnrelatedApp1/
                                               UnrelatedApp2/
                                               TheAppIWantToMigrate/
                trunk/
                               UnrelatedApp1/
                               UnrelatedApp2/
                               TheAppIWantToMigrate/
----

You want to transform the dump file so that TheAppIWantToMigrate can be
subject to a regular branchy lift. A way to dissect out the code of
interest would be with the following series of filters applied:

----
repocutter expunge '^Directory1' '^Directory2'
repocutter pathrename '^TheDirIWantToMigrate/' ''
repocutter expunge '^branches/crazy-feature/UnrelatedApp1/
repocutter pathrename 'branches/crazy-feature/TheAppIWantToMigrate/' 'branches/crazy-feature/'
repocutter expunge '^tags/v1.001/UnrelatedApp1/'
repocutter expunge '^tags/v1.001/UnrelatedApp2/'
repocutter pathrename '^tags/v1.001/TheAppIWantToMigrate/' 'tags/v1.001/'
repocutter expunge '^trunk/UnrelatedApp1/'
repocutter expunge '^trunk/UnrelatedApp2/'
repocutter pathrename '^trunk/TheAppIWantToMigrate/' 'trunk/'
----

[[limitations]]
== LIMITATIONS ==

The sift and expunge operations can produce output dumps that are
invalid.  The problem is copyfrom operations (Subversion branch and
tag creations).  If an included revision includes a copyfrom reference
to an excluded one, the reference target won't be in the emitted dump;
it won't load correctly in Subversion, and while reposurgeon has
fallback logic that backs down to the latest existing revision before
the kissing one this expedient is fragile. The revision number in a
copyfrom header pointing to a missing revision will be zero. Attempts
to be clever about this won't work; the problem is inherent in the
data model of Subversion.

[[author]]
== AUTHOR ==
Eric S. Raymond <esr@thyrsus.com>. This tool is
distributed with reposurgeon; see the
http://www.catb.org/~esr/reposurgeon[project page].

// end
