PyHardLinkBackup 1.8.1__py3-none-any.whl

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 (45) hide show
  1. PyHardLinkBackup/__init__.py +7 -0
  2. PyHardLinkBackup/__main__.py +10 -0
  3. PyHardLinkBackup/backup.py +297 -0
  4. PyHardLinkBackup/cli_app/__init__.py +41 -0
  5. PyHardLinkBackup/cli_app/phlb.py +136 -0
  6. PyHardLinkBackup/cli_dev/__init__.py +70 -0
  7. PyHardLinkBackup/cli_dev/__main__.py +10 -0
  8. PyHardLinkBackup/cli_dev/benchmark.py +138 -0
  9. PyHardLinkBackup/cli_dev/code_style.py +12 -0
  10. PyHardLinkBackup/cli_dev/debugging.py +47 -0
  11. PyHardLinkBackup/cli_dev/packaging.py +62 -0
  12. PyHardLinkBackup/cli_dev/shell_completion.py +23 -0
  13. PyHardLinkBackup/cli_dev/testing.py +52 -0
  14. PyHardLinkBackup/cli_dev/update_readme_history.py +33 -0
  15. PyHardLinkBackup/compare_backup.py +259 -0
  16. PyHardLinkBackup/constants.py +18 -0
  17. PyHardLinkBackup/logging_setup.py +124 -0
  18. PyHardLinkBackup/rebuild_databases.py +217 -0
  19. PyHardLinkBackup/tests/__init__.py +36 -0
  20. PyHardLinkBackup/tests/test_backup.py +1167 -0
  21. PyHardLinkBackup/tests/test_compare_backup.py +167 -0
  22. PyHardLinkBackup/tests/test_doc_write.py +26 -0
  23. PyHardLinkBackup/tests/test_doctests.py +10 -0
  24. PyHardLinkBackup/tests/test_project_setup.py +46 -0
  25. PyHardLinkBackup/tests/test_readme.py +75 -0
  26. PyHardLinkBackup/tests/test_readme_history.py +9 -0
  27. PyHardLinkBackup/tests/test_rebuild_database.py +266 -0
  28. PyHardLinkBackup/utilities/__init__.py +0 -0
  29. PyHardLinkBackup/utilities/file_hash_database.py +62 -0
  30. PyHardLinkBackup/utilities/file_size_database.py +46 -0
  31. PyHardLinkBackup/utilities/filesystem.py +257 -0
  32. PyHardLinkBackup/utilities/humanize.py +39 -0
  33. PyHardLinkBackup/utilities/rich_utils.py +237 -0
  34. PyHardLinkBackup/utilities/sha256sums.py +61 -0
  35. PyHardLinkBackup/utilities/tee.py +40 -0
  36. PyHardLinkBackup/utilities/tests/__init__.py +0 -0
  37. PyHardLinkBackup/utilities/tests/test_file_hash_database.py +153 -0
  38. PyHardLinkBackup/utilities/tests/test_file_size_database.py +151 -0
  39. PyHardLinkBackup/utilities/tests/test_filesystem.py +167 -0
  40. PyHardLinkBackup/utilities/tests/unittest_utilities.py +78 -0
  41. PyHardLinkBackup/utilities/tyro_cli_shared_args.py +29 -0
  42. pyhardlinkbackup-1.8.1.dist-info/METADATA +700 -0
  43. pyhardlinkbackup-1.8.1.dist-info/RECORD +45 -0
  44. pyhardlinkbackup-1.8.1.dist-info/WHEEL +4 -0
  45. pyhardlinkbackup-1.8.1.dist-info/entry_points.txt +2 -0
