<commit_before>#
# Cvs.pm - package for manipulating CVS archives
#
# $Id: Cvs.pm,v 1.2 2001-12-12 13:31:50 vg Exp $

package Cvs;
use strict;

##### ctor ####

sub new {
    my $proto = shift;
    my $class = ref($proto) || $proto;
    my $self = {};
    $self->{NAME} = undef;
    $self->{HEAD} = undef;
    $self->{FLAGS} = undef;
    if ($^O eq "MSWin32" || $^O eq "os2" ) {
    $self->{CVS_BINARY} = "cvsclt2.exe";
    } else {
    $self->{CVS_BINARY} = "cvs.clt";
    }
    $self->{REV_DATA} = {};
    $self->{REV_SORTED} = [];
    $self->{REV_TAGS} = {};
    $self->{TAGS} = {};
    $self->{"_PARSED"} = undef;
    $self->{"_SORTED"} = undef;
    $self->{"_TAGGED"} = undef;
    bless ($self, $class);
    return $self;
}

#### methods to access per object data ####

sub name {
    my $self = shift;
    if ( @_ ) {
    $self->{name} = shift;
    }
    return $self->{name};
}

sub cvs_binary {
    my $self = shift;
    if ( @_ ) {
    $self->{CVS_BINARY} = shift;
    }
    return $self->{CVS_BINARY};
}

sub get_data_by_rev {
    my $self = shift;
    $self->parse_log();

    return $self->{REV_DATA};
}

