#!/bin/sh
# the next line restarts using tclsh \
exec tclsh "$0" -- ${1+"$@"}


proc cleanup_old {root} {
  if {[ file isdirectory $root ]} {
    puts "Deleting $root"
    file delete -force $root
  }
  set oldirs [glob -nocomplain -- svn_test*]
  foreach od $oldirs {
    puts "Deleting $od"
    file delete -force $od
  }
  puts "==============================="
}

proc repository {Root topdir} {
  global taghead

  puts "==============================="
  puts "MAKING REPOSITORY $Root"

  # Create the repository
  file mkdir $Root
  set exec_cmd "svnadmin create $Root"
  puts "$exec_cmd"
  set ret [catch {eval "exec $exec_cmd"} out]
  if {$ret} {
    puts $out
    puts "COULD NOT CREATE REPOSITORY $Root"
    exit 1
  }
  puts "CREATED $Root"

  file mkdir [file join $topdir $taghead(trunk)]
  file mkdir [file join $topdir $taghead(branch)]
  file mkdir [file join $topdir $taghead(tag)]
  puts "==============================="
  puts "IMPORTING FILETREE"
  set exec_cmd "svn import $topdir file:///$Root -m \"Imported\""
  puts "$exec_cmd"
  set ret [catch {eval "exec $exec_cmd"} out]
  puts $out
  puts "IMPORT FINISHED"
}

proc checkout_branch {Root tag} {
  global taghead

  puts "==============================="
  puts "CHECKING OUT $tag"
  # Check out 
  if {$tag eq $taghead(trunk)} {
    set exec_cmd "svn co file:///$Root/$taghead(trunk) svn_test_$tag"
  } else {
    set exec_cmd "svn co file:///$Root/$taghead(branch)/$tag svn_test_$tag"
  }
  puts "$exec_cmd"
  set ret [catch {eval "exec $exec_cmd"} out]
  puts $out
  puts "CHECKOUT FINISHED"
}

proc newbranch {Root oldtag newtag} {
  global taghead

  if {$oldtag eq $taghead(trunk)} {
    set exec_cmd "svn copy file:///$Root/$oldtag file:///$Root/$taghead(branch)/$newtag -m \"Branch $newtag\""
  } else {
    set exec_cmd "svn copy file:///$Root/$taghead(branch)/$oldtag file:///$Root/$taghead(branch)/$newtag -m \"Branch $newtag\""
  }
  puts "$exec_cmd"
  set ret [catch {eval "exec $exec_cmd"} out]
  puts $out
  puts "CHECKING OUT BRANCH"
  set exec_cmd "svn co file:///$Root/$taghead(branch)/$newtag svn_test_$newtag"
  puts "$exec_cmd"
  set ret [catch {eval "exec $exec_cmd"} out]
}

proc tag {Root tag fname {upd {0}} } {
  global taghead

  # This tests a peculiarity of SVN, that a tag makes a new revision
  if {$upd} {
    set exec_cmd "svn update \"$fname\""
    puts "$exec_cmd"
    set ret [catch {eval "exec $exec_cmd"} out]
    puts $out
  }

  set exec_cmd "svn copy --parents -m\"file_tag\" $fname file://$Root/$taghead(tag)/$tag/$fname"
  puts "$exec_cmd"
  set ret [catch {eval "exec $exec_cmd"} out]
  puts $out
  if {$ret} {
    exit 1
  }
}

proc tagall {Root tag} {
  global taghead

  # Need to update . or the tag may be in the wrong place
  set exec_cmd "svn update \".\""
  puts "$exec_cmd"
  set ret [catch {eval "exec $exec_cmd"} out]
  puts $out

  puts "Tag: $tag"
  set exec_cmd "svn copy \".\" file:///$Root/$taghead(tag)/$tag -m \"Tag $tag\""
  puts "$exec_cmd"
  set ret [catch {eval "exec $exec_cmd"} out]
  puts $out
}

proc merge {fromtag totag} {
  global WD

  cd svn_test_$totag
  set exec_cmd "svn update"
  set ret [catch {eval "exec $exec_cmd"} out]
  puts $out
  # This puts mergeinfo only into .
  # --- Recording mergeinfo for merge between repository URLs into '.'
  set exec_cmd "svn merge --reintegrate ^/branches/$fromtag"
  set ret [catch {eval "exec $exec_cmd"} out]
  puts $out
  commit "Merge branch A to trunk"
  cd $WD
}

proc writefile {filename string {encoding {}} } {
  puts " append \"$string\" to $filename"
  set fp [open "$filename" a]
  if {$encoding != ""} {
    chan configure $fp -encoding $encoding
  }
  puts $fp $string
  chan close $fp
}

proc addfile {filename branch} {
  puts "Add $filename on $branch"
  set exec_cmd "svn add $filename"
  puts "$exec_cmd"
  set ret [catch {eval "exec $exec_cmd"} out]
  puts $out
}

