cvsserver: generalize getmeta() to recognize commit refs
This allows getmeta() to recognize any commitish (sha1, tag/branch name, etc). Signed-off-by: Matthew Ogilvie <mmogilvi_git@miniinfo.net> Signed-off-by: Junio C Hamano <gitster@pobox.com>
Matthew Ogilvie committed
Oct 13, 2012 at 23:42 UTC
bfdafa099eb9fd8c3b99f5debdddfa251dcdd855
1 file changed
+145
-11
git-cvsserver.perl
+145
-11
@@ -4041,6 +4041,19 @@ sub getlog
4041
This function takes a filename (with path) argument and returns a hashref of
4042
metadata for that file.
4043
4044
+There are several ways $revision can be specified:
4045
+
4046
+ - A reference to hash that contains a "tag" that is the
4047
+ actual revision (one of the below). TODO: Also allow it to
4048
+ specify a "date" in the hash.
4049
+ - undef, to refer to the latest version on the main branch.
4050
+ - Full CVS client revision number (mapped to integer in DB, without the
4051
+ "1." prefix),
4052
+ - Complex CVS-compatible "special" revision number for
4053
+ non-linear history (see comment below)
4054
+ - git commit sha1 hash
4055
+ - branch or tag name
4056
+
4057
=cut
4058
4059
sub getmeta
@@ -4051,23 +4064,144 @@ sub getmeta
4064
my $tablename_rev = $self->tablename("revision");
4065
my $tablename_head = $self->tablename("head");
4066
4054
- my $db_query;
4055
- if ( defined($revision) and $revision =~ /^1\.(\d+)$/ )
4067
+ if ( ref($revision) eq "HASH" )
4068
{
4057
- my ($intRev) = $1;
4058
- $db_query = $self->{dbh}->prepare_cached("SELECT * FROM $tablename_rev WHERE name=? AND revision=?",{},1);
4059
- $db_query->execute($filename, $intRev);
4069
+ $revision = $revision->{tag};
4070
+ }
4071
+
4072
+ # Overview of CVS revision numbers:
4073
+ #
4074
+ # General CVS numbering scheme:
4075
+ # - Basic mainline branch numbers: "1.1", "1.2", "1.3", etc.
4076
+ # - Result of "cvs checkin -r" (possible, but not really
4077
+ # recommended): "2.1", "2.2", etc
4078
+ # - Branch tag: "1.2.0.n", where "1.2" is revision it was branched
4079
+ # from, "0" is a magic placeholder that identifies it as a
4080
+ # branch tag instead of a version tag, and n is 2 times the
4081
+ # branch number off of "1.2", starting with "2".
4082
+ # - Version on a branch: "1.2.n.x", where "1.2" is branch-from, "n"
4083
+ # is branch number off of "1.2" (like n above), and "x" is
4084
+ # the version number on the branch.
4085
+ # - Branches can branch off of branches: "1.3.2.7.4.1" (even number
4086
+ # of components).
4087
+ # - Odd "n"s are used by "vendor branches" that result
4088
+ # from "cvs import". Vendor branches have additional
4089
+ # strangeness in the sense that the main rcs "head" of the main
4090
+ # branch will (temporarily until first normal commit) point
4091
+ # to the version on the vendor branch, rather than the actual
4092
+ # main branch. (FUTURE: This may provide an opportunity
4093
+ # to use "strange" revision numbers for fast-forward-merged
4094
+ # branch tip when CVS client is asking for the main branch.)
4095
+ #
4096
+ # git-cvsserver CVS-compatible special numbering schemes:
4097
+ # - Currently git-cvsserver only tries to be identical to CVS for
4098
+ # simple "1.x" numbers on the "main" branch (as identified
4099
+ # by the module name that was originally cvs checkout'ed).
4100
+ # - The database only stores the "x" part, for historical reasons.
4101
+ # But most of the rest of the cvsserver preserves
4102
+ # and thinks using the full revision number.
4103
+ # - To handle non-linear history, it uses a version of the form
4104
+ # "2.1.1.2000.b.b.b."..., where the 2.1.1.2000 is to help uniquely
4105
+ # identify this as a special revision number, and there are
4106
+ # 20 b's that together encode the sha1 git commit from which
4107
+ # this version of this file originated. Each b is
4108
+ # the numerical value of the corresponding byte plus
4109
+ # 100.
4110
+ # - "plus 100" avoids "0"s, and also reduces the
4111
+ # likelyhood of a collision in the case that someone someday
4112
+ # writes an import tool that tries to preserve original
4113
+ # CVS revision numbers, and the original CVS data had done
4114
+ # lots of branches off of branches and other strangeness to
4115
+ # end up with a real version number that just happens to look
4116
+ # like this special revision number form. Also, if needed
4117
+ # there are several ways to extend/identify alternative encodings
4118
+ # within the "2.1.1.2000" part if necessary.
4119
+ # - Unlike real CVS revisions, you can't really reconstruct what
4120
+ # relation a revision of this form has to other revisions.
4121
+ # - FUTURE: TODO: Rework database somehow to make up and remember
4122
+ # fully-CVS-compatible branches and branch version numbers.
4123
+
4124
+ my $meta;
4125
+ if ( defined($revision) )
4126
+ {
4127
+ if ( $revision =~ /^1\.(\d+)$/ )
4128
+ {
4129
+ my ($intRev) = $1;
4130
+ my $db_query;
4131
+ $db_query = $self->{dbh}->prepare_cached(
4132
+ "SELECT * FROM $tablename_rev WHERE name=? AND revision=?",
4133
+ {},1);
4134
+ $db_query->execute($filename, $intRev);
4135
+ $meta = $db_query->fetchrow_hashref;
4136
+ }
4137
+ elsif ( $revision =~ /^2\.1\.1\.2000(\.[1-3][0-9][0-9]){20}$/ )
4138
+ {
4139
+ my ($commitHash)=($revision=~/^2\.1\.1\.2000(.*)$/);
4140
+ $commitHash=~s/\.([0-9]+)/sprintf("%02x",$1-100)/eg;
4141
+ if($commitHash=~/^[0-9a-f]{40}$/)
4142
+ {
4143
+ return $self->getMetaFromCommithash($filename,$commitHash);
4144
+ }
4145
+
4146
+ # error recovery: fall back on head version below
4147
+ print "E Failed to find $filename version=$revision or commit=$commitHash\n";
4148
+ $log->warning("failed get $revision with commithash=$commitHash");
4149
+ undef $revision;
4150
+ }
4151
+ elsif ( $revision =~ /^[0-9a-f]{40}$/ )
4152
+ {
4153
+ # Try DB first. This is mostly only useful for req_annotate(),
4154
+ # which only calls this for stuff that should already be in
4155
+ # the DB. It is fairly likely to be a waste of time
4156
+ # in most other cases [unless the file happened to be
4157
+ # modified in $revision specifically], but
4158
+ # it is probably in the noise compared to how long
4159
+ # getMetaFromCommithash() will take.
4160
+ my $db_query;
4161
+ $db_query = $self->{dbh}->prepare_cached(
4162
+ "SELECT * FROM $tablename_rev WHERE name=? AND commithash=?",
4163
+ {},1);
4164
+ $db_query->execute($filename, $revision);
4165
+ $meta = $db_query->fetchrow_hashref;
4166
+
4167
+ if(! $meta)
4168
+ {
4169
+ my($revCommit)=$self->lookupCommitRef($revision);
4170
+ if($revCommit=~/^[0-9a-f]{40}$/)
4171
+ {
4172
+ return $self->getMetaFromCommithash($filename,$revCommit);
4173
+ }
4174
+
4175
+ # error recovery: nothing found:
4176
+ print "E Failed to find $filename version=$revision\n";
4177
+ $log->warning("failed get $revision");
4178
+ return $meta;
4179
+ }
4180
+ }
4181
+ else
4182
+ {
4183
+ my($revCommit)=$self->lookupCommitRef($revision);
4184
+ if($revCommit=~/^[0-9a-f]{40}$/)
4185
+ {
4186
+ return $self->getMetaFromCommithash($filename,$revCommit);
4187
+ }
4188
+
4189
+ # error recovery: fall back on head version below
4190
+ print "E Failed to find $filename version=$revision\n";
4191
+ $log->warning("failed get $revision");
4192
+ undef $revision; # Allow fallback
4193
+ }
4194
}
4061
- elsif ( defined($revision) and $revision =~ /^[a-zA-Z0-9]{40}$/ )
4195
+
4196
+ if(!defined($revision))
4197
{
4063
- $db_query = $self->{dbh}->prepare_cached("SELECT * FROM $tablename_rev WHERE name=? AND commithash=?",{},1);
4064
- $db_query->execute($filename, $revision);
4065
- } else {
4066
- $db_query = $self->{dbh}->prepare_cached("SELECT * FROM $tablename_head WHERE name=?",{},1);
4198
+ my $db_query;
4199
+ $db_query = $self->{dbh}->prepare_cached(
4200
+ "SELECT * FROM $tablename_head WHERE name=?",{},1);
4201
$db_query->execute($filename);
4202
+ $meta = $db_query->fetchrow_hashref;
4203
}
4204
4070
- my $meta = $db_query->fetchrow_hashref;
4205
if($meta)
4206
{
4207
$meta->{revision} = "1.$meta->{revision}";