#does a dual git/fossil repo make sense if both are committing??
#does a dual git/fossil repo make sense if both are committing??
# see: https://fossil-scm.org/home/doc/trunk/www/inout.wiki for bidirectional sync info
# see: https://fossil-scm.org/home/doc/trunk/www/inout.wiki for bidirectional sync info
proc workingdir_state {{abspath {}} args} {
proc workingdir_state {{abspath {}} args} {
#we should try to minimize executable calls
#an extra git/fossil executable call required for tags
#git seems to require more executable calls
set defaults [list\
set defaults [list\
-repotypes [list fossil git]\
-repotypes [list fossil git]\
-repopaths ""\
-repopaths ""\
@ -408,6 +413,8 @@ namespace eval punk::repo {
set revision ""
set revision ""
set revision_iso8601 ""
set revision_iso8601 ""
set pathdict [dict create]
set pathdict [dict create]
set branch ""
set tags ""
if {![llength $repotypes_to_query]} {
if {![llength $repotypes_to_query]} {
error "No tracking information available for project at $repodir with the chosen repotypes '$opt_repotypes'. Ensure project workingdir is a fossil (or git) checkout"
error "No tracking information available for project at $repodir with the chosen repotypes '$opt_repotypes'. Ensure project workingdir is a fossil (or git) checkout"
@ -437,6 +444,38 @@ namespace eval punk::repo {
}
}
set revision_iso8601 "${revision_ymd}T${revision_hms}${revision_tz}"
set revision_iso8601 "${revision_ymd}T${revision_hms}${revision_tz}"
#REVIEW! what are the semantic difference between tags in fossil v git?
#fossil has tagtypes such as propagated and singleton(onetime)
#if we get all tag info for the revision - we can get the current branch (branch=somename tag) at the same time
#by retrieving with --raw - we have to process some prefixes such as sym- but probably best not done here
#we will return all tags that apply to the current revision and let the caller decide the meanings
if {![catch {punk::mix::util::do_in_path $repodir [list exec {*}$fossil_cmd tag ls --raw $revision]} cmdresult]} {
set branchinfo [lindex [grep {branch=*} $cmdresult] 0] ;#first line match - should only be one
set branch [lindex [split $branchinfo =] 1]
set tags [list]
foreach ln [split $cmdresult \n] {
if {[string trim $ln] eq ""} {
continue
}
lappend tags [string trim $ln]
}
}
#set tags_info [lindex [grep {tags:*} $fossilstate 0] ;#first line match - should only be one
set tags $cmdresult ;#review - we have short tags vs longer.. e.g v0.1a vs v0.1a-184-g856fab4 - which is returned? Also how are multiple separated?
}
#often there will be no tag - so the common case is actually an error "fatal: not ag exactly matchs 'xxxx...'"
# -- --- --- --- ---
# -- --- --- --- ---
#could use %ci for ISO8601 data - see git-show manpage, but this will be in timezone of developer's machine - we need it in UTC for comparison to fossil outputs and other devs
#could use %ci for ISO8601 data - see git-show manpage, but this will be in timezone of developer's machine - we need it in UTC for comparison to fossil outputs and other devs
@ -600,9 +647,11 @@ namespace eval punk::repo {
puts stderr "workingdir_state - repotype $rt not supported"
puts stderr "workingdir_state - repotype $rt not supported"
}
}
}
}
dict set resultdict revision $revision
dict set resultdict branch $branch
dict set resultdict revision_iso8601 $revision_iso8601
dict set resultdict tags $tags
dict set resultdict paths $pathdict
dict set resultdict revision $revision
dict set resultdict revision_iso8601 $revision_iso8601
dict set resultdict paths $pathdict
return $resultdict
return $resultdict
}
}
proc workingdir_state_summary {repostate args} {
proc workingdir_state_summary {repostate args} {
@ -610,8 +659,13 @@ namespace eval punk::repo {
error "workingdir_state_summary error repostate doesn't appear to be a repostate dict. (use workingdir_state <path> to create)"
error "workingdir_state_summary error repostate doesn't appear to be a repostate dict. (use workingdir_state <path> to create)"
}
}
package require overtype
package require overtype
#the revision branch and tags are highly relevant to the file state - and workingdir_state currently retrieves them anyway
# - so we'll include them in the defaults
# - when we are including working dir state as part of other output - we could be duplicating branch/tag retrievals
# - todo - flags to stop duplicating effort ??
set defaults [dict create\
set defaults [dict create\
-fields {ahead behind unchanged changed new missing extra}\
flush stdout; #we are writing this prompt on stderr, but stdout could still be writing to screen
flush stdout; #we are writing this prompt on stderr, but stdout could still be writing to screen
#our first char on stderr is based on the 'lastchar' of stdout which we have recorded but may not have arrived on screen.
#our first char on stderr is based on the 'lastchar' of stdout which we have recorded but may not have arrived on screen.
#The issue we're trying to avoid is the (stderr)prompt arriving midway through a large stdout chunk
#The issue we're trying to avoid is the (stderr)prompt arriving midway through a large stdout chunk
#REVIEW - this basic attempt to get stderr/stdout to cooperate is experimental and unlikely to achieve the desired effect
#REVIEW - this basic attempt to get stderr/stdout to cooperate is experimental and unlikely to achieve the desired effect in all situations
#It the above flush does seem to help though.
#note that our 'flush stdout' tcl call does not wait if stdout is non-blocking
#note that our 'flush stdout' tcl call does not wait if stdout is non-blocking
#todo - investigate if the overhead is reasonable for a special channel that accepts stdout and stderr records with a reader to send to console in chunk-sizes we know will be emitted correctly
#todo - investigate if the overhead is reasonable for a special channel that accepts stdout and stderr records with a reader to send to console in chunk-sizes we know will be emitted correctly
# - reader of such channel could be ok to be blocking (on read? on write to real channels?)... except everything still needs to be interruptable by things like signals?
# - reader of such channel could be ok to be blocking (on read? on write to real channels?)... except everything still needs to be interruptable by things like signals?
#does a dual git/fossil repo make sense if both are committing??
#does a dual git/fossil repo make sense if both are committing??
# see: https://fossil-scm.org/home/doc/trunk/www/inout.wiki for bidirectional sync info
# see: https://fossil-scm.org/home/doc/trunk/www/inout.wiki for bidirectional sync info
proc workingdir_state {{abspath {}} args} {
proc workingdir_state {{abspath {}} args} {
#we should try to minimize executable calls
#an extra git/fossil executable call required for tags
#git seems to require more executable calls
set defaults [list\
set defaults [list\
-repotypes [list fossil git]\
-repotypes [list fossil git]\
-repopaths ""\
-repopaths ""\
@ -408,6 +413,8 @@ namespace eval punk::repo {
set revision ""
set revision ""
set revision_iso8601 ""
set revision_iso8601 ""
set pathdict [dict create]
set pathdict [dict create]
set branch ""
set tags ""
if {![llength $repotypes_to_query]} {
if {![llength $repotypes_to_query]} {
error "No tracking information available for project at $repodir with the chosen repotypes '$opt_repotypes'. Ensure project workingdir is a fossil (or git) checkout"
error "No tracking information available for project at $repodir with the chosen repotypes '$opt_repotypes'. Ensure project workingdir is a fossil (or git) checkout"
@ -437,6 +444,38 @@ namespace eval punk::repo {
}
}
set revision_iso8601 "${revision_ymd}T${revision_hms}${revision_tz}"
set revision_iso8601 "${revision_ymd}T${revision_hms}${revision_tz}"
#REVIEW! what are the semantic difference between tags in fossil v git?
#fossil has tagtypes such as propagated and singleton(onetime)
#if we get all tag info for the revision - we can get the current branch (branch=somename tag) at the same time
#by retrieving with --raw - we have to process some prefixes such as sym- but probably best not done here
#we will return all tags that apply to the current revision and let the caller decide the meanings
if {![catch {punk::mix::util::do_in_path $repodir [list exec {*}$fossil_cmd tag ls --raw $revision]} cmdresult]} {
set branchinfo [lindex [grep {branch=*} $cmdresult] 0] ;#first line match - should only be one
set branch [lindex [split $branchinfo =] 1]
set tags [list]
foreach ln [split $cmdresult \n] {
if {[string trim $ln] eq ""} {
continue
}
lappend tags [string trim $ln]
}
}
#set tags_info [lindex [grep {tags:*} $fossilstate 0] ;#first line match - should only be one
set tags $cmdresult ;#review - we have short tags vs longer.. e.g v0.1a vs v0.1a-184-g856fab4 - which is returned? Also how are multiple separated?
}
#often there will be no tag - so the common case is actually an error "fatal: not ag exactly matchs 'xxxx...'"
# -- --- --- --- ---
# -- --- --- --- ---
#could use %ci for ISO8601 data - see git-show manpage, but this will be in timezone of developer's machine - we need it in UTC for comparison to fossil outputs and other devs
#could use %ci for ISO8601 data - see git-show manpage, but this will be in timezone of developer's machine - we need it in UTC for comparison to fossil outputs and other devs
@ -600,9 +647,11 @@ namespace eval punk::repo {
puts stderr "workingdir_state - repotype $rt not supported"
puts stderr "workingdir_state - repotype $rt not supported"
}
}
}
}
dict set resultdict revision $revision
dict set resultdict branch $branch
dict set resultdict revision_iso8601 $revision_iso8601
dict set resultdict tags $tags
dict set resultdict paths $pathdict
dict set resultdict revision $revision
dict set resultdict revision_iso8601 $revision_iso8601
dict set resultdict paths $pathdict
return $resultdict
return $resultdict
}
}
proc workingdir_state_summary {repostate args} {
proc workingdir_state_summary {repostate args} {
@ -610,8 +659,13 @@ namespace eval punk::repo {
error "workingdir_state_summary error repostate doesn't appear to be a repostate dict. (use workingdir_state <path> to create)"
error "workingdir_state_summary error repostate doesn't appear to be a repostate dict. (use workingdir_state <path> to create)"
}
}
package require overtype
package require overtype
#the revision branch and tags are highly relevant to the file state - and workingdir_state currently retrieves them anyway
# - so we'll include them in the defaults
# - when we are including working dir state as part of other output - we could be duplicating branch/tag retrievals
# - todo - flags to stop duplicating effort ??
set defaults [dict create\
set defaults [dict create\
-fields {ahead behind unchanged changed new missing extra}\
#does a dual git/fossil repo make sense if both are committing??
#does a dual git/fossil repo make sense if both are committing??
# see: https://fossil-scm.org/home/doc/trunk/www/inout.wiki for bidirectional sync info
# see: https://fossil-scm.org/home/doc/trunk/www/inout.wiki for bidirectional sync info
proc workingdir_state {{abspath {}} args} {
proc workingdir_state {{abspath {}} args} {
#we should try to minimize executable calls
#an extra git/fossil executable call required for tags
#git seems to require more executable calls
set defaults [list\
set defaults [list\
-repotypes [list fossil git]\
-repotypes [list fossil git]\
-repopaths ""\
-repopaths ""\
@ -408,6 +413,8 @@ namespace eval punk::repo {
set revision ""
set revision ""
set revision_iso8601 ""
set revision_iso8601 ""
set pathdict [dict create]
set pathdict [dict create]
set branch ""
set tags ""
if {![llength $repotypes_to_query]} {
if {![llength $repotypes_to_query]} {
error "No tracking information available for project at $repodir with the chosen repotypes '$opt_repotypes'. Ensure project workingdir is a fossil (or git) checkout"
error "No tracking information available for project at $repodir with the chosen repotypes '$opt_repotypes'. Ensure project workingdir is a fossil (or git) checkout"
@ -437,6 +444,38 @@ namespace eval punk::repo {
}
}
set revision_iso8601 "${revision_ymd}T${revision_hms}${revision_tz}"
set revision_iso8601 "${revision_ymd}T${revision_hms}${revision_tz}"
#REVIEW! what are the semantic difference between tags in fossil v git?
#fossil has tagtypes such as propagated and singleton(onetime)
#if we get all tag info for the revision - we can get the current branch (branch=somename tag) at the same time
#by retrieving with --raw - we have to process some prefixes such as sym- but probably best not done here
#we will return all tags that apply to the current revision and let the caller decide the meanings
if {![catch {punk::mix::util::do_in_path $repodir [list exec {*}$fossil_cmd tag ls --raw $revision]} cmdresult]} {
set branchinfo [lindex [grep {branch=*} $cmdresult] 0] ;#first line match - should only be one
set branch [lindex [split $branchinfo =] 1]
set tags [list]
foreach ln [split $cmdresult \n] {
if {[string trim $ln] eq ""} {
continue
}
lappend tags [string trim $ln]
}
}
#set tags_info [lindex [grep {tags:*} $fossilstate 0] ;#first line match - should only be one
set tags $cmdresult ;#review - we have short tags vs longer.. e.g v0.1a vs v0.1a-184-g856fab4 - which is returned? Also how are multiple separated?
}
#often there will be no tag - so the common case is actually an error "fatal: not ag exactly matchs 'xxxx...'"
# -- --- --- --- ---
# -- --- --- --- ---
#could use %ci for ISO8601 data - see git-show manpage, but this will be in timezone of developer's machine - we need it in UTC for comparison to fossil outputs and other devs
#could use %ci for ISO8601 data - see git-show manpage, but this will be in timezone of developer's machine - we need it in UTC for comparison to fossil outputs and other devs
@ -600,9 +647,11 @@ namespace eval punk::repo {
puts stderr "workingdir_state - repotype $rt not supported"
puts stderr "workingdir_state - repotype $rt not supported"
}
}
}
}
dict set resultdict revision $revision
dict set resultdict branch $branch
dict set resultdict revision_iso8601 $revision_iso8601
dict set resultdict tags $tags
dict set resultdict paths $pathdict
dict set resultdict revision $revision
dict set resultdict revision_iso8601 $revision_iso8601
dict set resultdict paths $pathdict
return $resultdict
return $resultdict
}
}
proc workingdir_state_summary {repostate args} {
proc workingdir_state_summary {repostate args} {
@ -610,8 +659,13 @@ namespace eval punk::repo {
error "workingdir_state_summary error repostate doesn't appear to be a repostate dict. (use workingdir_state <path> to create)"
error "workingdir_state_summary error repostate doesn't appear to be a repostate dict. (use workingdir_state <path> to create)"
}
}
package require overtype
package require overtype
#the revision branch and tags are highly relevant to the file state - and workingdir_state currently retrieves them anyway
# - so we'll include them in the defaults
# - when we are including working dir state as part of other output - we could be duplicating branch/tag retrievals
# - todo - flags to stop duplicating effort ??
set defaults [dict create\
set defaults [dict create\
-fields {ahead behind unchanged changed new missing extra}\
#We can pipe commands into repl's stdin without the prompt interfering with the output.
#We can pipe commands into repl's stdin without the prompt interfering with the output.
#Although all command output for each line goes to stdout - not just what is emmited with puts
#Although all command output for each line goes to stdout - not just what is emitted with puts
if {$::tcl_interactive} {
if {$::tcl_interactive} {
flush stdout; #we are writing this prompt on stderr, but stdout could still be writing to screen
#our first char on stderr is based on the 'lastchar' of stdout which we have recorded but may not have arrived on screen.
#The issue we're trying to avoid is the (stderr)prompt arriving midway through a large stdout chunk
#REVIEW - this basic attempt to get stderr/stdout to cooperate is experimental and unlikely to achieve the desired effect
#note that our 'flush stdout' tcl call does not wait if stdout is non-blocking
#todo - investigate if the overhead is reasonable for a special channel that accepts stdout and stderr records with a reader to send to console in chunk-sizes we know will be emitted correctly
# - reader of such channel could be ok to be blocking (on read? on write to real channels?)... except everything still needs to be interruptable by things like signals?
#? - we want ordinary puts to stderr to be prioritized? to arrive on-screen - just not at arbitrary locations within stdout, and still must be correctly ordered wrt all other stderr
# - in our repl and code threads we don't want to put stderr/stdout writes in blocking mode and have code waiting on it
#fallback - try external executable. Which is a bit ugly
#fallback - try external executable. Which is a bit ugly
#tput can work on windows too if it's installed e.g from msys2
#tput can't seem to get dimensions (on FreeBSD at least) when not run interactively - ie via exec. (always returns 80x24 no matter if run with <@stdin)
#bizarrely - tput can work with exec on windows if it's installed e.g from msys2
#but can be *slow* compared to unix e.g 400ms+ vs <2ms on FreeBSD !
#but can be *slow* compared to unix e.g 400ms+ vs <2ms on FreeBSD !
set tputcmd [auto_execok tput]
#stty -a is 400ms+ vs 500us+ on FreeBSD
if {$tputcmd ne ""} {
if {![catch {exec {*}$tputcmd cols lines} values} {
if {"windows" eq $::tcl_platform(platform)} {
lassign $values cols rows
set tputcmd [auto_execok tput]
}
if {$tputcmd ne ""} {
if {![catch {exec {*}$tputcmd cols lines} values]} {
lassign $values cols rows
}
}
}
if {![string is integer -strict $cols] || ![string is integer -strict $rows]} {
#same for all platforms? tested on windows, wsl, FreeBSD
#exec stty -a gives a result on the first line like:
#speed xxxx baud; rows rr; columns cc;
#review - more robust parsing - do we know it's first line?
set sttycmd [auto_execok stty]
if {$sttycmd ne ""} {
#the more parseable: stty -g doesn't give rows/columns
if {![catch {exec {*}$sttycmd -a} result]} {
lassign [split $result \n] firstline
set lineparts [split $firstline {;}] ;#we seem to get segments that look well behaved enough to be treated as tcl lists - review - regex?
set rowinfo [lsearch -index end -inline $lineparts rows]
if {[llength $rowinfo] == 2} {
set rows [lindex $rowinfo 0]
}
set colinfo [lsearch -index end -inline $lineparts columns]
if {[llength $colinfo] == 2} {
set cols [lindex $colinfo 0]
}
}
}
}
}
}
}
if {[string is integer -strict $cols] && [string is integer -strict $rows]} {
if {[string is integer -strict $cols] && [string is integer -strict $rows]} {
#does a dual git/fossil repo make sense if both are committing??
#does a dual git/fossil repo make sense if both are committing??
# see: https://fossil-scm.org/home/doc/trunk/www/inout.wiki for bidirectional sync info
# see: https://fossil-scm.org/home/doc/trunk/www/inout.wiki for bidirectional sync info
proc workingdir_state {{abspath {}} args} {
proc workingdir_state {{abspath {}} args} {
#we should try to minimize executable calls
#an extra git/fossil executable call required for tags
#git seems to require more executable calls
set defaults [list\
set defaults [list\
-repotypes [list fossil git]\
-repotypes [list fossil git]\
-repopaths ""\
-repopaths ""\
@ -408,6 +413,8 @@ namespace eval punk::repo {
set revision ""
set revision ""
set revision_iso8601 ""
set revision_iso8601 ""
set pathdict [dict create]
set pathdict [dict create]
set branch ""
set tags ""
if {![llength $repotypes_to_query]} {
if {![llength $repotypes_to_query]} {
error "No tracking information available for project at $repodir with the chosen repotypes '$opt_repotypes'. Ensure project workingdir is a fossil (or git) checkout"
error "No tracking information available for project at $repodir with the chosen repotypes '$opt_repotypes'. Ensure project workingdir is a fossil (or git) checkout"
@ -437,6 +444,38 @@ namespace eval punk::repo {
}
}
set revision_iso8601 "${revision_ymd}T${revision_hms}${revision_tz}"
set revision_iso8601 "${revision_ymd}T${revision_hms}${revision_tz}"
#REVIEW! what are the semantic difference between tags in fossil v git?
#fossil has tagtypes such as propagated and singleton(onetime)
#if we get all tag info for the revision - we can get the current branch (branch=somename tag) at the same time
#by retrieving with --raw - we have to process some prefixes such as sym- but probably best not done here
#we will return all tags that apply to the current revision and let the caller decide the meanings
if {![catch {punk::mix::util::do_in_path $repodir [list exec {*}$fossil_cmd tag ls --raw $revision]} cmdresult]} {
set branchinfo [lindex [grep {branch=*} $cmdresult] 0] ;#first line match - should only be one
set branch [lindex [split $branchinfo =] 1]
set tags [list]
foreach ln [split $cmdresult \n] {
if {[string trim $ln] eq ""} {
continue
}
lappend tags [string trim $ln]
}
}
#set tags_info [lindex [grep {tags:*} $fossilstate 0] ;#first line match - should only be one
set tags $cmdresult ;#review - we have short tags vs longer.. e.g v0.1a vs v0.1a-184-g856fab4 - which is returned? Also how are multiple separated?
}
#often there will be no tag - so the common case is actually an error "fatal: not ag exactly matchs 'xxxx...'"
# -- --- --- --- ---
# -- --- --- --- ---
#could use %ci for ISO8601 data - see git-show manpage, but this will be in timezone of developer's machine - we need it in UTC for comparison to fossil outputs and other devs
#could use %ci for ISO8601 data - see git-show manpage, but this will be in timezone of developer's machine - we need it in UTC for comparison to fossil outputs and other devs
@ -600,9 +647,11 @@ namespace eval punk::repo {
puts stderr "workingdir_state - repotype $rt not supported"
puts stderr "workingdir_state - repotype $rt not supported"
}
}
}
}
dict set resultdict revision $revision
dict set resultdict branch $branch
dict set resultdict revision_iso8601 $revision_iso8601
dict set resultdict tags $tags
dict set resultdict paths $pathdict
dict set resultdict revision $revision
dict set resultdict revision_iso8601 $revision_iso8601
dict set resultdict paths $pathdict
return $resultdict
return $resultdict
}
}
proc workingdir_state_summary {repostate args} {
proc workingdir_state_summary {repostate args} {
@ -610,8 +659,13 @@ namespace eval punk::repo {
error "workingdir_state_summary error repostate doesn't appear to be a repostate dict. (use workingdir_state <path> to create)"
error "workingdir_state_summary error repostate doesn't appear to be a repostate dict. (use workingdir_state <path> to create)"
}
}
package require overtype
package require overtype
#the revision branch and tags are highly relevant to the file state - and workingdir_state currently retrieves them anyway
# - so we'll include them in the defaults
# - when we are including working dir state as part of other output - we could be duplicating branch/tag retrievals
# - todo - flags to stop duplicating effort ??
set defaults [dict create\
set defaults [dict create\
-fields {ahead behind unchanged changed new missing extra}\