PyHardLinkBackup 1.4.0__tar.gz → 1.4.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. {pyhardlinkbackup-1.4.0 → pyhardlinkbackup-1.4.1}/PKG-INFO +6 -4
  2. {pyhardlinkbackup-1.4.0 → pyhardlinkbackup-1.4.1}/PyHardLinkBackup/__init__.py +1 -1
  3. {pyhardlinkbackup-1.4.0 → pyhardlinkbackup-1.4.1}/PyHardLinkBackup/backup.py +2 -1
  4. {pyhardlinkbackup-1.4.0 → pyhardlinkbackup-1.4.1}/PyHardLinkBackup/tests/test_backup.py +93 -61
  5. {pyhardlinkbackup-1.4.0 → pyhardlinkbackup-1.4.1}/README.md +5 -3
  6. {pyhardlinkbackup-1.4.0 → pyhardlinkbackup-1.4.1}/.editorconfig +0 -0
  7. {pyhardlinkbackup-1.4.0 → pyhardlinkbackup-1.4.1}/.github/workflows/tests.yml +0 -0
  8. {pyhardlinkbackup-1.4.0 → pyhardlinkbackup-1.4.1}/.gitignore +0 -0
  9. {pyhardlinkbackup-1.4.0 → pyhardlinkbackup-1.4.1}/.idea/.gitignore +0 -0
  10. {pyhardlinkbackup-1.4.0 → pyhardlinkbackup-1.4.1}/.pre-commit-config.yaml +0 -0
  11. {pyhardlinkbackup-1.4.0 → pyhardlinkbackup-1.4.1}/.pre-commit-hooks.yaml +0 -0
  12. {pyhardlinkbackup-1.4.0 → pyhardlinkbackup-1.4.1}/.run/Template Python tests.run.xml +0 -0
  13. {pyhardlinkbackup-1.4.0 → pyhardlinkbackup-1.4.1}/.run/Unittests - __all__.run.xml +0 -0
  14. {pyhardlinkbackup-1.4.0 → pyhardlinkbackup-1.4.1}/.run/cli.py --help.run.xml +0 -0
  15. {pyhardlinkbackup-1.4.0 → pyhardlinkbackup-1.4.1}/.run/dev-cli update.run.xml +0 -0
  16. {pyhardlinkbackup-1.4.0 → pyhardlinkbackup-1.4.1}/.run/only DocTests.run.xml +0 -0
  17. {pyhardlinkbackup-1.4.0 → pyhardlinkbackup-1.4.1}/.run/only DocWrite.run.xml +0 -0
  18. {pyhardlinkbackup-1.4.0 → pyhardlinkbackup-1.4.1}/.venv-app/lib/python3.12/site-packages/cli_base/tests/shell_complete_snapshots/.gitignore +0 -0
  19. {pyhardlinkbackup-1.4.0 → pyhardlinkbackup-1.4.1}/PyHardLinkBackup/__main__.py +0 -0
  20. {pyhardlinkbackup-1.4.0 → pyhardlinkbackup-1.4.1}/PyHardLinkBackup/cli_app/__init__.py +0 -0
  21. {pyhardlinkbackup-1.4.0 → pyhardlinkbackup-1.4.1}/PyHardLinkBackup/cli_app/phlb.py +0 -0
  22. {pyhardlinkbackup-1.4.0 → pyhardlinkbackup-1.4.1}/PyHardLinkBackup/cli_dev/__init__.py +0 -0
  23. {pyhardlinkbackup-1.4.0 → pyhardlinkbackup-1.4.1}/PyHardLinkBackup/cli_dev/benchmark.py +0 -0
  24. {pyhardlinkbackup-1.4.0 → pyhardlinkbackup-1.4.1}/PyHardLinkBackup/cli_dev/code_style.py +0 -0
  25. {pyhardlinkbackup-1.4.0 → pyhardlinkbackup-1.4.1}/PyHardLinkBackup/cli_dev/packaging.py +0 -0
  26. {pyhardlinkbackup-1.4.0 → pyhardlinkbackup-1.4.1}/PyHardLinkBackup/cli_dev/shell_completion.py +0 -0
  27. {pyhardlinkbackup-1.4.0 → pyhardlinkbackup-1.4.1}/PyHardLinkBackup/cli_dev/testing.py +0 -0
  28. {pyhardlinkbackup-1.4.0 → pyhardlinkbackup-1.4.1}/PyHardLinkBackup/cli_dev/update_readme_history.py +0 -0
  29. {pyhardlinkbackup-1.4.0 → pyhardlinkbackup-1.4.1}/PyHardLinkBackup/constants.py +0 -0
  30. {pyhardlinkbackup-1.4.0 → pyhardlinkbackup-1.4.1}/PyHardLinkBackup/logging_setup.py +0 -0
  31. {pyhardlinkbackup-1.4.0 → pyhardlinkbackup-1.4.1}/PyHardLinkBackup/rebuild_databases.py +0 -0
  32. {pyhardlinkbackup-1.4.0 → pyhardlinkbackup-1.4.1}/PyHardLinkBackup/tests/__init__.py +0 -0
  33. {pyhardlinkbackup-1.4.0 → pyhardlinkbackup-1.4.1}/PyHardLinkBackup/tests/test_doc_write.py +0 -0
  34. {pyhardlinkbackup-1.4.0 → pyhardlinkbackup-1.4.1}/PyHardLinkBackup/tests/test_doctests.py +0 -0
  35. {pyhardlinkbackup-1.4.0 → pyhardlinkbackup-1.4.1}/PyHardLinkBackup/tests/test_project_setup.py +0 -0
  36. {pyhardlinkbackup-1.4.0 → pyhardlinkbackup-1.4.1}/PyHardLinkBackup/tests/test_readme.py +0 -0
  37. {pyhardlinkbackup-1.4.0 → pyhardlinkbackup-1.4.1}/PyHardLinkBackup/tests/test_readme_history.py +0 -0
  38. {pyhardlinkbackup-1.4.0 → pyhardlinkbackup-1.4.1}/PyHardLinkBackup/tests/test_rebuild_database.py +0 -0
  39. {pyhardlinkbackup-1.4.0 → pyhardlinkbackup-1.4.1}/PyHardLinkBackup/utilities/__init__.py +0 -0
  40. {pyhardlinkbackup-1.4.0 → pyhardlinkbackup-1.4.1}/PyHardLinkBackup/utilities/file_hash_database.py +0 -0
  41. {pyhardlinkbackup-1.4.0 → pyhardlinkbackup-1.4.1}/PyHardLinkBackup/utilities/file_size_database.py +0 -0
  42. {pyhardlinkbackup-1.4.0 → pyhardlinkbackup-1.4.1}/PyHardLinkBackup/utilities/filesystem.py +0 -0
  43. {pyhardlinkbackup-1.4.0 → pyhardlinkbackup-1.4.1}/PyHardLinkBackup/utilities/humanize.py +0 -0
  44. {pyhardlinkbackup-1.4.0 → pyhardlinkbackup-1.4.1}/PyHardLinkBackup/utilities/rich_utils.py +0 -0
  45. {pyhardlinkbackup-1.4.0 → pyhardlinkbackup-1.4.1}/PyHardLinkBackup/utilities/sha256sums.py +0 -0
  46. {pyhardlinkbackup-1.4.0 → pyhardlinkbackup-1.4.1}/PyHardLinkBackup/utilities/tee.py +0 -0
  47. {pyhardlinkbackup-1.4.0 → pyhardlinkbackup-1.4.1}/PyHardLinkBackup/utilities/tests/__init__.py +0 -0
  48. {pyhardlinkbackup-1.4.0 → pyhardlinkbackup-1.4.1}/PyHardLinkBackup/utilities/tests/test_file_hash_database.py +0 -0
  49. {pyhardlinkbackup-1.4.0 → pyhardlinkbackup-1.4.1}/PyHardLinkBackup/utilities/tests/test_file_size_database.py +0 -0
  50. {pyhardlinkbackup-1.4.0 → pyhardlinkbackup-1.4.1}/PyHardLinkBackup/utilities/tests/test_filesystem.py +0 -0
  51. {pyhardlinkbackup-1.4.0 → pyhardlinkbackup-1.4.1}/PyHardLinkBackup/utilities/tyro_cli_shared_args.py +0 -0
  52. {pyhardlinkbackup-1.4.0 → pyhardlinkbackup-1.4.1}/cli.py +0 -0
  53. {pyhardlinkbackup-1.4.0 → pyhardlinkbackup-1.4.1}/dev-cli.py +0 -0
  54. {pyhardlinkbackup-1.4.0 → pyhardlinkbackup-1.4.1}/dist/.gitignore +0 -0
  55. {pyhardlinkbackup-1.4.0 → pyhardlinkbackup-1.4.1}/docs/README.md +0 -0
  56. {pyhardlinkbackup-1.4.0 → pyhardlinkbackup-1.4.1}/docs/about-docs.md +0 -0
  57. {pyhardlinkbackup-1.4.0 → pyhardlinkbackup-1.4.1}/noxfile.py +0 -0
  58. {pyhardlinkbackup-1.4.0 → pyhardlinkbackup-1.4.1}/pyproject.toml +0 -0
  59. {pyhardlinkbackup-1.4.0 → pyhardlinkbackup-1.4.1}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: PyHardLinkBackup
