Eskil

Check-in [0827ab89b0]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Added SVN support to VFS and dirdiff
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 0827ab89b052d69c6be6de8836efbb53fc71feae
User & Date: peter 2014-11-14 00:14:19.606
Context
2014-11-16
23:27
First working plugin in dirdiff check-in: d8ae37f9be user: peter tags: trunk
2014-11-14
00:14
Added SVN support to VFS and dirdiff check-in: 0827ab89b0 user: peter tags: trunk
2014-11-13
23:12
Moved mount to rev, making dirdiff support extendable to other vcs check-in: d0b469ea11 user: peter tags: trunk
Changes
Unified Diff Ignore Whitespace Patch
Changes to src/rev.tcl.
1083
1084
1085
1086
1087
1088
1089





1090
1091
1092
1093
1094
1095
1096
    }
}

# Mount a directory revision as a VFS, and return the mount point
proc eskil::rev::FOSSIL::mount {dir rev} {
    return [vcsvfs::fossil::mount $dir $rev]
}






# View log between displayed versions
proc eskil::rev::CVS::viewLog {top filename revs} {
    set cmd [list exec cvs -q log -N]
    if {[llength $revs] > 1} {
        lappend cmd -r[join $revs ":"]
    } else {







>
>
>
>
>







1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
    }
}

# Mount a directory revision as a VFS, and return the mount point
proc eskil::rev::FOSSIL::mount {dir rev} {
    return [vcsvfs::fossil::mount $dir $rev]
}

# Mount a directory revision as a VFS, and return the mount point
proc eskil::rev::SVN::mount {dir rev} {
    return [vcsvfs::svn::mount $dir $rev]
}

