diff --git a/CHANGELOG.md b/CHANGELOG.md
index d6070f53..db8318f7 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -91,6 +91,24 @@ final release.
----
+## Version 1.5.5 [2022-01-05]
+
+### Release Notes
+
+This is a bugfix release that fixes an issues with the backup tool crashing the app if the project
+path and backup path are on different drive locations. This issue only affects Windows.
+
+### Detailed Changelog
+
+**Bugfixes**
+
+* Fixed a bug with using the commonpath command in Python which will raise an error if the two
+ paths don't have a common root. This is particularly an issue on Windows where the paths can be
+ on different drives. The command was used in the project backup function, and has now been
+ replaced by a safer check. Issue #954.
+
+----
+
## Version 1.5.4 [2022-01-04]
### Release Notes
diff --git a/novelwriter/core/project.py b/novelwriter/core/project.py
index bc39bfc2..b286ab32 100644
--- a/novelwriter/core/project.py
+++ b/novelwriter/core/project.py
@@ -827,7 +827,7 @@ class NWProject():
), nwAlert.ERROR, exception=exc)
return False
- if os.path.commonpath([self.projPath, baseDir]) == self.projPath:
+ if baseDir and baseDir.startswith(self.projPath):
self.theParent.makeAlert(self.tr(
"Cannot backup project because the backup path is within the "
"project folder to be backed up. Please choose a different "
diff --git a/sample/nwProject.nwx b/sample/nwProject.nwx
index c8245673..8f3d1f66 100644
--- a/sample/nwProject.nwx
+++ b/sample/nwProject.nwx
@@ -7,7 +7,7 @@
Jay Doh
1267
198
- 62103
+ 62543
False
diff --git a/tests/test_core/test_core_project.py b/tests/test_core/test_core_project.py
index 931359c7..7ae461eb 100644
--- a/tests/test_core/test_core_project.py
+++ b/tests/test_core/test_core_project.py
@@ -1223,26 +1223,30 @@ def testCoreProject_Backup(monkeypatch, mockGUI, nwMinimal, tmpDir):
# No project
mockGUI.hasProject = False
- assert not theProject.zipIt(doNotify=False)
+ assert theProject.zipIt(doNotify=False) is False
mockGUI.hasProject = True
# Invalid path
theProject.mainConf.backupPath = None
- assert not theProject.zipIt(doNotify=False)
+ assert theProject.zipIt(doNotify=False) is False
# Missing project name
theProject.mainConf.backupPath = tmpDir
theProject.projName = ""
- assert not theProject.zipIt(doNotify=False)
+ assert theProject.zipIt(doNotify=False) is False
# Non-existent folder
theProject.mainConf.backupPath = os.path.join(tmpDir, "nonexistent")
theProject.projName = "Test Minimal"
- assert not theProject.zipIt(doNotify=False)
+ assert theProject.zipIt(doNotify=False) is False
# Same folder as project (causes infinite loop in zipping)
theProject.mainConf.backupPath = nwMinimal
- assert not theProject.zipIt(doNotify=False)
+ assert theProject.zipIt(doNotify=False) is False
+
+ # Subfolder of project (causes infinite loop in zipping)
+ theProject.mainConf.backupPath = os.path.join(nwMinimal, "subdir")
+ assert theProject.zipIt(doNotify=False) is False
# Set a valid folder
theProject.mainConf.backupPath = tmpDir
@@ -1250,15 +1254,15 @@ def testCoreProject_Backup(monkeypatch, mockGUI, nwMinimal, tmpDir):
# Can't make folder
with monkeypatch.context() as mp:
mp.setattr("os.mkdir", causeOSError)
- assert not theProject.zipIt(doNotify=False)
+ assert theProject.zipIt(doNotify=False) is False
# Can't write archive
with monkeypatch.context() as mp:
mp.setattr("shutil.make_archive", causeOSError)
- assert not theProject.zipIt(doNotify=False)
+ assert theProject.zipIt(doNotify=False) is False
# Test correct settings
- assert theProject.zipIt(doNotify=True)
+ assert theProject.zipIt(doNotify=True) is True
theFiles = os.listdir(os.path.join(tmpDir, "Test Minimal"))
assert len(theFiles) == 1