diff --git a/scripts/Commands/InitCommand.php b/scripts/Commands/InitCommand.php
index 720dcd119..26ab54c9e 100644
--- a/scripts/Commands/InitCommand.php
+++ b/scripts/Commands/InitCommand.php
@@ -105,10 +105,11 @@ class InitCommand extends Command
      */
     protected function cloneBookStackViaGit(string $installDir): void
     {
-        $errors = (new ProgramRunner('git', '/usr/bin/git'))
+        $git = (new ProgramRunner('git', '/usr/bin/git'))
             ->withTimeout(240)
-            ->withIdleTimeout(15)
-            ->runCapturingStdErr([
+            ->withIdleTimeout(15);
+
+        $errors = $git->runCapturingStdErr([
                 'clone', '-q',
                 '--branch', 'release',
                 '--single-branch',
@@ -119,6 +120,12 @@ class InitCommand extends Command
         if ($errors) {
             throw new CommandError("Failed git clone with errors:\n" . $errors);
         }
+
+        // Disable file permission tracking for git repo
+        $git->runCapturingStdErr([
+            '-C', $installDir,
+            'config', 'core.fileMode', 'false'
+        ]);
     }
 
     /**
diff --git a/scripts/Commands/RestoreCommand.php b/scripts/Commands/RestoreCommand.php
index 6c2b7deb5..a7c8ac5ff 100644
--- a/scripts/Commands/RestoreCommand.php
+++ b/scripts/Commands/RestoreCommand.php
@@ -48,7 +48,6 @@ class RestoreCommand extends Command
 
         $zipPath = realpath($input->getArgument('backup-zip'));
         $zip = new BackupZip($zipPath);
-        // TODO - Fix folders not being picked up here:
         $contents = $zip->getContentsOverview();
 
         $output->writeln("\n<info>Contents found in the backup ZIP:</info>");
@@ -79,9 +78,10 @@ class RestoreCommand extends Command
         }
         $zip->extractInto($extractDir);
 
+        $envChanges = [];
         if ($contents['env']['exists']) {
             $output->writeln("<info>Restoring and merging .env file...</info>");
-            $this->restoreEnv($extractDir, $appDir);
+            $envChanges = $this->restoreEnv($extractDir, $appDir, $output, $interactions);
         }
 
         $folderLocations = ['themes', 'public/uploads', 'storage/uploads'];
@@ -92,18 +92,22 @@ class RestoreCommand extends Command
             }
         }
 
+        $artisan = (new ArtisanRunner($appDir));
         if ($contents['db']['exists']) {
             $output->writeln("<info>Restoring database from SQL dump...</info>");
             $this->restoreDatabase($appDir, $extractDir);
 
             $output->writeln("<info>Running database migrations...</info>");
-            $artisan = (new ArtisanRunner($appDir));
             $artisan->run(['migrate', '--force']);
         }
 
-        // TODO - Handle change of URL?
-        // TODO - Update system URL (via BookStack artisan command) if
-        //   there's been a change from old backup env
+        if ($envChanges && $envChanges['old_url'] !== $envChanges['new_url']) {
+            $output->writeln("<info>App URL change made, Updating database with URL change...</info>");
+            $artisan->run([
+                'bookstack:update-url',
+                $envChanges['old_url'], $envChanges['new_url'],
+            ]);
+        }
 
         $output->writeln("<info>Clearing app caches...</info>");
         $artisan->run(['cache:clear']);
@@ -118,7 +122,7 @@ class RestoreCommand extends Command
         return Command::SUCCESS;
     }
 
-    protected function restoreEnv(string $extractDir, string $appDir)
+    protected function restoreEnv(string $extractDir, string $appDir, OutputInterface $output, InteractiveConsole $interactions): array
     {
         $oldEnv = EnvironmentLoader::load($extractDir);
         $currentEnv = EnvironmentLoader::load($appDir);
@@ -149,7 +153,23 @@ class RestoreCommand extends Command
             copy($appEnvPath, $appEnvPath . '.backup');
         }
 
+        $oldUrl = $oldEnv['APP_URL'] ?? '';
+        $newUrl = $currentEnv['APP_URL'] ?? '';
+        $returnData = [
+            'old_url' => $oldUrl,
+            'new_url' => $oldUrl,
+        ];
+
+        if ($oldUrl !== $newUrl) {
+            $output->writeln("Found different APP_URL values:");
+            $changedUrl = $interactions->choice('Which would you like to use?', array_filter([$oldUrl, $newUrl]));
+            $envContents = preg_replace('/^APP_URL=.*?$/', 'APP_URL="' . $changedUrl . '"', $envContents);
+            $returnData['new_url'] = $changedUrl;
+        }
+
         file_put_contents($appDir . DIRECTORY_SEPARATOR . '.env', $envContents);
+
+        return $returnData;
     }
 
     protected function restoreFolder(string $folderSubPath, string $appDir, string $extractDir): void
diff --git a/scripts/Services/BackupZip.php b/scripts/Services/BackupZip.php
index 798e24477..cb3ca280a 100644
--- a/scripts/Services/BackupZip.php
+++ b/scripts/Services/BackupZip.php
@@ -30,15 +30,15 @@ class BackupZip
             ],
             'themes' => [
                 'desc' => 'Themes Folder',
-                'exists' => $this->zip->locateName('/themes/') !== false,
+                'exists' => $this->hasFolder('themes/'),
             ],
             'public/uploads' => [
                 'desc' => 'Public File Uploads',
-                'exists' => $this->zip->locateName('/public/uploads/') !== false,
+                'exists' => $this->hasFolder('public/uploads/'),
             ],
             'storage/uploads' => [
                 'desc' => 'Private File Uploads',
-                'exists' => $this->zip->locateName('/storage/uploads/') !== false,
+                'exists' => $this->hasFolder('storage/uploads/'),
             ],
             'db' => [
                 'desc' => 'Database Dump',
@@ -54,4 +54,15 @@ class BackupZip
             throw new \Exception("Failed extraction of ZIP into [{$directoryPath}].");
         }
     }
+
+    protected function hasFolder($folderPath): bool
+    {
+        for ($i = 0; $i < $this->zip->numFiles; $i++) {
+            $filePath = $this->zip->getNameIndex($i);
+            if (str_starts_with($filePath, $folderPath)) {
+                return true;
+            }
+        }
+        return false;
+    }
 }
diff --git a/scripts/Services/InteractiveConsole.php b/scripts/Services/InteractiveConsole.php
index 8d8f92626..0fdabe052 100644
--- a/scripts/Services/InteractiveConsole.php
+++ b/scripts/Services/InteractiveConsole.php
@@ -2,6 +2,7 @@
 
 namespace Cli\Services;
 
+use Symfony\Component\Console\Question\ChoiceQuestion;
 use Symfony\Component\Console\Helper\QuestionHelper;
 use Symfony\Component\Console\Input\InputInterface;
 use Symfony\Component\Console\Output\OutputInterface;
@@ -9,14 +10,11 @@ use Symfony\Component\Console\Question\ConfirmationQuestion;
 
 class InteractiveConsole
 {
-
-
     public function __construct(
         protected QuestionHelper $helper,
         protected InputInterface $input,
         protected OutputInterface $output,
-    )
-    {
+    ) {
     }
 
     public function confirm(string $text): bool
@@ -24,4 +22,10 @@ class InteractiveConsole
         $question = new ConfirmationQuestion($text . " (y/n)\n", false);
         return $this->helper->ask($this->input, $this->output, $question);
     }
-}
\ No newline at end of file
+
+    public function choice(string $question, array $answers)
+    {
+        $question = new ChoiceQuestion($question, $answers, $answers[0]);
+        return $this->helper->ask($this->input, $this->output, $question);
+    }
+}
diff --git a/scripts/Services/MySqlRunner.php b/scripts/Services/MySqlRunner.php
index 1b1091c77..40b1d98fd 100644
--- a/scripts/Services/MySqlRunner.php
+++ b/scripts/Services/MySqlRunner.php
@@ -39,7 +39,7 @@ class MySqlRunner
                 '-u', $this->user,
                 '-p' . $this->password,
                 $this->database,
-                '-e' . "'show tables;'"
+                '-e', "show tables;"
             ]);
 
         return !$output;