sub get_sorted_revs {
    my $self = shift;

    if ( $self->{"_SORTED"} ) {
    return $self->{REV_TAGS};
    }

    $self->parse_log();

    sub by_rev {
    # comparison function for sorting
    my (@field_a, @field_b, $min_field, $i);

    @field_a = split /\./, $a;
    @field_b = split /\./, $b;
    $min_field = ($#field_a > $#field_b) ? $#field_b : $#field_a;

    for ($i = 0; $i <= $min_field; $i++)
      {
          if ( ($field_a[$i] < $field_b[$i]) ) {
          return -1;
          }
          if ( ($field_a[$i] > $field_b[$i]) ) {
          return 1;
          }
      }

    if ( $#field_a == $#field_b ) {
        return 0;
    }
    # eg. 1.70 sorts before 1.70.1.0
    ($#field_a < $#field_b) ? return -1 : return 1;
    }


    @{$self->{REV_SORTED}} = sort by_rev (keys %{$self->{REV_DATA}});
    $self->{"_SORTED"} = 1;
    return $self->{REV_SORTED};
}

sub get_tags_by_rev {
    my $self = shift;
    my ($tag, $rev);

    if ( $self->{"_TAGGED"} ) {
    return $self->{REV_TAGS};
    }

    $self->parse_log();
    foreach $tag (keys %{$self->{TAGS}}) {
    $rev = $self->{TAGS}->{$tag};
    push (@{$self->{REV_TAGS}->{$rev}}, $tag);
    }

    $self->{"_TAGGED"} = 1;
    return $self->{REV_TAGS};
}

sub get_flags {
    my $self = shift;
    $self->parse_log();

    return $self->{FLAGS};
}

sub get_tags {
    my $self = shift;

    $self->parse_log();
    return $self->{TAGS};
}

sub get_head {
    my $self = shift;

    $self->parse_log();
    return $self->{HEAD};
}

#### methods to manipulate archive ####
sub delete_rev {
    my $self = shift;
    my $rev = shift;
    my $file = $self->name;
    my $out;

    if ($^O eq "MSWin32") {
    open (CVSDELETE,
          "$self->{CVS_BINARY} admin -o$rev $file 2>nul |");
    } elsif ( $^O eq "os2" ) {
    open (CVSDELETE,
          "$self->{CVS_BINARY} admin -o$rev $file 2>nul |");
    } else {
    open (CVSDELETE,
          "$self->{CVS_BINARY} admin -o$rev $file 2>/dev/null |");
    }
    while(<CVSDELETE>) {
    /deleting revision $rev/ && return 1;
    }
    close(CVSDELETE);
    return 0;
}

#### private methods ####
sub parse_log {
    my $self = shift;
    if ( $self->{"_PARSED"} ) {
    return;
    }
    my $file = $self->name;
    my $in_revisions = 0;
    my $in_tags = 0;
    my $rev_data = {};
    my ($rev, $date, $author, $state, $comment, @branches);

    open(CVSLOG, "$self->{CVS_BINARY} log $file |");

    while( <CVSLOG> ) {
    chomp;

    if ( $in_revisions ) {

        /revision\s((\d|\.)+)$/o && do { $rev = $1; next; };
<commit_msg>implement is_tag(), get_branch_rev(), update() and commit()<commit_after>#
# Cvs.pm - package for manipulating CVS archives
#
# $Id: Cvs.pm,v 1.3 2002-01-18 17:51:05 hr Exp $

package Cvs;
use strict;

##### ctor ####

sub new {
    my $proto = shift;
    my $class = ref($proto) || $proto;
    my $self = {};
    $self->{NAME} = undef;
    $self->{HEAD} = undef;
    $self->{FLAGS} = undef;
    if ($^O eq "MSWin32" || $^O eq "os2" ) {
    $self->{CVS_BINARY} = "cvsclt2.exe";
    } else {
    $self->{CVS_BINARY} = "cvs.clt2";
    }
    $self->{REV_DATA} = {};
    $self->{REV_SORTED} = [];
    $self->{REV_TAGS} = {};
    $self->{TAGS} = {};
    $self->{"_PARSED"} = undef;
    $self->{"_SORTED"} = undef;
    $self->{"_TAGGED"} = undef;
    bless ($self, $class);
    return $self;
}

#### methods to access per object data ####

sub name {
    my $self = shift;
    if ( @_ ) {
    $self->{name} = shift;
    }
    return $self->{name};
}

sub cvs_binary {
    my $self = shift;
    if ( @_ ) {
    $self->{CVS_BINARY} = shift;
    }
    return $self->{CVS_BINARY};
}

sub get_data_by_rev {
    my $self = shift;
    $self->parse_log();

    return $self->{REV_DATA};
}

sub get_sorted_revs {
    my $self = shift;

    if ( $self->{"_SORTED"} ) {
    return $self->{REV_TAGS};
    }

    $self->parse_log();

    sub by_rev {
    # comparison function for sorting
    my (@field_a, @field_b, $min_field, $i);

    @field_a = split /\./, $a;
    @field_b = split /\./, $b;
    $min_field = ($#field_a > $#field_b) ? $#field_b : $#field_a;

    for ($i = 0; $i <= $min_field; $i++)
      {
          if ( ($field_a[$i] < $field_b[$i]) ) {
          return -1;
          }
          if ( ($field_a[$i] > $field_b[$i]) ) {
          return 1;
          }
      }

    if ( $#field_a == $#field_b ) {
        return 0;
    }
    # eg. 1.70 sorts before 1.70.1.0
    ($#field_a < $#field_b) ? return -1 : return 1;
    }


    @{$self->{REV_SORTED}} = sort by_rev (keys %{$self->{REV_DATA}});
    $self->{"_SORTED"} = 1;
    return $self->{REV_SORTED};
}

sub get_tags_by_rev {
    my $self = shift;
    my ($tag, $rev);

    if ( $self->{"_TAGGED"} ) {
    return $self->{REV_TAGS};
    }

    $self->parse_log();
    foreach $tag (keys %{$self->{TAGS}}) {
    $rev = $self->{TAGS}->{$tag};
    push (@{$self->{REV_TAGS}->{$rev}}, $tag);
    }

    $self->{"_TAGGED"} = 1;
    return $self->{REV_TAGS};
}

sub get_flags {
    my $self = shift;
    $self->parse_log();

    return $self->{FLAGS};
}

sub get_tags {
    my $self = shift;

    $self->parse_log();
    return $self->{TAGS};
}

sub get_head {
    my $self = shift;

    $self->parse_log();
    return $self->{HEAD};
}

sub is_tag {
    my $self = shift;
    my $tag  = shift;

    my $tags_ref = $self->get_tags();
    return exists($$tags_ref{$tag}) ? 1 : 0;
}

sub get_branch_rev {
    # check if $label is branch label and returns revision
    my $self  = shift;
    my $label = shift;

    my $tags_ref = $self->get_tags();
    my $rev = $$tags_ref{$label};
    return 0 if !defined($rev);
    my @field = split('\.', $rev);
    # $label is a branch label if rev is of form (...)x.y.0.z
    return 0 if $field[-2] != 0;
    $field[-2] = $field[-1];
    # remove last
    pop @field;
    return join('.', @field);
}


#### methods to manipulate archive ####
sub delete_rev {
    my $self = shift;
    my $rev = shift;
    my $file = $self->name;

    if ( $^O eq "MSWin32" || $^O eq 'os2' ) {
    open (CVSDELETE,
          "$self->{CVS_BINARY} admin -o$rev $file 2>nul |");
    } else {
    open (CVSDELETE,
          "$self->{CVS_BINARY} admin -o$rev $file 2>/dev/null |");
    }
    while(<CVSDELETE>) {
        /deleting revision $rev/ && return 1;
    }
    close(CVSDELETE);
    return 0;
}

sub update {
    # Update archive with options $options.
    # Returns 'success' on success or reason of failure.
    # If no update happens because file was up-to-date
    # consider operation a success.
    my $self = shift;
    my $options = shift;

    my $file = $self->name;
    if ( $^O eq "MSWin32" || $^O eq 'os2' ) {
    open (CVSUPDATE,
          "$self->{CVS_BINARY} update $options $file 2>&1 |");
    } else {
    open (CVSUPDATE,
          "$self->{CVS_BINARY} update $options $file 2>&1 |");
    }
    my $conflict = 0;
    my $notknown = 0;
    while(<CVSUPDATE>) {
        /conflicts during merge/ && ++$conflict;
        /nothing known about/ && ++$notknown;
    }
    close(CVSUPDATE);
    if ( $conflict || $notknown ) {
        my $failure = 'unkownfailure';
        $failure = 'conflict' if $conflict;
        $failure = 'notknown' if $notknown;
        return $failure
    }
    return 'success'
}

sub commit {
    # commit $file with option $option
    # return 'success' or reason for failure
    my $self = shift;
    my $options = shift;

    my $file = $self->name;
    if ( $^O eq "MSWin32" || $^O eq 'os2' ) {
    open (CVSCOMMIT,
          "$self->{CVS_BINARY} commit $options $file 2>&1 |");
    } else {
    open (CVSCOMMIT,
          "$self->{CVS_BINARY} commit $options $file 2>&1 |");
    }
    my $conflict = 0;
    my $uptodate = 0;
    my $notknown = 0;
    my $success  = 0;
    while(<CVSCOMMIT>) {
        /Up-to-date check failed/ && ++$uptodate;
        /nothing known about/ && ++$notknown;
        /had a conflict and has not been modified/ && ++$conflict;
        /new revision:/ && ++$success;
    }
    close(CVSCOMMIT);
    if ( !$success ) {
        my $failure = 'unkownfailure';
        $failure = 'conflict' if $conflict;
        $failure = 'notuptodate' if $uptodate;
        $failure = 'notknown' if $notknown;
        return $failure
    }
    return 'success'
}


#### private methods ####
sub parse_log {
    my $self = shift;
    if ( $self->{"_PARSED"} ) {
    return;
    }
    my $file = $self->name;
    my $in_revisions = 0;
    my $in_tags = 0;
    my $rev_data = {};
    my ($rev, $date, $author, $state, $comment, @branches);

    open(CVSLOG, "$self->{CVS_BINARY} log $file |");

    while( <CVSLOG> ) {
    chomp;

    if ( $in_revisions ) {

        /revision\s((\d|\.)+)$/o && do { $rev = $1; next; };

{'fraction_non_alphanumeric': 0.19967383549077566, 'fraction_numerical': 0.01253694832331057, 'mean_word_length': 2.2436363636363637, 'pattern_counts': {'":': 0, '<': 15, '<?xml version=': 0, '>': 125, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 2, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2728335', 'n_tokens_mistral': 4017, 'n_tokens_neox': 3873, 'n_words': 893}