proc delfile {filename branch} {
  puts "Delete $filename on $branch"
  file delete $filename
  set exec_cmd "svn delete $filename"
  puts "$exec_cmd"
  set ret [catch {eval "exec $exec_cmd"} out]
  puts $out
}

proc movefile {oldname newname} {
  # move, mv, rename
  puts "svn mv, move, rename $oldname $newname"
  set exec_cmd "svn rename $oldname $newname"
  puts "$exec_cmd"
  set ret [catch {eval "exec $exec_cmd"} out]
  puts $out
}

proc commit {comment} {
  set exec_cmd "svn commit -m \"$comment\""
  # This is so the timestamp is different from the last commit
  after 1000
  puts "$exec_cmd"
  set ret [catch {eval "exec $exec_cmd"} out]
  puts $out
}

proc mkfiles {topdir} {
  global WD
  global taghead

  puts "MAKING FILETREE"
  # Make some files to put in the repository
  set trunkhead [file join $topdir $taghead(trunk)]
  file mkdir $trunkhead

  cd $trunkhead

  # Make some text files
  foreach n {1 2 3 4} {
    writefile "File$n.txt" "Initial"
  }
  writefile "File Spc.txt" "Initial"
  writefile "FTags.txt" "Initial"
  writefile "UpdTags.txt" "Initial"
  writefile "F-utf-8.txt" "\u20AC20" "utf-8"
  writefile "F-iso8859-1.txt" "Copyright \xA9 2025" "iso8859-1"
  foreach D {Dir1 "Dir 2"} {
    puts $D
    file mkdir $D
    foreach n {1 2 " 3" 4} {
      set subf [file join $D "F$n.txt"]
      writefile $subf "Initial"
    }
  }
  cd $WD
}

proc modfiles {string} {
  global tcl_platform

  set tmpfile "list.tmp"
  file delete -force $tmpfile
  if {$tcl_platform(platform) eq "windows"} {
    puts "Must be a PC"
    set ret [catch {eval "exec [auto_execok dir] /b F*.txt /s > $tmpfile"} out]
  } else {
    set ret [catch {eval "exec find . -name F*.txt -o -name .svn -prune -a -type f > $tmpfile"} out]
  }
  if {$ret} {
    puts "Find failed"
    puts $out
    exit 1
  }
  set fl [open $tmpfile r]
  while { [gets $fl item] >= 0} {
    writefile $item "$string"
  }
  close $fl
  file delete -force $tmpfile
}

proc getrev {filename} {
  # Find out current revision

  set exec_cmd "svn log -q $filename"
  puts "$exec_cmd"
  set ret [catch {eval "exec $exec_cmd"} out]
  puts $out
  foreach logline [split $out "\n"] {
    if {[string match "r*" $logline]} {
      set latest [lindex $logline 0]
      break
    }
  }
  return $latest
}

proc conflict {filename} {
  # Create a conflict

  set latest [getrev $filename]
  # Save a copy
  file copy $filename Ftmp.txt
  # Make a change
  writefile $filename "Conflict A"
  set exec_cmd "svn commit -m \"change1\" $filename"
  puts "$exec_cmd"
  set ret [catch {eval "exec $exec_cmd"} out]
  puts $out
  # Check out latest revision
  set exec_cmd "svn update -r $latest $filename"
  puts "$exec_cmd"
  set ret [catch {eval "exec $exec_cmd"} out]
  puts $out
  # Make a different change (we hope)
  file delete -force -- $filename
  file rename Ftmp.txt $filename
  writefile $filename "Conflict B"
  # Check out head, which now conflicts with our change
  set exec_cmd "svn update --non-interactive -r HEAD $filename"
  puts "$exec_cmd"
  set ret [catch {eval "exec $exec_cmd"} out]
  puts $out
}

##############################################
set branching_desired 1
set leave_a_mess 1

for {set i 1} {$i < [llength $argv]} {incr i} {
  set arg [lindex $argv $i]

  switch -regexp $arg {
    {^--*nobranch} {
      set branching_desired 0
    }
    {^--*nomess} {
      set leave_a_mess 0
    }
  }
}

if [file isdirectory .svn] {
  puts "Please don't do that here.  There's already a .svn directory."
  exit 1
}

set WD [pwd]
set SVNROOT [file join $WD "SVN_REPOSITORY"]
set taghead(trunk) "trunk"
set taghead(branch) "branches"
set taghead(tag) "tags"

cleanup_old $SVNROOT

mkfiles "svn_test"
repository $SVNROOT "svn_test"
checkout_branch "$SVNROOT" "$taghead(trunk)"

puts "==============================="
puts "First revision on $taghead(trunk)"
cd svn_test_$taghead(trunk)
set exec_cmd "svn propset -R svn:ignore .DS_Store ."
puts "$exec_cmd"
set ret [catch {eval "exec $exec_cmd"} out]
if {$ret} {
  puts $out
}

modfiles "Main 1"
writefile Ftrunk.txt "Main 1"
addfile Ftrunk.txt $taghead(trunk)
commit "First revision on $taghead(trunk)"
tagall $SVNROOT "tagA"
tagall $SVNROOT "tagC"
cd $WD