# View log between displayed versions
proc eskil::rev::CVS::viewLog {top filename revs} {
    set cmd [list exec cvs -q log -N]
    if {[llength $revs] > 1} {
        lappend cmd -r[join $revs ":"]
    } else {
Changes to src/vcsvfs.tcl.
10
11
12
13
14
15
16



17
18
19
20
21
22
23
package provide vcsvfs 0.1

namespace eval vcsvfs {
    variable DataRefChan
    namespace eval fossil {
        variable mpoints {}
    }



}

# Create a Virtual File System showing a revision of a fossil checkout
#
# dir: Directory in a fossil checkout
# rev: Revision to mount
#







>
>
>







10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package provide vcsvfs 0.1

namespace eval vcsvfs {
    variable DataRefChan
    namespace eval fossil {
        variable mpoints {}
    }
    namespace eval svn {
        variable mpoints {}
    }
}

# Create a Virtual File System showing a revision of a fossil checkout
#
# dir: Directory in a fossil checkout
# rev: Revision to mount
#
93
94
95
96
97
98
99





























































































100
101
102
103
104
105
106

proc vcsvfs::fossil::unmount {dir} {
    variable mpoints
    # TBD: Find the mountpoint
    #dict unset mpoints $mountpoint
    #vfs::filesystem unmount $mountpoint
}






























































































# Handler for Reflected Channel
proc vcsvfs::DataRefChan {id cmd chId args} {
    variable DataRefChan
    switch $cmd {
        initialize {
            set mode [lindex $args 0]







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







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
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202

proc vcsvfs::fossil::unmount {dir} {
    variable mpoints
    # TBD: Find the mountpoint
    #dict unset mpoints $mountpoint
    #vfs::filesystem unmount $mountpoint
}

# Create a Virtual File System showing a revision of an SVN checkout
#
# dir: Directory in an SVN checkout
# rev: Revision to mount
#
# Returns: path to the generated VFS
proc vcsvfs::svn::mount {dir rev} {
    variable mpoints
    set dir [file normalize $dir]

    # Command must be run within the dir, so temporarily change pwd
    set oldpwd [pwd]
    cd $dir

    # The mount point will normally be at the wc root, even if
    # a sub directory was given.
    # Locate root for the given directory.
    set info [exec svn info]
    if {![regexp -line {Working Copy Root Path:\s*(\S.*)} $info -> root]} {
        # Fallback to given dir
        set root .
    }
    # TBD: Always root at given dir, for speed
    set root .
    set root [file normalize $root]
    cd $root

    # Getting files via ls
    set allfiles [exec svn ls -R -r $rev]
    foreach line [split $allfiles \n] {
        # Each line is one file/dir name
        set fName $line
        if {[string index $fName end] eq "/"} {
            # This is a directory, strip the /
            set fName [string range $fName 0 end-1]
            dict set finfo $fName isfile 0
            dict set finfo $fName isdir 1
        } else {
            # This is a file
            dict set finfo $fName isfile 1
            dict set finfo $fName isdir 0
        }
        # 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
            set parentStr [file join $parentStr $dirPath]
        }
    }

    set xml [exec svn ls -R -r $rev --xml]
    # TBD real xml parser
    foreach line [split $xml \n] {
        if {[regexp {<name>(.*)</name>} $line -> fName]} {
            continue
        }
        if {[regexp {<date>(.*)</date>} $line -> fDate]} {
            dict set finfo $fName mtimestr $fDate
            continue
        }
        if {[regexp {<size>(.*)</size>} $line -> fSize]} {
            dict set finfo $fName size $fSize
            continue
        }
    }

    cd $oldpwd

    # Generate a mount point.
    set tail [string range $dir [string length $root] end]
    set mountpoint "${root} ($rev)"

    dict set mpoints $mountpoint "finfo" $finfo
    dict set mpoints $mountpoint "origroot" $root
    dict set mpoints $mountpoint "rev" $rev
    vfs::filesystem mount $mountpoint [list vcsvfs::svn::Vfs]

    set result $mountpoint$tail
    #puts $result
    #puts [dict size $finfo]
    #puts [dict get $finfo [lindex $finfo 0]]
    return $result
}

proc vcsvfs::svn::unmount {dir} {
    variable mpoints
    # TBD: Find the mountpoint
    #dict unset mpoints $mountpoint
    #vfs::filesystem unmount $mountpoint
}

# Handler for Reflected Channel
proc vcsvfs::DataRefChan {id cmd chId args} {
    variable DataRefChan
    switch $cmd {
        initialize {
            set mode [lindex $args 0]
145
146
147
148
149
150
151






152
153
154
155
156
157
158
159
    set types [lindex $args 1]
    set allowFile 0
    set allowDir 0
    if {[vfs::matchDirectories $types]} {set allowDir 1}
    if {[vfs::matchFiles $types]} {set allowFile 1}

    set result {}






    foreach child [dict keys [dict get $finfo $relative child]] {
        if {![string match $pattern $child]} continue
        set local [file join $relative $child]
        if {[dict get $finfo $local isfile] && !$allowFile} continue
        if {[dict get $finfo $local isdir] && !$allowDir} continue
        lappend result [file join $actual $child]
    }
    return $result







>
>
>
>
>
>
|







241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
    set types [lindex $args 1]
    set allowFile 0
    set allowDir 0
    if {[vfs::matchDirectories $types]} {set allowDir 1}
    if {[vfs::matchFiles $types]} {set allowFile 1}

    set result {}
    if {[dict exists $finfo $relative child]} {
        set childD [dict get $finfo $relative child]
    } else {
        # Empty dir
        return {}
    }
    foreach child [dict keys $childD] {
        if {![string match $pattern $child]} continue
        set local [file join $relative $child]
        if {[dict get $finfo $local isfile] && !$allowFile} continue
        if {[dict get $finfo $local isdir] && !$allowDir} continue
        lappend result [file join $actual $child]
    }
    return $result
249
250
251
252
253
254
255


























































































256
257
258
259
260
261
262
            # Read-only, always error
        }
    }
    vfs::filesystem posixerror $::vfs::posix(EACCES)
    return -code error $::vfs::posix(EACCES)
}




























































































##################################################################
# Test structure
##################################################################
if 0 {
# File traversing stuff from wiki...
proc ftw_1 {{dirs .}} {







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
            # Read-only, always error
        }
    }
    vfs::filesystem posixerror $::vfs::posix(EACCES)
    return -code error $::vfs::posix(EACCES)
}

# The handler for the mounted VFS
proc vcsvfs::svn::Vfs {subcmd root relative actual args} {
    variable mpoints
    #puts "\nsvnVfs called:"
    #puts " Su $subcmd"
    #puts " Ro $root"
    #puts " Re $relative"
    #puts " Ac $actual"
    #puts " Ar $args"

    set origroot [dict get $mpoints $root origroot]
    set finfo [dict get $mpoints $root finfo]

    if {![dict exists $finfo $relative]} {
        # Unknown path
        vfs::filesystem posixerror $::vfs::posix(EACCES)
        return -code error $::vfs::posix(EACCES)
    }
    set finfor [dict get $finfo $relative]
    #puts " $finfor"

    switch $subcmd {
        access {
            set mode [vfs::accessMode [lindex $args 0]]
            # Only read supported
            if {$mode ne "R"} {
                vfs::filesystem posixerror $::vfs::posix(EACCES)
                return -code error $::vfs::posix(EACCES)
            }
            return
        }
        fileattributes {
            #set index [lindex $args 0]
            #set value [lindex $args 1]
            return
        }
        matchindirectory {
            return [vcsvfs::MatchInDirectory $finfo $relative $actual {*}$args]
        }
        open {
            set mode [lindex $args 0]
            if {$mode == {}} {set mode r}
            #set permissions [lindex $args 1]
            if {$mode ne "r"} {
                # Read only
                vfs::filesystem posixerror $::vfs::posix(EACCES)
                return -code error $::vfs::posix(EACCES)
            }

            set oldpwd [pwd]
            cd [dict get $mpoints $root "origroot"]
            set rev [dict get $mpoints $root rev]
            # Read through a pipe to get a binary channel
            set ch [open [list |svn cat -r $rev $relative] rb]
            set data [read $ch]
            close $ch
            cd $oldpwd

            set chId [vcsvfs::CreateDataRefChan $data]
            return [list $chId ""]
        }
        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]
            if {[dict get $finfor isdir]} {
                # TBD, fake mtime etc. for directory?
                dict set res type directory
            } else {
                if {![dict exists $finfor mtime]} {
                    set str [dict get $finfor mtimestr]
                    # TBD parse to mtime correct?
                    # Remove any decimals from time string
                    regsub {\.\d+Z} $str "" str
                    set mtime [clock scan $str -gmt 1]
                    dict set finfor "mtime" $mtime
                    # Cache in main dictionary too
                    dict set mpoints $root "finfo" $relative "mtime" $mtime
                }
                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
        }
    }
    vfs::filesystem posixerror $::vfs::posix(EACCES)
    return -code error $::vfs::posix(EACCES)
}

##################################################################
# Test structure
##################################################################
if 0 {
# File traversing stuff from wiki...
proc ftw_1 {{dirs .}} {