36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
|
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
|
+
+
+
-
-
+
+
-
-
-
-
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
|
# Locate fossil root for the given directory.
set info [exec fossil info]
regexp -line {local-root:\s*(\S.*)} $info -> root
set root [file normalize $root]
cd $root
# Getting files via artifact
# This is a quick and robust way to get the file tree and each file's sha
# Other info is trickier and is handled below
set artifact [exec fossil artifact $rev]
set commitTime 0
set cTime now
set finfo {}
foreach line [split $artifact \n] {
# Expected format in a line:
# F tests/left.txt c1572b3809a1ba6ab2de9307c96b1cfeefdcf0ba
# D 2015-02-23T23:30:07.509
if {[regexp {D (.*)} $line -> cTime]} {
# Remove decimals and middle T
regsub {\.\d+} $cTime "" cTime
regsub {T} $cTime " " cTime
set commitTime [clock scan $cTime -gmt 1]
}
if {[regexp {F (\S+) (\S+)} $line -> fName fSha]} {
# File names can have spaces, coded with \s
set fName [string map {\\s " "} $fName]
dict set finfo $fName sha $fSha
dict set finfo $fName mtimestr $cTime ;# Anything
dict set finfo $fName type file
dict set finfo $fName isfile 1
dict set finfo $fName isdir 0
# TBD: Delay calling whatis until size is needed
# Expected format in a line:
# Setting size is delayed until needed since the needed
# calls are relatively expensive.
# size: 629 bytes
set whatis [exec fossil whatis $fSha]
regexp {size:\s+(\d+)} $whatis -> fSize
dict set finfo $fName size $fSize
# Mark all known directory paths and build up file tree info
set parentStr ""
foreach dirPath [file split $fName] {
dict set finfo $parentStr child $dirPath 1
dict set finfo $parentStr isfile 0
dict set finfo $parentStr isdir 1
dict set finfo $parentStr type directory
set parentStr [file join $parentStr $dirPath]
}
}
}
# Getting files via http fileage to aquire file times
# Since dates are parsed from the age string they are rather imprecise
# Use a while around it to be able to break free easily (faking goto)
while 1 {
set html [exec fossil http << "GET /fileage?name=$rev"]
regexp {Files in.*} $html html
regexp {\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}} $html cTime2
set commitTime2 [clock scan $cTime2 -gmt 1]
#puts "CT $commitTime CT2 $commitTime2"
foreach row [regexp -all -inline {<tr>.*?</tr>} $html] {
set cols [regexp -all -inline {<td>(.*?)</td>} $row]
set col1 [string trim [lindex $cols 1]]
set col2 [string trim [lindex $cols 3]]
# First column is age, in readable format
# e.g. "current" "36.4 minutes" "97.0 days" "1.06 years"
if {$col1 eq ""} continue
if {$col1 eq "current"} {
set fTime $commitTime
} else {
set value [lindex $col1 0]
set unit [lindex $col1 1]
switch -glob $unit {
second* {
set value [expr {int($value)}]
set unit second
}
minute* {
set value [expr {int($value*60)}]
set unit second
}
hour* {
set value [expr {int($value*60*60)}]
set unit second
}
day* {
set value [expr {int($value*60*60*24)}]
set unit second
}
year* {
set value [expr {int($value*60*60*24*365)}]
set unit second
}
default {
puts "Unhandled unit: $unit in '$col1'"
set value [expr {int($value)}]
}
}
set fTime [expr {$commitTime - $value}]
}
#puts "AGE $col1 -> $fTime"
set html [exec fossil http << "GET /fileage?name=$rev"]
if {![regexp {Files in.*} $html html]} {
# Not the expected format of response, skip
break
}
if {![regexp {\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}} $html cTime2]} {
# Not the expected format of response, skip
break
}
# This is currently unused since we do not trust the formatted time in
# the web page. The time stamp from the artifact is used later.
set commitTime2 [clock scan $cTime2 -gmt 1]
#puts "CT $commitTime CT2 $commitTime2"
# Rows in the HTML table
foreach row [regexp -all -inline {<tr>.*?</tr>} $html] {
# Columns in the HTML table
set cols [regexp -all -inline {<td>(.*?)</td>} $row]
set col1 [string trim [lindex $cols 1]]
set col2 [string trim [lindex $cols 3]]
# First column is age, in readable format
# e.g. "current" "36.4 minutes" "97.0 days" "1.06 years"
if {$col1 eq ""} continue
if {$col1 eq "current"} {
set fTime $commitTime
} else {
set value [lindex $col1 0]
set unit [lindex $col1 1]
switch -glob $unit {
second* {
set value [expr {int($value)}]
set unit second
}
minute* {
set value [expr {int($value*60)}]
set unit second
}
hour* {
set value [expr {int($value*60*60)}]
set unit second
}
day* {
set value [expr {int($value*60*60*24)}]
set unit second
}
year* {
set value [expr {int($value*60*60*24*365)}]
set unit second
}
default {
puts "Unhandled unit: $unit in '$col1'"
set value [expr {int($value)}]
}
}
set fTime [expr {$commitTime - $value}]
}
#puts "AGE $col1 -> $fTime"
# Second column is file names, separated by <br>
# Remove links
regsub -all {<a .*?>} $col2 "" col2
regsub -all {</a>} $col2 "" col2
regsub -all {\n} $col2 "" col2
regsub -all {<br>} $col2 "\n" col2
set col2 [string trim $col2]
foreach fName [split $col2 \n] {
#puts $fName
dict set finfo $fName mtime $fTime
}
# Second column is file names, separated by <br>
# Remove links
regsub -all {<a .*?>} $col2 "" col2
regsub -all {</a>} $col2 "" col2
regsub -all {\n} $col2 "" col2
regsub -all {<br>} $col2 "\n" col2
set col2 [string trim $col2]
foreach fName [split $col2 \n] {
# Check that it matches something filled in from the artifact
if {[dict exists $finfo $fName]} {
dict set finfo $fName mtime $fTime
}
}
}
# Kill surrounding while loop
break
}
cd $oldpwd
# Generate a mount point.
set tail [string range $dir [string length $root] end]
set mountpoint "${root} ($rev)"
|
296
297
298
299
300
301
302
303
304
305
306
307
308
309
|
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
|
+
|
} else {
# This is a file
dict set finfo $fName isfile 1
dict set finfo $fName isdir 0
dict set finfo $fName "type" file
dict set finfo $fName "sha" $sha
dict set finfo $fName "size" $size
# TBD: Delay this call until mtime is needed?
set mtime [exec git log --pretty=format:%ct -n 1 $fName]
dict set finfo $fName "mtime" $mtime
}
# Mark all known directory paths and build up file tree info
set parentStr ""
foreach dirPath [file split $fName] {
dict set finfo $parentStr child $dirPath 1
|
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
|
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
|
+
+
+
+
+
+
+
+
+
+
-
+
+
-
+
+
|
#set data [read $chId]
#close $chId
cd $oldpwd
#set chId [vcsvfs::CreateDataRefChan $data]
return [list $chId [list vcsvfs::ReadAllBeforeClose $chId]]
}
# Fossil may delay filling in size, this takes car of that
proc vcsvfs::fossil::size {finfo} {
# Use "fossil whatis" on its sha
# Expected format in a line:
# size: 629 bytes
set whatis [exec fossil whatis [dict get $finfo sha]]
regexp {size:\s+(\d+)} $whatis -> fSize
return $fSize
}
# Parse a time string from Fossil
proc vcsvfs::fossil::mTime {mtimestr} {
proc vcsvfs::fossil::mTime {finfo} {
set mtimestr [dict get $finfo mtimestr]
# TBD parse to mtime correct?
set mtime [clock scan $mtimestr -gmt 1]
return $mtime
}
# Parse a time string from Subversion
proc vcsvfs::svn::mTime {mtimestr} {
proc vcsvfs::svn::mTime {finfo} {
set mtimestr [dict get $finfo mtimestr]
# TBD parse to mtime correct?
# Remove any decimals from time string
regsub {\.\d+Z} $mtimestr "" mtimestr
set mtime [clock scan $mtimestr -gmt 1]
return $mtime
}
|
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
|
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
|
+
-
+
-
+
+
+
+
+
+
|
return [vcsvfs::${vcstype}::openFile $rootD $relative]
}
stat {
set res [dict create dev 0 ino 0 "mode" 0 nlink 0 uid 0 gid 0 \
size 0 atime 0 mtime 0 ctime 0 type file]
dict set res type [dict get $finfor type]
if {[dict get $finfor isfile]} {
# Fill in any postponed info
if {![dict exists $finfor mtime]} {
set mtime [vcsvfs::${vcstype}::mTime \
set mtime [vcsvfs::${vcstype}::mTime $finfor]
[dict get $finfor mtimestr]]
dict set finfor "mtime" $mtime
# Cache in main dictionary too
dict set mpoints $root "finfo" $relative "mtime" $mtime
}
if {![dict exists $finfor size]} {
set size [vcsvfs::${vcstype}::size $finfor]
dict set finfor "size" $size
# Cache in main dictionary too
dict set mpoints $root "finfo" $relative "size" $size
}
dict set res "mtime" [dict get $finfor "mtime"]
dict set res size [dict get $finfor size]
}
return $res
}
createdirectory - deletefile - removedirectory - utime {
# Read-only, always error
|