if {$branching_desired} {
  puts "==============================="
  puts "MAKING BRANCH A"
  newbranch $SVNROOT $taghead(trunk) branchA
  cd $WD/svn_test_branchA
  writefile FbranchA.txt "BranchA 1"
  addfile FbranchA.txt branchA
  commit "Add file FbranchA.txt on branch A"
  cd $WD

  puts "==============================="
  puts "First revision on Branch A"
  cd $WD/svn_test_branchA
  modfiles "BranchA 1"
  commit "First revision on branch A"
  cd $WD

  puts "==============================="
  puts "Second revision on Branch A"
  cd $WD/svn_test_branchA
  modfiles "BranchA 2"
  commit "Second revision on branch A"
  tagall $SVNROOT "tagAA"
  cd $WD

  # Branch C
  puts "==============================="
  puts "MAKING BRANCH C FROM SAME ROOT"
  newbranch $SVNROOT $taghead(trunk) branchC
  cd $WD/svn_test_branchC
  modfiles "BranchC 1"
  writefile FbranchC.txt "BranchC 1"
  addfile FbranchC.txt branchC
  commit "Add file FC on Branch C"
  cd $WD

  puts "==============================="
  puts "Merging BranchA to trunk"
  merge branchA trunk
  cd $WD
}

# Make more modifications on trunk
puts "==============================="
puts "Second revision on $taghead(trunk)"
cd $WD/svn_test_trunk
modfiles "Main 2"
writefile "F-utf-8.txt" "\xFEi\u014B" "utf-8"
writefile "F-iso8859-1.txt" "caf\u00E9" "iso8859-1"
commit "Second revision on $taghead(trunk)"
# series of tags with no update
foreach t {one ten one_hundred one_thousand ten_thousand one_hundred_thousand} {
  tag $SVNROOT $t FTags.txt 0
}
cd $WD

puts "==============================="
puts "Third revision on $taghead(trunk)"
cd $WD/svn_test_trunk
modfiles "Main 3"
commit "Third revision on $taghead(trunk)"
tagall $SVNROOT "tagB"
tag $SVNROOT "one" "Dir1/F1.txt"
cd $WD

if {$branching_desired} {
  # Branch off of the branch
  puts "==============================="
  puts "MAKING BRANCH AA"
  newbranch $SVNROOT branchA branchAA
  cd $WD/svn_test_branchAA
  modfiles "BranchAA 1"
  writefile FbranchAA.txt "BranchAA 1"
  addfile FbranchAA.txt branchAA
  delfile Ftrunk.txt branchAA
  # series of tags with update
  foreach t {first second third} {
    tag $SVNROOT $t UpdTags.txt 1
  }
  commit "Changes on Branch AA"
  cd $WD

  # Make another revision on branchA after
  # branchAA has branched off
  puts "==============================="
  puts "Third revision on Branch A"
  cd $WD/svn_test_branchA
  modfiles "BranchA 3"
  commit "Third revision on Branch A"
  cd $WD

  # Branch B
  puts "==============================="
  puts "MAKING BRANCH B"
  newbranch $SVNROOT $taghead(trunk) branchB
  cd $WD/svn_test_branchB
  # Empty branch. Don't check out or update.
  set exec_cmd "svn mkdir -m\"empty_branch\" file://$SVNROOT/$taghead(branch)/branchD"
  puts "$exec_cmd"
  set ret [catch {eval "exec $exec_cmd"} out]
  puts $out
  if {$ret} {
    exit 1
  }
  modfiles "BranchB 1"
  writefile FbranchB.txt "BranchB 1"
  addfile FbranchB.txt branchB
  commit "Add file FB on Branch B"
  cd $WD
}

if {$leave_a_mess} {
  # Leave the trunk with uncommitted changes
  puts "==============================="
  puts "Making Uncommitted changes on trunk"
  cd $WD/svn_test_trunk
  # Local only
  writefile FileLocal.txt "Pending"
  # Conflict
  conflict Ftrunk.txt
  # Newly added
  writefile FileAdd.txt "Pending"
  addfile FileAdd.txt trunk
  # Rename a file. "moved to" and "moved from"
  movefile File4.txt FileMoved.txt
  # Plain, empty directory
  file mkdir Dir3
  file mkdir "Dir1/New dir"
  # Missing
  file delete -- File3.txt trunk
  # Modify
  writefile File2.txt "Pending"
  writefile "F-utf-8.txt" "\xA53378" "utf-8"
  writefile "F-iso8859-1.txt" "\xA9 2022-2024" "iso8859-1"
  writefile "Dir1/F 3.txt" "Pending"
  delfile "Dir1/F2.txt" trunk
  writefile "Dir 2/F1.txt" "Pending"
  movefile "Dir1/F4.txt" "Dir1/FMoved.txt"
  # Change a file that's been moved.
  writefile "Dir1/FMoved.txt" "Post-move change"
  cd $WD
}

# Remove the source
file delete -force -- svn_test

