You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

421 lines
22 KiB

# -*- tcl -*-
# Maintenance Instruction: leave the 999999.xxx.x as is and use 'pmix make' or src/make.tcl to update from <pkg>-buildversion.txt
#
# Please consider using a BSD or MIT style license for greatest compatibility with the Tcl ecosystem.
# Code using preferred Tcl licenses can be eligible for inclusion in Tcllib, Tklib and the punk package repository.
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
# (C) 2023
#
# @@ Meta Begin
# Application punk::mix::commandset::repo 0.1.0
# Meta platform tcl
# Meta license <unspecified>
# @@ Meta End
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
## Requirements
##e.g package require frobz
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
namespace eval punk::mix::commandset::repo {
namespace export *
proc tickets {{project ""}} {
#todo
set result ""
if {[string length $project]} {
puts stderr "project status unimplemented"
return
}
set active_dir [pwd]
append result "Retrieving top 10 tickets only (for more, use fossil timeline -n <int> -t t)" \n
append result [exec fossil timeline -n 10 -t t]
return $result
}
proc fossilize { args} {
#check if project already managed by fossil.. initialise and check in if not.
puts stderr "unimplemented"
}
proc unfossilize {projectname args} {
#remove/archive .fossil
puts stderr "unimplemented"
}
proc state {} {
set result ""
set repopaths [punk::repo::find_repos [pwd]]
set repos [dict get $repopaths repos]
if {![llength $repos]} {
append result [dict get $repopaths warnings]
} else {
append result [dict get $repopaths warnings]
lassign [lindex $repos 0] repopath repotypes
if {"fossil" in $repotypes} {
append result \n "Fossil repo based at $repopath"
set repostate [punk::repo::workingdir_state $repopath -repopaths $repopaths -repotypes fossil]
append result \n [punk::repo::workingdir_state_summary $repostate]
}
if {"git" in $repotypes} {
append result \n "Git repo based at $repopath"
set repostate [punk::repo::workingdir_state $repopath -repopaths $repopaths -repotypes git]
append result \n [punk::repo::workingdir_state_summary $repostate]
}
}
return $result
}
proc fossil-move-repository {{path ""}} {
set searchbase [pwd]
set projectinfo [punk::repo::find_repos $searchbase]
set projectbase [dict get $projectinfo closest]
set is_fossil [expr {"fossil" in [dict get $projectinfo closest_types]}]
if {[catch {
package require sqlite3
} errM]} {
puts stderr "sqlite3 package failed to load"
puts stderr "Try using 'fossil test-move-repository <targetpath>' from within an open checkout folder, or ensure that the Tcl sqlite3 package is available."
return
}
set ansiprompt [a+ green bold]
set ansiwarn [a+ red bold]
set ansihighlight [a+ cyan bold]
set ansireset [a]
set in_checkout 0
set is_checkout_relink 0; #whether we are attempting to link a checkout that has lost its repo
#we may also encounter a different kind of relink candidate - other checkouts of the same repo that we examine and find don't point back.
if {$projectbase eq "" || !$is_fossil} {
set repodbs [glob -dir $searchbase -type f -tail *.fossil]
if {![llength $repodbs]} {
puts stderr "Current directory does not seem to be directly below a fossil checkout, and no .fossil files found"
puts stderr "Please move to a folder containing the .fossil repository database to move, or to a folder directly within a fossil checkout (and with no intermediate git/fossil repos)"
return
}
set choice_files [list]
set i 1
set menu_message ""
append menu_message "${ansiprompt}Select the number of the fossil repo db to potentially move (confirmation will be requested before any action is taken)${ansireset}" \n
foreach db $repodbs {
sqlite3 dbinfo [file join $searchbase $db]
set ckouts [dbinfo eval {select name from config where name like 'ckout:%'}]
dbinfo close
lappend choice_files [list index $i repofile $db checkouts [llength $ckouts]]
append menu_message "$i $db checkouts: [llength $ckouts]" \n
incr i
}
puts stdout $menu_message
set max [llength $choice_files]
if {$max == 1} {
set rangemsg "the number 1"
} else {
set rangemsg "a number from 1 to $max"
}
set answer [punk::repo::askuser "${ansiprompt}Enter $rangemsg to select a .fossil repository database to show details and potentially move. (or N to abort)${ansireset}"]
if {![string is integer -strict $answer]} {
puts stderr "Aborting"
return
}
set index [expr {int($answer) -1}]
if {$index >= 0 && $index <= $max-1} {
set repo_file_choice [lindex $choice_files $index]
set repo_file [dict get $repo_file_choice repofile]
set repo_file [file join $searchbase $repo_file]
puts stdout "Selected fossil repo database file: $repo_file"
} else {
puts stderr " No menu number matched - aborting."
return
}
} else {
if {[file exists $projectbase/_FOSSIL_]} {
set cdbfile [file join $projectbase/_FOSSIL_]
} elseif {[file exists $projectbase/.fslckout]} {
set cdbfile [file join $projectbase/.fslckout]
} else {
puts stderr "No checkout database (_FOSSIL_ or .fslckout) found in nearest repository folder $projectbase (looked upwards from $searchbase)"
puts stderr "Unable to locate repository databases for potential move. Please move to a checkout folder or a folder containing .fossil repositories"
puts stderr "If run from a location where repositories are found, fossil-move-repository will give you the option to select a repository or cancel the operation"
return
}
set in_checkout 1
sqlite3 cdb $cdbfile
set repo_file [cdb eval {select value from vvar where name='repository'}]
cdb close
if {[string length [string trim $repo_file]] && [file pathtype $repo_file] eq "relative"} {
set repo_file [file join $projectbase $repo_file]
}
if {![string length [string trim $repo_file]] || ![file exists $repo_file]} {
puts stderr "${ansiwarn}Checkout at $projectbase points to repository '$repo_file' - but it doesn't seem to exist${ansireset}"
set answer [punk::repo::askuser "${ansiprompt}Do you want to link this to an existing repository file? (Y|N)${ansireset}"]
if {[string match y* [string tolower $answer]]} {
set is_checkout_relink 1
} else {
puts stderr "Aborting - Unable to link this checkout dir to a repository database file"
return
}
}
}
set pname [file rootname [file tail $repo_file]]
set full_path_repo_file [file join $searchbase $repo_file]
if {[file isfile $full_path_repo_file]} {
sqlite3 dbinfo [file join $searchbase $repo_file]
set ckouts [dbinfo eval {select name from config where name like 'ckout:%'}]
dbinfo close
if {![llength $ckouts]} {
puts stdout "Repository db at [file join $searchbase $repo_file] appears to have no open checkouts"
} else {
puts stdout "Repository db at [file join $searchbase $repo_file] appears to have [llength $ckouts] open checkouts:"
foreach ck $ckouts {
puts stdout [string range $ck 6 end]
}
}
} else {
puts stderr "${ansiwarn}Missing repository db at $full_path_repo_file${ansireset}"
}
puts stdout "${ansihighlight}Report for all projects with repository file name $pname${ansireset}"
puts stdout [punk::mix::commandset::project::collection::detail $pname]
puts stdout [punk::mix::commandset::project::collection::work $pname -detail 1]
#todo
#ask user if they want to select a different pname
set wantrenameprompt "${ansiprompt}Would you like to rename the .fossil file? (Y|N)${ansireset}"
append wantrenameprompt \n "${ansiprompt}.eg change $pname.fossil to something else such as ${pname}_new.fossil${ansireset}"
set answer [punk::repo::askuser $wantrenameprompt]
set pname2 $pname
if {[string match y* [string tolower $answer]]} {
set dorenameprompt "${ansiprompt}Enter the new name and hit enter. (Just an alphanumeric name (possibly with dots/dashes/underscores) without .fossil and without any path)${ansireset}"
set namechoice [punk::repo::askuser $dorenameprompt]
if {[string length $namechoice]} {
set permittedmap [list . "" - "" _ ""]
if {[string is alnum -strict [string map $permittedmap $namechoice]]} {
set pname2 $namechoice
} else {
puts stderr "Entered name was invalid. Must be numbers,letters,underscore,dot,dash"
}
}
puts stdout "Continuing with name $pname2 - cancel at next prompt if this is incorrect"
}
set target_repodb_folder [punk::repo::fossil_get_repository_folder_for_project $pname2 -parentfolder $searchbase -askpath 1]
#target_repodb_folder might be same as source folder - check for same file if name wasn't changed?
if {![string length $target_repodb_folder]} {
puts stderr "No usable repository database folder selected for $pname2.fossil file"
return
}
set existing_target_repofile 0
if {[file exists $target_repodb_folder/$pname2.fossil]} {
set existing_target_repofile 1
puts stdout "${ansiwarn}NOTICE: $target_repodb_folder/$pname2.fossil already exists${ansireset}"
if {!$is_checkout_relink} {
set finalquestion "${ansiprompt}Are you sure you want to switch the repository $repo_file for the open checkout(s) to the existing file $target_repodb_folder/$pname2.fossil? (Y|N)${ansireset}"
} else {
set finalquestion "${ansiprompt}Are you sure you want to attempt to linke the repository (previously linked with '$repo_file') for the open checkout(s) to the existing file $target_repodb_folder/$pname2.fossil? (Y|N)${ansireset}"
}
} else {
if {!$is_checkout_relink} {
set finalquestion "${ansiprompt}Proceed to move repository $repo_file to the new file $target_repodb_folder/$pname2.fossil? Y|N${ansireset}"
} else {
set finalquestion "${ansiprompt}Proceed to attempt link for missing repo db $repo_file to the new file $target_repodb_folder/$pname2.fossil? Y|N${ansireset}"
}
}
set line "${ansiwarn}[string repeat - [string length $finalquestion]]${ansireset}"
set finalprompt $line\n
append finalprompt $finalquestion \n
append finalprompt $line \n
set answer [punk::repo::askuser $finalprompt]
if {[string match y* [string tolower $answer]]} {
if {!$existing_target_repofile && !$is_checkout_relink} {
if {[catch {
file copy $repo_file $target_repodb_folder/$pname2.fossil
} errM]} {
puts stderr "${ansiwarn}FAILED to copy $repo_file to $target_repodb_folder/$pname2.fossil - aborting${ansireset}"
puts stderr "Error message was:\n $errM"
return
}
if {$in_checkout} {
#in_checkout means we can assume projectbase var exists
#there may be other checkouts on the old repo
#if so, we will remind the user of their existence
if {[catch {exec fossil test-move-repository $target_repodb_folder/$pname2.fossil} errM]} {
puts stderr "${ansiwarn}The fossil test-move-repository command appears to have failed${ansireset}"
puts stderr "$errM"
} else {
sqlite3 oldrepo $repo_file
set ckouts [oldrepo eval {select name from config where name like 'ckout:%'}]
set pcode [oldrepo eval {select value from config where name = 'project-code'}]
oldrepo close
if {[string length $pcode] < 20} {
puts stderr "WARNING: Failed to get project-code from repo db $repo_file"
}
set other_checkouts [list]
set norm_projectbase [file normalize $projectbase]
foreach ck $ckouts {
set ckfolder [string trim [string range $ck 6 end]]
if {![file isdirectory $ckfolder]} {
#as the process was launched within a checkout - we won't bother user with reports of non-existant other checkouts
continue
}
if {[file normalize $ckfolder] ne $norm_projectbase} {
lappend other_checkouts $ckfolder
}
}
if {[llength $other_checkouts]} {
puts stderr "${ansiwarn}Other checkouts of $repo_file that may need consideration${ansireset}"
foreach other $other_checkouts {
puts stdout $other
}
}
}
} else {
#we aren't in a checkout - moving a repo to a new db location and/or name so there's no reason to prefer one checkout over another.. presumably the user either wants to move them all - or be asked..
sqlite3 oldrepo $repo_file
set ckouts [oldrepo eval {select name from config where name like 'ckout:%'}]
oldrepo close
if {[llength $ckouts] > 1} {
puts stdout "There are [llength $ckouts] checkouts for the repository you are moving"
puts stdout "You will be asked for each checkout if you want to adjust it to point to $target_repodb_folder/$pname2.folder"
}
set original_cwd [pwd]
foreach ck $ckouts {
set ckfolder [string trim [string range $ck 6 end]]
if {![file isdirectory $ckfolder]} {
puts stderr "old repo shows a checkout at $ckfolder - but it doesn't seem to exist. Ignoring"
continue
}
cd $ckfolder
puts stdout [exec fossil info]
puts stdout [state]
set answer [punk::repo::askuser "${ansiprompt}Do you want to point this checkout to $target_repodb_folder/$pname2.folder? (Y|N) Q to stop processing checkouts${ansireset}"]
if {[string match q* [string tolower $answer]]} {
puts stderr "User aborting loop"
break
}
if {[string match y* [string tolower $answer]]} {
if {[catch {exec fossil test-move-repository $target_repodb_folder/$pname2.fossil} moveresult]} {
puts stderr "${ansiwarn}The fossil test-move-repository command appears to have failed${ansireset}"
puts stderr "$moveresult"
} else {
puts stdout "OK - move performed with result:"
puts stdout $moveresult
}
}
}
cd $original_cwd
}
} else {
if {$is_checkout_relink} {
#relinking a lost checkout to an existing repo.. we should probably check it's other checkouts and see if they point back
if {[catch {exec fossil test-move-repository $target_repodb_folder/$pname2.fossil} errM]} {
puts stderr "${ansiwarn}The fossil test-move-repository command appears to have failed${ansireset}"
puts stderr "$errM"
}
} else {
if {$in_checkout} {
if {[catch {exec fossil test-move-repository $target_repodb_folder/$pname2.fossil} errM]} {
puts stderr "${ansiwarn}The fossil test-move-repository command appears to have failed${ansireset}"
puts stderr "$errM"
}
} else {
#not in checkout - we're wanting what pointed to one repo to point to a different existing one - presumably for all checkouts
sqlite3 newrepo $target_repodb_folder/$pname2.fossil
set newpname [newrepo eval {select value from config where name = 'project-name'}]
set newpcode [newrepo eval {select value from config where name = 'project-code'}]
set newckouts [newrepo eval {select name from config where name like 'ckout:%'}]
newrepo close
sqlite3 oldrepo $repo_file
set oldpname [oldrepo eval {select value from config where name = 'project-name'}]
set oldpcode [oldrepo eval {select value from config where name = 'project-code'}]
set oldckouts [oldrepo eval {select name from config where name like 'ckout:%'}]
oldrepo close
if {$newpname eq $oldpname} {
set ansi_newpname [a+ green bold]$newpname[a]
set ansi_oldpname [a+ green bold]$oldpname[a]
} else {
set ansi_newpname [a+ cyan bold]$newpname[a]
set ansi_oldpname [a+ red bold]$oldpname[a]
}
if {$newpcode eq $oldpcode} {
set ansi_newpcode [a+ green bold]$newpcode[a]
set ansi_oldpcode [a+ green bold]$oldpcode[a]
} else {
set ansi_newpcode [a+ cyan bold]$newpcode[a]
set ansi_oldpcode [a+ red bold]$oldpcode[a]
}
puts stdout "Target repository $target_repodb_folder/$pname2.fossil has project-name: $ansi_newpname and [llength $newckouts] existing checkouts"
puts stdout "Target project code: $ansi_newpcode"
puts stdout "Source repository $repo_file has project-name: $ansi_oldpname and [llength $oldckouts] existing checkouts"
puts stdout "Source project code: $ansi_oldpcode"
if {[llength $oldckouts] > 1} {
puts stdout "You will be asked for each checkout if you want to adjust it to point to $target_repodb_folder/$pname2.folder"
}
set original_cwd [pwd]
foreach ck $oldckouts {
set ckfolder [string trim [string range $ck 6 end]]
if {![file isdirectory $ckfolder]} {
puts stderr "old repo shows a checkout at $ckfolder - but it doesn't seem to exist. Ignoring"
continue
}
cd $ckfolder
puts stdout [exec fossil info]
puts stdout [state]
set answer [punk::repo::askuser "${ansiprompt}Do you want to point this checkout to $target_repodb_folder/$pname2.folder? (Y|N) Q to stop processing checkouts${ansireset}"]
if {[string match q* [string tolower $answer]]} {
puts stderr "User aborting loop"
break
}
if {[string match y* [string tolower $answer]]} {
if {[catch {exec fossil test-move-repository $target_repodb_folder/$pname2.fossil} moveresult]} {
puts stderr "${ansiwarn}The fossil test-move-repository command appears to have failed${ansireset}"
puts stderr "$moveresult"
} else {
puts stdout "OK - move performed with result:"
puts stdout $moveresult
}
}
}
cd $original_cwd
}
}
}
puts stdout "-done-"
} else {
puts stdout "-cancelled by user-"
}
}
}
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
## Ready
package provide punk::mix::commandset::repo [namespace eval punk::mix::commandset::repo {
variable version
set version 0.1.0
}]
return