foam_sync.ps1 (LIFEBALANCE) 2025-06-19T01:06:29Z

This commit is contained in:
Ben Miller
2025-06-19 01:06:29 -06:00
parent eb8b59ee9f
commit 1439b682d6
2 changed files with 235 additions and 212 deletions

View File

@ -35,259 +35,280 @@
$frequencyMinutes = 2 # How often the Scheduled Task should attempt to run this script
$executionTimeLimitBufferSeconds = 30 # Buffer: task stops if it runs longer than (frequency - buffer)
# --- Script Setup ---
$scriptPath = $MyInvocation.MyCommand.Path
$scriptDir = Split-Path -Path $scriptPath -Parent
$scriptName = (Get-Item $scriptPath).Name
Write-Host "Script: $scriptName at $scriptPath"
Write-Host "Repository directory: $scriptDir"
Write-Host "Sync frequency: Every $frequencyMinutes minutes"
# --- Scheduled Task Setup ---
$taskName = "FoamGitSync"
$taskDescription = "Periodically synchronizes the Git repository at $scriptDir using $scriptName."
# Run as the user who executes this script.
$taskPrincipal = New-ScheduledTaskPrincipal -UserId $env:USERNAME -LogonType Interactive
# Define a very long duration (e.g., 40 years)
$practicallyIndefiniteDuration = New-TimeSpan -Days (365 * 40 + 10) # Approx 40 years, accounting for leap years
# Trigger configuration
$trigger = New-ScheduledTaskTrigger -Once -At (Get-Date) -RepetitionInterval (New-TimeSpan -Minutes $frequencyMinutes) -RepetitionDuration $practicallyIndefiniteDuration
# Action configuration:
# We use an indirection technique to ensure the window is truly hidden.
# The scheduled task launches an initial PowerShell.
# This initial PowerShell then uses Start-Process to launch the *actual* script in a new, hidden PowerShell process.
# 1. Innermost PowerShell's -File argument: Path to the script, with internal double quotes escaped as ""
# e.g., \"C:\path\to your script.ps1\"
$scriptPathForInnermostFileParam = $scriptPath.Replace('"', '""')
$innermostFileArg = "\`"$scriptPathForInnermostFileParam\`""
# 2. Argument string for the innermost PowerShell instance (the one executing the actual script)
# e.g., -NoProfile -NonInteractive -ExecutionPolicy Bypass -File \"C:\path\to your script.ps1\"
$innermostPSArgs = "-NoProfile -NonInteractive -ExecutionPolicy Bypass -File $innermostFileArg"
# 3. The innermostPSArgs string needs its single quotes escaped (as '') because it will be wrapped in single quotes for Start-Process's -ArgumentList
$innermostPSArgsEscapedForStartProcess = $innermostPSArgs.Replace("'", "''")
# 4. Command string that the first PowerShell instance (launched by Task Scheduler) will execute using Start-Process.
# e.g., Start-Process -FilePath powershell.exe -ArgumentList '-NoProfile -NonInteractive -ExecutionPolicy Bypass -File \"C:\path\to your script.ps1\"' -WindowStyle Hidden
$commandToRunViaStartProcess = "Start-Process -FilePath powershell.exe -ArgumentList '$innermostPSArgsEscapedForStartProcess' -WindowStyle Hidden"
# 5. Argument string FOR THE TASK SCHEDULER to pass to the first powershell.exe.
# The $commandToRunViaStartProcess is the value for -Command, and needs to be quoted for registration.
# e.g., -NoProfile -NonInteractive -ExecutionPolicy Bypass -Command "Start-Process -FilePath powershell.exe -ArgumentList '-NoProfile -NonInteractive -ExecutionPolicy Bypass -File \"C:\path\to your script.ps1\"' -WindowStyle Hidden"
$actionArgumentForRegistration = "-NoProfile -NonInteractive -ExecutionPolicy Bypass -Command \`"$commandToRunViaStartProcess\`""
# 6. Expected argument string WHEN RETRIEVED by Get-ScheduledTask.
# Task Scheduler/PowerShell often strips the outermost quotes from the -Command value when retrieved.
$expectedRetrievedActionArgument = "-NoProfile -NonInteractive -ExecutionPolicy Bypass -Command $commandToRunViaStartProcess"
$action = New-ScheduledTaskAction -Execute "powershell.exe" -Argument $actionArgumentForRegistration
# Task settings
# Calculate a realistic execution time limit based on the frequency
$executionTimeLimitTotalSeconds = ($frequencyMinutes * 60) - $executionTimeLimitBufferSeconds
if ($executionTimeLimitTotalSeconds -lt 30) {
# Ensure a minimum sensible execution time (e.g., 30 seconds)
$executionTimeLimitTotalSeconds = 30
# --- Log File Setup ---
# Log inside the repository, in a .logs subfolder. Ensure this is in .gitignore
$scriptDirForLog = $PSScriptRoot # Use PSScriptRoot for robustness in determining script's dir
$logDir = Join-Path -Path $scriptDirForLog -ChildPath ".logs"
if (-not (Test-Path -Path $logDir)) {
New-Item -ItemType Directory -Path $logDir -Force | Out-Null
}
$taskExecutionTimeLimit = New-TimeSpan -Seconds $executionTimeLimitTotalSeconds
$settings = New-ScheduledTaskSettingsSet -ExecutionTimeLimit $taskExecutionTimeLimit
$logFilePath = Join-Path -Path $logDir -ChildPath "foam_sync.log"
Start-Transcript -Path $logFilePath -Append -IncludeInvocationHeader -Force
# Check and configure the scheduled task
try {
$existingTask = Get-ScheduledTask -TaskName $taskName -ErrorAction SilentlyContinue
# Wrap main script logic in try for Stop-Transcript in finally
if ($existingTask) {
Write-Host "Scheduled task '$taskName' already exists. Checking configuration..."
$currentTrigger = $existingTask.Triggers[0]
$currentAction = $existingTask.Actions[0]
# You could also compare $existingTask.Principal.UserId with $taskPrincipal.UserId
# and $existingTask.Description with $taskDescription if strict matching is needed for those.
# --- Script Setup ---
$scriptPath = $MyInvocation.MyCommand.Path
$scriptDir = Split-Path -Path $scriptPath -Parent # This is the Git repository root
$scriptName = (Get-Item $scriptPath).Name
# Check Trigger
$triggerMatches = $false
if ($currentTrigger -is [Microsoft.Management.Infrastructure.CimInstance] `
-and $currentTrigger.RepetitionInterval.TotalMinutes -eq $frequencyMinutes `
-and $currentTrigger.RepetitionDuration -eq $practicallyIndefiniteDuration) {
$triggerMatches = $true
}
Write-Host "Script: $scriptName at $scriptPath"
Write-Host "Repository directory: $scriptDir"
Write-Host "Sync frequency: Every $frequencyMinutes minutes"
Write-Host "Log file: $logFilePath"
# Check Action
$actionMatches = $false
if ($currentAction -is [Microsoft.Management.Infrastructure.CimInstance] `
-and $currentAction.Execute -eq "powershell.exe" `
-and $currentAction.Argument -eq $expectedRetrievedActionArgument) {
$actionMatches = $true
}
$taskDescription = "Periodically synchronizes the Git repository at $scriptDir using $scriptName."
# Check Principal (example, can be expanded)
$principalMatches = ($existingTask.Principal.UserId -eq $taskPrincipal.UserId)
# Run as the user who executes this script.
$taskPrincipal = New-ScheduledTaskPrincipal -UserId $env:USERNAME -LogonType Interactive
# Define a very long duration (e.g., 40 years)
$practicallyIndefiniteDuration = New-TimeSpan -Days (365 * 40 + 10) # Approx 40 years, accounting for leap years
# Trigger configuration
$trigger = New-ScheduledTaskTrigger -Once -At (Get-Date) -RepetitionInterval (New-TimeSpan -Minutes $frequencyMinutes) -RepetitionDuration $practicallyIndefiniteDuration
# Check Settings (specifically ExecutionTimeLimit for this change)
$settingsMatch = $false
if ($existingTask.Settings.ExecutionTimeLimit -eq $taskExecutionTimeLimit) {
$settingsMatch = $true
}
# Action configuration:
# We use an indirection technique to ensure the window is truly hidden.
# The scheduled task launches an initial PowerShell.
# This initial PowerShell then uses Start-Process to launch the *actual* script in a new, hidden PowerShell process.
if ($triggerMatches -and $actionMatches -and $principalMatches -and $settingsMatch) {
Write-Host "Scheduled task '$taskName' is already correctly configured."
# 1. Innermost PowerShell's -File argument: Path to the script, with internal double quotes escaped as ""
# e.g., \"C:\path\to your script.ps1\"
$scriptPathForInnermostFileParam = $scriptPath.Replace('"', '""')
$innermostFileArg = "\`"$scriptPathForInnermostFileParam\`""
# 2. Argument string for the innermost PowerShell instance (the one executing the actual script)
# e.g., -NoProfile -NonInteractive -ExecutionPolicy Bypass -File \"C:\path\to your script.ps1\"
$innermostPSArgs = "-NoProfile -NonInteractive -ExecutionPolicy Bypass -File $innermostFileArg"
# 3. The innermostPSArgs string needs its single quotes escaped (as '') because it will be wrapped in single quotes for Start-Process's -ArgumentList
$innermostPSArgsEscapedForStartProcess = $innermostPSArgs.Replace("'", "''")
# 4. Command string that the first PowerShell instance (launched by Task Scheduler) will execute using Start-Process.
# e.g., Start-Process -FilePath powershell.exe -ArgumentList '-NoProfile -NonInteractive -ExecutionPolicy Bypass -File \"C:\path\to your script.ps1\"' -WindowStyle Hidden
$commandToRunViaStartProcess = "Start-Process -FilePath powershell.exe -ArgumentList '$innermostPSArgsEscapedForStartProcess' -WindowStyle Hidden"
# 5. Argument string FOR THE TASK SCHEDULER to pass to the first powershell.exe.
# The $commandToRunViaStartProcess is the value for -Command, and needs to be quoted for registration.
# e.g., -NoProfile -NonInteractive -ExecutionPolicy Bypass -Command "Start-Process -FilePath powershell.exe -ArgumentList '-NoProfile -NonInteractive -ExecutionPolicy Bypass -File \"C:\path\to your script.ps1\"' -WindowStyle Hidden"
$actionArgumentForRegistration = "-NoProfile -NonInteractive -ExecutionPolicy Bypass -Command \`"$commandToRunViaStartProcess\`""
# 6. Expected argument string WHEN RETRIEVED by Get-ScheduledTask.
# Task Scheduler/PowerShell often strips the outermost quotes from the -Command value when retrieved.
$expectedRetrievedActionArgument = "-NoProfile -NonInteractive -ExecutionPolicy Bypass -Command $commandToRunViaStartProcess"
$action = New-ScheduledTaskAction -Execute "powershell.exe" -Argument $actionArgumentForRegistration
# Task settings
# Calculate a realistic execution time limit based on the frequency
$executionTimeLimitTotalSeconds = ($frequencyMinutes * 60) - $executionTimeLimitBufferSeconds
if ($executionTimeLimitTotalSeconds -lt 30) {
# Ensure a minimum sensible execution time (e.g., 30 seconds)
$executionTimeLimitTotalSeconds = 30
}
$taskExecutionTimeLimit = New-TimeSpan -Seconds $executionTimeLimitTotalSeconds
$settings = New-ScheduledTaskSettingsSet -ExecutionTimeLimit $taskExecutionTimeLimit
# Check and configure the scheduled task
try {
$existingTask = Get-ScheduledTask -TaskName $taskName -ErrorAction SilentlyContinue
if ($existingTask) {
Write-Host "Scheduled task '$taskName' already exists. Checking configuration..."
$currentTrigger = $existingTask.Triggers[0]
$currentAction = $existingTask.Actions[0]
# You could also compare $existingTask.Principal.UserId with $taskPrincipal.UserId
# and $existingTask.Description with $taskDescription if strict matching is needed for those.
# Check Trigger
$triggerMatches = $false
if ($currentTrigger -is [Microsoft.Management.Infrastructure.CimInstance] `
-and $currentTrigger.RepetitionInterval.TotalMinutes -eq $frequencyMinutes `
-and $currentTrigger.RepetitionDuration -eq $practicallyIndefiniteDuration) {
$triggerMatches = $true
}
# Check Action
$actionMatches = $false
if ($currentAction -is [Microsoft.Management.Infrastructure.CimInstance] `
-and $currentAction.Execute -eq "powershell.exe" `
-and $currentAction.Argument -eq $expectedRetrievedActionArgument) {
$actionMatches = $true
}
# Check Principal (example, can be expanded)
$principalMatches = ($existingTask.Principal.UserId -eq $taskPrincipal.UserId)
# Check Settings (specifically ExecutionTimeLimit for this change)
$settingsMatch = $false
if ($existingTask.Settings.ExecutionTimeLimit -eq $taskExecutionTimeLimit) {
$settingsMatch = $true
}
if ($triggerMatches -and $actionMatches -and $principalMatches -and $settingsMatch) {
Write-Host "Scheduled task '$taskName' is already correctly configured."
}
else {
Write-Host "Scheduled task '$taskName' configuration differs. Attempting to update in-place..."
try {
Set-ScheduledTask -TaskName $taskName -Action $action -Trigger $trigger -Settings $settings -Principal $taskPrincipal -ErrorAction Stop
Write-Host "Scheduled task '$taskName' updated successfully."
}
catch {
Write-Warning "Failed to update scheduled task '$taskName' in-place. Error: $($_.Exception.Message)"
Write-Warning "The task remains in its previous state. Manual intervention may be required or re-run with Administrator privileges."
# We intentionally DO NOT unregister here to avoid the scenario you described.
}
}
}
else {
Write-Host "Scheduled task '$taskName' configuration differs. Attempting to update in-place..."
Write-Host "Creating scheduled task '$taskName'..."
try {
Set-ScheduledTask -TaskName $taskName -Action $action -Trigger $trigger -Settings $settings -Principal $taskPrincipal -ErrorAction Stop
Write-Host "Scheduled task '$taskName' updated successfully."
Register-ScheduledTask -TaskName $taskName -Description $taskDescription -Principal $taskPrincipal -Trigger $trigger -Action $action -Settings $settings -ErrorAction Stop
Write-Host "Scheduled task '$taskName' created successfully."
}
catch {
Write-Warning "Failed to update scheduled task '$taskName' in-place. Error: $($_.Exception.Message)"
Write-Warning "The task remains in its previous state. Manual intervention may be required or re-run with Administrator privileges."
# We intentionally DO NOT unregister here to avoid the scenario you described.
Write-Warning "Failed to create scheduled task '$taskName'. Error: $($_.Exception.Message)"
Write-Warning "You may need to run this script as Administrator."
}
}
}
else {
Write-Host "Creating scheduled task '$taskName'..."
try {
Register-ScheduledTask -TaskName $taskName -Description $taskDescription -Principal $taskPrincipal -Trigger $trigger -Action $action -Settings $settings -ErrorAction Stop
Write-Host "Scheduled task '$taskName' created successfully."
}
catch {
Write-Warning "Failed to create scheduled task '$taskName'. Error: $($_.Exception.Message)"
Write-Warning "You may need to run this script as Administrator."
}
catch {
# This outer catch is for unexpected errors, e.g., if Get-ScheduledTask had -ErrorAction Stop
Write-Warning "An unexpected error occurred during scheduled task setup for '$taskName'. Error: $($_.Exception.Message)"
}
}
catch {
# This outer catch is for unexpected errors, e.g., if Get-ScheduledTask had -ErrorAction Stop
Write-Warning "An unexpected error occurred during scheduled task setup for '$taskName'. Error: $($_.Exception.Message)"
}
# --- Git Operations ---
Write-Host "Navigating to repository: $scriptDir"
try {
Set-Location -Path $scriptDir -ErrorAction Stop
}
catch {
Write-Error "Unable to find repository at $scriptDir. Exiting script."
exit 1
}
# --- Git Operations ---
Write-Host "Navigating to repository: $scriptDir"
try {
Set-Location -Path $scriptDir -ErrorAction Stop
}
catch {
Write-Error "Unable to find repository at $scriptDir. Exiting script."
exit 1
}
Write-Host "Staging all changes with 'git add .'"
git add .
if ($LASTEXITCODE -ne 0) {
Write-Warning "'git add .' command failed with exit code $LASTEXITCODE."
}
Write-Host "Checking for staged changes with 'git diff --staged --quiet'..."
git diff --staged --quiet
$changesStaged = ($LASTEXITCODE -ne 0)
if ($changesStaged) {
Write-Host "Staged changes detected. Creating commit..."
$commitMessage = "$scriptName ($($env:COMPUTERNAME)) $(Get-Date -Format 'yyyy-MM-ddTHH:mm:ssZ')"
Write-Host "Commit message: $commitMessage"
git commit -m $commitMessage
Write-Host "Staging all changes with 'git add .'"
git add .
if ($LASTEXITCODE -ne 0) {
Write-Warning "'git commit' command failed with exit code $LASTEXITCODE."
Write-Warning "'git add .' command failed with exit code $LASTEXITCODE."
}
Write-Host "Checking for staged changes with 'git diff --staged --quiet'..."
git diff --staged --quiet
$changesStaged = ($LASTEXITCODE -ne 0)
if ($changesStaged) {
Write-Host "Staged changes detected. Creating commit..."
$commitMessage = "$scriptName ($($env:COMPUTERNAME)) $(Get-Date -Format 'yyyy-MM-ddTHH:mm:ssZ')"
Write-Host "Commit message: $commitMessage"
git commit -m $commitMessage
if ($LASTEXITCODE -ne 0) {
Write-Warning "'git commit' command failed with exit code $LASTEXITCODE."
}
else {
Write-Host "Commit created successfully."
}
}
else {
Write-Host "Commit created successfully."
Write-Host "No relevant changes detected to commit."
Write-Host "Resetting staging area with 'git reset HEAD --quiet'."
git reset HEAD --quiet
}
}
else {
Write-Host "No relevant changes detected to commit."
Write-Host "Resetting staging area with 'git reset HEAD --quiet'."
git reset HEAD --quiet
}
Write-Host "Updating remote 'origin' with 'git remote update origin --prune'..."
git remote update origin --prune
if ($LASTEXITCODE -ne 0) {
Write-Error "Unable to update remote 'origin'. Exiting script."
exit 1
}
Write-Host "Updating remote 'origin' with 'git remote update origin --prune'..."
git remote update origin --prune
if ($LASTEXITCODE -ne 0) {
Write-Error "Unable to update remote 'origin'. Exiting script."
exit 1
}
$localCommit = (git rev-parse HEAD 2>$null).Trim()
if ($LASTEXITCODE -ne 0 -or -not $localCommit) { Write-Error "Failed to get local HEAD commit. Exiting script."; exit 1 }
$localCommit = (git rev-parse HEAD 2>$null).Trim()
if ($LASTEXITCODE -ne 0 -or -not $localCommit) { Write-Error "Failed to get local HEAD commit. Exiting script."; exit 1 }
$remoteBranch = "origin/main"
$remoteCommit = (git rev-parse $remoteBranch 2>$null).Trim()
if ($LASTEXITCODE -ne 0 -or -not $remoteCommit) { Write-Error "Failed to get remote '$remoteBranch' commit. Exiting script."; exit 1 }
$remoteBranch = "origin/main"
$remoteCommit = (git rev-parse $remoteBranch 2>$null).Trim()
if ($LASTEXITCODE -ne 0 -or -not $remoteCommit) { Write-Error "Failed to get remote '$remoteBranch' commit. Exiting script."; exit 1 }
Write-Host "Local HEAD commit: $localCommit"
Write-Host "Remote '$remoteBranch' commit: $remoteCommit"
Write-Host "Local HEAD commit: $localCommit"
Write-Host "Remote '$remoteBranch' commit: $remoteCommit"
if ($localCommit -eq $remoteCommit) {
Write-Host "Local and remote are already in sync."
exit 0
}
if ($localCommit -eq $remoteCommit) {
Write-Host "Local and remote are already in sync."
exit 0
}
$sleepyTime = Get-Random -Minimum 1 -Maximum 15
Write-Host "Local and remote differ. Sleeping for $sleepyTime seconds..."
Start-Sleep -Seconds $sleepyTime
$sleepyTime = Get-Random -Minimum 1 -Maximum 15
Write-Host "Local and remote differ. Sleeping for $sleepyTime seconds..."
Start-Sleep -Seconds $sleepyTime
$localCommitAfterSleep = (git rev-parse HEAD 2>$null).Trim()
if ($LASTEXITCODE -ne 0 -or -not $localCommitAfterSleep) { Write-Error "Failed to re-get local HEAD commit. Exiting script."; exit 1 }
$remoteCommitAfterSleep = (git rev-parse $remoteBranch 2>$null).Trim()
if ($LASTEXITCODE -ne 0 -or -not $remoteCommitAfterSleep) { Write-Error "Failed to re-get remote '$remoteBranch' commit. Exiting script."; exit 1 }
$localCommitAfterSleep = (git rev-parse HEAD 2>$null).Trim()
if ($LASTEXITCODE -ne 0 -or -not $localCommitAfterSleep) { Write-Error "Failed to re-get local HEAD commit. Exiting script."; exit 1 }
$remoteCommitAfterSleep = (git rev-parse $remoteBranch 2>$null).Trim()
if ($LASTEXITCODE -ne 0 -or -not $remoteCommitAfterSleep) { Write-Error "Failed to re-get remote '$remoteBranch' commit. Exiting script."; exit 1 }
if ($localCommitAfterSleep -eq $remoteCommitAfterSleep) {
Write-Host "Local and remote became synchronized during sleep."
exit 0
}
$localCommit = $localCommitAfterSleep
$remoteCommit = $remoteCommitAfterSleep
if ($localCommitAfterSleep -eq $remoteCommitAfterSleep) {
Write-Host "Local and remote became synchronized during sleep."
exit 0
}
$localCommit = $localCommitAfterSleep
$remoteCommit = $remoteCommitAfterSleep
Write-Host "Proceeding with sync logic..."
Write-Host "Proceeding with sync logic..."
git merge-base --is-ancestor $localCommit $remoteCommit
$localIsAncestorOfRemote = ($LASTEXITCODE -eq 0)
git merge-base --is-ancestor $localCommit $remoteCommit
$localIsAncestorOfRemote = ($LASTEXITCODE -eq 0)
git merge-base --is-ancestor $remoteCommit $localCommit
$remoteIsAncestorOfLocal = ($LASTEXITCODE -eq 0)
git merge-base --is-ancestor $remoteCommit $localCommit
$remoteIsAncestorOfLocal = ($LASTEXITCODE -eq 0)
if ($localIsAncestorOfRemote) {
Write-Host "Remote '$remoteBranch' is ahead. Pulling with rebase..."
git pull --rebase origin main
if ($LASTEXITCODE -ne 0) { Write-Error "'git pull --rebase' failed. Manual intervention may be required."; exit 1 }
}
elseif ($remoteIsAncestorOfLocal) {
Write-Host "Local HEAD is ahead. Pushing..."
git push origin main
if ($LASTEXITCODE -ne 0) { Write-Error "'git push' failed. Manual intervention may be required."; exit 1 }
}
else {
Write-Host "Local HEAD and remote '$remoteBranch' have diverged."
$remoteTimestampStr = (git log --pretty=format:"%at" -n 1 $remoteBranch 2>$null).Trim()
if ($LASTEXITCODE -ne 0 -or -not $remoteTimestampStr) { Write-Error "Failed to get remote commit timestamp for '$remoteBranch'. Exiting script."; exit 1 }
$remoteTimestamp = [long]$remoteTimestampStr
$localTimestampStr = (git log --pretty=format:"%at" -n 1 HEAD 2>$null).Trim()
if ($LASTEXITCODE -ne 0 -or -not $localTimestampStr) { Write-Error "Failed to get local commit timestamp for HEAD. Exiting script."; exit 1 }
$localTimestamp = [long]$localTimestampStr
# It's good practice to check if conversion was successful, though [long] will error on failure.
Write-Host "Local timestamp: $localTimestamp, Remote timestamp: $remoteTimestamp"
if ($remoteTimestamp -gt $localTimestamp) {
Write-Host "Remote is newer. Pulling with rebase, strategy 'theirs'..."
git pull --rebase -X theirs origin main
if ($localIsAncestorOfRemote) {
Write-Host "Remote '$remoteBranch' is ahead. Pulling with rebase..."
git pull --rebase origin main
if ($LASTEXITCODE -ne 0) { Write-Error "'git pull --rebase' failed. Manual intervention may be required."; exit 1 }
}
elseif ($remoteIsAncestorOfLocal) {
Write-Host "Local HEAD is ahead. Pushing..."
git push origin main
if ($LASTEXITCODE -ne 0) { Write-Error "'git push' failed. Manual intervention may be required."; exit 1 }
}
else {
Write-Host "Local is newer or same age. Pulling with rebase, strategy 'ours'..."
git pull --rebase -X ours origin main
Write-Host "Local HEAD and remote '$remoteBranch' have diverged."
$remoteTimestampStr = (git log --pretty=format:"%at" -n 1 $remoteBranch 2>$null).Trim()
if ($LASTEXITCODE -ne 0 -or -not $remoteTimestampStr) { Write-Error "Failed to get remote commit timestamp for '$remoteBranch'. Exiting script."; exit 1 }
$remoteTimestamp = [long]$remoteTimestampStr
$localTimestampStr = (git log --pretty=format:"%at" -n 1 HEAD 2>$null).Trim()
if ($LASTEXITCODE -ne 0 -or -not $localTimestampStr) { Write-Error "Failed to get local commit timestamp for HEAD. Exiting script."; exit 1 }
$localTimestamp = [long]$localTimestampStr
# It's good practice to check if conversion was successful, though [long] will error on failure.
Write-Host "Local timestamp: $localTimestamp, Remote timestamp: $remoteTimestamp"
if ($remoteTimestamp -gt $localTimestamp) {
Write-Host "Remote is newer. Pulling with rebase, strategy 'theirs'..."
git pull --rebase -X theirs origin main
}
else {
Write-Host "Local is newer or same age. Pulling with rebase, strategy 'ours'..."
git pull --rebase -X ours origin main
}
if ($LASTEXITCODE -ne 0) { Write-Error "Rebase during divergence failed. Manual intervention may be required."; exit 1 }
Write-Host "Pushing changes after rebase..."
git push origin main
if ($LASTEXITCODE -ne 0) { Write-Error "'git push' after rebase failed. Manual intervention may be required."; exit 1 }
}
if ($LASTEXITCODE -ne 0) { Write-Error "Rebase during divergence failed. Manual intervention may be required."; exit 1 }
Write-Host "Pushing changes after rebase..."
git push origin main
if ($LASTEXITCODE -ne 0) { Write-Error "'git push' after rebase failed. Manual intervention may be required."; exit 1 }
Write-Host "Synchronization process completed successfully."
exit 0
}
finally {
Stop-Transcript
}
Write-Host "Synchronization process completed successfully."
exit 0