611
{
612
$filename = filecleanup($filename);
613
614
- my $meta = $updater->getmeta($filename);
614
+ # no -r, -A, or -D with add
615
+ my $stickyInfo = resolveStickyInfo($filename);
616
+
617
+ my $meta = $updater->getmeta($filename,$stickyInfo);
618
my $wrev = revparse($filename);
619
620
if ($wrev && $meta && ($wrev=~/^-/))
637
638
# this is an "entries" line
639
my $kopts = kopts_from_path($filename,"sha1",$meta->{filehash});
637
- $log->debug("/$filepart/$meta->{revision}//$kopts/");
638
- print "/$filepart/$meta->{revision}//$kopts/\n";
640
+ my $entryLine = "/$filepart/$meta->{revision}//$kopts/";
641
+ $entryLine .= getStickyTagOrDate($stickyInfo);
642
+ $log->debug($entryLine);
643
+ print "$entryLine\n";
644
# permissions
645
$log->debug("SEND : u=$meta->{mode},g=$meta->{mode},o=$meta->{mode}");
646
print "u=$meta->{mode},g=$meta->{mode},o=$meta->{mode}\n";
671
print "$filename\n";
672
my $kopts = kopts_from_path($filename,"file",
673
$state->{entries}{$filename}{modified_filename});
669
- print "/$filepart/0//$kopts/\n";
674
+ print "/$filepart/0//$kopts/" .
675
+ getStickyTagOrDate($stickyInfo) . "\n";
676
677
my $requestedKopts = $state->{opt}{k};
678
if(defined($requestedKopts))
740
next;
741
}
742
737
- my $meta = $updater->getmeta($filename);
743
+ # only from entries
744
+ my $stickyInfo = resolveStickyInfo($filename);
745
+
746
+ my $meta = $updater->getmeta($filename,$stickyInfo);
747
my $wrev = revparse($filename);
748
749
unless ( defined ( $wrev ) )
773
print "Checked-in $dirpart\n";
774
print "$filename\n";
775
my $kopts = kopts_from_path($filename,"sha1",$meta->{filehash});
767
- print "/$filepart/-$wrev//$kopts/\n";
776
+ print "/$filepart/-$wrev//$kopts/" . getStickyTagOrDate($stickyInfo) . "\n";
777
778
$rmcount++;
779
}
953
return 1;
954
}
955
956
+ my $stickyInfo = { 'tag' => $state->{opt}{r},
957
+ 'date' => $state->{opt}{D} };
958
+
959
my $module = $state->{args}[0];
960
$state->{module} = $module;
961
my $checkout_path = $module;
973
my $updater = GITCVS::updater->new($state->{CVSROOT}, $module, $log);
974
$updater->update();
975
964
- $checkout_path =~ s|/$||; # get rid of trailing slashes
976
+ my $headHash;
977
+ if( defined($stickyInfo) && defined($stickyInfo->{tag}) )
978
+ {
979
+ $headHash = $updater->lookupCommitRef($stickyInfo->{tag});
980
+ if( !defined($headHash) )
981
+ {
982
+ print "error 1 no such tag `$stickyInfo->{tag}'\n";
983
+ cleanupWorkTree();
984
+ exit;
985
+ }
986
+ }
987
966
- # Eclipse seems to need the Clear-sticky command
967
- # to prepare the 'Entries' file for the new directory.
968
- print "Clear-sticky $checkout_path/\n";
969
- print $state->{CVSROOT} . "/$module/\n";
970
- print "Clear-static-directory $checkout_path/\n";
971
- print $state->{CVSROOT} . "/$module/\n";
972
- print "Clear-sticky $checkout_path/\n"; # yes, twice
973
- print $state->{CVSROOT} . "/$module/\n";
974
- print "Template $checkout_path/\n";
975
- print $state->{CVSROOT} . "/$module/\n";
976
- print "0\n";
977
-
978
- # instruct the client that we're checking out to $checkout_path
979
- print "E cvs checkout: Updating $checkout_path\n";
988
+ $checkout_path =~ s|/$||; # get rid of trailing slashes
989
990
my %seendirs = ();
991
my $lastdir ='';
992
984
- # recursive
985
- sub prepdir {
986
- my ($dir, $repodir, $remotedir, $seendirs) = @_;
987
- my $parent = dirname($dir);
988
- $dir =~ s|/+$||;
989
- $repodir =~ s|/+$||;
990
- $remotedir =~ s|/+$||;
991
- $parent =~ s|/+$||;
992
- $log->debug("announcedir $dir, $repodir, $remotedir" );
993
-
994
- if ($parent eq '.' || $parent eq './') {
995
- $parent = '';
996
- }
997
- # recurse to announce unseen parents first
998
- if (length($parent) && !exists($seendirs->{$parent})) {
999
- prepdir($parent, $repodir, $remotedir, $seendirs);
1000
- }
1001
- # Announce that we are going to modify at the parent level
1002
- if ($parent) {
1003
- print "E cvs checkout: Updating $remotedir/$parent\n";
1004
- } else {
1005
- print "E cvs checkout: Updating $remotedir\n";
1006
- }
1007
- print "Clear-sticky $remotedir/$parent/\n";
1008
- print "$repodir/$parent/\n";
1009
-
1010
- print "Clear-static-directory $remotedir/$dir/\n";
1011
- print "$repodir/$dir/\n";
1012
- print "Clear-sticky $remotedir/$parent/\n"; # yes, twice
1013
- print "$repodir/$parent/\n";
1014
- print "Template $remotedir/$dir/\n";
1015
- print "$repodir/$dir/\n";
1016
- print "0\n";
1017
-
1018
- $seendirs->{$dir} = 1;
1019
- }
1020
-
1021
- foreach my $git ( @{$updater->gethead} )
993
+ prepDirForOutput(
994
+ ".",
995
+ $state->{CVSROOT} . "/$module",
996
+ $checkout_path,
997
+ \%seendirs,
998
+ 'checkout',
999
+ $state->{dirArgs} );
1000
+
1001
+ foreach my $git ( @{$updater->getAnyHead($headHash)} )
1002
{
1003
# Don't want to check out deleted files
1004
next if ( $git->{filehash} eq "deleted" );
1006
my $fullName = $git->{name};
1007
( $git->{name}, $git->{dir} ) = filenamesplit($git->{name});
1008
1029
- if (length($git->{dir}) && $git->{dir} ne './'
1030
- && $git->{dir} ne $lastdir ) {
1031
- unless (exists($seendirs{$git->{dir}})) {
1032
- prepdir($git->{dir}, $state->{CVSROOT} . "/$module/",
1033
- $checkout_path, \%seendirs);
1034
- $lastdir = $git->{dir};
1035
- $seendirs{$git->{dir}} = 1;
1036
- }
1037
- print "E cvs checkout: Updating /$checkout_path/$git->{dir}\n";
1038
- }
1009
+ unless (exists($seendirs{$git->{dir}})) {
1010
+ prepDirForOutput($git->{dir}, $state->{CVSROOT} . "/$module/",
1011
+ $checkout_path, \%seendirs, 'checkout',
1012
+ $state->{dirArgs} );
1013
+ $lastdir = $git->{dir};
1014
+ $seendirs{$git->{dir}} = 1;
1015
+ }
1016
1017
# modification time of this file
1018
print "Mod-time $git->{modified}\n";
1032
1033
# this is an "entries" line
1034
my $kopts = kopts_from_path($fullName,"sha1",$git->{filehash});
1058
- print "/$git->{name}/$git->{revision}//$kopts/\n";
1035
+ print "/$git->{name}/$git->{revision}//$kopts/" .
1036
+ getStickyTagOrDate($stickyInfo) . "\n";
1037
# permissions
1038
print "u=$git->{mode},g=$git->{mode},o=$git->{mode}\n";
1039
1046
statecleanup();
1047
}
1048
1049
+# used by req_co and req_update to set up directories for files
1050
+# recursively handles parents
1051
+sub prepDirForOutput
1052
+{
1053
+ my ($dir, $repodir, $remotedir, $seendirs, $request, $dirArgs) = @_;
1054
+
1055
+ my $parent = dirname($dir);
1056
+ $dir =~ s|/+$||;
1057
+ $repodir =~ s|/+$||;
1058
+ $remotedir =~ s|/+$||;
1059
+ $parent =~ s|/+$||;
1060
+
1061
+ if ($parent eq '.' || $parent eq './')
1062
+ {
1063
+ $parent = '';
1064
+ }
1065
+ # recurse to announce unseen parents first
1066
+ if( length($parent) &&
1067
+ !exists($seendirs->{$parent}) &&
1068
+ ( $request eq "checkout" ||
1069
+ exists($dirArgs->{$parent}) ) )
1070
+ {
1071
+ prepDirForOutput($parent, $repodir, $remotedir,
1072
+ $seendirs, $request, $dirArgs);
1073
+ }
1074
+ # Announce that we are going to modify at the parent level
1075
+ if ($dir eq '.' || $dir eq './')
1076
+ {
1077
+ $dir = '';
1078
+ }
1079
+ if(exists($seendirs->{$dir}))
1080
+ {
1081
+ return;
1082
+ }
1083
+ $log->debug("announcedir $dir, $repodir, $remotedir" );
1084
+ my($thisRemoteDir,$thisRepoDir);
1085
+ if ($dir ne "")
1086
+ {
1087
+ $thisRepoDir="$repodir/$dir";
1088
+ if($remotedir eq ".")
1089
+ {
1090
+ $thisRemoteDir=$dir;
1091
+ }
1092
+ else
1093
+ {
1094
+ $thisRemoteDir="$remotedir/$dir";
1095
+ }
1096
+ }
1097
+ else
1098
+ {
1099
+ $thisRepoDir=$repodir;
1100
+ $thisRemoteDir=$remotedir;
1101
+ }
1102
+ unless ( $state->{globaloptions}{-Q} || $state->{globaloptions}{-q} )
1103
+ {
1104
+ print "E cvs $request: Updating $thisRemoteDir\n";
1105
+ }
1106
+
1107
+ my ($opt_r)=$state->{opt}{r};
1108
+ my $stickyInfo;
1109
+ if(exists($state->{opt}{A}))
1110
+ {
1111
+ # $stickyInfo=undef;
1112
+ }
1113
+ elsif( defined($opt_r) && $opt_r ne "" )
1114
+ # || ( defined($state->{opt}{D}) && $state->{opt}{D} ne "" ) # TODO
1115
+ {
1116
+ $stickyInfo={ 'tag' => (defined($opt_r)?$opt_r:undef) };
1117
+
1118
+ # TODO: Convert -D value into the form 2011.04.10.04.46.57,
1119
+ # similar to an entry line's sticky date, without the D prefix.
1120
+ # It sometimes (always?) arrives as something more like
1121
+ # '10 Apr 2011 04:46:57 -0000'...
1122
+ # $stickyInfo={ 'date' => (defined($stickyDate)?$stickyDate:undef) };
1123
+ }
1124
+ else
1125
+ {
1126
+ $stickyInfo=getDirStickyInfo($state->{prependdir} . $dir);
1127
+ }
1128
+
1129
+ my $stickyResponse;
1130
+ if(defined($stickyInfo))
1131
+ {
1132
+ $stickyResponse = "Set-sticky $thisRemoteDir/\n" .
1133
+ "$thisRepoDir/\n" .
1134
+ getStickyTagOrDate($stickyInfo) . "\n";
1135
+ }
1136
+ else
1137
+ {
1138
+ $stickyResponse = "Clear-sticky $thisRemoteDir/\n" .
1139
+ "$thisRepoDir/\n";
1140
+ }
1141
+
1142
+ unless ( $state->{globaloptions}{-n} )
1143
+ {
1144
+ print $stickyResponse;
1145
+
1146
+ print "Clear-static-directory $thisRemoteDir/\n";
1147
+ print "$thisRepoDir/\n";
1148
+ print $stickyResponse; # yes, twice
1149
+ print "Template $thisRemoteDir/\n";
1150
+ print "$thisRepoDir/\n";
1151
+ print "0\n";
1152
+ }
1153
+
1154
+ $seendirs->{$dir} = 1;
1155
+
1156
+ # FUTURE: This would more accurately emulate CVS by sending
1157
+ # another copy of sticky after processing the files in that
1158
+ # directory. Or intermediate: perhaps send all sticky's for
1159
+ # $seendirs after after processing all files.
1160
+}
1161
+
1162
# update \n
1163
# Response expected: yes. Actually do a cvs update command. This uses any
1164
# previous Argument, Directory, Entry, or Modified requests, if they have
1202
1203
#$log->debug("update state : " . Dumper($state));
1204
1114
- my $last_dirname = "///";
1205
+ my($repoDir);
1206
+ $repoDir=$state->{CVSROOT} . "/$state->{module}/$state->{prependdir}";
1207
+
1208
+ my %seendirs = ();
1209
1210
# foreach file specified on the command line ...
1117
- foreach my $filename ( @{$state->{args}} )
1211
+ foreach my $argsFilename ( @{$state->{args}} )
1212
{
1119
- $filename = filecleanup($filename);
1213
+ my $filename;
1214
+ $filename = filecleanup($argsFilename);
1215
1216
$log->debug("Processing file $filename");
1217
1123
- unless ( $state->{globaloptions}{-Q} || $state->{globaloptions}{-q} )
1124
- {
1125
- my $cur_dirname = dirname($filename);
1126
- if ( $cur_dirname ne $last_dirname )
1127
- {
1128
- $last_dirname = $cur_dirname;
1129
- if ( $cur_dirname eq "" )
1130
- {
1131
- $cur_dirname = ".";
1132
- }
1133
- print "E cvs update: Updating $cur_dirname\n";
1134
- }
1135
- }
1136
-
1218
# if we have a -C we should pretend we never saw modified stuff
1219
if ( exists ( $state->{opt}{C} ) )
1220
{
1223
$state->{entries}{$filename}{unchanged} = 1;
1224
}
1225
1145
- my $meta;
1146
- if ( defined($state->{opt}{r}) and $state->{opt}{r} =~ /^(1\.\d+)$/ )
1147
- {
1148
- $meta = $updater->getmeta($filename, $1);
1149
- } else {
1150
- $meta = $updater->getmeta($filename);
1151
- }
1226
+ my $stickyInfo = resolveStickyInfo($filename,
1227
+ $state->{opt}{r},
1228
+ $state->{opt}{D},
1229
+ exists($state->{opt}{A}));
1230
+ my $meta = $updater->getmeta($filename, $stickyInfo);
1231
1232
# If -p was given, "print" the contents of the requested revision.
1233
if ( exists ( $state->{opt}{p} ) ) {
1240
next;
1241
}
1242
1243
+ # Directories:
1244
+ prepDirForOutput(
1245
+ dirname($argsFilename),
1246
+ $repoDir,
1247
+ ".",
1248
+ \%seendirs,
1249
+ "update",
1250
+ $state->{dirArgs} );
1251
+
1252
+ my $wrev = revparse($filename);
1253
+
1254
if ( ! defined $meta )
1255
{
1256
$meta = {
1258
revision => '0',
1259
filehash => 'added'
1260
};
1261
+ if($wrev ne "0")
1262
+ {
1263
+ $meta->{filehash}='deleted';
1264
+ }
1265
}
1266
1267
my $oldmeta = $meta;
1268
1175
- my $wrev = revparse($filename);
1176
-
1269
# If the working copy is an old revision, lets get that version too for comparison.
1178
- if ( defined($wrev) and $wrev ne $meta->{revision} )
1270
+ my $oldWrev=$wrev;
1271
+ if(defined($oldWrev))
1272
{
1180
- $oldmeta = $updater->getmeta($filename, $wrev);
1273
+ $oldWrev=~s/^-//;
1274
+ if($oldWrev ne $meta->{revision})
1275
+ {
1276
+ $oldmeta = $updater->getmeta($filename, $oldWrev);
1277
+ }
1278
}
1279
1280
#$log->debug("Target revision is $meta->{revision}, current working revision is $wrev");
1292
if ( defined ( $wrev )
1293
and defined($meta->{revision})
1294
and $wrev eq $meta->{revision}
1295
+ and $wrev ne "0"
1296
and defined($state->{entries}{$filename}{modified_hash})
1297
and not exists ( $state->{opt}{C} ) )
1298
{
1303
next;
1304
}
1305
1208
- if ( $meta->{filehash} eq "deleted" )
1306
+ if ( $meta->{filehash} eq "deleted" && $wrev ne "0" )
1307
{
1308
# TODO: If it has been modified in the sandbox, error out
1309
# with the appropriate message, rather than deleting a modified
1365
$log->debug("Updating existing file 'Update-existing $dirpart'");
1366
} else {
1367
# instruct client we're sending a file to put in this path as a new file
1270
- print "Clear-static-directory $dirpart\n";
1271
- print $state->{CVSROOT} . "/$state->{module}/$dirpart\n";
1272
- print "Clear-sticky $dirpart\n";
1273
- print $state->{CVSROOT} . "/$state->{module}/$dirpart\n";
1368
1369
$log->debug("Creating new file 'Created $dirpart'");
1370
print "Created $dirpart\n";
1373
1374
# this is an "entries" line
1375
my $kopts = kopts_from_path($filename,"sha1",$meta->{filehash});
1282
- $log->debug("/$filepart/$meta->{revision}//$kopts/");
1283
- print "/$filepart/$meta->{revision}//$kopts/\n";
1376
+ my $entriesLine = "/$filepart/$meta->{revision}//$kopts/";
1377
+ $entriesLine .= getStickyTagOrDate($stickyInfo);
1378
+ $log->debug($entriesLine);
1379
+ print "$entriesLine\n";
1380
1381
# permissions
1382
$log->debug("SEND : u=$meta->{mode},g=$meta->{mode},o=$meta->{mode}");
1424
my $kopts = kopts_from_path("$dirpart/$filepart",
1425
"file",$mergedFile);
1426
$log->debug("/$filepart/$meta->{revision}//$kopts/");
1331
- print "/$filepart/$meta->{revision}//$kopts/\n";
1427
+ my $entriesLine="/$filepart/$meta->{revision}//$kopts/";
1428
+ $entriesLine .= getStickyTagOrDate($stickyInfo);
1429
+ print "$entriesLine\n";
1430
}
1431
}
1432
elsif ( $return == 1 )
1442
print $state->{CVSROOT} . "/$state->{module}/$filename\n";
1443
my $kopts = kopts_from_path("$dirpart/$filepart",
1444
"file",$mergedFile);
1347
- print "/$filepart/$meta->{revision}/+/$kopts/\n";
1445
+ my $entriesLine = "/$filepart/$meta->{revision}/+/$kopts/";
1446
+ $entriesLine .= getStickyTagOrDate($stickyInfo);
1447
+ print "$entriesLine\n";
1448
}
1449
}
1450
else
1472
1473
}
1474
1475
+ # prepDirForOutput() any other existing directories unless they already
1476
+ # have the right sticky tag:
1477
+ unless ( $state->{globaloptions}{n} )
1478
+ {
1479
+ my $dir;
1480
+ foreach $dir (keys(%{$state->{dirMap}}))
1481
+ {
1482
+ if( ! $seendirs{$dir} &&
1483
+ exists($state->{dirArgs}{$dir}) )
1484
+ {
1485
+ my($oldTag);
1486
+ $oldTag=$state->{dirMap}{$dir}{tagspec};
1487
+
1488
+ unless( ( exists($state->{opt}{A}) &&
1489
+ defined($oldTag) ) ||
1490
+ ( defined($state->{opt}{r}) &&
1491
+ ( !defined($oldTag) ||
1492
+ $state->{opt}{r} ne $oldTag ) ) )
1493
+ # TODO?: OR sticky dir is different...
1494
+ {
1495
+ next;
1496
+ }
1497
+
1498
+ prepDirForOutput(
1499
+ $dir,
1500
+ $repoDir,
1501
+ ".",
1502
+ \%seendirs,
1503
+ 'update',
1504
+ $state->{dirArgs} );
1505
+ }
1506
+
1507
+ # TODO?: Consider sending a final duplicate Sticky response
1508
+ # to more closely mimic real CVS.
1509
+ }
1510
+ }
1511
+
1512
print "ok\n";
1513
}
1514
1541
my $updater = GITCVS::updater->new($state->{CVSROOT}, $state->{module}, $log);
1542
$updater->update();
1543
1407
- # Remember where the head was at the beginning.
1408
- my $parenthash = `git show-ref -s refs/heads/$state->{module}`;
1409
- chomp $parenthash;
1410
- if ($parenthash !~ /^[0-9a-f]{40}$/) {
1411
- print "error 1 pserver cannot find the current HEAD of module";
1412
- cleanupWorkTree();
1413
- exit;
1414
- }
1415
-
1416
- setupWorkTree($parenthash);
1417
-
1418
- $log->info("Lockless commit start, basing commit on '$work->{workDir}', index file is '$work->{index}'");
1419
-
1420
- $log->info("Created index '$work->{index}' for head $state->{module} - exit status $?");
1421
-
1544
my @committedfiles = ();
1545
my %oldmeta;
1546
+ my $stickyInfo;
1547
+ my $branchRef;
1548
+ my $parenthash;
1549
1550
# foreach file specified on the command line ...
1551
foreach my $filename ( @{$state->{args}} )
1555
1556
next unless ( exists $state->{entries}{$filename}{modified_filename} or not $state->{entries}{$filename}{unchanged} );
1557
1433
- my $meta = $updater->getmeta($filename);
1558
+ #####
1559
+ # Figure out which branch and parenthash we are committing
1560
+ # to, and setup worktree:
1561
+
1562
+ # should always come from entries:
1563
+ my $fileStickyInfo = resolveStickyInfo($filename);
1564
+ if( !defined($branchRef) )
1565
+ {
1566
+ $stickyInfo = $fileStickyInfo;
1567
+ if( defined($stickyInfo) &&
1568
+ ( defined($stickyInfo->{date}) ||
1569
+ !defined($stickyInfo->{tag}) ) )
1570
+ {
1571
+ print "error 1 cannot commit with sticky date for file `$filename'\n";
1572
+ cleanupWorkTree();
1573
+ exit;
1574
+ }
1575
+
1576
+ $branchRef = "refs/heads/$state->{module}";
1577
+ if ( defined($stickyInfo) && defined($stickyInfo->{tag}) )
1578
+ {
1579
+ $branchRef = "refs/heads/$stickyInfo->{tag}";
1580
+ }
1581
+
1582
+ $parenthash = `git show-ref -s $branchRef`;
1583
+ chomp $parenthash;
1584
+ if ($parenthash !~ /^[0-9a-f]{40}$/)
1585
+ {
1586
+ if ( defined($stickyInfo) && defined($stickyInfo->{tag}) )
1587
+ {
1588
+ print "error 1 sticky tag `$stickyInfo->{tag}' for file `$filename' is not a branch\n";
1589
+ }
1590
+ else
1591
+ {
1592
+ print "error 1 pserver cannot find the current HEAD of module";
1593
+ }
1594
+ cleanupWorkTree();
1595
+ exit;
1596
+ }
1597
+
1598
+ setupWorkTree($parenthash);
1599
+
1600
+ $log->info("Lockless commit start, basing commit on '$work->{workDir}', index file is '$work->{index}'");
1601
+
1602
+ $log->info("Created index '$work->{index}' for head $state->{module} - exit status $?");
1603
+ }
1604
+ elsif( !refHashEqual($stickyInfo,$fileStickyInfo) )
1605
+ {
1606
+ #TODO: We could split the cvs commit into multiple
1607
+ # git commits by distinct stickyTag values, but that
1608
+ # is lowish priority.
1609
+ print "error 1 Committing different files to different"
1610
+ . " branches is not currently supported\n";
1611
+ cleanupWorkTree();
1612
+ exit;
1613
+ }
1614
+
1615
+ #####
1616
+ # Process this file:
1617
+
1618
+ my $meta = $updater->getmeta($filename,$stickyInfo);
1619
$oldmeta{$filename} = $meta;
1620
1621
my $wrev = revparse($filename);
1717
}
1718
1719
### Emulate git-receive-pack by running hooks/update
1535
- my @hook = ( $ENV{GIT_DIR}.'hooks/update', "refs/heads/$state->{module}",
1720
+ my @hook = ( $ENV{GIT_DIR}.'hooks/update', $branchRef,
1721
$parenthash, $commithash );
1722
if( -x $hook[0] ) {
1723
unless( system( @hook ) == 0 )
1731
1732
### Update the ref
1733
if (system(qw(git update-ref -m), "cvsserver ci",
1549
- "refs/heads/$state->{module}", $commithash, $parenthash)) {
1734
+ $branchRef, $commithash, $parenthash)) {
1735
$log->warn("update-ref for $state->{module} failed.");
1736
print "error 1 Cannot commit -- update first\n";
1737
cleanupWorkTree();
1745
1746
local $SIG{PIPE} = sub { die 'pipe broke' };
1747
1563
- print $pipe "$parenthash $commithash refs/heads/$state->{module}\n";
1748
+ print $pipe "$parenthash $commithash $branchRef\n";
1749
1750
close $pipe || die "bad pipe: $! $?";
1751
}
1755
### Then hooks/post-update
1756
$hook = $ENV{GIT_DIR}.'hooks/post-update';
1757
if (-x $hook) {
1573
- system($hook, "refs/heads/$state->{module}");
1758
+ system($hook, $branchRef);
1759
}
1760
1761
# foreach file specified on the command line ...
1763
{
1764
$filename = filecleanup($filename);
1765
1581
- my $meta = $updater->getmeta($filename);
1766
+ my $meta = $updater->getmeta($filename,$stickyInfo);
1767
unless (defined $meta->{revision}) {
1768
$meta->{revision} = "1.1";
1769
}
1787
print "Checked-in $dirpart\n";
1788
print "$filename\n";
1789
my $kopts = kopts_from_path($filename,"sha1",$meta->{filehash});
1605
- print "/$filepart/$meta->{revision}//$kopts/\n";
1790
+ print "/$filepart/$meta->{revision}//$kopts/" .
1791
+ getStickyTagOrDate($stickyInfo) . "\n";
1792
}
1793
}
1794
1825
next;
1826
}
1827
1642
- my $meta = $updater->getmeta($filename);
1643
- my $oldmeta = $meta;
1644
-
1828
my $wrev = revparse($filename);
1829
1830
+ my $stickyInfo = resolveStickyInfo($filename);
1831
+ my $meta = $updater->getmeta($filename,$stickyInfo);
1832
+ my $oldmeta = $meta;
1833
+
1834
# If the working copy is an old revision, lets get that
1835
# version too for comparison.
1836
if ( defined($wrev) and $wrev ne $meta->{revision} )
1837
{
1651
- $oldmeta = $updater->getmeta($filename, $wrev);
1838
+ my($rmRev)=$wrev;
1839
+ $rmRev=~s/^-//;
1840
+ $oldmeta = $updater->getmeta($filename, $rmRev);
1841
}
1842
1843
# TODO : All possible statuses aren't yet implemented
1880
# same revision but there are local changes
1881
if ( defined ( $wrev ) and defined($meta->{revision}) and
1882
$wrev eq $meta->{revision} and
1883
+ $wrev ne "0" and
1884
$state->{entries}{$filename}{modified_filename} )
1885
{
1886
$status ||= "Locally Modified";
1896
}
1897
1898
if ( defined ( $state->{entries}{$filename}{revision} ) and
1709
- not defined ( $meta->{revision} ) )
1899
+ ( !defined($meta->{revision}) ||
1900
+ $meta->{revision} eq "0" ) )
1901
{
1902
$status ||= "Locally Added";
1903
}
1993
# be providing status on ...
1994
argsfromdir($updater);
1995
1996
+ my($foundDiff);
1997
+
1998
# foreach file specified on the command line ...
1806
- foreach my $filename ( @{$state->{args}} )
1999
+ foreach my $argFilename ( @{$state->{args}} )
2000
{
1808
- $filename = filecleanup($filename);
2001
+ my($filename) = filecleanup($argFilename);
2002
2003
my ( $fh, $file1, $file2, $meta1, $meta2, $filediff );
2004
2005
my $wrev = revparse($filename);
2006
1814
- # We need _something_ to diff against
1815
- next unless ( defined ( $wrev ) );
2007
+ # Priority for revision1:
2008
+ # 1. First -r (missing file: check -N)
2009
+ # 2. wrev from client's Entry line
2010
+ # - missing line/file: check -N
2011
+ # - "0": added file not committed (empty contents for rev1)
2012
+ # - Prefixed with dash (to be removed): check -N
2013
1817
- # if we have a -r switch, use it
2014
if ( defined ( $revision1 ) )
2015
{
1820
- ( undef, $file1 ) = tempfile( DIR => $TEMP_DIR, OPEN => 0 );
2016
$meta1 = $updater->getmeta($filename, $revision1);
1822
- unless ( defined ( $meta1 ) and $meta1->{filehash} ne "deleted" )
2017
+ }
2018
+ elsif( defined($wrev) && $wrev ne "0" )
2019
+ {
2020
+ my($rmRev)=$wrev;
2021
+ $rmRev=~s/^-//;
2022
+ $meta1 = $updater->getmeta($filename, $rmRev);
2023
+ }
2024
+ if ( !defined($meta1) ||
2025
+ $meta1->{filehash} eq "deleted" )
2026
+ {
2027
+ if( !exists($state->{opt}{N}) )
2028
{
1824
- print "E File $filename at revision $revision1 doesn't exist\n";
2029
+ if(!defined($revision1))
2030
+ {
2031
+ print "E File $filename at revision $revision1 doesn't exist\n";
2032
+ }
2033
next;
2034
}
1827
- transmitfile($meta1->{filehash}, { targetfile => $file1 });
1828
- }
1829
- # otherwise we just use the working copy revision
1830
- else
1831
- {
1832
- ( undef, $file1 ) = tempfile( DIR => $TEMP_DIR, OPEN => 0 );
1833
- $meta1 = $updater->getmeta($filename, $wrev);
1834
- transmitfile($meta1->{filehash}, { targetfile => $file1 });
2035
+ elsif( !defined($meta1) )
2036
+ {
2037
+ $meta1 = {
2038
+ name => $filename,
2039
+ revision => '0',
2040
+ filehash => 'deleted'
2041
+ };
2042
+ }
2043
}
2044
2045
+ # Priority for revision2:
2046
+ # 1. Second -r (missing file: check -N)
2047
+ # 2. Modified file contents from client
2048
+ # 3. wrev from client's Entry line
2049
+ # - missing line/file: check -N
2050
+ # - Prefixed with dash (to be removed): check -N
2051
+
2052
# if we have a second -r switch, use it too
2053
if ( defined ( $revision2 ) )
2054
{
1840
- ( undef, $file2 ) = tempfile( DIR => $TEMP_DIR, OPEN => 0 );
2055
$meta2 = $updater->getmeta($filename, $revision2);
1842
-
1843
- unless ( defined ( $meta2 ) and $meta2->{filehash} ne "deleted" )
1844
- {
1845
- print "E File $filename at revision $revision2 doesn't exist\n";
1846
- next;
1847
- }
1848
-
1849
- transmitfile($meta2->{filehash}, { targetfile => $file2 });
2056
}
1851
- # otherwise we just use the working copy
1852
- else
2057
+ elsif(defined($state->{entries}{$filename}{modified_filename}))
2058
{
2059
$file2 = $state->{entries}{$filename}{modified_filename};
2060
+ $meta2 = {
2061
+ name => $filename,
2062
+ revision => '0',
2063
+ filehash => 'modified'
2064
+ };
2065
}
1856
-
1857
- # if we have been given -r, and we don't have a $file2 yet, lets
1858
- # get one
1859
- if ( defined ( $revision1 ) and not defined ( $file2 ) )
2066
+ elsif( defined($wrev) && ($wrev!~/^-/) )
2067
{
1861
- ( undef, $file2 ) = tempfile( DIR => $TEMP_DIR, OPEN => 0 );
2068
+ if(!defined($revision1)) # no revision and no modifications:
2069
+ {
2070
+ next;
2071
+ }
2072
$meta2 = $updater->getmeta($filename, $wrev);
1863
- transmitfile($meta2->{filehash}, { targetfile => $file2 });
2073
+ }
2074
+ if(!defined($file2))
2075
+ {
2076
+ if ( !defined($meta2) ||
2077
+ $meta2->{filehash} eq "deleted" )
2078
+ {
2079
+ if( !exists($state->{opt}{N}) )
2080
+ {
2081
+ if(!defined($revision2))
2082
+ {
2083
+ print "E File $filename at revision $revision2 doesn't exist\n";
2084
+ }
2085
+ next;
2086
+ }
2087
+ elsif( !defined($meta2) )
2088
+ {
2089
+ $meta2 = {
2090
+ name => $filename,
2091
+ revision => '0',
2092
+ filehash => 'deleted'
2093
+ };
2094
+ }
2095
+ }
2096
}
2097
1866
- # We need to have retrieved something useful
1867
- next unless ( defined ( $meta1 ) );
1868
-
1869
- # Files to date if the working copy and repo copy have the same
1870
- # revision, and the working copy is unmodified
1871
- if ( not defined ( $meta2 ) and $wrev eq $meta1->{revision} and
1872
- ( ( $state->{entries}{$filename}{unchanged} and
1873
- ( not defined ( $state->{entries}{$filename}{conflict} ) or
1874
- $state->{entries}{$filename}{conflict} !~ /^\+=/ ) ) or
1875
- ( defined($state->{entries}{$filename}{modified_hash}) and
1876
- $state->{entries}{$filename}{modified_hash} eq
1877
- $meta1->{filehash} ) ) )
2098
+ if( $meta1->{filehash} eq $meta2->{filehash} )
2099
{
2100
+ $log->info("unchanged $filename");
2101
next;
2102
}
2103
1882
- # Apparently we only show diffs for locally modified files
1883
- unless ( defined($meta2) or
1884
- defined ( $state->{entries}{$filename}{modified_filename} ) )
2104
+ # Retrieve revision contents:
2105
+ ( undef, $file1 ) = tempfile( DIR => $TEMP_DIR, OPEN => 0 );
2106
+ transmitfile($meta1->{filehash}, { targetfile => $file1 });
2107
+
2108
+ if(!defined($file2))
2109
{
1886
- next;
2110
+ ( undef, $file2 ) = tempfile( DIR => $TEMP_DIR, OPEN => 0 );
2111
+ transmitfile($meta2->{filehash}, { targetfile => $file2 });
2112
}
2113
1889
- print "M Index: $filename\n";
2114
+ # Generate the actual diff:
2115
+ print "M Index: $argFilename\n";
2116
print "M =======" . ( "=" x 60 ) . "\n";
2117
print "M RCS file: $state->{CVSROOT}/$state->{module}/$filename,v\n";
1892
- if ( defined ( $meta1 ) )
2118
+ if ( defined ( $meta1 ) && $meta1->{revision} ne "0" )
2119
{
2120
print "M retrieving revision $meta1->{revision}\n"
2121
}
1896
- if ( defined ( $meta2 ) )
2122
+ if ( defined ( $meta2 ) && $meta2->{revision} ne "0" )
2123
{
2124
print "M retrieving revision $meta2->{revision}\n"
2125
}
2140
}
2141
}
2142
}
1917
- print "$filename\n";
2143
+ print "$argFilename\n";
2144
2145
$log->info("Diffing $filename -r $meta1->{revision} -r " .
2146
( $meta2->{revision} or "workingcopy" ));
2147
1922
- ( $fh, $filediff ) = tempfile ( DIR => $TEMP_DIR );
1923
-
1924
- if ( exists $state->{opt}{u} )
2148
+ # TODO: Use --label instead of -L because -L is no longer
2149
+ # documented and may go away someday. Not sure if there there are
2150
+ # versions that only support -L, which would make this change risky?
2151
+ # http://osdir.com/ml/bug-gnu-utils-gnu/2010-12/msg00060.html
2152
+ # ("man diff" should actually document the best migration strategy,
2153
+ # [current behavior, future changes, old compatibility issues
2154
+ # or lack thereof, etc], not just stop mentioning the option...)
2155
+ # TODO: Real CVS seems to include a date in the label, before
2156
+ # the revision part, without the keyword "revision". The following
2157
+ # has minimal changes compared to original versions of
2158
+ # git-cvsserver.perl. (Mostly tab vs space after filename.)
2159
+
2160
+ my (@diffCmd) = ( 'diff' );
2161
+ if ( exists($state->{opt}{N}) )
2162
{
1926
- system("diff -u -L '$filename revision $meta1->{revision}'" .
1927
- " -L '$filename " .
1928
- ( defined($meta2->{revision}) ?
1929
- "revision $meta2->{revision}" :
1930
- "working copy" ) .
1931
- "' $file1 $file2 > $filediff" );
1932
- } else {
1933
- system("diff $file1 $file2 > $filediff");
2163
+ push @diffCmd,"-N";
2164
}
2165
+ if ( exists $state->{opt}{u} )
2166
+ {
2167
+ push @diffCmd,("-u","-L");
2168
+ if( $meta1->{filehash} eq "deleted" )
2169
+ {
2170
+ push @diffCmd,"/dev/null";
2171
+ } else {
2172
+ push @diffCmd,("$argFilename\trevision $meta1->{revision}");
2173
+ }
2174
1936
- while ( <$fh> )
2175
+ if( defined($meta2->{filehash}) )
2176
+ {
2177
+ if( $meta2->{filehash} eq "deleted" )
2178
+ {
2179
+ push @diffCmd,("-L","/dev/null");
2180
+ } else {
2181
+ push @diffCmd,("-L",
2182
+ "$argFilename\trevision $meta2->{revision}");
2183
+ }
2184
+ } else {
2185
+ push @diffCmd,("-L","$argFilename\tworking copy");
2186
+ }
2187
+ }
2188
+ push @diffCmd,($file1,$file2);
2189
+ if(!open(DIFF,"-|",@diffCmd))
2190
{
1938
- print "M $_";
2191
+ $log->warn("Unable to run diff: $!");
2192
}
1940
- close $fh;
2193
+ my($diffLine);
2194
+ while(defined($diffLine=<DIFF>))
2195
+ {
2196
+ print "M $diffLine";
2197
+ $foundDiff=1;
2198
+ }
2199
+ close(DIFF);
2200
}
2201
1943
- print "ok\n";
2202
+ if($foundDiff)
2203
+ {
2204
+ print "error \n";
2205
+ }
2206
+ else
2207
+ {
2208
+ print "ok\n";
2209
+ }
2210
}
2211
2212
sub req_log
2426
$opt = { A => 0, N => 0, P => 0, R => 0, c => 0, f => 0, l => 0, n => 0, p => 0, s => 0, r => 1, D => 1, d => 1, k => 1, j => 1, } if ( $type eq "co" );
2427
$opt = { v => 0, l => 0, R => 0 } if ( $type eq "status" );
2428
$opt = { A => 0, P => 0, C => 0, d => 0, f => 0, l => 0, R => 0, p => 0, k => 1, r => 1, D => 1, j => 1, I => 1, W => 1 } if ( $type eq "update" );
2163
- $opt = { l => 0, R => 0, k => 1, D => 1, D => 1, r => 2 } if ( $type eq "diff" );
2429
+ $opt = { l => 0, R => 0, k => 1, D => 1, D => 1, r => 2, N => 0 } if ( $type eq "diff" );
2430
$opt = { c => 0, R => 0, l => 0, f => 0, F => 1, m => 1, r => 1 } if ( $type eq "ci" );
2431
$opt = { k => 1, m => 1 } if ( $type eq "add" );
2432
$opt = { f => 0, l => 0, R => 0 } if ( $type eq "remove" );
3367
return $ret;
3368
}
3369
3370
+# Test if the (deep) values of two references to a hash are the same.
3371
+sub refHashEqual
3372
+{
3373
+ my($v1,$v2) = @_;
3374
+
3375
+ my $out;
3376
+ if(!defined($v1))
3377
+ {
3378
+ if(!defined($v2))
3379
+ {
3380
+ $out=1;
3381
+ }
3382
+ }
3383
+ elsif( !defined($v2) ||
3384
+ scalar(keys(%{$v1})) != scalar(keys(%{$v2})) )
3385
+ {
3386
+ # $out=undef;
3387
+ }
3388
+ else
3389
+ {
3390
+ $out=1;
3391
+
3392
+ my $key;
3393
+ foreach $key (keys(%{$v1}))
3394
+ {
3395
+ if( !exists($v2->{$key}) ||
3396
+ defined($v1->{$key}) ne defined($v2->{$key}) ||
3397
+ ( defined($v1->{$key}) &&
3398
+ $v1->{$key} ne $v2->{$key} ) )
3399
+ {
3400
+ $out=undef;
3401
+ last;
3402
+ }
3403
+ }
3404
+ }
3405
+
3406
+ return $out;
3407
+}
3408
+
3409
3410
package GITCVS::log;
3411
3811
my $lastcommit = $self->_get_prop("last_commit");
3812
3813
if (defined $lastcommit && $lastcommit eq $commitsha1) { # up-to-date
3814
+ # invalidate the gethead cache
3815
+ $self->clearCommitRefCaches();
3816
return 1;
3817
}
3818