11<#
22. NOTES
3- PSTimeMachine.ps1 - Version 1.0.2
3+ PSTimeMachine.ps1 - Version 1.0.3-beta6
44(c) 2019 Colin Cogle <colin@colincogle.name>
55
66This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
@@ -91,16 +91,19 @@ Param(
9191 [Switch ]$NoStatistics = $false
9292)
9393
94- # Save the old error preference. It's rude to clobber the user's environment.
94+ # Save the old error preference and location . It's rude to clobber the user's environment.
9595New-Variable - Option Constant - Name OldErrorActionPreference - Value $ErrorActionPreference
96+ New-Variable - Option Constant - Name OldLocation - Value (Get-Location )
9697$ErrorActionPreference = " Stop"
98+ Set-Location - Path $SourcePath
9799
98100# We're going to create a folder to hold the backup, named for the current date and time.
99101# Format everything except the year with leading zeroes.
100102New-Variable - Option Constant - Name Today - Value (Get-Date )
101103New-Variable - Option Constant - Name FolderName - Value ((" {0:yyyy}-{0:MM}-{0:dd}T{0:hh}-{0:mm}-{0:ss}" -f $Today ) + " .inProgress" )
102104
103105# Start logging?
106+ $DoVerboseCopy = $VerbosePreference -eq " Continue"
104107If (-Not $NoLogging ) {
105108 $script :LogFile = (New-TemporaryFile )
106109 Start-Transcript - Path (($script :LogFile ).Name)
@@ -131,63 +134,69 @@ $bytesCopied = 0
131134$bytesTotal = 0
132135
133136Try {
134- # Has hardlinking support been disabled? If so, just do a copy.
135- If ($NoHardLinks ) {
136- Write-Verbose " Hard-linking is disabled at user request. Copying all files."
137- Copy-Item - Path (Join-Path - Path $SourcePath " *" ) - Destination (Join-Path - Path $DestinationPath $FolderName ) - Recurse
137+ $DoCopyOnlyBackup = $NoHardLinks
138+
139+ # Look for old backups in the same destination.
140+ $PreviousBackups = (Get-ChildItem - Attributes Directory - Path $DestinationPath - Exclude " *.inProgress" - ErrorAction SilentlyContinue `
141+ | Where-Object {$_.Name -CMatch " \d{4}-\d{2}-\d{2}T\d{2}-\d{2}-\d{2}(\.inProgress)?" } `
142+ | Sort-Object - Descending CreationTime
143+ )
144+ If ($PreviousBackups.Count -gt 0 ) {
145+ Write-Verbose " Previous backup found. Doing an incremental backup."
146+ $DoCopyOnlyBackup = $false
147+ $PreviousBackup = $PreviousBackups [0 ].Name
138148 }
139149 Else {
140- # Look for old backups in the same destination.
141- $PreviousBackups = (Get-ChildItem - Attributes Directory - Path $DestinationPath - Exclude " *.inProgress" - ErrorAction SilentlyContinue `
142- | Where-Object {$_.Name -CMatch " \d{4}-\d{2}-\d{2}T\d{2}-\d{2}-\d{2}(\.inProgress)?" } `
143- | Sort-Object - Descending CreationTime
144- )
145- If ($PreviousBackups.Count -eq 0 ) {
146- If ($FailIfOldBackupsAreMissing ) {
147- Throw [System.Management.Automation.ItemNotFoundException ] " No previous backups were found. Exiting at user request."
148- }
149- Write-Verbose " No previous backups exist. Creating an initial backup."
150-
151- $DoVerboseCopy = $VerbosePreference -eq " Continue"
152- Copy-Item - Path (Join-Path - Path $SourcePath " *" ) - Destination (Join-Path - Path $DestinationPath $FolderName ) - Recurse - Verbose:$DoVerboseCopy
150+ If ($FailIfOldBackupsAreMissing ) {
151+ Throw [System.Management.Automation.ItemNotFoundException ] " No previous backups were found. Exiting at user request."
153152 }
154- Else {
155- # Get the most recent backup name.
156- $PreviousBackup = $PreviousBackups [ 0 ].Name
153+ Write-Verbose " Previous backups not found. Doing a copy-only backup. "
154+ $DoCopyOnlyBackup = $true
155+ }
157156
158- Get-ChildItem - Path " $SourcePath \" - Recurse - ErrorAction Stop | ForEach-Object {
159- $RelativeSourceItemPath = (($_.FullName ) -Replace [regex ]::Escape(" $ ( (Get-Item $SourcePath ).FullName) " ), " " )
157+ Get-ChildItem - Path " $SourcePath \" - Recurse - FollowSymLink | ForEach-Object {
158+ $RelativeSourceItemPath = $_.FullName | Resolve-Path - Relative
159+ Write-Debug " Analyzing $_ "
160160
161- # Create directories, but compare files.
162- If ($_.Attributes -CMatch " Directory" ) {
163- Write-Debug " Creating folder $ ( Join-Path - Path $DestinationPath $FolderName $RelativeSourceItemPath ) "
164- New-Item - Type Directory - Path (Join-Path - Path $DestinationPath $FolderName $RelativeSourceItemPath ) | Out-Null
161+ # Don't copy the log file.
162+ If ($_.Name -eq (($script :LogFile ).Name)) {
163+ Continue
164+ }
165+
166+ # Create directories, but compare files.
167+ If ($_.Attributes -CMatch " Directory" ) {
168+ Write-Debug " Creating folder $ ( Join-Path - Path $DestinationPath $FolderName $RelativeSourceItemPath ) "
169+ New-Item - Type Directory - Path (Join-Path - Path $DestinationPath $FolderName $RelativeSourceItemPath ) | Out-Null
170+ }
171+ Else {
172+ # Compare file sizes and dates to determine if something has changed.
173+ If (-Not $DoCopyOnlyBackup ) {
174+ $PreviousCopyOfFile = Get-Item - Path (Join-Path - Path $DestinationPath $PreviousBackup $RelativeSourceItemPath )
175+ If (($_.LastWriteTime -eq $PreviousCopyOfFile.LastWriteTime ) -And ($_.Length -eq $PreviousCopyOfFile.Length )) {
176+ # The file has not changed since the last backup. Create a hard link.
177+ $DestinationHardlink = @ {
178+ ItemType = " HardLink"
179+ Target = ($PreviousCopyOfFile.FullName )
180+ Path = ($PreviousCopyOfFile | Split-Path - Parent) -Replace [regex ]::Escape($PreviousBackup ), $FolderName
181+ Name = ($PreviousCopyOfFile.Name )
182+ }
183+ Write-Verbose " Linking: $RelativeSourceItemPath "
184+ New-Item @DestinationHardlink | Out-Null
165185 }
166186 Else {
167- # Compare file sizes and dates to determine if something has changed.
168- $PreviousCopyOfFile = Get-Item - Path (Join-Path - Path $DestinationPath $PreviousBackup $RelativeSourceItemPath )
169- If (($_.LastWriteTime -eq $PreviousCopyOfFile.LastWriteTime ) -And ($_.Length -eq $PreviousCopyOfFile.Length )) {
170- # The file has not changed since the last backup. Create a hard link.
171- $DestinationHardlink = @ {
172- ItemType = " HardLink"
173- Target = ($PreviousCopyOfFile.FullName )
174- Path = ($PreviousCopyOfFile | Split-Path - Parent) -Replace [regex ]::Escape($PreviousBackup ), $FolderName
175- Name = ($PreviousCopyOfFile.Name )
176- ErrorAction = " Stop"
177- }
178- Write-Verbose " Linking: $RelativeSourceItemPath "
179- New-Item @DestinationHardlink | Out-Null
180- }
181- Else {
182- Write-Verbose " Copying: $RelativeSourceItemPath "
183- Copy-Item - Path $_.FullName - Destination (Join-Path - Path $DestinationPath $FolderName $RelativeSourceItemPath ) - ErrorAction Stop
184- $bytesCopied += $_.Length
185- }
186- $bytesTotal += $_.Length
187+ Write-Verbose " Copying: $RelativeSourceItemPath "
188+ Copy-Item - Path $_.FullName - Destination (Join-Path - Path $DestinationPath $FolderName $RelativeSourceItemPath ) - Verbose:$DoVerboseCopy
189+ $bytesCopied += $_.Length
187190 }
191+ } Else {
192+ Write-Verbose " Copying: $RelativeSourceItemPath "
193+ Copy-Item - Path $_.FullName - Destination (Join-Path - Path $DestinationPath $FolderName $RelativeSourceItemPath ) - Verbose:$DoVerboseCopy
194+ $bytesCopied += $_.Length
188195 }
196+ $bytesTotal += $_.Length
189197 }
190198 }
199+
191200 Write-Output " Backup completed at $ ( Get-Date ) ."
192201 Rename-Item - Path (Join-Path - Path $DestinationPath $FolderName ) - NewName ($FolderName -CReplace ' \.inProgress' )
193202 If (-Not $NoStatistics -And $bytesTotal -gt 0 ) {
@@ -202,11 +211,12 @@ Catch {
202211Finally {
203212 If (-Not $NoLogging ) {
204213 Write-Verbose " Moving log file to backup destination."
205- Stop-Transcript - ErrorAction SilentlyContinue
214+ Stop-Transcript - ErrorAction SilentlyContinue | Out-Null
206215 Write-Debug " Moving $ ( $_.FullName ) to $ ( Join-Path - Path $DestinationPath ' PSTimeMachine.log' ) "
207216 Move-Item - Path (($script :LogFile ).Name) - Destination (Join-Path - Path $DestinationPath $FolderName " PSTimeMachine.log" ) - ErrorAction Continue
208217 }
209218
210- # Restore the user's preferred error action preference .
219+ # Restore the user's environment .
211220 $ErrorActionPreference = $OldErrorActionPreference
212- }
221+ Set-Location - Path $OldLocation
222+ }
0 commit comments