3
- Version: 1.4.0
3
+ Version: 1.4.1
4
4
  Summary: HardLink/Deduplication Backups with Python
5
5
  Project-URL: Documentation, https://github.com/jedie/PyHardLinkBackup
6
6
  Project-URL: Source, https://github.com/jedie/PyHardLinkBackup
@@ -231,6 +231,8 @@ Overview of main changes:
231
231
 
232
232
  [comment]: <> (✂✂✂ auto generated history start ✂✂✂)
233
233
 
234
+ * [v1.4.1](https://github.com/jedie/PyHardLinkBackup/compare/v1.4.0...v1.4.1)
235
+ * 2026-01-16 - Bugfix large file handling
234
236
  * [v1.4.0](https://github.com/jedie/PyHardLinkBackup/compare/v1.3.0...v1.4.0)
235
237
  * 2026-01-16 - Create log file in backup and a summary.txt
236
238
  * 2026-01-16 - Run CI tests on macos, too.
@@ -247,12 +249,12 @@ Overview of main changes:
247
249
  * 2026-01-14 - Enhance progress bars
248
250
  * 2026-01-14 - A a note to rsync --link-dest
249
251
  * 2026-01-14 - Use cli_base.cli_tools.test_utils.base_testcases
250
- * [v1.1.0](https://github.com/jedie/PyHardLinkBackup/compare/v1.0.1...v1.1.0)
251
- * 2026-01-14 - Change backup timestamp directory to old schema: '%Y-%m-%d-%H%M%S'
252
- * 2026-01-14 - Add "Overview of main changes" to README
253
252
 
254
253
  <details><summary>Expand older history entries ...</summary>
255
254
 
255
+ * [v1.1.0](https://github.com/jedie/PyHardLinkBackup/compare/v1.0.1...v1.1.0)
256
+ * 2026-01-14 - Change backup timestamp directory to old schema: '%Y-%m-%d-%H%M%S'
257
+ * 2026-01-14 - Add "Overview of main changes" to README
256
258
  * [v1.0.1](https://github.com/jedie/PyHardLinkBackup/compare/v1.0.0...v1.0.1)
257
259
  * 2026-01-13 - Store SHA256SUMS files in backup directories
258
260
  * [v1.0.0](https://github.com/jedie/PyHardLinkBackup/compare/v0.13.0...v1.0.0)
@@ -3,5 +3,5 @@
3
3
  """
4
4
 
5
5
  # See https://packaging.python.org/en/latest/specifications/version-specifiers/
6
- __version__ = '1.4.0'
6
+ __version__ = '1.4.1'
7
7
  __author__ = 'Jens Diemer <PyHardLinkBackup@jensdiemer.de>'
@@ -138,12 +138,13 @@ def backup_one_file(
138
138
  backup_result.hardlinked_size += size
139
139
  else:
140
140
  logger.info('Copy unique file: %s to %s', src_path, dst_path)
141
+ file_hash = copy_and_hash(src_path, dst_path)
141
142
  hash_db[file_hash] = dst_path
142
143
  backup_result.copied_files += 1
143
144
  backup_result.copied_size += size
144
145
 
145
146
  # Keep original file metadata (permission bits, time stamps, and flags)
146
- shutil.copy2(src_path, dst_path)
147
+ shutil.copystat(src_path, dst_path)
147
148
  else:
148
149
  # A file with this size not backuped before -> Can't be duplicate -> copy and hash
149
150
  file_hash = copy_and_hash(src_path, dst_path)
@@ -144,16 +144,13 @@ class BackupTreeTestCase(
144
144
  (sub_dir / 'file.txt').write_text('This is file in subdir')
145
145
 
146
146
  # Only files bigger than MIN_SIZE will be considered for hardlinking:
147
- size_db_min_file = src_root / 'min_sized_file1.bin'
148
- size_db_min_file.write_bytes(b'X' * FileSizeDatabase.MIN_SIZE)
147
+ (src_root / 'min_sized_file1.bin').write_bytes(b'X' * FileSizeDatabase.MIN_SIZE)
149
148
 
150
149
  # Same content and big enough to be considered for hardlinking:
151
- size_db_min_file = src_root / 'min_sized_file2.bin'
152
- size_db_min_file.write_bytes(b'X' * FileSizeDatabase.MIN_SIZE)
150
+ (src_root / 'min_sized_file2.bin').write_bytes(b'X' * FileSizeDatabase.MIN_SIZE)
153
151
 
154
152
  # Larger then CHUNK_SIZE file will be handled differently:
155
- large_file = src_root / 'large_file.bin'
156
- large_file.write_bytes(b'Y' * (CHUNK_SIZE + 1))
153
+ (src_root / 'large_file1.bin').write_bytes(b'Y' * (CHUNK_SIZE + 1))
157
154
 
158
155
  excluded_dir = src_root / '.cache'
159
156
  excluded_dir.mkdir()
@@ -176,7 +173,7 @@ class BackupTreeTestCase(
176
173
  backup_root=backup_root,
177
174
  excludes=('.cache',),
178
175
  log_manager=LoggingManager(
179
- console_level=DEFAULT_CONSOLE_LOG_LEVEL,
176
+ console_level='info',
180
177
  file_level=DEFAULT_LOG_FILE_LEVEL,
181
178
  ),
182
179
  )
@@ -208,6 +205,7 @@ class BackupTreeTestCase(
208
205
  copied_small_size=50,
209
206
  error_count=0,
210
207
  ),
208
+ redirected_out.stdout,
211
209
  )
212
210
 
213
211
  # The sources:
@@ -219,7 +217,7 @@ class BackupTreeTestCase(
219
217
  .cache/tempfile.tmp 12:00:00 file 1 38 41d7a2c9
220
218
  file2.txt 12:00:00 hardlink 2 14 8a11514a
221
219
  hardlink2file1 12:00:00 hardlink 2 14 8a11514a
222
- large_file.bin 12:00:00 file 1 67108865 9671eaac
220
+ large_file1.bin 12:00:00 file 1 67108865 9671eaac
223
221
  min_sized_file1.bin 12:00:00 file 1 1000 f0d93de4
224
222
  min_sized_file2.bin 12:00:00 file 1 1000 f0d93de4
225
223
  subdir/file.txt 12:00:00 file 1 22 c0167e63
@@ -234,10 +232,10 @@ class BackupTreeTestCase(
234
232
  root=backup_dir,
235
233
  expected_overview="""
236
234
  path birthtime type nlink size CRC32
237
- SHA256SUMS <mock> file 1 410 45c07cf7
235
+ SHA256SUMS <mock> file 1 411 b02da51e
238
236
  file2.txt 12:00:00 file 1 14 8a11514a
239
237
  hardlink2file1 12:00:00 file 1 14 8a11514a
240
- large_file.bin 12:00:00 file 1 67108865 9671eaac
238
+ large_file1.bin 12:00:00 file 1 67108865 9671eaac
241
239
  min_sized_file1.bin 12:00:00 hardlink 2 1000 f0d93de4
242
240
  min_sized_file2.bin 12:00:00 hardlink 2 1000 f0d93de4
243
241
  subdir/SHA256SUMS <mock> file 1 75 1af5ecc7
@@ -252,12 +250,31 @@ class BackupTreeTestCase(
252
250
  backup_root=backup_root,
253
251
  expected="""
254
252
  bb/c4/bbc4de2ca238d1… -> source/2026-01-01-123456/min_sized_file1.bin
255
- e6/37/e6374ac11d9049… -> source/2026-01-01-123456/large_file.bin
253
+ e6/37/e6374ac11d9049… -> source/2026-01-01-123456/large_file1.bin
256
254
  """,
257
255
  )
258
256
 
259
257
  #######################################################################################
260
- # Just backup again:
258
+ # Backup again with new added files:
259
+
260
+ # New small file with different size and different content:
261
+ (src_root / 'small_file_newA.txt').write_text('A new file')
262
+
263
+ # Add small file that size exists, but has different content:
264
+ (src_root / 'small_file_newB.txt').write_text('This is file 2')
265
+
266
+ # Bigger file with new size and new content:
267
+ (src_root / 'min_sized_file_newA.bin').write_bytes(b'A' * (FileSizeDatabase.MIN_SIZE + 1))
268
+
269
+ # Bigger file with existing size, but different content:
270
+ (src_root / 'min_sized_file_newB.bin').write_bytes(b'B' * FileSizeDatabase.MIN_SIZE)
271
+
272
+ # Add a larger then CHUNK_SIZE file with same existing size, but different content:
273
+ (src_root / 'large_file2.bin').write_bytes(b'Y' * (CHUNK_SIZE + 1))
274
+
275
+ # FIXME: freezegun doesn't handle this, see: https://github.com/spulec/freezegun/issues/392
276
+ # Set modification times to a fixed time for easier testing:
277
+ set_file_times(src_root, dt=parse_dt('2026-01-01T12:00:00+0000'))
261
278
 
262
279
  with (
263
280
  patch('PyHardLinkBackup.backup.iter_scandir_files', SortedIterScandirFiles),
@@ -269,7 +286,7 @@ class BackupTreeTestCase(
269
286
  backup_root=backup_root,
270
287
  excludes=('.cache',),
271
288
  log_manager=LoggingManager(
272
- console_level=DEFAULT_CONSOLE_LOG_LEVEL,
289
+ console_level='info',
273
290
  file_level=DEFAULT_LOG_FILE_LEVEL,
274
291
  ),
275
292
  )
@@ -280,23 +297,6 @@ class BackupTreeTestCase(
280
297
  str(Path(backup_dir).relative_to(temp_path)),
281
298
  'backup/source/2026-01-02-123456',
282
299
  )
283
- self.assertEqual(
284
- result,
285
- BackupResult(
286
- backup_dir=backup_dir,
287
- log_file=result.log_file,
288
- backup_count=7,
289
- backup_size=67110929,
290
- symlink_files=1,
291
- hardlinked_files=3, # <<< More hardlinks this time!
292
- hardlinked_size=67110865,
293
- copied_files=3,
294
- copied_size=50,
295
- copied_small_files=3,
296
- copied_small_size=50,
297
- error_count=0,
298
- ),
299
- )
300
300
  # The second backup:
301
301
  # * /.cache/ -> excluded
302
302
  # * min_sized_file1.bin and min_sized_file2.bin -> hardlinked
@@ -304,26 +304,51 @@ class BackupTreeTestCase(
304
304
  assert_fs_tree_overview(
305
305
  root=backup_dir,
306
306
  expected_overview="""
307
- path birthtime type nlink size CRC32
308
- SHA256SUMS <mock> file 1 410 45c07cf7
309
- file2.txt 12:00:00 file 1 14 8a11514a
310
- hardlink2file1 12:00:00 file 1 14 8a11514a
311
- large_file.bin 12:00:00 hardlink 2 67108865 9671eaac
312
- min_sized_file1.bin 12:00:00 hardlink 4 1000 f0d93de4
313
- min_sized_file2.bin 12:00:00 hardlink 4 1000 f0d93de4
314
- subdir/SHA256SUMS <mock> file 1 75 1af5ecc7
315
- subdir/file.txt 12:00:00 file 1 22 c0167e63
316
- symlink2file1 12:00:00 symlink 2 14 8a11514a
307
+ path birthtime type nlink size CRC32
308
+ SHA256SUMS <mock> file 1 845 6596856a
309
+ file2.txt 12:00:00 file 1 14 8a11514a
310
+ hardlink2file1 12:00:00 file 1 14 8a11514a
311
+ large_file1.bin 12:00:00 hardlink 3 67108865 9671eaac
312
+ large_file2.bin 12:00:00 hardlink 3 67108865 9671eaac
313
+ min_sized_file1.bin 12:00:00 hardlink 4 1000 f0d93de4
314
+ min_sized_file2.bin 12:00:00 hardlink 4 1000 f0d93de4
315
+ min_sized_file_newA.bin 12:00:00 file 1 1001 a48f0e33
316
+ min_sized_file_newB.bin 12:00:00 file 1 1000 7d9c564d
317
+ small_file_newA.txt 12:00:00 file 1 10 76d1acf1
318
+ small_file_newB.txt 12:00:00 file 1 14 131800f0
319
+ subdir/SHA256SUMS <mock> file 1 75 1af5ecc7
320
+ subdir/file.txt 12:00:00 file 1 22 c0167e63
321
+ symlink2file1 12:00:00 symlink 2 14 8a11514a
317
322
  """,
318
323
  )
324
+ self.assertEqual(
325
+ result,
326
+ BackupResult(
327
+ backup_dir=backup_dir,
328
+ log_file=result.log_file,
329
+ backup_count=12,
330
+ backup_size=134221819,
331
+ symlink_files=1,
332
+ hardlinked_files=4,
333
+ hardlinked_size=134219730,
334
+ copied_files=7,
335
+ copied_size=2075,
336
+ copied_small_files=5,
337
+ copied_small_size=74,
338
+ error_count=0,
339
+ ),
340
+ redirected_out.stdout,
341
+ )
319
342
 
320
343
  # The FileHashDatabase remains the same:
321
344
  with self.assertLogs('PyHardLinkBackup', level=logging.DEBUG):
322
345
  assert_hash_db_info(
323
346
  backup_root=backup_root,
324
347
  expected="""
348
+ 23/d2/23d2ce40d26211… -> source/2026-01-02-123456/min_sized_file_newA.bin
349
+ 9a/56/9a567077114134… -> source/2026-01-02-123456/min_sized_file_newB.bin
325
350
  bb/c4/bbc4de2ca238d1… -> source/2026-01-01-123456/min_sized_file1.bin
326
- e6/37/e6374ac11d9049… -> source/2026-01-01-123456/large_file.bin
351
+ e6/37/e6374ac11d9049… -> source/2026-01-01-123456/large_file1.bin
327
352
  """,
328
353
  )
329
354
 
@@ -365,16 +390,21 @@ class BackupTreeTestCase(
365
390
  assert_fs_tree_overview(
366
391
  root=backup_dir,
367
392
  expected_overview="""
368
- path birthtime type nlink size CRC32
369
- SHA256SUMS <mock> file 1 410 45c07cf7
370
- file2.txt 12:00:00 file 1 14 8a11514a
371
- hardlink2file1 12:00:00 file 1 14 8a11514a
372
- large_file.bin 12:00:00 hardlink 3 67108865 9671eaac
373
- min_sized_file1.bin 12:00:00 hardlink 2 1000 f0d93de4
374
- min_sized_file2.bin 12:00:00 hardlink 2 1000 f0d93de4
375
- subdir/SHA256SUMS <mock> file 1 75 1af5ecc7
376
- subdir/file.txt 12:00:00 file 1 22 c0167e63
377
- symlink2file1 12:00:00 symlink 2 14 8a11514a
393
+ path birthtime type nlink size CRC32
394
+ SHA256SUMS <mock> file 1 845 6596856a
395
+ file2.txt 12:00:00 file 1 14 8a11514a
396
+ hardlink2file1 12:00:00 file 1 14 8a11514a
397
+ large_file1.bin 12:00:00 hardlink 5 67108865 9671eaac
398
+ large_file2.bin 12:00:00 hardlink 5 67108865 9671eaac
399
+ min_sized_file1.bin 12:00:00 hardlink 2 1000 f0d93de4
400
+ min_sized_file2.bin 12:00:00 hardlink 2 1000 f0d93de4
401
+ min_sized_file_newA.bin 12:00:00 hardlink 2 1001 a48f0e33
402
+ min_sized_file_newB.bin 12:00:00 hardlink 2 1000 7d9c564d
403
+ small_file_newA.txt 12:00:00 file 1 10 76d1acf1
404
+ small_file_newB.txt 12:00:00 file 1 14 131800f0
405
+ subdir/SHA256SUMS <mock> file 1 75 1af5ecc7
406
+ subdir/file.txt 12:00:00 file 1 22 c0167e63
407
+ symlink2file1 12:00:00 symlink 2 14 8a11514a
378
408
  """,
379
409
  )
380
410
 
@@ -383,16 +413,16 @@ class BackupTreeTestCase(
383
413
  BackupResult(
384
414
  backup_dir=backup_dir,
385
415
  log_file=result.log_file,
386
- backup_count=7,
387
- backup_size=67110929,
416
+ backup_count=12,
417
+ backup_size=134221819,
388
418
  symlink_files=1,
389
- hardlinked_files=2, # <<< Less hardlinks this time, because of missing link source!
390
- hardlinked_size=67109865,
391
- copied_files=4,
392
- copied_size=1050,
393
- copied_small_files=3,
394
- copied_small_size=50,
395
- error_count=0,
419
+ hardlinked_files=5,
420
+ hardlinked_size=134220731,
421
+ copied_files=6,
422
+ copied_size=1074,
423
+ copied_small_files=5,
424
+ copied_small_size=74,
425
+ error_count=0
396
426
  ),
397
427
  )
398
428
 
@@ -402,8 +432,10 @@ class BackupTreeTestCase(
402
432
  assert_hash_db_info(
403
433
  backup_root=backup_root,
404
434
  expected="""
435
+ 23/d2/23d2ce40d26211… -> source/2026-01-02-123456/min_sized_file_newA.bin
436
+ 9a/56/9a567077114134… -> source/2026-01-02-123456/min_sized_file_newB.bin
405
437
  bb/c4/bbc4de2ca238d1… -> source/2026-01-03-123456/min_sized_file1.bin
406
- e6/37/e6374ac11d9049… -> source/2026-01-01-123456/large_file.bin
438
+ e6/37/e6374ac11d9049… -> source/2026-01-01-123456/large_file1.bin
407
439
  """,
408
440
  )
409
441
 
@@ -216,6 +216,8 @@ Overview of main changes:
216
216
 
217
217
  [comment]: <> (✂✂✂ auto generated history start ✂✂✂)
218
218
 
219
+ * [v1.4.1](https://github.com/jedie/PyHardLinkBackup/compare/v1.4.0...v1.4.1)
220
+ * 2026-01-16 - Bugfix large file handling
219
221
  * [v1.4.0](https://github.com/jedie/PyHardLinkBackup/compare/v1.3.0...v1.4.0)
220
222
  * 2026-01-16 - Create log file in backup and a summary.txt
221
223
  * 2026-01-16 - Run CI tests on macos, too.
@@ -232,12 +234,12 @@ Overview of main changes:
232
234
  * 2026-01-14 - Enhance progress bars
233
235
  * 2026-01-14 - A a note to rsync --link-dest
234
236
  * 2026-01-14 - Use cli_base.cli_tools.test_utils.base_testcases
235
- * [v1.1.0](https://github.com/jedie/PyHardLinkBackup/compare/v1.0.1...v1.1.0)
236
- * 2026-01-14 - Change backup timestamp directory to old schema: '%Y-%m-%d-%H%M%S'
237
- * 2026-01-14 - Add "Overview of main changes" to README
238
237
 
239
238
  <details><summary>Expand older history entries ...</summary>
240
239
 
240
+ * [v1.1.0](https://github.com/jedie/PyHardLinkBackup/compare/v1.0.1...v1.1.0)
241
+ * 2026-01-14 - Change backup timestamp directory to old schema: '%Y-%m-%d-%H%M%S'
242
+ * 2026-01-14 - Add "Overview of main changes" to README
241
243
  * [v1.0.1](https://github.com/jedie/PyHardLinkBackup/compare/v1.0.0...v1.0.1)
242
244
  * 2026-01-13 - Store SHA256SUMS files in backup directories
243
245
  * [v1.0.0](https://github.com/jedie/PyHardLinkBackup/compare/v0.13.0...v1.0.0)