@@ -0,0 +1,700 @@
1
+ Metadata-Version: 2.4
2
+ Name: PyHardLinkBackup
3
+ Version: 1.8.1
4
+ Summary: HardLink/Deduplication Backups with Python
5
+ Project-URL: Documentation, https://github.com/jedie/PyHardLinkBackup
6
+ Project-URL: Source, https://github.com/jedie/PyHardLinkBackup
7
+ Author-email: Jens Diemer <PyHardLinkBackup@jensdiemer.de>
8
+ License: GPL-3.0-or-later
9
+ Requires-Python: >=3.12
10
+ Requires-Dist: bx-py-utils
11
+ Requires-Dist: cli-base-utilities>=0.27.1
12
+ Requires-Dist: rich
13
+ Requires-Dist: tyro
14
+ Description-Content-Type: text/markdown
15
+
16
+ # PyHardLinkBackup
17
+
18
+ [![tests](https://github.com/jedie/PyHardLinkBackup/actions/workflows/tests.yml/badge.svg?branch=main)](https://github.com/jedie/PyHardLinkBackup/actions/workflows/tests.yml)
19
+ [![codecov](https://codecov.io/github/jedie/PyHardLinkBackup/branch/main/graph/badge.svg)](https://app.codecov.io/github/jedie/PyHardLinkBackup)
20
+ [![PyHardLinkBackup @ PyPi](https://img.shields.io/pypi/v/PyHardLinkBackup?label=PyHardLinkBackup%20%40%20PyPi)](https://pypi.org/project/PyHardLinkBackup/)
21
+ [![Python Versions](https://img.shields.io/pypi/pyversions/PyHardLinkBackup)](https://github.com/jedie/PyHardLinkBackup/blob/main/pyproject.toml)
22
+ [![License GPL-3.0-or-later](https://img.shields.io/pypi/l/PyHardLinkBackup)](https://github.com/jedie/PyHardLinkBackup/blob/main/LICENSE)
23
+
24
+ PyHardLinkBackup is a cross-platform backup tool designed for efficient, reliable, and accessible backups.
25
+ Similar to `rsync --link-dest`, but with global deduplication across all backups and all paths, not just between two directories.
26
+
27
+ Some aspects:
28
+
29
+ - Creates deduplicated, versioned backups using hardlinks, minimizing storage usage by linking identical files across all backup snapshots.
30
+ - Employs a global deduplication database (by file size and SHA256 hash) per backup root, ensuring that duplicate files are detected and hardlinked even if they are moved or renamed between backups.
31
+ - Backups are stored as regular files and directories—no proprietary formats—so you can access your data directly without special tools.
32
+ - Deleting old snapshots does not affect the integrity of remaining backups.
33
+ - Linux and macOS are fully supported (Windows support is experimental)
34
+
35
+ Limitations:
36
+
37
+ - Requires a filesystem that supports hardlinks (e.g., btrfs, zfs, ext4, APFS, NTFS with limitations).
38
+ - Empty directories are not backed up.
39
+
40
+
41
+ ## installation
42
+
43
+ You can use [pipx](https://pipx.pypa.io/stable/installation/) to install and use PyHardLinkBackup, e.g.:
44
+
45
+ ```bash
46
+ sudo apt install pipx
47
+
48
+ pipx install PyHardLinkBackup
49
+ ```
50
+
51
+ After this you can call the CLI via `phlb` command.
52
+ The main command is `phlb backup <source> <destination>` to create a backup.
53
+
54
+ e.g.:
55
+
56
+ ```bash
57
+ phlb backup /path/to/source /path/to/destination
58
+ ```
59
+
60
+ This will create a snapshot in `/path/to/destination` using hard links for deduplication. You can safely delete old snapshots without affecting others.
61
+
62
+
63
+ [comment]: <> (✂✂✂ auto generated backup help start ✂✂✂)
64
+ ```
65
+ usage: phlb backup [-h] [BACKUP OPTIONS]
66
+
67
+ Backup the source directory to the destination directory using hard links for deduplication.
68
+
69
+ ╭─ positional arguments ───────────────────────────────────────────────────────────────────────────────────────────────╮
70
+ │ source Source directory to back up. (required) │
71
+ │ destination Destination directory for the backup. (required) │
72
+ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
73
+ ╭─ options ────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
74
+ │ -h, --help show this help message and exit │
75
+ │ --name {None}|STR Optional name for the backup (used to create a subdirectory in the backup destination). If not │
76
+ │ provided, the name of the source directory is used. (default: None) │
77
+ │ --one-file-system, --no-one-file-system │
78
+ │ Do not cross filesystem boundaries. (default: True) │
79
+ │ --excludes [STR [STR ...]] │
80
+ │ List of directories to exclude from backup. (default: __pycache__ .cache .temp .tmp .tox .nox) │
81
+ │ --verbosity {debug,info,warning,error} │
82
+ │ Log level for console logging. (default: warning) │
83
+ │ --log-file-level {debug,info,warning,error} │
84
+ │ Log level for the log file (default: info) │
85
+ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
86
+ ```
87
+ [comment]: <> (✂✂✂ auto generated backup help end ✂✂✂)
88
+
89
+
90
+ ## Screenshots
91
+ ### Screenshot - running a backup
92
+
93
+ ----
94
+
95
+ ![2026-01-19_phlb1.png](https://raw.githubusercontent.com/jedie/jedie.github.io/main/screenshots/PyHardLinkBackup/2026-01-19_phlb1.png "2026-01-19_phlb1.png")
96
+
97
+ ----
98
+
99
+ ### Screenshot - backup finished
100
+
101
+ ----
102
+
103
+ ![2026-01-19_phlb2.png](https://raw.githubusercontent.com/jedie/jedie.github.io/main/screenshots/PyHardLinkBackup/2026-01-19_phlb2.png "2026-01-19_phlb2.png")
104
+
105
+ ----
106
+
107
+ (more screenshots here: [jedie.github.io/tree/main/screenshots/PyHardLinkBackup](https://github.com/jedie/jedie.github.io/tree/main/screenshots/PyHardLinkBackup))
108
+
109
+
110
+ ### update
111
+
112
+ If you use pipx, just call:
113
+ ```bash
114
+ pipx upgrade PyHardLinkBackup
115
+ ```
116
+ see: https://pipx.pypa.io/stable/docs/#pipx-upgrade
117
+
118
+
119
+ ### Troubleshooting
120
+
121
+ - **Permission Errors:** Ensure you have read access to source and write access to destination.
122
+ - **Hardlink Limits:** Some filesystems (e.g., NTFS) have limits on the number of hardlinks per file.
123
+ - **Symlink Handling:** Broken symlinks are handled gracefully; see logs for details.
124
+ - **Backup Deletion:** Deleting a snapshot does not affect deduplication of other backups.
125
+ - **Log Files:** Check the log file in each backup directory for error details.
126
+
127
+
128
+ To lower the priority of the backup process (useful to reduce system impact during heavy backups), you can use `nice` and `ionice` on Linux systems:
129
+
130
+ ```bash
131
+ nice -n 19 ionice -c3 phlb backup /path/to/source /path/to/destination
132
+ ```
133
+ - `nice -n 19` sets the lowest CPU priority.
134
+ - `ionice -c3` sets the lowest I/O priority (idle class).
135
+
136
+ Adjust priority of an already running backup:
137
+ ```bash
138
+ renice 19 -p $(pgrep phlb) && ionice -c3 -p $(pgrep phlb)
139
+ ```
140
+
141
+
142
+ ### complete help for main CLI app
143
+
144
+ [comment]: <> (✂✂✂ auto generated main help start ✂✂✂)
145
+ ```
146
+ usage: phlb [-h] {backup,compare,rebuild,version}
147
+
148
+
149
+
150
+ ╭─ options ────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
151
+ │ -h, --help show this help message and exit │
152
+ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
153
+ ╭─ subcommands ────────────────────────────────────────────────────────────────────────────────────────────────────────╮
154
+ │ (required) │
155
+ │ • backup Backup the source directory to the destination directory using hard links for deduplication. │
156
+ │ • compare Compares a source tree with the last backup and validates all known file hashes. │
157
+ │ • rebuild Rebuild the file hash and size database by scanning all backup files. And also verify SHA256SUMS and/or │
158
+ │ store missing hashes in SHA256SUMS files. │
159
+ │ • version Print version and exit │
160
+ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
161
+ ```
162
+ [comment]: <> (✂✂✂ auto generated main help end ✂✂✂)
163
+
164
+
165
+
166
+
167
+
168
+ ## concept
169
+
170
+ ### Implementation boundaries
171
+
172
+ * pure Python using >=3.12
173
+ * pathlib for path handling
174
+ * iterate filesystem with `os.scandir()`
175
+
176
+ ### overview
177
+
178
+ * Backups should be saved as normal files in the filesystem:
179
+ * non-proprietary format
180
+ * accessible without any extra software or extra meta files
181
+ * Create backups with versioning
182
+ * every backup run creates a complete filesystem snapshot tree
183
+ * every snapshot tree can be deleted, without affecting the other snapshots
184
+ * Deduplication with hardlinks:
185
+ * space-efficient incremental backups by linking unchanged files across snapshots instead of duplicating them
186
+ * find duplicate files everywhere (even if renamed or moved files)
187
+
188
+
189
+ ### used solutions
190
+
191
+ * Used `sha256` hash algorithm to identify file content
192
+ * Small file handling
193
+ * Always copy small files and never hardlink them
194
+ * Don't store size and hash of these files in the deduplication lookup tables
195
+
196
+ #### Deduplication lookup methods
197
+
198
+ To avoid unnecessary file copy operations, we need a fast method to find duplicate files.
199
+ Our approach is based on two steps: file size and file content hash.
200
+ Because the file size is very fast to compare.
201
+
202
+ ###### size "database"
203
+
204
+ We store all existing file sizes as empty files in a special folder structure:
205
+
206
+ * 1st level: first 2 digits of the size in bytes
207
+ * 2nd level: next 2 digits of the size in bytes
208
+ * file: full size in bytes as filename
209
+
210
+ e.g.: file size `123456789` bytes stored in: `{destination}/.phlb/size-lookup/89/67/123456789`
211
+ We skip files lower than `1000` bytes, so no filling with leading zeros is needed ;)
212
+
213
+ ###### hash "database"
214
+
215
+ We store the `file hash` <-> `hardlink pointer` mapping in a special folder structure:
216
+
217
+ * 1st level: first 2 chars of the hex encoded hash
218
+ * 2nd level: next 2 chars of the hex encoded hash
219
+ * file: full hex encoded hash as filename
220
+
221
+ e.g.: hash like `abcdef123...` stored in: `{destination}/.phlb/hash-lookup/ab/cd/abcdef123...`
222
+ The file contains only the relative path to the first hardlink of this file content.
223
+
224
+
225
+ ## start development
226
+
227
+ At least `uv` is needed. Install e.g.: via pipx:
228
+ ```bash
229
+ apt-get install pipx
230
+ pipx install uv
231
+ ```
232
+
233
+ Clone the project and just start the CLI help commands.
234
+ A virtual environment will be created/updated automatically.
235
+
236
+ ```bash
237
+ ~$ git clone https://github.com/jedie/PyHardLinkBackup.git
238
+ ~$ cd PyHardLinkBackup
239
+ ~/PyHardLinkBackup$ ./cli.py --help
240
+ ~/PyHardLinkBackup$ ./dev-cli.py --help
241
+ ```
242
+
243
+ [comment]: <> (✂✂✂ auto generated dev help start ✂✂✂)
244
+ ```
245
+ usage: ./dev-cli.py [-h] {benchmark-hashes,coverage,fs-info,install,lint,mypy,nox,pip-audit,publish,scan-benchmark,shell-completion,test,update,update-readme-history,update-test-snapshot-files,version}
246
+
247
+
248
+
249
+ ╭─ options ────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
250
+ │ -h, --help show this help message and exit │
251
+ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
252
+ ╭─ subcommands ────────────────────────────────────────────────────────────────────────────────────────────────────────╮
253
+ │ (required) │
254
+ │ • benchmark-hashes │
255
+ │ Benchmark different file hashing algorithms on the given path. │
256
+ │ • coverage Run tests and show coverage report. │
257
+ │ • fs-info Display information about the filesystem under the given path. │
258
+ │ • install Install requirements and 'PyHardLinkBackup' via pip as editable. │
259
+ │ • lint Check/fix code style by run: "ruff check --fix" │
260
+ │ • mypy Run Mypy (configured in pyproject.toml) │
261
+ │ • nox Run nox │
262
+ │ • pip-audit Run pip-audit check against current requirements files │
263
+ │ • publish Build and upload this project to PyPi │
264
+ │ • scan-benchmark │
265
+ │ Benchmark our filesystem scan routine. │
266
+ │ • shell-completion │
267
+ │ Setup shell completion for this CLI (Currently only for bash shell) │
268
+ │ • test Run unittests │
269
+ │ • update Update dependencies (uv.lock) and git pre-commit hooks │
270
+ │ • update-readme-history │
271
+ │ Update project history base on git commits/tags in README.md Will be exited with 1 if the README.md │
272
+ │ was updated otherwise with 0. │
273
+ │ │
274
+ │ Also, callable via e.g.: │
275
+ │ python -m cli_base update-readme-history -v │
276
+ │ • update-test-snapshot-files │
277
+ │ Update all test snapshot files (by remove and recreate all snapshot files) │
278
+ │ • version Print version and exit │
279
+ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
280
+ ```
281
+ [comment]: <> (✂✂✂ auto generated dev help end ✂✂✂)
282
+
283
+
284
+ ## Backwards-incompatible changes
285
+
286
+ ### v1.0.0
287
+
288
+ v1 is a complete rewrite of PyHardLinkBackup.
289
+
290
+ Overview of main changes:
291
+
292
+ * Remove Django dependency:
293
+ * No SQlite database anymore -> Data for deduplication stored in filesystem only
294
+ * No Django Admin, because we have no database anymore ;)
295
+ * Change hash algorithm from SHA512 to SHA256, because it's faster and still secure enough
296
+ * Don't store `*.sha512` for every file anymore -> We store one `SHA256SUMS` file in every backup directory
297
+
298
+ ## History
299
+
300
+ [comment]: <> (✂✂✂ auto generated history start ✂✂✂)
301
+
302
+ * [v1.8.1](https://github.com/jedie/PyHardLinkBackup/compare/v1.8.0...v1.8.1)
303
+ * 2026-01-24 - Update packaging commands related to new direct "uv" usage
304
+ * 2026-01-24 - Bugfix "rebuild" command
305
+ * 2026-01-24 - Update requirements
306
+ * 2026-01-24 - CI: Use "main" instead of version pinning and fix code coverage
307
+ * 2026-01-24 - Simplify CLIs using uv direct
308
+ * 2026-01-22 - rebuid command: skip hashing same files by check the inode uniqueness
309
+ * 2026-01-22 - Add "fs-info" in dev cli
310
+ * 2026-01-22 - rebuild command: fix wrong progress bar
311
+ * [v1.8.0](https://github.com/jedie/PyHardLinkBackup/compare/v1.7.3...v1.8.0)
312
+ * 2026-01-22 - Add optional "--name" to enforce a name for the backup sub directory
313
+ * 2026-01-22 - Do not cross filesystem boundaries as default
314
+ * 2026-01-22 - Display progress also for large unique file copy
315
+ * 2026-01-22 - Optimize progress bars for smaller screens
316
+ * [v1.7.3](https://github.com/jedie/PyHardLinkBackup/compare/v1.7.2...v1.7.3)
317
+ * 2026-01-21 - Handle directory symlinks correct
318
+ * [v1.7.2](https://github.com/jedie/PyHardLinkBackup/compare/v1.7.1...v1.7.2)
319
+ * 2026-01-21 - Display "Remaining time" to files and sizes, too.
320
+
321
+ <details><summary>Expand older history entries ...</summary>
322
+
323
+ * [v1.7.1](https://github.com/jedie/PyHardLinkBackup/compare/v1.7.0...v1.7.1)
324
+ * 2026-01-19 - Update requirements to fix problems under Windows
325
+ * [v1.7.0](https://github.com/jedie/PyHardLinkBackup/compare/v1.6.0...v1.7.0)
326
+ * 2026-01-19 - Speedup and enhance unittest
327
+ * 2026-01-17 - Remove unfinished copied files on errors
328
+ * 2026-01-17 - Display/update progress on very lage files #75 and enhance all bars
329
+ * 2026-01-18 - Expand tests: Check file open calls
330
+ * 2026-01-17 - expand tests
331
+ * 2026-01-17 - simplify tests
332
+ * 2026-01-17 - Warn if broken symlink found
333
+ * 2026-01-17 - Update README
334
+ * [v1.6.0](https://github.com/jedie/PyHardLinkBackup/compare/v1.5.0...v1.6.0)
335
+ * 2026-01-17 - Fix flaky test, because of terminal size
336
+ * 2026-01-17 - Bugfix: Don't hash new large files twice
337
+ * 2026-01-17 - Use compare also in backup tests
338
+ * [v1.5.0](https://github.com/jedie/PyHardLinkBackup/compare/v1.4.1...v1.5.0)
339
+ * 2026-01-17 - NEW: Compare command to verify source tree with last backup
340
+ * [v1.4.1](https://github.com/jedie/PyHardLinkBackup/compare/v1.4.0...v1.4.1)
341
+ * 2026-01-16 - Bugfix large file handling
342
+ * [v1.4.0](https://github.com/jedie/PyHardLinkBackup/compare/v1.3.0...v1.4.0)
343
+ * 2026-01-16 - Create log file in backup and a summary.txt
344
+ * 2026-01-16 - Run CI tests on macos, too.
345
+ * 2026-01-16 - add dev cli command "scan-benchmark"
346
+ * [v1.3.0](https://github.com/jedie/PyHardLinkBackup/compare/v1.2.0...v1.3.0)
347
+ * 2026-01-15 - Verify SHA256SUMS files in "rebuild" command, too.
348
+ * 2026-01-15 - Code cleanup: use more generic names for and in BackupProgress
349
+ * 2026-01-15 - Add tests for rebuild
350
+ * 2026-01-15 - Add command to "rebuld" the size and hash filesystem database
351
+ * 2026-01-15 - Add screenshots in the README
352
+ * [v1.2.0](https://github.com/jedie/PyHardLinkBackup/compare/v1.1.0...v1.2.0)
353
+ * 2026-01-15 - Add error handling: Log exception but continue with the backup
354
+ * 2026-01-15 - Check permission and hadlink support on destination path
355
+ * 2026-01-14 - Enhance progress bars
356
+ * 2026-01-14 - A a note to rsync --link-dest
357
+ * 2026-01-14 - Use cli_base.cli_tools.test_utils.base_testcases
358
+ * [v1.1.0](https://github.com/jedie/PyHardLinkBackup/compare/v1.0.1...v1.1.0)
359
+ * 2026-01-14 - Change backup timestamp directory to old schema: '%Y-%m-%d-%H%M%S'
360
+ * 2026-01-14 - Add "Overview of main changes" to README
361
+ * [v1.0.1](https://github.com/jedie/PyHardLinkBackup/compare/v1.0.0...v1.0.1)
362
+ * 2026-01-13 - Store SHA256SUMS files in backup directories
363
+ * [v1.0.0](https://github.com/jedie/PyHardLinkBackup/compare/v0.13.0...v1.0.0)
364
+ * 2026-01-13 - Change "./cli.py" to "phlb" (because it's the name installed via pipx)
365
+ * 2026-01-13 - Update README
366
+ * 2026-01-13 - Fix benchmark moved to dev CLI ;)
367
+ * 2026-01-13 - Remove tyro warning
368
+ * 2026-01-13 - Move "benchmark_hashes" from app to dev cli (It's more for testing)
369
+ * 2026-01-13 - Rename [project.scripts] hooks
370
+ * 2026-01-13 - Add DocWrite, handle broken symlinks, keep file meta, handle missing hardlink sources
371
+ * 2026-01-12 - First working iteration with rich progess bar
372
+ * 2026-01-08 - Rewrite everything
373
+ * [v0.13.0](https://github.com/jedie/PyHardLinkBackup/compare/v0.12.3...v0.13.0)
374
+ * 2020-03-18 - release v0.13.0
375
+ * 2020-03-17 - deactivate pypy tests in travis, because of SQLite errors, like:
376
+ * 2020-03-17 - make CI pipeline easier
377
+ * 2020-03-17 - update README
378
+ * 2020-03-17 - Fix misleading error msg for dst OSError, bad exception handling #23
379
+ * 2020-03-17 - Change Django Admin header
380
+ * 2020-03-17 - Fix "run django server doesn't work" #39
381
+ * 2020-03-17 - test release v0.12.4.rc1
382
+ * 2020-03-17 - Simplify backup process bar update code
383
+ * 2020-03-17 - Bugfix add command if "phlb_config.ini" doesn't match with database entry
384
+ * 2020-03-17 - bugfix "add" command
385
+ * 2020-03-17 - change CHUNK_SIZE in ini config to MIN_CHUNK_SIZE
386
+ * 2020-03-17 - update requirements
387
+ * 2020-03-17 - release v0.12.4.rc0
388
+ * 2020-03-17 - dynamic chunk size
389
+ * 2020-03-17 - ignore *.sha512 by default
390
+ * 2020-03-17 - Update boot_pyhardlinkbackup.sh
391
+ * [v0.12.3](https://github.com/jedie/PyHardLinkBackup/compare/v0.12.2...v0.12.3)
392
+ * 2020-03-17 - update README.rst
393
+ * 2020-03-17 - don't publish if tests fail
394
+ * 2020-03-17 - cleanup pytest config
395
+ * 2020-03-17 - Fix "Files to backup" message and update tests
396
+ * 2020-03-16 - Fix #44 - wroing file size in process bar
397
+ * 2020-03-16 - just warn if used directly (needfull for devlopment to call this directly ;)
398
+ * 2020-03-16 - update requirements
399
+ * 2020-03-16 - +pytest-randomly
400
+ * [v0.12.2](https://github.com/jedie/PyHardLinkBackup/compare/v0.12.1...v0.12.2)
401
+ * 2020-03-06 - repare v0.12.2 release
402
+ * 2020-03-06 - enhance log file content
403
+ * 2020-03-06 - update requirements
404
+ * 2020-03-06 - Update README.creole
405
+ * 2020-03-05 - Fix #40 by decrease log level
406
+ * 2020-03-05 - Update boot_pyhardlinkbackup.cmd
407
+ * 2020-03-05 - Update boot_pyhardlinkbackup.sh
408
+ * [v0.12.1](https://github.com/jedie/PyHardLinkBackup/compare/v0.12.0...v0.12.1)
409
+ * 2020-03-05 - update tests and set version to 0.12.1
410
+ * 2020-03-05 - less verbose pytest output
411
+ * 2020-03-05 - pyhardlinkbackup.ini -> PyHardLinkBackup.ini
412
+ * 2020-03-05 - revert renaming the main destination directory back to: "PyHardLinkBackup"
413
+ * [v0.12.0](https://github.com/jedie/PyHardLinkBackup/compare/v0.11.0...v0.12.0)
414
+ * 2020-03-05 - repare v0.12.0 release
415
+ * 2020-03-05 - use poetry_publish.tests.test_project_setup code parts
416
+ * 2020-03-05 - Update README.creole
417
+ * 2020-03-05 - renove unused code parts
418
+ * 2020-03-05 - don't test with pypy
419
+ * 2020-03-05 - fix code style
420
+ * 2020-03-05 - Fix tests with different python versions
421
+ * 2020-03-05 - bugfix test: getting manage.py in tox
422
+ * 2020-03-05 - update requirements
423
+ * 2020-03-05 - Don't create "summary" file: log everything in .log file
424
+ * 2020-02-16 - Handle Backup errors.
425
+ * 2020-02-16 - Bugfix processed files count
426
+ * 2020-02-16 - Bugfix: Mark backup run instance as "completed"
427
+ * 2020-02-16 - code cleanup
428
+ * 2020-02-16 - Use assert_is_file() from django-tools and Path().read_text()
429
+ * 2020-02-16 - Don't init a second PathHelper instance!
430
+ * 2020-02-02 - WIP: update Tests
431
+ * 2020-02-02 - run linters as last step
432
+ * 2020-02-02 - remove unused import
433
+ * 2020-02-02 - update IterFilesystem
434
+ * 2020-02-02 - bugfix file size formats
435
+ * 2020-02-02 - stats_helper.abort always exists in newer IterFilesystem version
436
+ * 2020-02-02 - Link or copy the log file to backup and fix summary/output
437
+ * 2020-02-02 - remove converage config from pytest.ini
438
+ * 2020-02-02 - pytest-django: reuse db + nomigrations
439
+ * 2020-02-02 - remove not needed django_project/wsgi.py
440
+ * 2020-02-02 - update README
441
+ * 2020-02-02 - pyhardlinkbackup/{phlb_cli.py => phlb/cli.py}
442
+ * 2020-02-02 - fix Django project setup and add tests for it
443
+ * 2020-02-02 - update Django project settings
444
+ * 2020-02-02 - test release 0.12.0.dev0
445
+ * 2020-02-02 - include poetry.lock file
446
+ * 2020-02-02 - set version to v0.11.0.dev0
447
+ * 2020-02-02 - remove flak8 test (will be done in ci via Makefile)
448
+ * 2020-02-02 - some code updates
449
+ * 2020-02-02 - apply "make fix-code-style"
450
+ * 2020-02-02 - update Makefile: poetry_publish -> pyhardlinkbackup ;)
451
+ * 2020-02-02 - add README.rst
452
+ * 2020-02-02 - /{PyHardLinkBackup => pyhardlinkbackup}/
453
+ * 2020-02-02 - + "make runserver"
454
+ * 2020-02-02 - delete setup.py and setup.cfg
455
+ * 2020-02-02 - WIP: use poetry and poetry-publish
456
+ * 2020-02-02 - update to Django v2.2.x LTS
457
+ * 2019-10-20 - fixup! WIP: update tests
458
+ * 2019-10-20 - +tests_require=['pytest',]
459
+ * 2019-10-20 - update "add" command
460
+ * 2019-10-20 - add setup.cfg
461
+ * 2019-10-13 - use https://github.com/jedie/IterFilesystem
462
+ * 2019-09-18 - remove support for old python versions
463
+ * 2019-09-18 - fix pytest run
464
+ * 2019-03-03 - use pytest + tox and add flake8+isort config files
465
+ * [v0.11.0](https://github.com/jedie/PyHardLinkBackup/compare/v0.10.1...v0.11.0)
466
+ * 2019-03-03 - +email
467
+ * 2019-03-03 - use django v1.11.x
468
+ * 2019-03-03 - update django
469
+ * 2019-03-03 - remove: create_dev_env.sh
470
+ * 2019-03-03 - just code formatting with black
471
+ * 2019-03-03 - update setup.py
472
+ * 2019-03-03 - +create_dev_env.sh
473
+ * 2018-09-12 - Update boot_pyhardlinkbackup.cmd
474
+ * 2018-08-03 - Update phlb_run_tests.sh
475
+ * 2018-08-03 - Update phlb_upgrade_PyHardLinkBackup.sh
476
+ * 2017-12-11 - code cleanup
477
+ * 2017-12-10 - Update boot_pyhardlinkbackup.sh
478
+ * 2017-12-10 - set DJANGO_SETTINGS_MODULE
479
+ * 2017-11-17 - +codecov.io
480
+ * [v0.10.1](https://github.com/jedie/PyHardLinkBackup/compare/v0.10.0...v0.10.1)
481
+ * 2016-09-09 - fix #24 by skip not existing files
482
+ * 2016-08-20 - fix typos, improve grammar, add borgbackup
483
+ * 2016-06-28 - use the origin model to use the config methods:
484
+ * 2016-06-27 - use get_model()
485
+ * 2016-06-27 - add missing migrations for:
486
+ * 2016-04-28 - Update README.creole
487
+ * 2016-04-27 - bugfix ~/
488
+ * [v0.10.0](https://github.com/jedie/PyHardLinkBackup/compare/v0.9.0...v0.10.0)
489
+ * 2016-04-27 - -%APPDATA% +%ProgramFiles%
490
+ * 2016-04-26 - v0.9.1
491
+ * 2016-04-26 - bugfix boot cmd
492
+ * 2016-04-26 - add note
493
+ * 2016-04-26 - migrate after boot
494
+ * 2016-04-26 - add migrate scripts
495
+ * 2016-03-08 - bugfix if path contains spaces
496
+ * 2016-02-29 - Update README.creole
497
+ * [v0.9.0](https://github.com/jedie/PyHardLinkBackup/compare/v0.8.0...v0.9.0)
498
+ * 2016-02-10 - v0.9.0
499
+ * 2016-02-10 - fix AppVeyor
500
+ * 2016-02-10 - fix path?
501
+ * 2016-02-10 - add pathlib_revised
502
+ * 2016-02-10 - typo
503
+ * 2016-02-08 - try to combine linux and windows tests coverage via:
504
+ * 2016-02-08 - move Path2() to external lib: https://github.com/jedie/pathlib_revised
505
+ * 2016-02-08 - Use existing hash files in "phlb add" command:
506
+ * 2016-02-08 - Work-a-round for Windows MAX_PATH limit: Use \?\ path prefix internally.
507
+ * [v0.8.0](https://github.com/jedie/PyHardLinkBackup/compare/v0.7.0...v0.8.0)
508
+ * 2016-02-04 - release v0.8.0
509
+ * 2016-02-04 - seems that windows/NTFS is less precise ;)
510
+ * 2016-02-04 - tqdm will not accept 0 bytes files ;)
511
+ * 2016-02-04 - new: "phlb add"
512
+ * 2016-02-04 - bugfix: display skip pattern info
513
+ * [v0.7.0](https://github.com/jedie/PyHardLinkBackup/compare/v0.6.4...v0.7.0)
514
+ * 2016-02-03 - release v0.7.0
515
+ * 2016-02-03 - remove obsolete cli command and update cli unittests
516
+ * 2016-02-03 - do 'migrate' on every upgrade run, too.
517
+ * 2016-02-03 - Update README.creole
518
+ * 2016-02-03 - Fix: #17 and save a "phlb_config.ini" in every backup:
519
+ * 2016-02-02 - New: verify a existing backup
520
+ * 2016-02-02 - remove editable=False
521
+ * [v0.6.4](https://github.com/jedie/PyHardLinkBackup/compare/v0.6.3...v0.6.4)
522
+ * 2016-02-01 - v0.6.4
523
+ * 2016-02-01 - prepare for v0.6.4 release
524
+ * 2016-02-01 - Fix #13 - temp rename error, because of the Windows API limitation
525
+ * 2016-01-31 - bugfix in scanner if symlink is broken
526
+ * 2016-01-31 - display local variables on low level errors
527
+ * [v0.6.3](https://github.com/jedie/PyHardLinkBackup/compare/v0.6.2...v0.6.3)
528
+ * 2016-01-29 - Less verbose and better information about SKIP_DIRS/SKIP_PATTERNS hits
529
+ * 2016-01-29 - error -> info
530
+ * 2016-01-29 - +keywords +license
531
+ * [v0.6.2](https://github.com/jedie/PyHardLinkBackup/compare/v0.6.1...v0.6.2)
532
+ * 2016-01-29 - fix tests by change the mtime of the test files to get always the same order
533
+ * 2016-01-28 - Handle unexpected errors and KeyboardInterrupt
534
+ * [v0.6.1](https://github.com/jedie/PyHardLinkBackup/compare/v0.6.0...v0.6.1)
535
+ * 2016-01-28 - release v0.6.1
536
+ * 2016-01-28 - fix #13 by use a better rename routine
537
+ * [v0.6.0](https://github.com/jedie/PyHardLinkBackup/compare/v0.5.1...v0.6.0)
538
+ * 2016-01-28 - start maring the admin nicer, see also: #7
539
+ * 2016-01-28 - faster backup by compare mtime/size only if old backup files exists
540
+ * [v0.5.1](https://github.com/jedie/PyHardLinkBackup/compare/v0.5.0...v0.5.1)
541
+ * 2016-01-27 - Squashed commit of the following:
542
+ * [v0.5.0](https://github.com/jedie/PyHardLinkBackup/compare/v0.4.2...v0.5.0)
543
+ * 2016-01-27 - release v0.5.0
544
+ * 2016-01-27 - refactory source tree scan and use pathlib everywhere:
545
+ * 2016-01-24 - try to fix DB memory usage
546
+ * 2016-01-24 - Simulate not working os.link with mock
547
+ * 2016-01-24 - fix if duration==0
548
+ * 2016-01-24 - use mock to simulate not readable files
549
+ * 2016-01-24 - just move class
550
+ * 2016-01-23 - fix #10 and add --name cli argument and add unittests for the changes
551
+ * 2016-01-23 - alternative solutions
552
+ * [v0.4.2](https://github.com/jedie/PyHardLinkBackup/compare/v0.4.1...v0.4.2)
553
+ * 2016-01-22 - work-a-round for junction under windows
554
+ * 2016-01-22 - Display if out path can't created
555
+ * 2016-01-22 - print some more status information in between.
556
+ * 2016-01-22 - Update README.creole
557
+ * 2016-01-22 - typo
558
+ * 2016-01-22 - try to reproduce #11
559
+ * 2016-01-22 - check if test settings are active
560
+ * 2016-01-22 - needless
561
+ * 2016-01-22 - importand to change into current directory for:
562
+ * [v0.4.1](https://github.com/jedie/PyHardLinkBackup/compare/v0.4.0...v0.4.1)
563
+ * 2016-01-21 - merge get log content and fix windows tests
564
+ * 2016-01-21 - Skip files that can't be read/write
565
+ * 2016-01-21 - Update README.creole
566
+ * [v0.4.0](https://github.com/jedie/PyHardLinkBackup/compare/v0.3.1...v0.4.0)
567
+ * 2016-01-21 - save summary and log file for every backup run
568
+ * 2016-01-21 - increase default chunk size to 20MB
569
+ * 2016-01-21 - typo
570
+ * 2016-01-21 - moved TODO:
571
+ * 2016-01-21 - journal_mode = MEMORY
572
+ * 2016-01-21 - wip
573
+ * 2016-01-21 - bugfix: use pathlib instance
574
+ * 2016-01-21 - activate py3.4 32bit tests
575
+ * 2016-01-21 - update
576
+ * 2016-01-21 - Bugfix for python < 3.5
577
+ * 2016-01-16 - WIP: Bugfix unittests for: Change .ini search path to every parent directory WIP: Save summary and log file for every backup run
578
+ * 2016-01-16 - Change .ini search path to every parent directory
579
+ * 2016-01-16 - Add a unittest for the NTFS 1024 hardlink limit
580
+ * 2016-01-15 - bugfix if unittests failed under appveyor
581
+ * 2016-01-15 - change timestamp stuff in unittests
582
+ * 2016-01-15 - Update README.creole
583
+ * 2016-01-15 - Create WindowsDevelopment.creole
584
+ * [v0.3.1](https://github.com/jedie/PyHardLinkBackup/compare/v0.3.0...v0.3.1)
585
+ * 2016-01-15 - v0.3.1
586
+ * 2016-01-15 - try to fix 'coveralls':
587
+ * 2016-01-15 - ignore temp cleanup error
588
+ * 2016-01-15 - fix some unittests under windows:
589
+ * 2016-01-15 - obsolete
590
+ * [v0.3.0](https://github.com/jedie/PyHardLinkBackup/compare/v0.2.0...v0.3.0)
591
+ * 2016-01-15 - release v0.3.0
592
+ * 2016-01-14 - Squashed commit of the following:
593
+ * [v0.2.0](https://github.com/jedie/PyHardLinkBackup/compare/v0.1.13...v0.2.0)
594
+ * 2016-01-13 - fix appveyor
595
+ * 2016-01-13 - Refactoring unitests and add more tests
596
+ * [v0.1.13](https://github.com/jedie/PyHardLinkBackup/compare/v0.1.12...v0.1.13)
597
+ * 2016-01-12 - 0.1.13
598
+ * 2016-01-11 - set python versions
599
+ * 2016-01-11 - test oder versions
600
+ * 2016-01-11 - print path with %s and not with %r
601
+ * 2016-01-11 - start backup unittests
602
+ * 2016-01-11 - wip
603
+ * 2016-01-11 - wip: fix CI
604
+ * 2016-01-11 - own tests run with :memory: database in temp dir
605
+ * 2016-01-11 - wtf
606
+ * 2016-01-11 - wip: fix appveyor
607
+ * 2016-01-11 - Update README.creole
608
+ * 2016-01-11 - Fix appveyor
609
+ * 2016-01-11 - bugfix
610
+ * [v0.1.12](https://github.com/jedie/PyHardLinkBackup/compare/v0.1.11...v0.1.12)
611
+ * 2016-01-11 - 0.1.12
612
+ * 2016-01-11 - unify .cmd files
613
+ * 2016-01-11 - update
614
+ * 2016-01-11 - use "phlb.exe helper" and merge code
615
+ * 2016-01-11 - bugfix
616
+ * 2016-01-11 - Appveyor work-a-round
617
+ * 2016-01-11 - +cmd_shell.cmd
618
+ * [v0.1.11](https://github.com/jedie/PyHardLinkBackup/compare/v0.1.10...v0.1.11)
619
+ * 2016-01-10 - wip
620
+ * 2016-01-10 - cd checkout dir
621
+ * 2016-01-10 - don't wait for user input
622
+ * 2016-01-10 - appveyor.yml
623
+ * 2016-01-10 - fix travis CI?
624
+ * 2016-01-10 - test 'py'
625
+ * 2016-01-10 - update boot
626
+ * 2016-01-10 - +migrate
627
+ * 2016-01-10 - try to use the own boot script
628
+ * 2016-01-10 - +# http://www.appveyor.com
629
+ * 2016-01-10 - add badge icons
630
+ * 2016-01-10 - coveralls token
631
+ * 2016-01-10 - migrate database
632
+ * 2016-01-10 - abs path to work?
633
+ * 2016-01-10 - fix?
634
+ * 2016-01-10 - test local .ini change config
635
+ * 2016-01-10 - travis
636
+ * 2016-01-10 - debug config
637
+ * 2016-01-10 - bugfix extras_require
638
+ * 2016-01-10 - simple cli output test
639
+ * 2016-01-10 - refactor cli and start travis CI
640
+ * 2016-01-10 - install scandir via extras_require
641
+ * [v0.1.10](https://github.com/jedie/PyHardLinkBackup/compare/v0.1.9...v0.1.10)
642
+ * 2016-01-10 - v0.1.10
643
+ * 2016-01-10 - cleanup after KeyboardInterrupt
644
+ * 2016-01-10 - always activate venv
645
+ * 2016-01-10 - shell scripts for linux
646
+ * 2016-01-10 - update README
647
+ * 2016-01-10 - add boot script for linux
648
+ * [v0.1.9](https://github.com/jedie/PyHardLinkBackup/compare/v0.1.8...v0.1.9)
649
+ * 2016-01-08 - Helper files for Windows:
650
+ * 2016-01-08 - Update setup.py
651
+ * 2016-01-08 - Update README.creole
652
+ * [v0.1.8](https://github.com/jedie/PyHardLinkBackup/compare/v0.1.7...v0.1.8)
653
+ * 2016-01-08 - django reloaded will not work under windows with venv
654
+ * 2016-01-08 - create more batch helper files
655
+ * 2016-01-08 - work-a-round for path problem under windows.
656
+ * [v0.1.7](https://github.com/jedie/PyHardLinkBackup/compare/v0.1.6...v0.1.7)
657
+ * 2016-01-08 - bugfix line endings in config file
658
+ * [v0.1.6](https://github.com/jedie/PyHardLinkBackup/compare/v0.1.5...v0.1.6)
659
+ * 2016-01-08 - remove checks: doesn't work with py3 venv
660
+ * [v0.1.5](https://github.com/jedie/PyHardLinkBackup/compare/v0.1.4...v0.1.5)
661
+ * 2016-01-08 - add "pip upgrade" batch
662
+ * 2016-01-08 - bugfix to include the default config ini file ;)
663
+ * [v0.1.4](https://github.com/jedie/PyHardLinkBackup/compare/v0.1.3...v0.1.4)
664
+ * 2016-01-07 - update windows boot file
665
+ * 2016-01-07 - include .ini files
666
+ * 2016-01-07 - rename
667
+ * [v0.1.3](https://github.com/jedie/PyHardLinkBackup/compare/v0.1.2...v0.1.3)
668
+ * 2016-01-07 - remove 'scandir' from 'install_requires'
669
+ * [v0.1.2](https://github.com/jedie/PyHardLinkBackup/compare/v0.1.1...v0.1.2)
670
+ * 2016-01-07 - install and use "scandir" only if needed
671
+ * [v0.1.1](https://github.com/jedie/PyHardLinkBackup/compare/629bc4f...v0.1.1)
672
+ * 2016-01-07 - fix headline levels (for reSt)
673
+ * 2016-01-07 - start boot script for windows
674
+ * 2016-01-07 - v0.1.1
675
+ * 2016-01-07 - refactory .ini config usage
676
+ * 2016-01-07 - WIP: Use .ini file for base config
677
+ * 2016-01-07 - Update README.creole
678
+ * 2016-01-06 - move database to ~/PyHardLinkBackups.sqlite3
679
+ * 2016-01-06 - PyHardlinkBackup -> PyHardLinkBackup
680
+ * 2016-01-06 - +.idea
681
+ * 2016-01-06 - Refactor:
682
+ * 2016-01-06 - add a auto login to django admin with auto created default user
683
+ * 2016-01-06 - nicer output
684
+ * 2016-01-06 - Use database to deduplicate
685
+ * 2016-01-06 - add human time
686
+ * 2016-01-06 - add dev reset script
687
+ * 2016-01-06 - force DJANGO_SETTINGS_MODULE
688
+ * 2016-01-06 - Display shortend hash and add filesize
689
+ * 2016-01-04 - refactor django stuff
690
+ * 2016-01-04 - add some meta files
691
+ * 2016-01-04 - start using django
692
+ * 2016-01-03 - use https://pypi.python.org/pypi/scandir as fallback
693
+ * 2015-12-29 - Create README.creole
694
+ * 2015-12-29 - Create proof_of_concept.py
695
+ * 2015-12-29 - Initial commit
696
+
697
+ </details>
698
+
699
+
700
+ [comment]: <> (✂✂✂ auto generated history end ✂✂✂)