# encoding: ascii
# api: powershell
# title: Move-FileSafely
# description: A wrapper around Move-Item which moves a whole folder tree and creates backup copies if the files already exist in the destination.
# version: 0.1
# type: function
# author: Joel Bennett
# license: CC0
# function: Test-CreateablePath
# x-poshcode-id: 1943
# x-archived: 2016-10-18T09:30:41
# x-published: 2010-06-29T21:21:00
#
#
function global:Move-FileSafely {
#.Synopsis
#  Moves files and folders from $source folder to $destination
#.Description 
#  Moves files from $source to $destination but if they already exist, it puts them in a parallel $BackupCopies folder instead
#.Parameter Source
#  The path to the source directory
#.Parameter Destination
#  The path to the destination. The CONTENTS of $source will be copied into this folder.
#.Parameter BackupCopies
#  The path to an alternate folder where files will be moved to if they already exist in the destination.
param($Source, $Destination, $BackupCopies=$(Join-Path $destination (Get-Date -f "backup yyyy-MM-dd")))
   function Test-CreateablePath {
      param($path)
      try {
         # if we can resolve the parent, then we'll be able to create this ...
         return Join-Path (Resolve-Path (Split-Path $path) -ErrorAction STOP) (Split-Path $path -Leaf)
      } catch {
         # otherwise, if it's a full absolute path, we'll be able to create it...
         return (split-path $path -isabsolute)
      }
   }
   
   $source = Resolve-Path $source -ErrorAction Stop # throw if source folder doesn't exist

   if(!(Test-CreateablePath $Destination)) {
      throw "Destination ($Destination) path must be a full path, or a subfolder of an existing folder"
   }
   if(!(Test-CreateablePath $BackupCopies)) {
      throw "BackupCopies ($BackupCopies) path must be a full path, or a subfolder of an existing folder"
   }

   mkdir $destination -Force -ErrorAction SilentlyContinue | Out-Null
   
   Get-ChildItem $source -recurse -ErrorAction SilentlyContinue | Move-Item -Destination { split-path $_.FullName.Replace($Source, $Destination) } -ErrorVariable Problems -ErrorAction SilentlyContinue
   foreach($file in $Problems | Select -Expand TargetObject | Where { Test-Path $_.FullName -Type Leaf }) { 
      $Target = $File.FullName.Replace($source, $backupCopies)
      mkdir (Split-Path $Target) -Force -ErrorAction SilentlyContinue | Out-Null
      move-item $File.FullName $Target
   }
   remove-item $source -Recurse -Force
}
