mirror of
https://github.com/restic/restic.git
synced 2026-02-23 01:06:23 +00:00
Compare commits
287 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e40191942d | ||
|
|
abd34ab76f | ||
|
|
4b43a269ee | ||
|
|
e2b7dc6528 | ||
|
|
d2431b667f | ||
|
|
b70fdf61c4 | ||
|
|
e6f25c4811 | ||
|
|
adb682bc43 | ||
|
|
1e9744c9a4 | ||
|
|
9a02f17cc2 | ||
|
|
c284712cae | ||
|
|
2dbdf381b2 | ||
|
|
a1a49ce211 | ||
|
|
3252e4200c | ||
|
|
8d9d218d1c | ||
|
|
0785fbd418 | ||
|
|
b358dd369b | ||
|
|
d67b9a32c6 | ||
|
|
ecfe59235e | ||
|
|
a868a30f4d | ||
|
|
347a645450 | ||
|
|
9f5565b0fc | ||
|
|
fd979ab4c5 | ||
|
|
375868edcf | ||
|
|
060d8b57e0 | ||
|
|
cc627e832b | ||
|
|
5a0f0e3faa | ||
|
|
b52f2aa9a4 | ||
|
|
60ea2435be | ||
|
|
159badf5ba | ||
|
|
903a3a31dc | ||
|
|
548227e6df | ||
|
|
cd03275005 | ||
|
|
e43c9202a6 | ||
|
|
c5e75d1c98 | ||
|
|
526956af35 | ||
|
|
256104111d | ||
|
|
21c83b1725 | ||
|
|
581c62ee72 | ||
|
|
ef7747313d | ||
|
|
18d4ac2fd9 | ||
|
|
9180e2c48a | ||
|
|
a63989afcd | ||
|
|
d3c0bd6d0e | ||
|
|
fcfa6f0355 | ||
|
|
580f90d745 | ||
|
|
c7b624ba0d | ||
|
|
ca4af43c03 | ||
|
|
1f2463f42e | ||
|
|
157c854d04 | ||
|
|
ffc276a603 | ||
|
|
e42b7db008 | ||
|
|
024148cac9 | ||
|
|
8943741a0b | ||
|
|
95c5517c35 | ||
|
|
06179a7e81 | ||
|
|
cf1fb50f9c | ||
|
|
6793300850 | ||
|
|
2cbdfbf652 | ||
|
|
b2208bb9c2 | ||
|
|
4c25495d68 | ||
|
|
abdd59ea1b | ||
|
|
05ca903d48 | ||
|
|
fd77646f8b | ||
|
|
2a67258867 | ||
|
|
fca4fe4459 | ||
|
|
26757ae2e5 | ||
|
|
9d6890a236 | ||
|
|
2218ecd049 | ||
|
|
d0974c155d | ||
|
|
8026e6fdfb | ||
|
|
01f9662614 | ||
|
|
f928aeec34 | ||
|
|
f77bc0fae8 | ||
|
|
eb6650b201 | ||
|
|
bc68d55e94 | ||
|
|
ecbbd851a1 | ||
|
|
336719b058 | ||
|
|
e9f1721678 | ||
|
|
64d98945a6 | ||
|
|
84f82dae1a | ||
|
|
6bfd9f833b | ||
|
|
bb1a22d1e6 | ||
|
|
438719f269 | ||
|
|
c83c03ed63 | ||
|
|
19b9c881ca | ||
|
|
4e34325035 | ||
|
|
78bd591c7c | ||
|
|
39ac12f6ea | ||
|
|
400730afca | ||
|
|
d80e108b03 | ||
|
|
846c2b6869 | ||
|
|
d8bbe5dc84 | ||
|
|
d926b9fd80 | ||
|
|
4ba8d40282 | ||
|
|
4fb1401266 | ||
|
|
6d4c40f8d0 | ||
|
|
56e394ac33 | ||
|
|
c3cc5d7cee | ||
|
|
6b12b92339 | ||
|
|
16c314ab7f | ||
|
|
1449d7dc29 | ||
|
|
0e78ac92d8 | ||
|
|
c703d21d55 | ||
|
|
1af96fc6dd | ||
|
|
9fac2ca832 | ||
|
|
a5c0cf2324 | ||
|
|
38926d8576 | ||
|
|
f279731168 | ||
|
|
76b616451f | ||
|
|
fd12a3af20 | ||
|
|
3cd92efdcf | ||
|
|
b804279fe8 | ||
|
|
a56b8fad87 | ||
|
|
4c00efd4bf | ||
|
|
b6f98bdb02 | ||
|
|
c4b2486b7c | ||
|
|
83ca08245b | ||
|
|
a069467e72 | ||
|
|
6a7c23d2ae | ||
|
|
cc847a3d6d | ||
|
|
baebf45e2e | ||
|
|
fa4f438bc1 | ||
|
|
4e0b2a8e3a | ||
|
|
0532f08048 | ||
|
|
a472868e06 | ||
|
|
e4fdc5eb76 | ||
|
|
09365cc4ea | ||
|
|
2aa6b49651 | ||
|
|
7877797c7e | ||
|
|
1a26355dbe | ||
|
|
c5829e9ffc | ||
|
|
b5b246edd5 | ||
|
|
ee5e14d536 | ||
|
|
09bd924710 | ||
|
|
a9c2e84ccd | ||
|
|
a144b81c4a | ||
|
|
3c453a4217 | ||
|
|
1e2f23d77a | ||
|
|
2c76e724ab | ||
|
|
577faa7570 | ||
|
|
6a34e0d10f | ||
|
|
b08f21cdc6 | ||
|
|
1c1fede399 | ||
|
|
63a0913e6e | ||
|
|
325957443e | ||
|
|
4b2d3b15a2 | ||
|
|
4e2a87c920 | ||
|
|
901e1b129c | ||
|
|
4478d633e2 | ||
|
|
92f516b1d4 | ||
|
|
03193e6d92 | ||
|
|
01fe719aff | ||
|
|
2c964df3e2 | ||
|
|
8919125b0b | ||
|
|
1f5137aa70 | ||
|
|
a95eb33616 | ||
|
|
e68a7fea8a | ||
|
|
2e7ec717c1 | ||
|
|
22d5061df2 | ||
|
|
4544a77172 | ||
|
|
b3a073e066 | ||
|
|
b077a1227b | ||
|
|
3f48e0e0f4 | ||
|
|
86f4b03730 | ||
|
|
c43c94776b | ||
|
|
0b776e63e7 | ||
|
|
360ff1806a | ||
|
|
1beeb7d0dd | ||
|
|
e978b36713 | ||
|
|
737d93860a | ||
|
|
011217e4bf | ||
|
|
362d5afec4 | ||
|
|
4172fcd167 | ||
|
|
518bf4e5f6 | ||
|
|
17312d3a98 | ||
|
|
4d5c7a8749 | ||
|
|
fc0295016a | ||
|
|
99b62c11b8 | ||
|
|
6d9a029e09 | ||
|
|
20352886f3 | ||
|
|
3622b60c13 | ||
|
|
065fe1e54f | ||
|
|
4dc0f24b38 | ||
|
|
fe99340e40 | ||
|
|
e377759c81 | ||
|
|
61f6db25f4 | ||
|
|
cabbbd2b14 | ||
|
|
cf4cf94418 | ||
|
|
34f27edc03 | ||
|
|
345b6c4694 | ||
|
|
e4a39e02d2 | ||
|
|
432e167255 | ||
|
|
594256bfa4 | ||
|
|
0fcb1e6b7a | ||
|
|
38795c66c9 | ||
|
|
c0960f538f | ||
|
|
5b6568875c | ||
|
|
d8dd79eb0b | ||
|
|
2bdeb645b9 | ||
|
|
9f2ffa3e50 | ||
|
|
d4bab5c133 | ||
|
|
3473d73d0c | ||
|
|
917cc542c9 | ||
|
|
a9cf5d482a | ||
|
|
75946e7c58 | ||
|
|
19035e977b | ||
|
|
d9ba9279e0 | ||
|
|
31e156c666 | ||
|
|
7e6fff324c | ||
|
|
e94d2da890 | ||
|
|
874b3dbbd9 | ||
|
|
0d01c27c9e | ||
|
|
30110fcfc2 | ||
|
|
673f0bbd6c | ||
|
|
5a77b2ab49 | ||
|
|
a951e7b126 | ||
|
|
d3f9c8b362 | ||
|
|
a4ff591165 | ||
|
|
49dd70c771 | ||
|
|
a64f24029b | ||
|
|
0886738d24 | ||
|
|
9fc38803e0 | ||
|
|
e5c929b793 | ||
|
|
0e0fee9c8f | ||
|
|
26769a39eb | ||
|
|
923be90906 | ||
|
|
84a22eac92 | ||
|
|
6eb1be0be4 | ||
|
|
f31bbcf1a9 | ||
|
|
5d09fca6a2 | ||
|
|
34671d7c9b | ||
|
|
4a524da736 | ||
|
|
e361cc3807 | ||
|
|
3cd8a7bc96 | ||
|
|
8206f85d2e | ||
|
|
7022144e0f | ||
|
|
1bee3e01fa | ||
|
|
624a2d8305 | ||
|
|
57c6233982 | ||
|
|
c161aba084 | ||
|
|
0279fd7212 | ||
|
|
dedf17f5e8 | ||
|
|
817890794d | ||
|
|
b9ada91054 | ||
|
|
dfb6d0fced | ||
|
|
c6c1dccc53 | ||
|
|
279566bafe | ||
|
|
c67a8452f7 | ||
|
|
5253ef218c | ||
|
|
0923976909 | ||
|
|
492baf991f | ||
|
|
0dfdc11ed9 | ||
|
|
54c6837ec4 | ||
|
|
e085713b35 | ||
|
|
e77d8c64a7 | ||
|
|
a410fa16a1 | ||
|
|
b3e1089cf9 | ||
|
|
7f8e269891 | ||
|
|
fcc9ce81ba | ||
|
|
b9d643358a | ||
|
|
ab5ef600a2 | ||
|
|
04c4033695 | ||
|
|
de37b68baa | ||
|
|
bdc206d440 | ||
|
|
efe2e792b3 | ||
|
|
6f3c23eba7 | ||
|
|
4b34bc3210 | ||
|
|
6ed9100aa1 | ||
|
|
c63b02d0f1 | ||
|
|
d0205ec889 | ||
|
|
d8dcbc89d1 | ||
|
|
be0a5b7f06 | ||
|
|
24ce08e122 | ||
|
|
864eaeab7c | ||
|
|
96311d1a2b | ||
|
|
da77f4a2e2 | ||
|
|
6bb1bcce03 | ||
|
|
6edf28d1e1 | ||
|
|
929afc63d5 | ||
|
|
99f7fd74e3 | ||
|
|
58306bfabb | ||
|
|
f6890210aa | ||
|
|
5873ab4031 | ||
|
|
ab7a3a803d | ||
|
|
1e868933c5 | ||
|
|
21f67a0a13 |
2
.github/PULL_REQUEST_TEMPLATE.md
vendored
2
.github/PULL_REQUEST_TEMPLATE.md
vendored
@@ -25,7 +25,7 @@ Link issues and relevant forum posts here.
|
||||
- [ ] I have read the [Contribution Guidelines](https://github.com/restic/restic/blob/master/CONTRIBUTING.md#providing-patches)
|
||||
- [ ] I have added tests for all changes in this PR
|
||||
- [ ] I have added documentation for the changes (in the manual)
|
||||
- [ ] There's a new file in a subdir of `changelog/x.y.z` that describe the changes for our users (template [here](https://github.com/restic/restic/blob/master/changelog/changelog-entry.tmpl))
|
||||
- [ ] There's a new file in `changelog/unreleased/` that describes the changes for our users (template [here](https://github.com/restic/restic/blob/master/changelog/TEMPLATE))
|
||||
- [ ] I have run `gofmt` on the code in all commits
|
||||
- [ ] All commit messages are formatted in the same style as [the other commits in the repo](https://github.com/restic/restic/blob/master/CONTRIBUTING.md#git-commits)
|
||||
- [ ] I'm done, this Pull Request is ready for review
|
||||
|
||||
33
.travis.yml
33
.travis.yml
@@ -1,33 +1,20 @@
|
||||
language: go
|
||||
sudo: false
|
||||
|
||||
go:
|
||||
- "1.8.x"
|
||||
- "1.9.x"
|
||||
- "1.10"
|
||||
|
||||
os:
|
||||
- linux
|
||||
- osx
|
||||
|
||||
env:
|
||||
matrix:
|
||||
RESTIC_TEST_FUSE=0
|
||||
|
||||
matrix:
|
||||
exclude:
|
||||
- os: osx
|
||||
go: "1.8.x"
|
||||
- os: osx
|
||||
go: "1.9.x"
|
||||
- os: linux
|
||||
go: "1.10"
|
||||
include:
|
||||
- os: linux
|
||||
go: "1.10"
|
||||
go: "1.9.x"
|
||||
env: RESTIC_TEST_FUSE=0 RESTIC_TEST_CLOUD_BACKENDS=0
|
||||
|
||||
# only run fuse and cloud backends tests on Travis for the latest Go on Linux
|
||||
- os: linux
|
||||
go: "1.10.x"
|
||||
sudo: true
|
||||
env:
|
||||
RESTIC_TEST_FUSE=1
|
||||
|
||||
- os: osx
|
||||
go: "1.10.x"
|
||||
env: RESTIC_TEST_FUSE=0 RESTIC_TEST_CLOUD_BACKENDS=0
|
||||
|
||||
branches:
|
||||
only:
|
||||
|
||||
273
CHANGELOG.md
273
CHANGELOG.md
@@ -1,3 +1,265 @@
|
||||
Changelog for restic 0.9.0 (2018-05-21)
|
||||
=======================================
|
||||
|
||||
The following sections list the changes in restic 0.9.0 relevant to
|
||||
restic users. The changes are ordered by importance.
|
||||
|
||||
Summary
|
||||
-------
|
||||
|
||||
* Fix #1608: Respect time stamp for new backup when reading from stdin
|
||||
* Fix #1652: Ignore/remove invalid lock files
|
||||
* Fix #1730: Ignore sockets for restore
|
||||
* Fix #1684: Fix backend tests for rest-server
|
||||
* Fix #1745: Correctly parse the argument to --tls-client-cert
|
||||
* Enh #1433: Support UTF-16 encoding and process Byte Order Mark
|
||||
* Enh #1561: Allow using rclone to access other services
|
||||
* Enh #1665: Improve cache handling for `restic check`
|
||||
* Enh #1721: Add `cache` command to list cache dirs
|
||||
* Enh #1758: Allow saving OneDrive folders in Windows
|
||||
* Enh #549: Rework archiver code
|
||||
* Enh #1552: Use Google Application Default credentials
|
||||
* Enh #1477: Accept AWS_SESSION_TOKEN for the s3 backend
|
||||
* Enh #1648: Ignore AWS permission denied error when creating a repository
|
||||
* Enh #1649: Add illumos/Solaris support
|
||||
* Enh #1676: Improve backup speed: Skip initial scan phase in quiet mode
|
||||
* Enh #1709: Improve messages `restic check` prints
|
||||
* Enh #827: Add --new-password-file flag for non-interactive password changes
|
||||
* Enh #1735: Allow keeping a time range of snaphots
|
||||
* Enh #1782: Use default AWS credentials chain for S3 backend
|
||||
|
||||
Details
|
||||
-------
|
||||
|
||||
* Bugfix #1608: Respect time stamp for new backup when reading from stdin
|
||||
|
||||
When reading backups from stdin (via `restic backup --stdin`), restic now uses the time stamp
|
||||
for the new backup passed in `--time`.
|
||||
|
||||
https://github.com/restic/restic/issues/1608
|
||||
https://github.com/restic/restic/pull/1703
|
||||
|
||||
* Bugfix #1652: Ignore/remove invalid lock files
|
||||
|
||||
This corrects a bug introduced recently: When an invalid lock file in the repo is encountered
|
||||
(e.g. if the file is empty), the code used to ignore that, but now returns the error. Now, invalid
|
||||
files are ignored for the normal lock check, and removed when `restic unlock --remove-all` is
|
||||
run.
|
||||
|
||||
https://github.com/restic/restic/issues/1652
|
||||
https://github.com/restic/restic/pull/1653
|
||||
|
||||
* Bugfix #1730: Ignore sockets for restore
|
||||
|
||||
We've received a report and correct the behavior in which the restore code aborted restoring a
|
||||
directory when a socket was encountered. Unix domain socket files cannot be restored (they are
|
||||
created on the fly once a process starts listening). The error handling was corrected, and in
|
||||
addition we're now ignoring sockets during restore.
|
||||
|
||||
https://github.com/restic/restic/issues/1730
|
||||
https://github.com/restic/restic/pull/1731
|
||||
|
||||
* Bugfix #1684: Fix backend tests for rest-server
|
||||
|
||||
The REST server for restic now requires an explicit parameter (`--no-auth`) if no
|
||||
authentication should be allowed. This is fixed in the tests.
|
||||
|
||||
https://github.com/restic/restic/pull/1684
|
||||
|
||||
* Bugfix #1745: Correctly parse the argument to --tls-client-cert
|
||||
|
||||
Previously, the --tls-client-cert method attempt to read ARGV[1] (hardcoded) instead of the
|
||||
argument that was passed to it. This has been corrected.
|
||||
|
||||
https://github.com/restic/restic/issues/1745
|
||||
https://github.com/restic/restic/pull/1746
|
||||
|
||||
* Enhancement #1433: Support UTF-16 encoding and process Byte Order Mark
|
||||
|
||||
On Windows, text editors commonly leave a Byte Order Mark at the beginning of the file to define
|
||||
which encoding is used (oftentimes UTF-16). We've added code to support processing the BOMs in
|
||||
text files, like the exclude files, the password file and the file passed via `--files-from`.
|
||||
This does not apply to any file being saved in a backup, those are not touched and archived as they
|
||||
are.
|
||||
|
||||
https://github.com/restic/restic/issues/1433
|
||||
https://github.com/restic/restic/issues/1738
|
||||
https://github.com/restic/restic/pull/1748
|
||||
|
||||
* Enhancement #1561: Allow using rclone to access other services
|
||||
|
||||
We've added the ability to use rclone to store backup data on all backends that it supports. This
|
||||
was done in collaboration with Nick, the author of rclone. You can now use it to first configure a
|
||||
service, then restic manages the rest (starting and stopping rclone). For details, please see
|
||||
the manual.
|
||||
|
||||
https://github.com/restic/restic/issues/1561
|
||||
https://github.com/restic/restic/pull/1657
|
||||
https://rclone.org
|
||||
|
||||
* Enhancement #1665: Improve cache handling for `restic check`
|
||||
|
||||
For safety reasons, restic does not use a local metadata cache for the `restic check` command,
|
||||
so that data is loaded from the repository and restic can check it's in good condition. When the
|
||||
cache is disabled, restic will fetch each tiny blob needed for checking the integrity using a
|
||||
separate backend request. For non-local backends, that will take a long time, and depending on
|
||||
the backend (e.g. B2) may also be much more expensive.
|
||||
|
||||
This PR adds a few commits which will change the behavior as follows:
|
||||
|
||||
* When `restic check` is called without any additional parameters, it will build a new cache in a
|
||||
temporary directory, which is removed at the end of the check. This way, we'll get readahead for
|
||||
metadata files (so restic will fetch the whole file when the first blob from the file is
|
||||
requested), but all data is freshly fetched from the storage backend. This is the default
|
||||
behavior and will work for almost all users.
|
||||
|
||||
* When `restic check` is called with `--with-cache`, the default on-disc cache is used. This
|
||||
behavior hasn't changed since the cache was introduced.
|
||||
|
||||
* When `--no-cache` is specified, restic falls back to the old behavior, and read all tiny blobs
|
||||
in separate requests.
|
||||
|
||||
https://github.com/restic/restic/issues/1665
|
||||
https://github.com/restic/restic/issues/1694
|
||||
https://github.com/restic/restic/pull/1696
|
||||
|
||||
* Enhancement #1721: Add `cache` command to list cache dirs
|
||||
|
||||
The command `cache` was added, it allows listing restic's cache directoriers together with
|
||||
the last usage. It also allows removing old cache dirs without having to access a repo, via
|
||||
`restic cache --cleanup`
|
||||
|
||||
https://github.com/restic/restic/issues/1721
|
||||
https://github.com/restic/restic/pull/1749
|
||||
|
||||
* Enhancement #1758: Allow saving OneDrive folders in Windows
|
||||
|
||||
Restic now contains a bugfix to two libraries, which allows saving OneDrive folders in
|
||||
Windows. In order to use the newer versions of the libraries, the minimal version required to
|
||||
compile restic is now Go 1.9.
|
||||
|
||||
https://github.com/restic/restic/issues/1758
|
||||
https://github.com/restic/restic/pull/1765
|
||||
|
||||
* Enhancement #549: Rework archiver code
|
||||
|
||||
The core archiver code and the complementary code for the `backup` command was rewritten
|
||||
completely. This resolves very annoying issues such as 549. The first backup with this release
|
||||
of restic will likely result in all files being re-read locally, so it will take a lot longer. The
|
||||
next backup after that will be fast again.
|
||||
|
||||
Basically, with the old code, restic took the last path component of each to-be-saved file or
|
||||
directory as the top-level file/directory within the snapshot. This meant that when called as
|
||||
`restic backup /home/user/foo`, the snapshot would contain the files in the directory
|
||||
`/home/user/foo` as `/foo`.
|
||||
|
||||
This is not the case any more with the new archiver code. Now, restic works very similar to what
|
||||
`tar` does: When restic is called with an absolute path to save, then it'll preserve the
|
||||
directory structure within the snapshot. For the example above, the snapshot would contain
|
||||
the files in the directory within `/home/user/foo` in the snapshot. For relative
|
||||
directories, it only preserves the relative path components. So `restic backup user/foo`
|
||||
will save the files as `/user/foo` in the snapshot.
|
||||
|
||||
While we were at it, the status display and notification system was completely rewritten. By
|
||||
default, restic now shows which files are currently read (unless `--quiet` is specified) in a
|
||||
multi-line status display.
|
||||
|
||||
The `backup` command also gained a new option: `--verbose`. It can be specified once (which
|
||||
prints a bit more detail what restic is doing) or twice (which prints a line for each
|
||||
file/directory restic encountered, together with some statistics).
|
||||
|
||||
Another issue that was resolved is the new code only reads two files at most. The old code would
|
||||
read way too many files in parallel, thereby slowing down the backup process on spinning discs a
|
||||
lot.
|
||||
|
||||
https://github.com/restic/restic/issues/549
|
||||
https://github.com/restic/restic/issues/1286
|
||||
https://github.com/restic/restic/issues/446
|
||||
https://github.com/restic/restic/issues/1344
|
||||
https://github.com/restic/restic/issues/1416
|
||||
https://github.com/restic/restic/issues/1456
|
||||
https://github.com/restic/restic/issues/1145
|
||||
https://github.com/restic/restic/issues/1160
|
||||
https://github.com/restic/restic/pull/1494
|
||||
|
||||
* Enhancement #1552: Use Google Application Default credentials
|
||||
|
||||
Google provide libraries to generate appropriate credentials with various fallback
|
||||
sources. This change uses the library to generate our GCS client, which allows us to make use of
|
||||
these extra methods.
|
||||
|
||||
This should be backward compatible with previous restic behaviour while adding the
|
||||
additional capabilities to auth from Google's internal metadata endpoints. For users
|
||||
running restic in GCP this can make authentication far easier than it was before.
|
||||
|
||||
https://github.com/restic/restic/pull/1552
|
||||
https://developers.google.com/identity/protocols/application-default-credentials
|
||||
|
||||
* Enhancement #1477: Accept AWS_SESSION_TOKEN for the s3 backend
|
||||
|
||||
Before, it was not possible to use s3 backend with AWS temporary security credentials(with
|
||||
AWS_SESSION_TOKEN). This change gives higher priority to credentials.EnvAWS credentials
|
||||
provider.
|
||||
|
||||
https://github.com/restic/restic/issues/1477
|
||||
https://github.com/restic/restic/pull/1479
|
||||
https://github.com/restic/restic/pull/1647
|
||||
|
||||
* Enhancement #1648: Ignore AWS permission denied error when creating a repository
|
||||
|
||||
It's not possible to use s3 backend scoped to a subdirectory(with specific permissions).
|
||||
Restic doesn't try to create repository in a subdirectory, when 'bucket exists' of parent
|
||||
directory check fails due to permission issues.
|
||||
|
||||
https://github.com/restic/restic/pull/1648
|
||||
|
||||
* Enhancement #1649: Add illumos/Solaris support
|
||||
|
||||
https://github.com/restic/restic/pull/1649
|
||||
|
||||
* Enhancement #1676: Improve backup speed: Skip initial scan phase in quiet mode
|
||||
|
||||
We've improved the backup speed when the quiet flag (`-q` or `--quiet`) is set by skipping the
|
||||
initial scan which gathers information for displaying the progress bar and the ETA
|
||||
estimation.
|
||||
|
||||
https://github.com/restic/restic/issues/1160
|
||||
https://github.com/restic/restic/pull/1676
|
||||
|
||||
* Enhancement #1709: Improve messages `restic check` prints
|
||||
|
||||
Some messages `restic check` prints are not really errors, so from now on restic does not treat
|
||||
them as errors any more and exits cleanly.
|
||||
|
||||
https://github.com/restic/restic/pull/1709
|
||||
https://forum.restic.net/t/what-is-the-standard-procedure-to-follow-if-a-backup-or-restore-is-interrupted/571/2
|
||||
|
||||
* Enhancement #827: Add --new-password-file flag for non-interactive password changes
|
||||
|
||||
This makes it possible to change a repository password without being prompted.
|
||||
|
||||
https://github.com/restic/restic/issues/827
|
||||
https://github.com/restic/restic/pull/1720
|
||||
https://forum.restic.net/t/changing-repo-password-without-prompt/591
|
||||
|
||||
* Enhancement #1735: Allow keeping a time range of snaphots
|
||||
|
||||
We've added the `--keep-within` option to the `forget` command. It instructs restic to keep
|
||||
all snapshots within the given duration since the newest snapshot. For example, running
|
||||
`restic forget --keep-within 5m7d` will keep all snapshots which have been made in the five
|
||||
months and seven days since the latest snapshot.
|
||||
|
||||
https://github.com/restic/restic/pull/1735
|
||||
|
||||
* Enhancement #1782: Use default AWS credentials chain for S3 backend
|
||||
|
||||
Adds support for file credentials to the S3 backend (e.g. ~/.aws/credentials), and reorders
|
||||
the credentials chain for the S3 backend to match AWS's standard, which is static credentials,
|
||||
env vars, credentials file, and finally remote.
|
||||
|
||||
https://github.com/restic/restic/pull/1782
|
||||
|
||||
|
||||
Changelog for restic 0.8.3 (2018-02-26)
|
||||
=======================================
|
||||
|
||||
@@ -34,6 +296,7 @@ Details
|
||||
|
||||
https://github.com/restic/restic/issues/1641
|
||||
https://github.com/restic/restic/pull/1643
|
||||
https://forum.restic.net/t/help-fixing-repo-no-such-file/485/3
|
||||
|
||||
* Bugfix #1638: Handle errors listing files in the backend
|
||||
|
||||
@@ -48,6 +311,7 @@ Details
|
||||
operation on the backend should that fail. It is now corrected.
|
||||
|
||||
https://github.com/restic/restic/pull/1638
|
||||
https://forum.restic.net/t/restic-backup-returns-0-exit-code-when-already-locked/484
|
||||
|
||||
* Enhancement #1497: Add --read-data-subset flag to check command
|
||||
|
||||
@@ -155,6 +419,7 @@ Details
|
||||
of data loss, just minor inconvenience for our users.
|
||||
|
||||
https://github.com/restic/restic/pull/1589
|
||||
https://forum.restic.net/t/error-loading-tree-check-prune-and-forget-gives-error-b2-backend/406
|
||||
|
||||
* Bugfix #1594: Google Cloud Storage: Use generic HTTP transport
|
||||
|
||||
@@ -693,7 +958,7 @@ Details
|
||||
* Enhancement #1203: Print stats on all BSD systems when SIGINFO (ctrl+t) is received
|
||||
|
||||
https://github.com/restic/restic/pull/1203
|
||||
https://github.com/restic/restic/pull/1082
|
||||
https://github.com/restic/restic/pull/1082#issuecomment-326279920
|
||||
|
||||
* Enhancement #1205: Allow specifying time/date for a backup with `--time`
|
||||
|
||||
@@ -733,12 +998,12 @@ Details
|
||||
* Enhancement #1055: Create subdirs below `data/` for local/sftp backends
|
||||
|
||||
The local and sftp backends now create the subdirs below `data/` on open/init. This way, restic
|
||||
makes sure that they always exist. This is connected to an issue for the sftp server:
|
||||
makes sure that they always exist. This is connected to an issue for the sftp server.
|
||||
|
||||
https://github.com/restic/restic/issues/1055
|
||||
https://github.com/restic/rest-server/pull/11#issuecomment-309879710
|
||||
https://github.com/restic/restic/pull/1077
|
||||
https://github.com/restic/restic/pull/1105
|
||||
https://github.com/restic/rest-server/pull/11#issuecomment-309879710
|
||||
|
||||
* Enhancement #1067: Allow loading credentials for s3 from IAM
|
||||
|
||||
@@ -750,7 +1015,7 @@ Details
|
||||
|
||||
* Enhancement #1073: Add `migrate` cmd to migrate from `s3legacy` to `default` layout
|
||||
|
||||
The `migrate` command for chaning the `s3legacy` layout to the `default` layout for s3
|
||||
The `migrate` command for changing the `s3legacy` layout to the `default` layout for s3
|
||||
backends has been improved: It can now be restarted with `restic migrate --force s3_layout`
|
||||
and automatically retries operations on error.
|
||||
|
||||
|
||||
119
Gopkg.lock
generated
119
Gopkg.lock
generated
@@ -10,38 +10,38 @@
|
||||
[[projects]]
|
||||
name = "cloud.google.com/go"
|
||||
packages = ["compute/metadata"]
|
||||
revision = "767c40d6a2e058483c25fa193e963a22da17236d"
|
||||
version = "v0.18.0"
|
||||
revision = "4b98a6370e36d7a85192e7bad08a4ebd82eac2a8"
|
||||
version = "v0.20.0"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/Azure/azure-sdk-for-go"
|
||||
packages = ["storage"]
|
||||
revision = "eae258195456be76b2ec9ad2ee2ab63cdda365d9"
|
||||
version = "v12.2.0-beta"
|
||||
packages = ["storage","version"]
|
||||
revision = "56332fec5b308fbb6615fa1af6117394cdba186d"
|
||||
version = "v15.0.0"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/Azure/go-autorest"
|
||||
packages = ["autorest","autorest/adal","autorest/azure","autorest/date"]
|
||||
revision = "c2a68353555b68de3ee8455a4fd3e890a0ac6d99"
|
||||
version = "v9.8.1"
|
||||
revision = "ed4b7f5bf1ec0c9ede1fda2681d96771282f2862"
|
||||
version = "v10.4.0"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/cenkalti/backoff"
|
||||
packages = ["."]
|
||||
revision = "61153c768f31ee5f130071d08fc82b85208528de"
|
||||
version = "v1.1.0"
|
||||
revision = "2ea60e5f094469f9e65adb9cd103795b73ae743e"
|
||||
version = "v2.0.0"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/cpuguy83/go-md2man"
|
||||
packages = ["md2man"]
|
||||
revision = "1d903dcb749992f3741d744c0f8376b4bd7eb3e1"
|
||||
version = "v1.0.7"
|
||||
revision = "20f5889cbdc3c73dbd2862796665e7c465ade7d1"
|
||||
version = "v1.0.8"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/dgrijalva/jwt-go"
|
||||
packages = ["."]
|
||||
revision = "dbeaa9332f19a944acb5736b4456cfcc02140e29"
|
||||
version = "v3.1.0"
|
||||
revision = "06ea1031745cb8b3dab3f6a236daf2b0aa468b7e"
|
||||
version = "v3.2.0"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
@@ -52,20 +52,26 @@
|
||||
[[projects]]
|
||||
name = "github.com/elithrar/simple-scrypt"
|
||||
packages = ["."]
|
||||
revision = "2325946f714c95de4a6088202c402fbdfa64163b"
|
||||
version = "v1.2.0"
|
||||
revision = "d150773194090feb6c897805a7bcea8d49544e2c"
|
||||
version = "v1.3.0"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/go-ini/ini"
|
||||
packages = ["."]
|
||||
revision = "32e4c1e6bc4e7d0d8451aa6b75200d19e37a536a"
|
||||
version = "v1.32.0"
|
||||
revision = "6333e38ac20b8949a8dd68baa3650f4dee8f39f0"
|
||||
version = "v1.33.0"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/golang/protobuf"
|
||||
packages = ["proto"]
|
||||
revision = "c65a0412e71e8b9b3bfd22925720d23c0f054237"
|
||||
revision = "925541529c1fa6821df4e44ce2723319eb2be768"
|
||||
version = "v1.0.0"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/google/go-cmp"
|
||||
packages = ["cmp","cmp/internal/diff","cmp/internal/function","cmp/internal/value"]
|
||||
revision = "8099a9787ce5dc5984ed879a3bda47dc730a8e97"
|
||||
version = "v0.1.0"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/inconshreveable/mousetrap"
|
||||
@@ -87,20 +93,27 @@
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/kurin/blazer"
|
||||
packages = ["b2","base","internal/b2types","internal/blog"]
|
||||
revision = "cd0304efa98725679cf68422cefa328d3d96f2f4"
|
||||
version = "v0.3.0"
|
||||
packages = ["b2","base","internal/b2assets","internal/b2types","internal/blog","x/window"]
|
||||
revision = "b7c9cf27cae3aec98c2caaeb5181608bfe05b17c"
|
||||
version = "v0.3.1"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/marstr/guid"
|
||||
packages = ["."]
|
||||
revision = "8bdf7d1a087ccc975cf37dd6507da50698fd19ca"
|
||||
revision = "8bd9a64bf37eb297b492a4101fb28e80ac0b290f"
|
||||
version = "v1.1.0"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/mattn/go-isatty"
|
||||
packages = ["."]
|
||||
revision = "0360b2af4f38e8d38c7fce2a9f4e702702d73a39"
|
||||
version = "v0.0.3"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/minio/minio-go"
|
||||
packages = [".","pkg/credentials","pkg/encrypt","pkg/policy","pkg/s3signer","pkg/s3utils","pkg/set"]
|
||||
revision = "14f1d472d115bac5ca4804094aa87484a72ced61"
|
||||
version = "4.0.6"
|
||||
revision = "66252c2a3c15f7b90cc8493d497a04ac3b6e3606"
|
||||
version = "5.0.0"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
@@ -112,7 +125,7 @@
|
||||
branch = "master"
|
||||
name = "github.com/ncw/swift"
|
||||
packages = ["."]
|
||||
revision = "ae9f0ea1605b9aa6434ed5c731ca35d83ba67c55"
|
||||
revision = "b2a7479cf26fa841ff90dd932d0221cb5c50782d"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/pkg/errors"
|
||||
@@ -129,14 +142,14 @@
|
||||
[[projects]]
|
||||
name = "github.com/pkg/sftp"
|
||||
packages = ["."]
|
||||
revision = "f6a9258a0f570c3a76681b897b6ded57cb0dfa88"
|
||||
version = "1.2.0"
|
||||
revision = "49488377fa2f14143ba3067cf7555f60f6c7b550"
|
||||
version = "1.5.0"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/pkg/xattr"
|
||||
packages = ["."]
|
||||
revision = "23c75e3f6c1d8b13b3dd905b011a7f38a06044b7"
|
||||
version = "v0.2.1"
|
||||
revision = "1d7b7ffe7c46974a836eb583b7452f22de1c18cf"
|
||||
version = "v0.2.3"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/restic/chunker"
|
||||
@@ -147,8 +160,8 @@
|
||||
[[projects]]
|
||||
name = "github.com/russross/blackfriday"
|
||||
packages = ["."]
|
||||
revision = "4048872b16cc0fc2c5fd9eacf0ed2c2fedaa0c8c"
|
||||
version = "v1.5"
|
||||
revision = "55d61fa8aa702f59229e6cff85793c22e580eaf5"
|
||||
version = "v1.5.1"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/satori/go.uuid"
|
||||
@@ -159,14 +172,14 @@
|
||||
[[projects]]
|
||||
name = "github.com/sirupsen/logrus"
|
||||
packages = ["."]
|
||||
revision = "d682213848ed68c0a260ca37d6dd5ace8423f5ba"
|
||||
version = "v1.0.4"
|
||||
revision = "c155da19408a8799da419ed3eeb0cb5db0ad5dbc"
|
||||
version = "v1.0.5"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/spf13/cobra"
|
||||
packages = [".","doc"]
|
||||
revision = "7b2c5ac9fc04fc5efafb60700713d4fa609b777b"
|
||||
version = "v0.0.1"
|
||||
revision = "a1f051bc3eba734da4772d60e2d677f47cf93ef4"
|
||||
version = "v0.0.2"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/spf13/pflag"
|
||||
@@ -177,44 +190,44 @@
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "golang.org/x/crypto"
|
||||
packages = ["curve25519","ed25519","ed25519/internal/edwards25519","internal/chacha20","pbkdf2","poly1305","scrypt","ssh","ssh/terminal"]
|
||||
revision = "3d37316aaa6bd9929127ac9a527abf408178ea7b"
|
||||
packages = ["argon2","blake2b","curve25519","ed25519","ed25519/internal/edwards25519","internal/chacha20","pbkdf2","poly1305","scrypt","ssh","ssh/terminal"]
|
||||
revision = "4ec37c66abab2c7e02ae775328b2ff001c3f025a"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "golang.org/x/net"
|
||||
packages = ["context","context/ctxhttp","idna","lex/httplex"]
|
||||
revision = "5ccada7d0a7ba9aeb5d3aca8d3501b4c2a509fec"
|
||||
packages = ["context","context/ctxhttp","http2","http2/hpack","idna","lex/httplex"]
|
||||
revision = "6078986fec03a1dcc236c34816c71b0e05018fda"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "golang.org/x/oauth2"
|
||||
packages = [".","google","internal","jws","jwt"]
|
||||
revision = "b28fcf2b08a19742b43084fb40ab78ac6c3d8067"
|
||||
revision = "fdc9e635145ae97e6c2cb777c48305600cf515cb"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "golang.org/x/sync"
|
||||
packages = ["errgroup"]
|
||||
revision = "fd80eb99c8f653c847d294a001bdf2a3a6f768f5"
|
||||
revision = "1d60e4601c6fd243af51cc01ddf169918a5407ca"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "golang.org/x/sys"
|
||||
packages = ["unix","windows"]
|
||||
revision = "af50095a40f9041b3b38960738837185c26e9419"
|
||||
packages = ["cpu","unix","windows"]
|
||||
revision = "7db1c3b1a98089d0071c84f646ff5c96aad43682"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "golang.org/x/text"
|
||||
packages = ["collate","collate/build","internal/colltab","internal/gen","internal/tag","internal/triegen","internal/ucd","language","secure/bidirule","transform","unicode/bidi","unicode/cldr","unicode/norm","unicode/rangetable"]
|
||||
revision = "e19ae1496984b1c655b8044a65c0300a3c878dd3"
|
||||
packages = ["collate","collate/build","encoding","encoding/internal","encoding/internal/identifier","encoding/unicode","internal/colltab","internal/gen","internal/tag","internal/triegen","internal/ucd","internal/utf8internal","language","runes","secure/bidirule","transform","unicode/bidi","unicode/cldr","unicode/norm","unicode/rangetable"]
|
||||
revision = "f21a4dfb5e38f5895301dc265a8def02365cc3d0"
|
||||
version = "v0.3.0"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "google.golang.org/api"
|
||||
packages = ["gensupport","googleapi","googleapi/internal/uritemplates","storage/v1"]
|
||||
revision = "65b0d8655182691ad23b4fac11e6f7b897d9b634"
|
||||
revision = "dbbc13f71100fa6ece308335445fca6bb0dd5c2f"
|
||||
|
||||
[[projects]]
|
||||
name = "google.golang.org/appengine"
|
||||
@@ -224,13 +237,19 @@
|
||||
|
||||
[[projects]]
|
||||
branch = "v2"
|
||||
name = "gopkg.in/tomb.v2"
|
||||
packages = ["."]
|
||||
revision = "d5d1b5820637886def9eef33e03a27a9f166942c"
|
||||
|
||||
[[projects]]
|
||||
name = "gopkg.in/yaml.v2"
|
||||
packages = ["."]
|
||||
revision = "d670f9405373e636a5a2765eea47fac0c9bc91a4"
|
||||
revision = "5420a8b6744d3b0345ab293f6fcba19c978f1183"
|
||||
version = "v2.2.1"
|
||||
|
||||
[solve-meta]
|
||||
analyzer-name = "dep"
|
||||
analyzer-version = 1
|
||||
inputs-digest = "336ac5c261c174cac89f9a7102b493f08edfbd51fd61d1673d1d2ec4132d80ab"
|
||||
inputs-digest = "a5de339cba7570216b212439b90e1e6c384c94be8342fe7755b7cb66aa0a3440"
|
||||
solver-name = "gps-cdcl"
|
||||
solver-version = 1
|
||||
|
||||
@@ -29,7 +29,7 @@ and add some data:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ restic -r /tmp/backup backup ~/work
|
||||
$ restic --repo /tmp/backup backup ~/work
|
||||
enter password for repository:
|
||||
scan [/home/user/work]
|
||||
scanned 764 directories, 1816 files in 0:00
|
||||
@@ -57,6 +57,7 @@ Therefore, restic supports the following backends for storing backups natively:
|
||||
- `BackBlaze B2 <https://restic.readthedocs.io/en/latest/030_preparing_a_new_repo.html#backblaze-b2>`__
|
||||
- `Microsoft Azure Blob Storage <https://restic.readthedocs.io/en/latest/030_preparing_a_new_repo.html#microsoft-azure-blob-storage>`__
|
||||
- `Google Cloud Storage <https://restic.readthedocs.io/en/latest/030_preparing_a_new_repo.html#google-cloud-storage>`__
|
||||
- And many other services via the `rclone <https://rclone.org>`__ `Backend <https://restic.readthedocs.io/en/latest/030_preparing_a_new_repo.html#other-services-via-rclone>`__
|
||||
|
||||
Design Principles
|
||||
-----------------
|
||||
@@ -112,8 +113,8 @@ complete text in ``LICENSE``.
|
||||
|
||||
.. |Documentation| image:: https://readthedocs.org/projects/restic/badge/?version=latest
|
||||
:target: https://restic.readthedocs.io/en/latest/?badge=latest
|
||||
.. |Build Status| image:: https://travis-ci.org/restic/restic.svg?branch=master
|
||||
:target: https://travis-ci.org/restic/restic
|
||||
.. |Build Status| image:: https://travis-ci.com/restic/restic.svg?branch=master
|
||||
:target: https://travis-ci.com/restic/restic
|
||||
.. |Build status| image:: https://ci.appveyor.com/api/projects/status/nuy4lfbgfbytw92q/branch/master?svg=true
|
||||
:target: https://ci.appveyor.com/project/fd0/restic/branch/master
|
||||
.. |Report Card| image:: https://goreportcard.com/badge/github.com/restic/restic
|
||||
|
||||
82
build.go
82
build.go
@@ -53,7 +53,7 @@ var config = Config{
|
||||
"github.com/restic/restic/internal/...",
|
||||
"github.com/restic/restic/cmd/...",
|
||||
},
|
||||
MinVersion: GoVersion{Major: 1, Minor: 8, Patch: 0}, // minimum Go version supported
|
||||
MinVersion: GoVersion{Major: 1, Minor: 9, Patch: 0}, // minimum Go version supported
|
||||
}
|
||||
|
||||
// Config configures the build.
|
||||
@@ -70,6 +70,7 @@ var (
|
||||
keepGopath bool
|
||||
runTests bool
|
||||
enableCGO bool
|
||||
enablePIE bool
|
||||
)
|
||||
|
||||
// specialDir returns true if the file begins with a special character ('.' or '_').
|
||||
@@ -225,9 +226,10 @@ func showUsage(output io.Writer) {
|
||||
fmt.Fprintf(output, " -T --test run tests\n")
|
||||
fmt.Fprintf(output, " -o --output set output file name\n")
|
||||
fmt.Fprintf(output, " --enable-cgo use CGO to link against libc\n")
|
||||
fmt.Fprintf(output, " --enable-pie use PIE buildmode\n")
|
||||
fmt.Fprintf(output, " --goos value set GOOS for cross-compilation\n")
|
||||
fmt.Fprintf(output, " --goarch value set GOARCH for cross-compilation\n")
|
||||
fmt.Fprintf(output, " --goarm value set GOARM for cross-compilation\n")
|
||||
fmt.Fprintf(output, " --goarm value set GOARM for cross-compilation\n")
|
||||
}
|
||||
|
||||
func verbosePrintf(message string, args ...interface{}) {
|
||||
@@ -253,10 +255,22 @@ func cleanEnv() (env []string) {
|
||||
}
|
||||
|
||||
// build runs "go build args..." with GOPATH set to gopath.
|
||||
func build(cwd, goos, goarch, goarm, gopath string, args ...string) error {
|
||||
func build(cwd string, ver GoVersion, goos, goarch, goarm, gopath string, args ...string) error {
|
||||
a := []string{"build"}
|
||||
a = append(a, "-asmflags", fmt.Sprintf("-trimpath=%s", gopath))
|
||||
a = append(a, "-gcflags", fmt.Sprintf("-trimpath=%s", gopath))
|
||||
|
||||
if ver.AtLeast(GoVersion{1, 10, 0}) {
|
||||
verbosePrintf("Go version is at least 1.10, using new syntax for -gcflags\n")
|
||||
// use new prefix
|
||||
a = append(a, "-asmflags", fmt.Sprintf("all=-trimpath=%s", gopath))
|
||||
a = append(a, "-gcflags", fmt.Sprintf("all=-trimpath=%s", gopath))
|
||||
} else {
|
||||
a = append(a, "-asmflags", fmt.Sprintf("-trimpath=%s", gopath))
|
||||
a = append(a, "-gcflags", fmt.Sprintf("-trimpath=%s", gopath))
|
||||
}
|
||||
if enablePIE {
|
||||
a = append(a, "-buildmode=pie")
|
||||
}
|
||||
|
||||
a = append(a, args...)
|
||||
cmd := exec.Command("go", a...)
|
||||
cmd.Env = append(cleanEnv(), "GOPATH="+gopath, "GOARCH="+goarch, "GOOS="+goos)
|
||||
@@ -270,7 +284,7 @@ func build(cwd, goos, goarch, goarm, gopath string, args ...string) error {
|
||||
cmd.Dir = cwd
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
verbosePrintf("go %s\n", args)
|
||||
verbosePrintf("go %s\n", a)
|
||||
|
||||
return cmd.Run()
|
||||
}
|
||||
@@ -366,30 +380,39 @@ func ParseGoVersion(s string) (v GoVersion) {
|
||||
|
||||
s = s[2:]
|
||||
data := strings.Split(s, ".")
|
||||
if len(data) != 3 {
|
||||
return
|
||||
if len(data) < 2 || len(data) > 3 {
|
||||
// invalid version
|
||||
return GoVersion{}
|
||||
}
|
||||
|
||||
major, err := strconv.Atoi(data[0])
|
||||
var err error
|
||||
|
||||
v.Major, err = strconv.Atoi(data[0])
|
||||
if err != nil {
|
||||
return
|
||||
return GoVersion{}
|
||||
}
|
||||
|
||||
minor, err := strconv.Atoi(data[1])
|
||||
if err != nil {
|
||||
return
|
||||
// try to parse the minor version while removing an eventual suffix (like
|
||||
// "rc2" or so)
|
||||
for s := data[1]; s != ""; s = s[:len(s)-1] {
|
||||
v.Minor, err = strconv.Atoi(s)
|
||||
if err == nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
patch, err := strconv.Atoi(data[2])
|
||||
if err != nil {
|
||||
return
|
||||
if v.Minor == 0 {
|
||||
// no minor version found
|
||||
return GoVersion{}
|
||||
}
|
||||
|
||||
v = GoVersion{
|
||||
Major: major,
|
||||
Minor: minor,
|
||||
Patch: patch,
|
||||
if len(data) >= 3 {
|
||||
v.Patch, err = strconv.Atoi(data[2])
|
||||
if err != nil {
|
||||
return GoVersion{}
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@@ -437,6 +460,8 @@ func main() {
|
||||
targetGOARCH := runtime.GOARCH
|
||||
targetGOARM := ""
|
||||
|
||||
gopath := ""
|
||||
|
||||
var outputFilename string
|
||||
|
||||
for i, arg := range params {
|
||||
@@ -459,10 +484,15 @@ func main() {
|
||||
case "-o", "--output":
|
||||
skipNext = true
|
||||
outputFilename = params[i+1]
|
||||
case "--tempdir":
|
||||
skipNext = true
|
||||
gopath = params[i+1]
|
||||
case "-T", "--test":
|
||||
runTests = true
|
||||
case "--enable-cgo":
|
||||
enableCGO = true
|
||||
case "--enable-pie":
|
||||
enablePIE = true
|
||||
case "--goos":
|
||||
skipNext = true
|
||||
targetGOOS = params[i+1]
|
||||
@@ -482,6 +512,8 @@ func main() {
|
||||
}
|
||||
}
|
||||
|
||||
verbosePrintf("detected Go version %v\n", ver)
|
||||
|
||||
if len(buildTags) == 0 {
|
||||
verbosePrintf("adding build-tag release\n")
|
||||
buildTags = []string{"release"}
|
||||
@@ -498,9 +530,11 @@ func main() {
|
||||
die("Getwd(): %v\n", err)
|
||||
}
|
||||
|
||||
gopath, err := ioutil.TempDir("", fmt.Sprintf("%v-build-", config.Name))
|
||||
if err != nil {
|
||||
die("TempDir(): %v\n", err)
|
||||
if gopath == "" {
|
||||
gopath, err = ioutil.TempDir("", fmt.Sprintf("%v-build-", config.Name))
|
||||
if err != nil {
|
||||
die("TempDir(): %v\n", err)
|
||||
}
|
||||
}
|
||||
|
||||
verbosePrintf("create GOPATH at %v\n", gopath)
|
||||
@@ -556,7 +590,7 @@ func main() {
|
||||
"-o", output, config.Main,
|
||||
}
|
||||
|
||||
err = build(filepath.Join(gopath, "src"), targetGOOS, targetGOARCH, targetGOARM, gopath, args...)
|
||||
err = build(filepath.Join(gopath, "src"), ver, targetGOOS, targetGOARCH, targetGOARM, gopath, args...)
|
||||
if err != nil {
|
||||
die("build failed: %v\n", err)
|
||||
}
|
||||
|
||||
@@ -2,9 +2,9 @@ Enhancement: Create subdirs below `data/` for local/sftp backends
|
||||
|
||||
The local and sftp backends now create the subdirs below `data/` on
|
||||
open/init. This way, restic makes sure that they always exist. This is
|
||||
connected to an issue for the sftp server:
|
||||
connected to an issue for the sftp server.
|
||||
|
||||
https://github.com/restic/rest-server/pull/11#issuecomment-309879710
|
||||
https://github.com/restic/restic/issues/1055
|
||||
https://github.com/restic/rest-server/pull/11#issuecomment-309879710
|
||||
https://github.com/restic/restic/pull/1077
|
||||
https://github.com/restic/restic/pull/1105
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
Enhancement: Add `migrate` cmd to migrate from `s3legacy` to `default` layout
|
||||
|
||||
The `migrate` command for chaning the `s3legacy` layout to the `default` layout
|
||||
The `migrate` command for changing the `s3legacy` layout to the `default` layout
|
||||
for s3 backends has been improved: It can now be restarted with `restic migrate
|
||||
--force s3_layout` and automatically retries operations on error.
|
||||
|
||||
|
||||
12
changelog/0.9.0_2018-05-21/issue-1433
Normal file
12
changelog/0.9.0_2018-05-21/issue-1433
Normal file
@@ -0,0 +1,12 @@
|
||||
Enhancement: Support UTF-16 encoding and process Byte Order Mark
|
||||
|
||||
On Windows, text editors commonly leave a Byte Order Mark at the beginning of
|
||||
the file to define which encoding is used (oftentimes UTF-16). We've added code
|
||||
to support processing the BOMs in text files, like the exclude files, the
|
||||
password file and the file passed via `--files-from`. This does not apply to
|
||||
any file being saved in a backup, those are not touched and archived as they
|
||||
are.
|
||||
|
||||
https://github.com/restic/restic/issues/1433
|
||||
https://github.com/restic/restic/issues/1738
|
||||
https://github.com/restic/restic/pull/1748
|
||||
10
changelog/0.9.0_2018-05-21/issue-1561
Normal file
10
changelog/0.9.0_2018-05-21/issue-1561
Normal file
@@ -0,0 +1,10 @@
|
||||
Enhancement: Allow using rclone to access other services
|
||||
|
||||
We've added the ability to use rclone to store backup data on all backends that
|
||||
it supports. This was done in collaboration with Nick, the author of rclone.
|
||||
You can now use it to first configure a service, then restic manages the rest
|
||||
(starting and stopping rclone). For details, please see the manual.
|
||||
|
||||
https://github.com/restic/restic/issues/1561
|
||||
https://github.com/restic/restic/pull/1657
|
||||
https://rclone.org
|
||||
7
changelog/0.9.0_2018-05-21/issue-1608
Normal file
7
changelog/0.9.0_2018-05-21/issue-1608
Normal file
@@ -0,0 +1,7 @@
|
||||
Bugfix: Respect time stamp for new backup when reading from stdin
|
||||
|
||||
When reading backups from stdin (via `restic backup --stdin`), restic now uses
|
||||
the time stamp for the new backup passed in `--time`.
|
||||
|
||||
https://github.com/restic/restic/issues/1608
|
||||
https://github.com/restic/restic/pull/1703
|
||||
9
changelog/0.9.0_2018-05-21/issue-1652
Normal file
9
changelog/0.9.0_2018-05-21/issue-1652
Normal file
@@ -0,0 +1,9 @@
|
||||
Bugfix: Ignore/remove invalid lock files
|
||||
|
||||
This corrects a bug introduced recently: When an invalid lock file in the repo
|
||||
is encountered (e.g. if the file is empty), the code used to ignore that, but
|
||||
now returns the error. Now, invalid files are ignored for the normal lock
|
||||
check, and removed when `restic unlock --remove-all` is run.
|
||||
|
||||
https://github.com/restic/restic/issues/1652
|
||||
https://github.com/restic/restic/pull/1653
|
||||
27
changelog/0.9.0_2018-05-21/issue-1665
Normal file
27
changelog/0.9.0_2018-05-21/issue-1665
Normal file
@@ -0,0 +1,27 @@
|
||||
Enhancement: Improve cache handling for `restic check`
|
||||
|
||||
For safety reasons, restic does not use a local metadata cache for the `restic
|
||||
check` command, so that data is loaded from the repository and restic can check
|
||||
it's in good condition. When the cache is disabled, restic will fetch each tiny
|
||||
blob needed for checking the integrity using a separate backend request. For
|
||||
non-local backends, that will take a long time, and depending on the backend
|
||||
(e.g. B2) may also be much more expensive.
|
||||
|
||||
This PR adds a few commits which will change the behavior as follows:
|
||||
|
||||
* When `restic check` is called without any additional parameters, it will
|
||||
build a new cache in a temporary directory, which is removed at the end of
|
||||
the check. This way, we'll get readahead for metadata files (so restic will
|
||||
fetch the whole file when the first blob from the file is requested), but
|
||||
all data is freshly fetched from the storage backend. This is the default
|
||||
behavior and will work for almost all users.
|
||||
|
||||
* When `restic check` is called with `--with-cache`, the default on-disc cache
|
||||
is used. This behavior hasn't changed since the cache was introduced.
|
||||
|
||||
* When `--no-cache` is specified, restic falls back to the old behavior, and
|
||||
read all tiny blobs in separate requests.
|
||||
|
||||
https://github.com/restic/restic/issues/1665
|
||||
https://github.com/restic/restic/issues/1694
|
||||
https://github.com/restic/restic/pull/1696
|
||||
8
changelog/0.9.0_2018-05-21/issue-1721
Normal file
8
changelog/0.9.0_2018-05-21/issue-1721
Normal file
@@ -0,0 +1,8 @@
|
||||
Enhancement: Add `cache` command to list cache dirs
|
||||
|
||||
The command `cache` was added, it allows listing restic's cache directoriers
|
||||
together with the last usage. It also allows removing old cache dirs without
|
||||
having to access a repo, via `restic cache --cleanup`
|
||||
|
||||
https://github.com/restic/restic/issues/1721
|
||||
https://github.com/restic/restic/pull/1749
|
||||
11
changelog/0.9.0_2018-05-21/issue-1730
Normal file
11
changelog/0.9.0_2018-05-21/issue-1730
Normal file
@@ -0,0 +1,11 @@
|
||||
Bugfix: Ignore sockets for restore
|
||||
|
||||
We've received a report and correct the behavior in which the restore code
|
||||
aborted restoring a directory when a socket was encountered. Unix domain socket
|
||||
files cannot be restored (they are created on the fly once a process starts
|
||||
listening). The error handling was corrected, and in addition we're now
|
||||
ignoring sockets during restore.
|
||||
|
||||
https://github.com/restic/restic/issues/1730
|
||||
https://github.com/restic/restic/pull/1731
|
||||
|
||||
8
changelog/0.9.0_2018-05-21/issue-1758
Normal file
8
changelog/0.9.0_2018-05-21/issue-1758
Normal file
@@ -0,0 +1,8 @@
|
||||
Enhancement: Allow saving OneDrive folders in Windows
|
||||
|
||||
Restic now contains a bugfix to two libraries, which allows saving OneDrive
|
||||
folders in Windows. In order to use the newer versions of the libraries, the
|
||||
minimal version required to compile restic is now Go 1.9.
|
||||
|
||||
https://github.com/restic/restic/issues/1758
|
||||
https://github.com/restic/restic/pull/1765
|
||||
43
changelog/0.9.0_2018-05-21/issue-549
Normal file
43
changelog/0.9.0_2018-05-21/issue-549
Normal file
@@ -0,0 +1,43 @@
|
||||
Enhancement: Rework archiver code
|
||||
|
||||
The core archiver code and the complementary code for the `backup` command was
|
||||
rewritten completely. This resolves very annoying issues such as 549. The first
|
||||
backup with this release of restic will likely result in all files being
|
||||
re-read locally, so it will take a lot longer. The next backup after that will
|
||||
be fast again.
|
||||
|
||||
Basically, with the old code, restic took the last path component of each
|
||||
to-be-saved file or directory as the top-level file/directory within the
|
||||
snapshot. This meant that when called as `restic backup /home/user/foo`, the
|
||||
snapshot would contain the files in the directory `/home/user/foo` as `/foo`.
|
||||
|
||||
This is not the case any more with the new archiver code. Now, restic works
|
||||
very similar to what `tar` does: When restic is called with an absolute path to
|
||||
save, then it'll preserve the directory structure within the snapshot. For the
|
||||
example above, the snapshot would contain the files in the directory within
|
||||
`/home/user/foo` in the snapshot. For relative directories, it only preserves
|
||||
the relative path components. So `restic backup user/foo` will save the files
|
||||
as `/user/foo` in the snapshot.
|
||||
|
||||
While we were at it, the status display and notification system was completely
|
||||
rewritten. By default, restic now shows which files are currently read (unless
|
||||
`--quiet` is specified) in a multi-line status display.
|
||||
|
||||
The `backup` command also gained a new option: `--verbose`. It can be specified
|
||||
once (which prints a bit more detail what restic is doing) or twice (which
|
||||
prints a line for each file/directory restic encountered, together with some
|
||||
statistics).
|
||||
|
||||
Another issue that was resolved is the new code only reads two files at most.
|
||||
The old code would read way too many files in parallel, thereby slowing down
|
||||
the backup process on spinning discs a lot.
|
||||
|
||||
https://github.com/restic/restic/issues/549
|
||||
https://github.com/restic/restic/issues/1286
|
||||
https://github.com/restic/restic/issues/446
|
||||
https://github.com/restic/restic/issues/1344
|
||||
https://github.com/restic/restic/issues/1416
|
||||
https://github.com/restic/restic/issues/1456
|
||||
https://github.com/restic/restic/issues/1145
|
||||
https://github.com/restic/restic/issues/1160
|
||||
https://github.com/restic/restic/pull/1494
|
||||
13
changelog/0.9.0_2018-05-21/pull-1552
Normal file
13
changelog/0.9.0_2018-05-21/pull-1552
Normal file
@@ -0,0 +1,13 @@
|
||||
Enhancement: Use Google Application Default credentials
|
||||
|
||||
Google provide libraries to generate appropriate credentials with various
|
||||
fallback sources. This change uses the library to generate our GCS client, which
|
||||
allows us to make use of these extra methods.
|
||||
|
||||
This should be backward compatible with previous restic behaviour while adding
|
||||
the additional capabilities to auth from Google's internal metadata endpoints.
|
||||
For users running restic in GCP this can make authentication far easier than it
|
||||
was before.
|
||||
|
||||
https://github.com/restic/restic/pull/1552
|
||||
https://developers.google.com/identity/protocols/application-default-credentials
|
||||
9
changelog/0.9.0_2018-05-21/pull-1647
Normal file
9
changelog/0.9.0_2018-05-21/pull-1647
Normal file
@@ -0,0 +1,9 @@
|
||||
Enhancement: Accept AWS_SESSION_TOKEN for the s3 backend
|
||||
|
||||
Before, it was not possible to use s3 backend with AWS temporary security
|
||||
credentials(with AWS_SESSION_TOKEN). This change gives higher priority to
|
||||
credentials.EnvAWS credentials provider.
|
||||
|
||||
https://github.com/restic/restic/issues/1477
|
||||
https://github.com/restic/restic/pull/1479
|
||||
https://github.com/restic/restic/pull/1647
|
||||
6
changelog/0.9.0_2018-05-21/pull-1648
Normal file
6
changelog/0.9.0_2018-05-21/pull-1648
Normal file
@@ -0,0 +1,6 @@
|
||||
Enhancement: Ignore AWS permission denied error when creating a repository
|
||||
|
||||
It's not possible to use s3 backend scoped to a subdirectory(with specific permissions).
|
||||
Restic doesn't try to create repository in a subdirectory, when 'bucket exists' of parent directory check fails due to permission issues.
|
||||
|
||||
https://github.com/restic/restic/pull/1648
|
||||
3
changelog/0.9.0_2018-05-21/pull-1649
Normal file
3
changelog/0.9.0_2018-05-21/pull-1649
Normal file
@@ -0,0 +1,3 @@
|
||||
Enhancement: Add illumos/Solaris support
|
||||
|
||||
https://github.com/restic/restic/pull/1649
|
||||
8
changelog/0.9.0_2018-05-21/pull-1676
Normal file
8
changelog/0.9.0_2018-05-21/pull-1676
Normal file
@@ -0,0 +1,8 @@
|
||||
Enhancement: Improve backup speed: Skip initial scan phase in quiet mode
|
||||
|
||||
We've improved the backup speed when the quiet flag (`-q` or `--quiet`) is set
|
||||
by skipping the initial scan which gathers information for displaying the
|
||||
progress bar and the ETA estimation.
|
||||
|
||||
https://github.com/restic/restic/pull/1676
|
||||
https://github.com/restic/restic/issues/1160
|
||||
6
changelog/0.9.0_2018-05-21/pull-1684
Normal file
6
changelog/0.9.0_2018-05-21/pull-1684
Normal file
@@ -0,0 +1,6 @@
|
||||
Bugfix: Fix backend tests for rest-server
|
||||
|
||||
The REST server for restic now requires an explicit parameter (`--no-auth`) if
|
||||
no authentication should be allowed. This is fixed in the tests.
|
||||
|
||||
https://github.com/restic/restic/pull/1684
|
||||
7
changelog/0.9.0_2018-05-21/pull-1709
Normal file
7
changelog/0.9.0_2018-05-21/pull-1709
Normal file
@@ -0,0 +1,7 @@
|
||||
Enhancement: Improve messages `restic check` prints
|
||||
|
||||
Some messages `restic check` prints are not really errors, so from now on
|
||||
restic does not treat them as errors any more and exits cleanly.
|
||||
|
||||
https://github.com/restic/restic/pull/1709
|
||||
https://forum.restic.net/t/what-is-the-standard-procedure-to-follow-if-a-backup-or-restore-is-interrupted/571/2
|
||||
7
changelog/0.9.0_2018-05-21/pull-1720
Normal file
7
changelog/0.9.0_2018-05-21/pull-1720
Normal file
@@ -0,0 +1,7 @@
|
||||
Enhancement: Add --new-password-file flag for non-interactive password changes
|
||||
|
||||
This makes it possible to change a repository password without being prompted.
|
||||
|
||||
https://github.com/restic/restic/issues/827
|
||||
https://github.com/restic/restic/pull/1720
|
||||
https://forum.restic.net/t/changing-repo-password-without-prompt/591
|
||||
9
changelog/0.9.0_2018-05-21/pull-1735
Normal file
9
changelog/0.9.0_2018-05-21/pull-1735
Normal file
@@ -0,0 +1,9 @@
|
||||
Enhancement: Allow keeping a time range of snaphots
|
||||
|
||||
We've added the `--keep-within` option to the `forget` command. It instructs
|
||||
restic to keep all snapshots within the given duration since the newest
|
||||
snapshot. For example, running `restic forget --keep-within 5m7d` will keep all
|
||||
snapshots which have been made in the five months and seven days since the
|
||||
latest snapshot.
|
||||
|
||||
https://github.com/restic/restic/pull/1735
|
||||
7
changelog/0.9.0_2018-05-21/pull-1746
Normal file
7
changelog/0.9.0_2018-05-21/pull-1746
Normal file
@@ -0,0 +1,7 @@
|
||||
Bugfix: Correctly parse the argument to --tls-client-cert
|
||||
|
||||
Previously, the --tls-client-cert method attempt to read ARGV[1] (hardcoded)
|
||||
instead of the argument that was passed to it. This has been corrected.
|
||||
|
||||
https://github.com/restic/restic/issues/1745
|
||||
https://github.com/restic/restic/pull/1746
|
||||
7
changelog/0.9.0_2018-05-21/pull-1782
Normal file
7
changelog/0.9.0_2018-05-21/pull-1782
Normal file
@@ -0,0 +1,7 @@
|
||||
Enhancement: Use default AWS credentials chain for S3 backend
|
||||
|
||||
Adds support for file credentials to the S3 backend (e.g. ~/.aws/credentials),
|
||||
and reorders the credentials chain for the S3 backend to match AWS's standard,
|
||||
which is static credentials, env vars, credentials file, and finally remote.
|
||||
|
||||
https://github.com/restic/restic/pull/1782
|
||||
@@ -18,11 +18,11 @@ Details
|
||||
{{ range $par := .Paragraphs }}
|
||||
{{ wrap $par 80 3 }}
|
||||
{{ end -}}
|
||||
{{ range $id := .Issues }}
|
||||
https://github.com/restic/restic/issues/{{ $id -}}
|
||||
{{ range $url := .IssueURLs }}
|
||||
{{ $url -}}
|
||||
{{ end -}}
|
||||
{{ range $id := .PRs }}
|
||||
https://github.com/restic/restic/pull/{{ $id -}}
|
||||
{{ range $url := .PRURLs }}
|
||||
{{ $url -}}
|
||||
{{ end -}}
|
||||
{{ range $url := .OtherURLs }}
|
||||
{{ $url -}}
|
||||
|
||||
@@ -2,8 +2,7 @@
|
||||
Changelog for restic {{ .Version }} ({{ .Date }})
|
||||
=======================================
|
||||
|
||||
The following sections list the changes in restic {{ .Version }} relevant to
|
||||
restic users. The changes are ordered by importance.
|
||||
The following sections list the changes in restic {{ .Version }} relevant to restic users. The changes are ordered by importance.
|
||||
|
||||
Summary
|
||||
-------
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
// +build !linux
|
||||
|
||||
package main
|
||||
|
||||
// IsProcessBackground should return true if it is running in the background or false if not
|
||||
func IsProcessBackground() bool {
|
||||
//TODO: Check if the process are running in the background in other OS than linux
|
||||
return false
|
||||
}
|
||||
@@ -64,7 +64,10 @@ func CleanupHandler(c <-chan os.Signal) {
|
||||
fmt.Fprintf(stderr, "%ssignal %v received, cleaning up\n", ClearLine(), s)
|
||||
|
||||
code := 0
|
||||
if s != syscall.SIGINT {
|
||||
|
||||
if s == syscall.SIGINT {
|
||||
code = 130
|
||||
} else {
|
||||
code = 1
|
||||
}
|
||||
|
||||
|
||||
@@ -2,21 +2,26 @@ package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"io"
|
||||
"bytes"
|
||||
"context"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
tomb "gopkg.in/tomb.v2"
|
||||
|
||||
"github.com/restic/restic/internal/archiver"
|
||||
"github.com/restic/restic/internal/debug"
|
||||
"github.com/restic/restic/internal/errors"
|
||||
"github.com/restic/restic/internal/fs"
|
||||
"github.com/restic/restic/internal/repository"
|
||||
"github.com/restic/restic/internal/restic"
|
||||
"github.com/restic/restic/internal/textfile"
|
||||
"github.com/restic/restic/internal/ui"
|
||||
"github.com/restic/restic/internal/ui/termstatus"
|
||||
)
|
||||
|
||||
var cmdBackup = &cobra.Command{
|
||||
@@ -42,11 +47,16 @@ given as the arguments.
|
||||
return errors.Fatal("cannot use both `--stdin` and `--files-from -`")
|
||||
}
|
||||
|
||||
if backupOptions.Stdin {
|
||||
return readBackupFromStdin(backupOptions, globalOptions, args)
|
||||
}
|
||||
var t tomb.Tomb
|
||||
term := termstatus.New(globalOptions.stdout, globalOptions.stderr, globalOptions.Quiet)
|
||||
t.Go(func() error { term.Run(t.Context(globalOptions.ctx)); return nil })
|
||||
|
||||
return runBackup(backupOptions, globalOptions, args)
|
||||
err := runBackup(backupOptions, globalOptions, term, args)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
t.Kill(nil)
|
||||
return t.Wait()
|
||||
},
|
||||
}
|
||||
|
||||
@@ -90,127 +100,6 @@ func init() {
|
||||
f.BoolVar(&backupOptions.WithAtime, "with-atime", false, "store the atime for all files and directories")
|
||||
}
|
||||
|
||||
func newScanProgress(gopts GlobalOptions) *restic.Progress {
|
||||
if gopts.Quiet {
|
||||
return nil
|
||||
}
|
||||
|
||||
p := restic.NewProgress()
|
||||
p.OnUpdate = func(s restic.Stat, d time.Duration, ticker bool) {
|
||||
if IsProcessBackground() {
|
||||
return
|
||||
}
|
||||
|
||||
PrintProgress("[%s] %d directories, %d files, %s", formatDuration(d), s.Dirs, s.Files, formatBytes(s.Bytes))
|
||||
}
|
||||
|
||||
p.OnDone = func(s restic.Stat, d time.Duration, ticker bool) {
|
||||
PrintProgress("scanned %d directories, %d files in %s\n", s.Dirs, s.Files, formatDuration(d))
|
||||
}
|
||||
|
||||
return p
|
||||
}
|
||||
|
||||
func newArchiveProgress(gopts GlobalOptions, todo restic.Stat) *restic.Progress {
|
||||
if gopts.Quiet {
|
||||
return nil
|
||||
}
|
||||
|
||||
archiveProgress := restic.NewProgress()
|
||||
|
||||
var bps, eta uint64
|
||||
itemsTodo := todo.Files + todo.Dirs
|
||||
|
||||
archiveProgress.OnUpdate = func(s restic.Stat, d time.Duration, ticker bool) {
|
||||
if IsProcessBackground() {
|
||||
return
|
||||
}
|
||||
|
||||
sec := uint64(d / time.Second)
|
||||
if todo.Bytes > 0 && sec > 0 && ticker {
|
||||
bps = s.Bytes / sec
|
||||
if s.Bytes >= todo.Bytes {
|
||||
eta = 0
|
||||
} else if bps > 0 {
|
||||
eta = (todo.Bytes - s.Bytes) / bps
|
||||
}
|
||||
}
|
||||
|
||||
itemsDone := s.Files + s.Dirs
|
||||
|
||||
status1 := fmt.Sprintf("[%s] %s %s / %s %d / %d items %d errors ",
|
||||
formatDuration(d),
|
||||
formatPercent(s.Bytes, todo.Bytes),
|
||||
formatBytes(s.Bytes), formatBytes(todo.Bytes),
|
||||
itemsDone, itemsTodo,
|
||||
s.Errors)
|
||||
status2 := fmt.Sprintf("ETA %s ", formatSeconds(eta))
|
||||
|
||||
if w := stdoutTerminalWidth(); w > 0 {
|
||||
maxlen := w - len(status2) - 1
|
||||
|
||||
if maxlen < 4 {
|
||||
status1 = ""
|
||||
} else if len(status1) > maxlen {
|
||||
status1 = status1[:maxlen-4]
|
||||
status1 += "... "
|
||||
}
|
||||
}
|
||||
|
||||
PrintProgress("%s%s", status1, status2)
|
||||
}
|
||||
|
||||
archiveProgress.OnDone = func(s restic.Stat, d time.Duration, ticker bool) {
|
||||
fmt.Printf("\nduration: %s\n", formatDuration(d))
|
||||
}
|
||||
|
||||
return archiveProgress
|
||||
}
|
||||
|
||||
func newArchiveStdinProgress(gopts GlobalOptions) *restic.Progress {
|
||||
if gopts.Quiet {
|
||||
return nil
|
||||
}
|
||||
|
||||
archiveProgress := restic.NewProgress()
|
||||
|
||||
var bps uint64
|
||||
|
||||
archiveProgress.OnUpdate = func(s restic.Stat, d time.Duration, ticker bool) {
|
||||
if IsProcessBackground() {
|
||||
return
|
||||
}
|
||||
|
||||
sec := uint64(d / time.Second)
|
||||
if s.Bytes > 0 && sec > 0 && ticker {
|
||||
bps = s.Bytes / sec
|
||||
}
|
||||
|
||||
status1 := fmt.Sprintf("[%s] %s %s/s", formatDuration(d),
|
||||
formatBytes(s.Bytes),
|
||||
formatBytes(bps))
|
||||
|
||||
if w := stdoutTerminalWidth(); w > 0 {
|
||||
maxlen := w - len(status1)
|
||||
|
||||
if maxlen < 4 {
|
||||
status1 = ""
|
||||
} else if len(status1) > maxlen {
|
||||
status1 = status1[:maxlen-4]
|
||||
status1 += "... "
|
||||
}
|
||||
}
|
||||
|
||||
PrintProgress("%s", status1)
|
||||
}
|
||||
|
||||
archiveProgress.OnDone = func(s restic.Stat, d time.Duration, ticker bool) {
|
||||
fmt.Printf("\nduration: %s\n", formatDuration(d))
|
||||
}
|
||||
|
||||
return archiveProgress
|
||||
}
|
||||
|
||||
// filterExisting returns a slice of all existing items, or an error if no
|
||||
// items exist at all.
|
||||
func filterExisting(items []string) (result []string, err error) {
|
||||
@@ -231,78 +120,33 @@ func filterExisting(items []string) (result []string, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func readBackupFromStdin(opts BackupOptions, gopts GlobalOptions, args []string) error {
|
||||
if len(args) != 0 {
|
||||
return errors.Fatal("when reading from stdin, no additional files can be specified")
|
||||
}
|
||||
|
||||
fn := opts.StdinFilename
|
||||
|
||||
if fn == "" {
|
||||
return errors.Fatal("filename for backup from stdin must not be empty")
|
||||
}
|
||||
|
||||
if filepath.Base(fn) != fn || path.Base(fn) != fn {
|
||||
return errors.Fatal("filename is invalid (may not contain a directory, slash or backslash)")
|
||||
}
|
||||
|
||||
if gopts.password == "" {
|
||||
return errors.Fatal("unable to read password from stdin when data is to be read from stdin, use --password-file or $RESTIC_PASSWORD")
|
||||
}
|
||||
|
||||
repo, err := OpenRepository(gopts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
lock, err := lockRepo(repo)
|
||||
defer unlockRepo(lock)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = repo.LoadIndex(gopts.ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
r := &archiver.Reader{
|
||||
Repository: repo,
|
||||
Tags: opts.Tags,
|
||||
Hostname: opts.Hostname,
|
||||
}
|
||||
|
||||
_, id, err := r.Archive(gopts.ctx, fn, os.Stdin, newArchiveStdinProgress(gopts))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
Verbosef("archived as %v\n", id.Str())
|
||||
return nil
|
||||
}
|
||||
|
||||
// readFromFile will read all lines from the given filename and write them to a
|
||||
// string array, if filename is empty readFromFile returns and empty string
|
||||
// array. If filename is a dash (-), readFromFile will read the lines from
|
||||
// the standard input.
|
||||
// readFromFile will read all lines from the given filename and return them as
|
||||
// a string array, if filename is empty readFromFile returns and empty string
|
||||
// array. If filename is a dash (-), readFromFile will read the lines from the
|
||||
// standard input.
|
||||
func readLinesFromFile(filename string) ([]string, error) {
|
||||
if filename == "" {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
var r io.Reader = os.Stdin
|
||||
if filename != "-" {
|
||||
f, err := os.Open(filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer f.Close()
|
||||
r = f
|
||||
var (
|
||||
data []byte
|
||||
err error
|
||||
)
|
||||
|
||||
if filename == "-" {
|
||||
data, err = ioutil.ReadAll(os.Stdin)
|
||||
} else {
|
||||
data, err = textfile.Read(filename)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var lines []string
|
||||
|
||||
scanner := bufio.NewScanner(r)
|
||||
scanner := bufio.NewScanner(bytes.NewReader(data))
|
||||
for scanner.Scan() {
|
||||
line := strings.TrimSpace(scanner.Text())
|
||||
// ignore empty lines
|
||||
@@ -323,47 +167,45 @@ func readLinesFromFile(filename string) ([]string, error) {
|
||||
return lines, nil
|
||||
}
|
||||
|
||||
func runBackup(opts BackupOptions, gopts GlobalOptions, args []string) error {
|
||||
// Check returns an error when an invalid combination of options was set.
|
||||
func (opts BackupOptions) Check(gopts GlobalOptions, args []string) error {
|
||||
if opts.FilesFrom == "-" && gopts.password == "" {
|
||||
return errors.Fatal("unable to read password from stdin when data is to be read from stdin, use --password-file or $RESTIC_PASSWORD")
|
||||
}
|
||||
|
||||
fromfile, err := readLinesFromFile(opts.FilesFrom)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// merge files from files-from into normal args so we can reuse the normal
|
||||
// args checks and have the ability to use both files-from and args at the
|
||||
// same time
|
||||
args = append(args, fromfile...)
|
||||
if len(args) == 0 {
|
||||
return errors.Fatal("nothing to backup, please specify target files/dirs")
|
||||
}
|
||||
|
||||
target := make([]string, 0, len(args))
|
||||
for _, d := range args {
|
||||
if a, err := filepath.Abs(d); err == nil {
|
||||
d = a
|
||||
if opts.Stdin {
|
||||
if opts.FilesFrom != "" {
|
||||
return errors.Fatal("--stdin and --files-from cannot be used together")
|
||||
}
|
||||
|
||||
if len(args) > 0 {
|
||||
return errors.Fatal("--stdin was specified and files/dirs were listed as arguments")
|
||||
}
|
||||
target = append(target, d)
|
||||
}
|
||||
|
||||
target, err = filterExisting(target)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// rejectFuncs collect functions that can reject items from the backup
|
||||
var rejectFuncs []RejectFunc
|
||||
return nil
|
||||
}
|
||||
|
||||
// collectRejectFuncs returns a list of all functions which may reject data
|
||||
// from being saved in a snapshot
|
||||
func collectRejectFuncs(opts BackupOptions, repo *repository.Repository, targets []string) (fs []RejectFunc, err error) {
|
||||
// allowed devices
|
||||
if opts.ExcludeOtherFS {
|
||||
f, err := rejectByDevice(target)
|
||||
f, err := rejectByDevice(targets)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
rejectFuncs = append(rejectFuncs, f)
|
||||
fs = append(fs, f)
|
||||
}
|
||||
|
||||
// exclude restic cache
|
||||
if repo.Cache != nil {
|
||||
f, err := rejectResticCache(repo)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
fs = append(fs, f)
|
||||
}
|
||||
|
||||
// add patterns from file
|
||||
@@ -372,7 +214,7 @@ func runBackup(opts BackupOptions, gopts GlobalOptions, args []string) error {
|
||||
}
|
||||
|
||||
if len(opts.Excludes) > 0 {
|
||||
rejectFuncs = append(rejectFuncs, rejectByPattern(opts.Excludes))
|
||||
fs = append(fs, rejectByPattern(opts.Excludes))
|
||||
}
|
||||
|
||||
if opts.ExcludeCaches {
|
||||
@@ -382,124 +224,27 @@ func runBackup(opts BackupOptions, gopts GlobalOptions, args []string) error {
|
||||
for _, spec := range opts.ExcludeIfPresent {
|
||||
f, err := rejectIfPresent(spec)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rejectFuncs = append(rejectFuncs, f)
|
||||
fs = append(fs, f)
|
||||
}
|
||||
|
||||
repo, err := OpenRepository(gopts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
lock, err := lockRepo(repo)
|
||||
defer unlockRepo(lock)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// exclude restic cache
|
||||
if repo.Cache != nil {
|
||||
f, err := rejectResticCache(repo)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
rejectFuncs = append(rejectFuncs, f)
|
||||
}
|
||||
|
||||
err = repo.LoadIndex(gopts.ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var parentSnapshotID *restic.ID
|
||||
|
||||
// Force using a parent
|
||||
if !opts.Force && opts.Parent != "" {
|
||||
id, err := restic.FindSnapshot(repo, opts.Parent)
|
||||
if err != nil {
|
||||
return errors.Fatalf("invalid id %q: %v", opts.Parent, err)
|
||||
}
|
||||
|
||||
parentSnapshotID = &id
|
||||
}
|
||||
|
||||
// Find last snapshot to set it as parent, if not already set
|
||||
if !opts.Force && parentSnapshotID == nil {
|
||||
id, err := restic.FindLatestSnapshot(gopts.ctx, repo, target, []restic.TagList{}, opts.Hostname)
|
||||
if err == nil {
|
||||
parentSnapshotID = &id
|
||||
} else if err != restic.ErrNoSnapshotFound {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if parentSnapshotID != nil {
|
||||
Verbosef("using parent snapshot %v\n", parentSnapshotID.Str())
|
||||
}
|
||||
|
||||
Verbosef("scan %v\n", target)
|
||||
|
||||
selectFilter := func(item string, fi os.FileInfo) bool {
|
||||
for _, reject := range rejectFuncs {
|
||||
if reject(item, fi) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
stat, err := archiver.Scan(target, selectFilter, newScanProgress(gopts))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
arch := archiver.New(repo)
|
||||
arch.Excludes = opts.Excludes
|
||||
arch.SelectFilter = selectFilter
|
||||
arch.WithAccessTime = opts.WithAtime
|
||||
|
||||
arch.Warn = func(dir string, fi os.FileInfo, err error) {
|
||||
// TODO: make ignoring errors configurable
|
||||
Warnf("%s\rwarning for %s: %v\n", ClearLine(), dir, err)
|
||||
}
|
||||
|
||||
timeStamp := time.Now()
|
||||
if opts.TimeStamp != "" {
|
||||
timeStamp, err = time.Parse(TimeFormat, opts.TimeStamp)
|
||||
if err != nil {
|
||||
return errors.Fatalf("error in time option: %v\n", err)
|
||||
}
|
||||
}
|
||||
|
||||
_, id, err := arch.Snapshot(gopts.ctx, newArchiveProgress(gopts, stat), target, opts.Tags, opts.Hostname, parentSnapshotID, timeStamp)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
Verbosef("snapshot %s saved\n", id.Str())
|
||||
|
||||
return nil
|
||||
return fs, nil
|
||||
}
|
||||
|
||||
// readExcludePatternsFromFiles reads all exclude files and returns the list of
|
||||
// exclude patterns.
|
||||
func readExcludePatternsFromFiles(excludeFiles []string) []string {
|
||||
var excludes []string
|
||||
for _, filename := range excludeFiles {
|
||||
err := func() (err error) {
|
||||
file, err := fs.Open(filename)
|
||||
data, err := textfile.Read(filename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
// return pre-close error if there was one
|
||||
if errClose := file.Close(); err == nil {
|
||||
err = errClose
|
||||
}
|
||||
}()
|
||||
|
||||
scanner := bufio.NewScanner(file)
|
||||
scanner := bufio.NewScanner(bytes.NewReader(data))
|
||||
for scanner.Scan() {
|
||||
line := strings.TrimSpace(scanner.Text())
|
||||
|
||||
@@ -525,3 +270,217 @@ func readExcludePatternsFromFiles(excludeFiles []string) []string {
|
||||
}
|
||||
return excludes
|
||||
}
|
||||
|
||||
// collectTargets returns a list of target files/dirs from several sources.
|
||||
func collectTargets(opts BackupOptions, args []string) (targets []string, err error) {
|
||||
if opts.Stdin {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
fromfile, err := readLinesFromFile(opts.FilesFrom)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// merge files from files-from into normal args so we can reuse the normal
|
||||
// args checks and have the ability to use both files-from and args at the
|
||||
// same time
|
||||
args = append(args, fromfile...)
|
||||
if len(args) == 0 && !opts.Stdin {
|
||||
return nil, errors.Fatal("nothing to backup, please specify target files/dirs")
|
||||
}
|
||||
|
||||
targets = args
|
||||
targets, err = filterExisting(targets)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return targets, nil
|
||||
}
|
||||
|
||||
// parent returns the ID of the parent snapshot. If there is none, nil is
|
||||
// returned.
|
||||
func findParentSnapshot(ctx context.Context, repo restic.Repository, opts BackupOptions, targets []string) (parentID *restic.ID, err error) {
|
||||
// Force using a parent
|
||||
if !opts.Force && opts.Parent != "" {
|
||||
id, err := restic.FindSnapshot(repo, opts.Parent)
|
||||
if err != nil {
|
||||
return nil, errors.Fatalf("invalid id %q: %v", opts.Parent, err)
|
||||
}
|
||||
|
||||
parentID = &id
|
||||
}
|
||||
|
||||
// Find last snapshot to set it as parent, if not already set
|
||||
if !opts.Force && parentID == nil {
|
||||
id, err := restic.FindLatestSnapshot(ctx, repo, targets, []restic.TagList{}, opts.Hostname)
|
||||
if err == nil {
|
||||
parentID = &id
|
||||
} else if err != restic.ErrNoSnapshotFound {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return parentID, nil
|
||||
}
|
||||
|
||||
func runBackup(opts BackupOptions, gopts GlobalOptions, term *termstatus.Terminal, args []string) error {
|
||||
err := opts.Check(gopts, args)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
targets, err := collectTargets(opts, args)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var t tomb.Tomb
|
||||
|
||||
p := ui.NewBackup(term, gopts.verbosity)
|
||||
|
||||
// use the terminal for stdout/stderr
|
||||
prevStdout, prevStderr := gopts.stdout, gopts.stderr
|
||||
defer func() {
|
||||
gopts.stdout, gopts.stderr = prevStdout, prevStderr
|
||||
}()
|
||||
gopts.stdout, gopts.stderr = p.Stdout(), p.Stderr()
|
||||
|
||||
if s, ok := os.LookupEnv("RESTIC_PROGRESS_FPS"); ok {
|
||||
fps, err := strconv.Atoi(s)
|
||||
if err == nil && fps >= 1 {
|
||||
if fps > 60 {
|
||||
fps = 60
|
||||
}
|
||||
p.MinUpdatePause = time.Second / time.Duration(fps)
|
||||
}
|
||||
}
|
||||
|
||||
t.Go(func() error { return p.Run(t.Context(gopts.ctx)) })
|
||||
|
||||
p.V("open repository")
|
||||
repo, err := OpenRepository(gopts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
p.V("lock repository")
|
||||
lock, err := lockRepo(repo)
|
||||
defer unlockRepo(lock)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// rejectFuncs collect functions that can reject items from the backup
|
||||
rejectFuncs, err := collectRejectFuncs(opts, repo, targets)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
p.V("load index files")
|
||||
err = repo.LoadIndex(gopts.ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
parentSnapshotID, err := findParentSnapshot(gopts.ctx, repo, opts, targets)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if parentSnapshotID != nil {
|
||||
p.V("using parent snapshot %v\n", parentSnapshotID.Str())
|
||||
}
|
||||
|
||||
selectFilter := func(item string, fi os.FileInfo) bool {
|
||||
for _, reject := range rejectFuncs {
|
||||
if reject(item, fi) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
timeStamp := time.Now()
|
||||
if opts.TimeStamp != "" {
|
||||
timeStamp, err = time.Parse(TimeFormat, opts.TimeStamp)
|
||||
if err != nil {
|
||||
return errors.Fatalf("error in time option: %v\n", err)
|
||||
}
|
||||
}
|
||||
|
||||
var targetFS fs.FS = fs.Local{}
|
||||
if opts.Stdin {
|
||||
p.V("read data from stdin")
|
||||
targetFS = &fs.Reader{
|
||||
ModTime: timeStamp,
|
||||
Name: opts.StdinFilename,
|
||||
Mode: 0644,
|
||||
ReadCloser: os.Stdin,
|
||||
}
|
||||
targets = []string{opts.StdinFilename}
|
||||
}
|
||||
|
||||
sc := archiver.NewScanner(targetFS)
|
||||
sc.Select = selectFilter
|
||||
sc.Error = p.ScannerError
|
||||
sc.Result = p.ReportTotal
|
||||
|
||||
p.V("start scan on %v", targets)
|
||||
t.Go(func() error { return sc.Scan(t.Context(gopts.ctx), targets) })
|
||||
|
||||
arch := archiver.New(repo, targetFS, archiver.Options{})
|
||||
arch.Select = selectFilter
|
||||
arch.WithAtime = opts.WithAtime
|
||||
arch.Error = p.Error
|
||||
arch.CompleteItem = p.CompleteItemFn
|
||||
arch.StartFile = p.StartFile
|
||||
arch.CompleteBlob = p.CompleteBlob
|
||||
|
||||
if parentSnapshotID == nil {
|
||||
parentSnapshotID = &restic.ID{}
|
||||
}
|
||||
|
||||
snapshotOpts := archiver.SnapshotOptions{
|
||||
Excludes: opts.Excludes,
|
||||
Tags: opts.Tags,
|
||||
Time: timeStamp,
|
||||
Hostname: opts.Hostname,
|
||||
ParentSnapshot: *parentSnapshotID,
|
||||
}
|
||||
|
||||
uploader := archiver.IndexUploader{
|
||||
Repository: repo,
|
||||
Start: func() {
|
||||
p.VV("uploading intermediate index")
|
||||
},
|
||||
Complete: func(id restic.ID) {
|
||||
p.V("uploaded intermediate index %v", id.Str())
|
||||
},
|
||||
}
|
||||
|
||||
t.Go(func() error {
|
||||
return uploader.Upload(gopts.ctx, t.Context(gopts.ctx), 30*time.Second)
|
||||
})
|
||||
|
||||
p.V("start backup on %v", targets)
|
||||
_, id, err := arch.Snapshot(gopts.ctx, targets, snapshotOpts)
|
||||
if err != nil {
|
||||
return errors.Fatalf("unable to save snapshot: %v", err)
|
||||
}
|
||||
|
||||
p.Finish()
|
||||
p.P("snapshot %s saved\n", id.Str())
|
||||
|
||||
// cleanly shutdown all running goroutines
|
||||
t.Kill(nil)
|
||||
|
||||
// let's see if one returned an error
|
||||
err = t.Wait()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
122
cmd/restic/cmd_cache.go
Normal file
122
cmd/restic/cmd_cache.go
Normal file
@@ -0,0 +1,122 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"time"
|
||||
|
||||
"github.com/restic/restic/internal/cache"
|
||||
"github.com/restic/restic/internal/errors"
|
||||
"github.com/restic/restic/internal/fs"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var cmdCache = &cobra.Command{
|
||||
Use: "cache",
|
||||
Short: "Operate on local cache directories",
|
||||
Long: `
|
||||
The "cache" command allows listing and cleaning local cache directories.
|
||||
`,
|
||||
DisableAutoGenTag: true,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return runCache(cacheOptions, globalOptions, args)
|
||||
},
|
||||
}
|
||||
|
||||
// CacheOptions bundles all options for the snapshots command.
|
||||
type CacheOptions struct {
|
||||
Cleanup bool
|
||||
MaxAge uint
|
||||
}
|
||||
|
||||
var cacheOptions CacheOptions
|
||||
|
||||
func init() {
|
||||
cmdRoot.AddCommand(cmdCache)
|
||||
|
||||
f := cmdCache.Flags()
|
||||
f.BoolVar(&cacheOptions.Cleanup, "cleanup", false, "remove old cache directories")
|
||||
f.UintVar(&cacheOptions.MaxAge, "max-age", 30, "max age in `days` for cache directories to be considered old")
|
||||
}
|
||||
|
||||
func runCache(opts CacheOptions, gopts GlobalOptions, args []string) error {
|
||||
if len(args) > 0 {
|
||||
return errors.Fatal("the cache command has no arguments")
|
||||
}
|
||||
|
||||
if gopts.NoCache {
|
||||
return errors.Fatal("Refusing to do anything, the cache is disabled")
|
||||
}
|
||||
|
||||
var (
|
||||
cachedir = gopts.CacheDir
|
||||
err error
|
||||
)
|
||||
|
||||
if cachedir == "" {
|
||||
cachedir, err = cache.DefaultDir()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if opts.Cleanup || gopts.CleanupCache {
|
||||
oldDirs, err := cache.OlderThan(cachedir, time.Duration(opts.MaxAge)*24*time.Hour)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(oldDirs) == 0 {
|
||||
Verbosef("no old cache dirs found\n")
|
||||
return nil
|
||||
}
|
||||
|
||||
Verbosef("remove %d old cache directories\n", len(oldDirs))
|
||||
|
||||
for _, item := range oldDirs {
|
||||
dir := filepath.Join(cachedir, item.Name())
|
||||
err = fs.RemoveAll(dir)
|
||||
if err != nil {
|
||||
Warnf("unable to remove %v: %v\n", dir, err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
tab := NewTable()
|
||||
tab.Header = fmt.Sprintf("%-14s %-16s %s", "Repository ID", "Last Used", "Old")
|
||||
tab.RowFormat = "%-14s %-16s %s"
|
||||
|
||||
dirs, err := cache.All(cachedir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(dirs) == 0 {
|
||||
Printf("no cache dirs found, basedir is %v\n", cachedir)
|
||||
return nil
|
||||
}
|
||||
|
||||
sort.Slice(dirs, func(i, j int) bool {
|
||||
return dirs[i].ModTime().Before(dirs[j].ModTime())
|
||||
})
|
||||
|
||||
for _, entry := range dirs {
|
||||
var old string
|
||||
if cache.IsOld(entry.ModTime(), time.Duration(opts.MaxAge)*24*time.Hour) {
|
||||
old = "yes"
|
||||
}
|
||||
|
||||
tab.Rows = append(tab.Rows, []interface{}{
|
||||
entry.Name()[:10],
|
||||
fmt.Sprintf("%d days ago", uint(time.Since(entry.ModTime()).Hours()/24)),
|
||||
old,
|
||||
})
|
||||
}
|
||||
|
||||
tab.Write(gopts.stdout)
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -58,7 +58,7 @@ func runCat(gopts GlobalOptions, args []string) error {
|
||||
// find snapshot id with prefix
|
||||
id, err = restic.FindSnapshot(repo, args[1])
|
||||
if err != nil {
|
||||
return err
|
||||
return errors.Fatalf("could not find snapshot: %v\n", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -11,6 +12,7 @@ import (
|
||||
|
||||
"github.com/restic/restic/internal/checker"
|
||||
"github.com/restic/restic/internal/errors"
|
||||
"github.com/restic/restic/internal/fs"
|
||||
"github.com/restic/restic/internal/restic"
|
||||
)
|
||||
|
||||
@@ -117,15 +119,55 @@ func newReadProgress(gopts GlobalOptions, todo restic.Stat) *restic.Progress {
|
||||
return readProgress
|
||||
}
|
||||
|
||||
// prepareCheckCache configures a special cache directory for check.
|
||||
//
|
||||
// * if --with-cache is specified, the default cache is used
|
||||
// * if the user explicitly requested --no-cache, we don't use any cache
|
||||
// * by default, we use a cache in a temporary directory that is deleted after the check
|
||||
func prepareCheckCache(opts CheckOptions, gopts *GlobalOptions) (cleanup func()) {
|
||||
cleanup = func() {}
|
||||
if opts.WithCache {
|
||||
// use the default cache, no setup needed
|
||||
return cleanup
|
||||
}
|
||||
|
||||
if gopts.NoCache {
|
||||
// don't use any cache, no setup needed
|
||||
return cleanup
|
||||
}
|
||||
|
||||
// use a cache in a temporary directory
|
||||
tempdir, err := ioutil.TempDir("", "restic-check-cache-")
|
||||
if err != nil {
|
||||
// if an error occurs, don't use any cache
|
||||
Warnf("unable to create temporary directory for cache during check, disabling cache: %v\n", err)
|
||||
gopts.NoCache = true
|
||||
return cleanup
|
||||
}
|
||||
|
||||
gopts.CacheDir = tempdir
|
||||
Verbosef("using temporary cache in %v\n", tempdir)
|
||||
|
||||
cleanup = func() {
|
||||
err := fs.RemoveAll(tempdir)
|
||||
if err != nil {
|
||||
Warnf("error removing temporary cache directory: %v\n", err)
|
||||
}
|
||||
}
|
||||
|
||||
return cleanup
|
||||
}
|
||||
|
||||
func runCheck(opts CheckOptions, gopts GlobalOptions, args []string) error {
|
||||
if len(args) != 0 {
|
||||
return errors.Fatal("check has no arguments")
|
||||
}
|
||||
|
||||
if !opts.WithCache {
|
||||
// do not use a cache for the checker
|
||||
gopts.NoCache = true
|
||||
}
|
||||
cleanup := prepareCheckCache(opts, &gopts)
|
||||
AddCleanupHandler(func() error {
|
||||
cleanup()
|
||||
return nil
|
||||
})
|
||||
|
||||
repo, err := OpenRepository(gopts)
|
||||
if err != nil {
|
||||
@@ -155,7 +197,7 @@ func runCheck(opts CheckOptions, gopts GlobalOptions, args []string) error {
|
||||
}
|
||||
|
||||
if dupFound {
|
||||
Printf("\nrun `restic rebuild-index' to correct this\n")
|
||||
Printf("This is non-critical, you can run `restic rebuild-index' to correct this\n")
|
||||
}
|
||||
|
||||
if len(errs) > 0 {
|
||||
@@ -166,16 +208,26 @@ func runCheck(opts CheckOptions, gopts GlobalOptions, args []string) error {
|
||||
}
|
||||
|
||||
errorsFound := false
|
||||
orphanedPacks := 0
|
||||
errChan := make(chan error)
|
||||
|
||||
Verbosef("check all packs\n")
|
||||
go chkr.Packs(gopts.ctx, errChan)
|
||||
|
||||
for err := range errChan {
|
||||
if checker.IsOrphanedPack(err) {
|
||||
orphanedPacks++
|
||||
Verbosef("%v\n", err)
|
||||
continue
|
||||
}
|
||||
errorsFound = true
|
||||
fmt.Fprintf(os.Stderr, "%v\n", err)
|
||||
}
|
||||
|
||||
if orphanedPacks > 0 {
|
||||
Verbosef("%d additional files were found in the repo, which likely contain duplicate data.\nYou can run `restic prune` to correct this.\n", orphanedPacks)
|
||||
}
|
||||
|
||||
Verbosef("check snapshots, trees and blobs\n")
|
||||
errChan = make(chan error)
|
||||
go chkr.Structure(gopts.ctx, errChan)
|
||||
|
||||
@@ -33,6 +33,7 @@ type ForgetOptions struct {
|
||||
Weekly int
|
||||
Monthly int
|
||||
Yearly int
|
||||
Within restic.Duration
|
||||
KeepTags restic.TagLists
|
||||
|
||||
Host string
|
||||
@@ -58,6 +59,7 @@ func init() {
|
||||
f.IntVarP(&forgetOptions.Weekly, "keep-weekly", "w", 0, "keep the last `n` weekly snapshots")
|
||||
f.IntVarP(&forgetOptions.Monthly, "keep-monthly", "m", 0, "keep the last `n` monthly snapshots")
|
||||
f.IntVarP(&forgetOptions.Yearly, "keep-yearly", "y", 0, "keep the last `n` yearly snapshots")
|
||||
f.VarP(&forgetOptions.Within, "keep-within", "", "keep snapshots that were created within `duration` before the newest (e.g. 1y5m7d)")
|
||||
|
||||
f.Var(&forgetOptions.KeepTags, "keep-tag", "keep snapshots with this `taglist` (can be specified multiple times)")
|
||||
// Sadly the commonly used shortcut `H` is already used.
|
||||
@@ -170,6 +172,7 @@ func runForget(opts ForgetOptions, gopts GlobalOptions, args []string) error {
|
||||
Weekly: opts.Weekly,
|
||||
Monthly: opts.Monthly,
|
||||
Yearly: opts.Yearly,
|
||||
Within: opts.Within,
|
||||
Tags: opts.KeepTags,
|
||||
}
|
||||
|
||||
@@ -178,6 +181,8 @@ func runForget(opts ForgetOptions, gopts GlobalOptions, args []string) error {
|
||||
}
|
||||
|
||||
if !policy.Empty() {
|
||||
Verbosef("Applying Policy: %v\n", policy)
|
||||
|
||||
for k, snapshotGroup := range snapshotGroups {
|
||||
var key key
|
||||
if json.Unmarshal([]byte(k), &key) != nil {
|
||||
|
||||
@@ -3,6 +3,9 @@ package main
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/restic/restic/internal/errors"
|
||||
"github.com/restic/restic/internal/repository"
|
||||
@@ -23,8 +26,13 @@ The "key" command manages keys (passwords) for accessing the repository.
|
||||
},
|
||||
}
|
||||
|
||||
var newPasswordFile string
|
||||
|
||||
func init() {
|
||||
cmdRoot.AddCommand(cmdKey)
|
||||
|
||||
flags := cmdKey.Flags()
|
||||
flags.StringVarP(&newPasswordFile, "new-password-file", "", "", "the file from which to load a new password")
|
||||
}
|
||||
|
||||
func listKeys(ctx context.Context, s *repository.Repository) error {
|
||||
@@ -64,6 +72,10 @@ func getNewPassword(gopts GlobalOptions) (string, error) {
|
||||
return testKeyNewPassword, nil
|
||||
}
|
||||
|
||||
if newPasswordFile != "" {
|
||||
return loadPasswordFromFile(newPasswordFile)
|
||||
}
|
||||
|
||||
// Since we already have an open repository, temporary remove the password
|
||||
// to prompt the user for the passwd.
|
||||
newopts := gopts
|
||||
@@ -182,3 +194,11 @@ func runKey(gopts GlobalOptions, args []string) error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func loadPasswordFromFile(pwdFile string) (string, error) {
|
||||
s, err := ioutil.ReadFile(pwdFile)
|
||||
if os.IsNotExist(err) {
|
||||
return "", errors.Fatalf("%s does not exist", pwdFile)
|
||||
}
|
||||
return strings.TrimSpace(string(s)), errors.Wrap(err, "Readfile")
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ The "list" command allows listing objects in the repository based on type.
|
||||
`,
|
||||
DisableAutoGenTag: true,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return runList(globalOptions, args)
|
||||
return runList(cmd, globalOptions, args)
|
||||
},
|
||||
}
|
||||
|
||||
@@ -26,9 +26,9 @@ func init() {
|
||||
cmdRoot.AddCommand(cmdList)
|
||||
}
|
||||
|
||||
func runList(opts GlobalOptions, args []string) error {
|
||||
func runList(cmd *cobra.Command, opts GlobalOptions, args []string) error {
|
||||
if len(args) != 1 {
|
||||
return errors.Fatal("type not specified")
|
||||
return errors.Fatal("type not specified, usage: " + cmd.Use)
|
||||
}
|
||||
|
||||
repo, err := OpenRepository(opts)
|
||||
|
||||
@@ -56,7 +56,8 @@ func printTree(ctx context.Context, repo *repository.Repository, id *restic.ID,
|
||||
Printf("%s\n", formatNode(prefix, entry, lsOptions.ListLong))
|
||||
|
||||
if entry.Type == "dir" && entry.Subtree != nil {
|
||||
if err = printTree(ctx, repo, entry.Subtree, filepath.Join(prefix, entry.Name)); err != nil {
|
||||
entryPath := prefix + string(filepath.Separator) + entry.Name
|
||||
if err = printTree(ctx, repo, entry.Subtree, entryPath); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -84,7 +85,7 @@ func runLs(opts LsOptions, gopts GlobalOptions, args []string) error {
|
||||
for sn := range FindFilteredSnapshots(ctx, repo, opts.Host, opts.Tags, opts.Paths, args) {
|
||||
Verbosef("snapshot %s of %v at %s):\n", sn.ID().Str(), sn.Paths, sn.Time)
|
||||
|
||||
if err = printTree(gopts.ctx, repo, sn.Tree, string(filepath.Separator)); err != nil {
|
||||
if err = printTree(gopts.ctx, repo, sn.Tree, ""); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
// +build !openbsd
|
||||
// +build !solaris
|
||||
// +build !windows
|
||||
|
||||
package main
|
||||
|
||||
@@ -16,7 +16,7 @@ and the version of this software.
|
||||
`,
|
||||
DisableAutoGenTag: true,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
fmt.Printf("restic %s\ncompiled with %v on %v/%v\n",
|
||||
fmt.Printf("restic %s compiled with %v on %v/%v\n",
|
||||
version, runtime.Version(), runtime.GOOS, runtime.GOARCH)
|
||||
},
|
||||
}
|
||||
|
||||
@@ -185,6 +185,11 @@ func isDirExcludedByFile(dir, tagFilename, header string) bool {
|
||||
func gatherDevices(items []string) (deviceMap map[string]uint64, err error) {
|
||||
deviceMap = make(map[string]uint64)
|
||||
for _, item := range items {
|
||||
item, err = filepath.Abs(filepath.Clean(item))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
fi, err := fs.Lstat(item)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -215,6 +220,8 @@ func rejectByDevice(samples []string) (RejectFunc, error) {
|
||||
return false
|
||||
}
|
||||
|
||||
item = filepath.Clean(item)
|
||||
|
||||
id, err := fs.DeviceID(fi)
|
||||
if err != nil {
|
||||
// This should never happen because gatherDevices() would have
|
||||
@@ -222,11 +229,14 @@ func rejectByDevice(samples []string) (RejectFunc, error) {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
for dir := item; dir != ""; dir = filepath.Dir(dir) {
|
||||
for dir := item; ; dir = filepath.Dir(dir) {
|
||||
debug.Log("item %v, test dir %v", item, dir)
|
||||
|
||||
allowedID, ok := allowed[dir]
|
||||
if !ok {
|
||||
if dir == filepath.Dir(dir) {
|
||||
break
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
/boot
|
||||
/dev
|
||||
/etc
|
||||
/home
|
||||
/lost+found
|
||||
/mnt
|
||||
/proc
|
||||
/root
|
||||
/run
|
||||
/sys
|
||||
/tmp
|
||||
/usr
|
||||
/var
|
||||
/opt/android-sdk
|
||||
/opt/bullet
|
||||
/opt/dex2jar
|
||||
/opt/jameica
|
||||
/opt/google
|
||||
/opt/JDownloader
|
||||
/opt/JDownloaderScripts
|
||||
/opt/opencascade
|
||||
/opt/vagrant
|
||||
/opt/visual-studio-code
|
||||
/opt/vtk6
|
||||
/bin
|
||||
/fonts*
|
||||
/srv/ftp
|
||||
/srv/http
|
||||
/sbin
|
||||
/lib
|
||||
/lib64
|
||||
@@ -64,8 +64,9 @@ func formatDuration(d time.Duration) string {
|
||||
}
|
||||
|
||||
func formatNode(prefix string, n *restic.Node, long bool) string {
|
||||
nodepath := prefix + string(filepath.Separator) + n.Name
|
||||
if !long {
|
||||
return filepath.Join(prefix, n.Name)
|
||||
return nodepath
|
||||
}
|
||||
|
||||
var mode os.FileMode
|
||||
@@ -91,6 +92,6 @@ func formatNode(prefix string, n *restic.Node, long bool) string {
|
||||
|
||||
return fmt.Sprintf("%s %5d %5d %6d %s %s%s",
|
||||
mode|n.Mode, n.UID, n.GID, n.Size,
|
||||
n.ModTime.Format(TimeFormat), filepath.Join(prefix, n.Name),
|
||||
n.ModTime.Format(TimeFormat), nodepath,
|
||||
target)
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
@@ -18,6 +17,7 @@ import (
|
||||
"github.com/restic/restic/internal/backend/gs"
|
||||
"github.com/restic/restic/internal/backend/local"
|
||||
"github.com/restic/restic/internal/backend/location"
|
||||
"github.com/restic/restic/internal/backend/rclone"
|
||||
"github.com/restic/restic/internal/backend/rest"
|
||||
"github.com/restic/restic/internal/backend/s3"
|
||||
"github.com/restic/restic/internal/backend/sftp"
|
||||
@@ -29,6 +29,7 @@ import (
|
||||
"github.com/restic/restic/internal/options"
|
||||
"github.com/restic/restic/internal/repository"
|
||||
"github.com/restic/restic/internal/restic"
|
||||
"github.com/restic/restic/internal/textfile"
|
||||
|
||||
"github.com/restic/restic/internal/errors"
|
||||
|
||||
@@ -42,6 +43,7 @@ type GlobalOptions struct {
|
||||
Repo string
|
||||
PasswordFile string
|
||||
Quiet bool
|
||||
Verbose int
|
||||
NoLock bool
|
||||
JSON bool
|
||||
CacheDir string
|
||||
@@ -58,6 +60,13 @@ type GlobalOptions struct {
|
||||
stdout io.Writer
|
||||
stderr io.Writer
|
||||
|
||||
// verbosity is set as follows:
|
||||
// 0 means: don't print any messages except errors, this is used when --quiet is specified
|
||||
// 1 is the default: print essential messages
|
||||
// 2 means: print more messages, report minor things, this is used when --verbose is specified
|
||||
// 3 means: print very detailed debug messages, this is used when --debug is specified
|
||||
verbosity uint
|
||||
|
||||
Options []string
|
||||
|
||||
extended options.Options
|
||||
@@ -80,11 +89,12 @@ func init() {
|
||||
f.StringVarP(&globalOptions.Repo, "repo", "r", os.Getenv("RESTIC_REPOSITORY"), "repository to backup to or restore from (default: $RESTIC_REPOSITORY)")
|
||||
f.StringVarP(&globalOptions.PasswordFile, "password-file", "p", os.Getenv("RESTIC_PASSWORD_FILE"), "read the repository password from a file (default: $RESTIC_PASSWORD_FILE)")
|
||||
f.BoolVarP(&globalOptions.Quiet, "quiet", "q", false, "do not output comprehensive progress report")
|
||||
f.CountVarP(&globalOptions.Verbose, "verbose", "v", "be verbose (specify --verbose multiple times or level `n`)")
|
||||
f.BoolVar(&globalOptions.NoLock, "no-lock", false, "do not lock the repo, this allows some operations on read-only repos")
|
||||
f.BoolVarP(&globalOptions.JSON, "json", "", false, "set output mode to JSON for commands that support it")
|
||||
f.StringVar(&globalOptions.CacheDir, "cache-dir", "", "set the cache directory")
|
||||
f.BoolVar(&globalOptions.NoCache, "no-cache", false, "do not use a local cache")
|
||||
f.StringSliceVar(&globalOptions.CACerts, "cacert", nil, "path to load root certificates from (default: use system certificates)")
|
||||
f.StringSliceVar(&globalOptions.CACerts, "cacert", nil, "`file` to load root certificates from (default: use system certificates)")
|
||||
f.StringVar(&globalOptions.TLSClientCert, "tls-client-cert", "", "path to a file containing PEM encoded TLS client certificate and private key")
|
||||
f.BoolVar(&globalOptions.CleanupCache, "cleanup-cache", false, "auto remove old cache directories")
|
||||
f.IntVar(&globalOptions.LimitUploadKb, "limit-upload", 0, "limits uploads to a maximum rate in KiB/s. (default: unlimited)")
|
||||
@@ -172,11 +182,9 @@ func Printf(format string, args ...interface{}) {
|
||||
|
||||
// Verbosef calls Printf to write the message when the verbose flag is set.
|
||||
func Verbosef(format string, args ...interface{}) {
|
||||
if globalOptions.Quiet {
|
||||
return
|
||||
if globalOptions.verbosity >= 1 {
|
||||
Printf(format, args...)
|
||||
}
|
||||
|
||||
Printf(format, args...)
|
||||
}
|
||||
|
||||
// PrintProgress wraps fmt.Printf to handle the difference in writing progress
|
||||
@@ -227,8 +235,8 @@ func Exitf(exitcode int, format string, args ...interface{}) {
|
||||
// resolvePassword determines the password to be used for opening the repository.
|
||||
func resolvePassword(opts GlobalOptions, env string) (string, error) {
|
||||
if opts.PasswordFile != "" {
|
||||
s, err := ioutil.ReadFile(opts.PasswordFile)
|
||||
if os.IsNotExist(err) {
|
||||
s, err := textfile.Read(opts.PasswordFile)
|
||||
if os.IsNotExist(errors.Cause(err)) {
|
||||
return "", errors.Fatalf("%s does not exist", opts.PasswordFile)
|
||||
}
|
||||
return strings.TrimSpace(string(s)), errors.Wrap(err, "Readfile")
|
||||
@@ -347,7 +355,11 @@ func OpenRepository(opts GlobalOptions) (*repository.Repository, error) {
|
||||
}
|
||||
|
||||
if stdoutIsTerminal() {
|
||||
Verbosef("password is correct\n")
|
||||
id := s.Config().ID
|
||||
if len(id) > 8 {
|
||||
id = id[:8]
|
||||
}
|
||||
Verbosef("repository %v opened successfully, password is correct\n", id)
|
||||
}
|
||||
|
||||
if opts.NoCache {
|
||||
@@ -378,7 +390,7 @@ func OpenRepository(opts GlobalOptions) (*repository.Repository, error) {
|
||||
Printf("removing %d old cache dirs from %v\n", len(oldCacheDirs), c.Base)
|
||||
|
||||
for _, item := range oldCacheDirs {
|
||||
dir := filepath.Join(c.Base, item)
|
||||
dir := filepath.Join(c.Base, item.Name())
|
||||
err = fs.RemoveAll(dir)
|
||||
if err != nil {
|
||||
Warnf("unable to remove %v: %v\n", dir, err)
|
||||
@@ -440,18 +452,6 @@ func parseConfig(loc location.Location, opts options.Options) (interface{}, erro
|
||||
cfg.ProjectID = os.Getenv("GOOGLE_PROJECT_ID")
|
||||
}
|
||||
|
||||
if cfg.JSONKeyPath == "" {
|
||||
if path := os.Getenv("GOOGLE_APPLICATION_CREDENTIALS"); path != "" {
|
||||
// Check read access
|
||||
if _, err := ioutil.ReadFile(path); err != nil {
|
||||
return nil, errors.Fatalf("Failed to read google credential from file %v: %v", path, err)
|
||||
}
|
||||
cfg.JSONKeyPath = path
|
||||
} else {
|
||||
return nil, errors.Fatal("No credential file path is set")
|
||||
}
|
||||
}
|
||||
|
||||
if err := opts.Apply(loc.Scheme, &cfg); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -521,6 +521,14 @@ func parseConfig(loc location.Location, opts options.Options) (interface{}, erro
|
||||
return nil, err
|
||||
}
|
||||
|
||||
debug.Log("opening rest repository at %#v", cfg)
|
||||
return cfg, nil
|
||||
case "rclone":
|
||||
cfg := loc.Config.(rclone.Config)
|
||||
if err := opts.Apply(loc.Scheme, &cfg); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
debug.Log("opening rest repository at %#v", cfg)
|
||||
return cfg, nil
|
||||
}
|
||||
@@ -576,6 +584,8 @@ func open(s string, gopts GlobalOptions, opts options.Options) (restic.Backend,
|
||||
be, err = b2.Open(globalOptions.ctx, cfg.(b2.Config), rt)
|
||||
case "rest":
|
||||
be, err = rest.Open(cfg.(rest.Config), rt)
|
||||
case "rclone":
|
||||
be, err = rclone.Open(cfg.(rclone.Config))
|
||||
|
||||
default:
|
||||
return nil, errors.Fatalf("invalid backend: %q", loc.Scheme)
|
||||
@@ -637,6 +647,8 @@ func create(s string, opts options.Options) (restic.Backend, error) {
|
||||
return b2.Create(globalOptions.ctx, cfg.(b2.Config), rt)
|
||||
case "rest":
|
||||
return rest.Create(cfg.(rest.Config), rt)
|
||||
case "rclone":
|
||||
return rclone.Open(cfg.(rclone.Config))
|
||||
}
|
||||
|
||||
debug.Log("invalid repository scheme: %v", s)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// +build debug
|
||||
// +build debug profile
|
||||
|
||||
package main
|
||||
|
||||
@@ -15,17 +15,21 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
listenMemoryProfile string
|
||||
memProfilePath string
|
||||
cpuProfilePath string
|
||||
insecure bool
|
||||
listenProfile string
|
||||
memProfilePath string
|
||||
cpuProfilePath string
|
||||
traceProfilePath string
|
||||
blockProfilePath string
|
||||
insecure bool
|
||||
)
|
||||
|
||||
func init() {
|
||||
f := cmdRoot.PersistentFlags()
|
||||
f.StringVar(&listenMemoryProfile, "listen-profile", "", "listen on this `address:port` for memory profiling")
|
||||
f.StringVar(&listenProfile, "listen-profile", "", "listen on this `address:port` for memory profiling")
|
||||
f.StringVar(&memProfilePath, "mem-profile", "", "write memory profile to `dir`")
|
||||
f.StringVar(&cpuProfilePath, "cpu-profile", "", "write cpu profile to `dir`")
|
||||
f.StringVar(&traceProfilePath, "trace-profile", "", "write trace to `dir`")
|
||||
f.StringVar(&blockProfilePath, "block-profile", "", "write block profile to `dir`")
|
||||
f.BoolVar(&insecure, "insecure-kdf", false, "use insecure KDF settings")
|
||||
}
|
||||
|
||||
@@ -36,18 +40,32 @@ func (fakeTestingTB) Logf(msg string, args ...interface{}) {
|
||||
}
|
||||
|
||||
func runDebug() error {
|
||||
if listenMemoryProfile != "" {
|
||||
fmt.Fprintf(os.Stderr, "running memory profile HTTP server on %v\n", listenMemoryProfile)
|
||||
if listenProfile != "" {
|
||||
fmt.Fprintf(os.Stderr, "running profile HTTP server on %v\n", listenProfile)
|
||||
go func() {
|
||||
err := http.ListenAndServe(listenMemoryProfile, nil)
|
||||
err := http.ListenAndServe(listenProfile, nil)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "memory profile listen failed: %v\n", err)
|
||||
fmt.Fprintf(os.Stderr, "profile HTTP server listen failed: %v\n", err)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
if memProfilePath != "" && cpuProfilePath != "" {
|
||||
return errors.Fatal("only one profile (memory or CPU) may be activated at the same time")
|
||||
profilesEnabled := 0
|
||||
if memProfilePath != "" {
|
||||
profilesEnabled++
|
||||
}
|
||||
if cpuProfilePath != "" {
|
||||
profilesEnabled++
|
||||
}
|
||||
if traceProfilePath != "" {
|
||||
profilesEnabled++
|
||||
}
|
||||
if blockProfilePath != "" {
|
||||
profilesEnabled++
|
||||
}
|
||||
|
||||
if profilesEnabled > 1 {
|
||||
return errors.Fatal("only one profile (memory, CPU, trace, or block) may be activated at the same time")
|
||||
}
|
||||
|
||||
var prof interface {
|
||||
@@ -58,6 +76,10 @@ func runDebug() error {
|
||||
prof = profile.Start(profile.Quiet, profile.NoShutdownHook, profile.MemProfile, profile.ProfilePath(memProfilePath))
|
||||
} else if cpuProfilePath != "" {
|
||||
prof = profile.Start(profile.Quiet, profile.NoShutdownHook, profile.CPUProfile, profile.ProfilePath(cpuProfilePath))
|
||||
} else if traceProfilePath != "" {
|
||||
prof = profile.Start(profile.Quiet, profile.NoShutdownHook, profile.TraceProfile, profile.ProfilePath(traceProfilePath))
|
||||
} else if blockProfilePath != "" {
|
||||
prof = profile.Start(profile.Quiet, profile.NoShutdownHook, profile.BlockProfile, profile.ProfilePath(blockProfilePath))
|
||||
}
|
||||
|
||||
if prof != nil {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// +build !debug
|
||||
// +build !debug,!profile
|
||||
|
||||
package main
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
// +build !openbsd
|
||||
// +build !solaris
|
||||
// +build !windows
|
||||
|
||||
package main
|
||||
@@ -170,7 +171,7 @@ func TestMount(t *testing.T) {
|
||||
rtest.SetupTarTestFixture(t, env.testdata, filepath.Join("testdata", "backup-data.tar.gz"))
|
||||
|
||||
// first backup
|
||||
testRunBackup(t, []string{env.testdata}, BackupOptions{}, env.gopts)
|
||||
testRunBackup(t, "", []string{env.testdata}, BackupOptions{}, env.gopts)
|
||||
snapshotIDs := testRunList(t, "snapshots", env.gopts)
|
||||
rtest.Assert(t, len(snapshotIDs) == 1,
|
||||
"expected one snapshot, got %v", snapshotIDs)
|
||||
@@ -178,7 +179,7 @@ func TestMount(t *testing.T) {
|
||||
checkSnapshots(t, env.gopts, repo, env.mountpoint, env.repo, snapshotIDs, 2)
|
||||
|
||||
// second backup, implicit incremental
|
||||
testRunBackup(t, []string{env.testdata}, BackupOptions{}, env.gopts)
|
||||
testRunBackup(t, "", []string{env.testdata}, BackupOptions{}, env.gopts)
|
||||
snapshotIDs = testRunList(t, "snapshots", env.gopts)
|
||||
rtest.Assert(t, len(snapshotIDs) == 2,
|
||||
"expected two snapshots, got %v", snapshotIDs)
|
||||
@@ -187,7 +188,7 @@ func TestMount(t *testing.T) {
|
||||
|
||||
// third backup, explicit incremental
|
||||
bopts := BackupOptions{Parent: snapshotIDs[0].String()}
|
||||
testRunBackup(t, []string{env.testdata}, bopts, env.gopts)
|
||||
testRunBackup(t, "", []string{env.testdata}, bopts, env.gopts)
|
||||
snapshotIDs = testRunList(t, "snapshots", env.gopts)
|
||||
rtest.Assert(t, len(snapshotIDs) == 3,
|
||||
"expected three snapshots, got %v", snapshotIDs)
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
|
||||
"github.com/restic/restic/internal/options"
|
||||
"github.com/restic/restic/internal/repository"
|
||||
"github.com/restic/restic/internal/restic"
|
||||
rtest "github.com/restic/restic/internal/test"
|
||||
)
|
||||
|
||||
@@ -189,6 +190,7 @@ func withTestEnvironment(t testing.TB) (env *testEnvironment, cleanup func()) {
|
||||
}
|
||||
|
||||
repository.TestUseLowSecurityKDFParameters(t)
|
||||
restic.TestDisableCheckPolynomial(t)
|
||||
|
||||
tempdir, err := ioutil.TempDir(rtest.TestTempDir, "restic-test-")
|
||||
rtest.OK(t, err)
|
||||
|
||||
@@ -3,6 +3,7 @@ package main
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
@@ -17,12 +18,14 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/restic/restic/internal/debug"
|
||||
"github.com/restic/restic/internal/errors"
|
||||
"github.com/restic/restic/internal/filter"
|
||||
"github.com/restic/restic/internal/fs"
|
||||
"github.com/restic/restic/internal/repository"
|
||||
"github.com/restic/restic/internal/restic"
|
||||
rtest "github.com/restic/restic/internal/test"
|
||||
"github.com/restic/restic/internal/ui/termstatus"
|
||||
"golang.org/x/sync/errgroup"
|
||||
)
|
||||
|
||||
func parseIDsFromReader(t testing.TB, rd io.Reader) restic.IDs {
|
||||
@@ -44,15 +47,36 @@ func parseIDsFromReader(t testing.TB, rd io.Reader) restic.IDs {
|
||||
|
||||
func testRunInit(t testing.TB, opts GlobalOptions) {
|
||||
repository.TestUseLowSecurityKDFParameters(t)
|
||||
restic.TestDisableCheckPolynomial(t)
|
||||
restic.TestSetLockTimeout(t, 0)
|
||||
|
||||
rtest.OK(t, runInit(opts, nil))
|
||||
t.Logf("repository initialized at %v", opts.Repo)
|
||||
}
|
||||
|
||||
func testRunBackup(t testing.TB, target []string, opts BackupOptions, gopts GlobalOptions) {
|
||||
t.Logf("backing up %v", target)
|
||||
rtest.OK(t, runBackup(opts, gopts, target))
|
||||
func testRunBackup(t testing.TB, dir string, target []string, opts BackupOptions, gopts GlobalOptions) {
|
||||
ctx, cancel := context.WithCancel(gopts.ctx)
|
||||
defer cancel()
|
||||
|
||||
var wg errgroup.Group
|
||||
term := termstatus.New(gopts.stdout, gopts.stderr, gopts.Quiet)
|
||||
wg.Go(func() error { term.Run(ctx); return nil })
|
||||
|
||||
gopts.stdout = ioutil.Discard
|
||||
t.Logf("backing up %v in %v", target, dir)
|
||||
if dir != "" {
|
||||
cleanup := fs.TestChdir(t, dir)
|
||||
defer cleanup()
|
||||
}
|
||||
|
||||
rtest.OK(t, runBackup(opts, gopts, term, target))
|
||||
|
||||
cancel()
|
||||
|
||||
err := wg.Wait()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func testRunList(t testing.TB, tpe string, opts GlobalOptions) restic.IDs {
|
||||
@@ -62,7 +86,7 @@ func testRunList(t testing.TB, tpe string, opts GlobalOptions) restic.IDs {
|
||||
globalOptions.stdout = os.Stdout
|
||||
}()
|
||||
|
||||
rtest.OK(t, runList(opts, []string{tpe}))
|
||||
rtest.OK(t, runList(cmdList, opts, []string{tpe}))
|
||||
return parseIDsFromReader(t, buf)
|
||||
}
|
||||
|
||||
@@ -218,7 +242,7 @@ func TestBackup(t *testing.T) {
|
||||
opts := BackupOptions{}
|
||||
|
||||
// first backup
|
||||
testRunBackup(t, []string{env.testdata}, opts, env.gopts)
|
||||
testRunBackup(t, filepath.Dir(env.testdata), []string{"testdata"}, opts, env.gopts)
|
||||
snapshotIDs := testRunList(t, "snapshots", env.gopts)
|
||||
rtest.Assert(t, len(snapshotIDs) == 1,
|
||||
"expected one snapshot, got %v", snapshotIDs)
|
||||
@@ -227,7 +251,7 @@ func TestBackup(t *testing.T) {
|
||||
stat1 := dirStats(env.repo)
|
||||
|
||||
// second backup, implicit incremental
|
||||
testRunBackup(t, []string{env.testdata}, opts, env.gopts)
|
||||
testRunBackup(t, filepath.Dir(env.testdata), []string{"testdata"}, opts, env.gopts)
|
||||
snapshotIDs = testRunList(t, "snapshots", env.gopts)
|
||||
rtest.Assert(t, len(snapshotIDs) == 2,
|
||||
"expected two snapshots, got %v", snapshotIDs)
|
||||
@@ -241,7 +265,7 @@ func TestBackup(t *testing.T) {
|
||||
testRunCheck(t, env.gopts)
|
||||
// third backup, explicit incremental
|
||||
opts.Parent = snapshotIDs[0].String()
|
||||
testRunBackup(t, []string{env.testdata}, opts, env.gopts)
|
||||
testRunBackup(t, filepath.Dir(env.testdata), []string{"testdata"}, opts, env.gopts)
|
||||
snapshotIDs = testRunList(t, "snapshots", env.gopts)
|
||||
rtest.Assert(t, len(snapshotIDs) == 3,
|
||||
"expected three snapshots, got %v", snapshotIDs)
|
||||
@@ -285,7 +309,7 @@ func TestBackupNonExistingFile(t *testing.T) {
|
||||
globalOptions.stderr = os.Stderr
|
||||
}()
|
||||
|
||||
p := filepath.Join(env.testdata, "0", "0")
|
||||
p := filepath.Join(env.testdata, "0", "0", "9")
|
||||
dirs := []string{
|
||||
filepath.Join(p, "0"),
|
||||
filepath.Join(p, "1"),
|
||||
@@ -295,198 +319,7 @@ func TestBackupNonExistingFile(t *testing.T) {
|
||||
|
||||
opts := BackupOptions{}
|
||||
|
||||
testRunBackup(t, dirs, opts, env.gopts)
|
||||
}
|
||||
|
||||
func TestBackupMissingFile1(t *testing.T) {
|
||||
env, cleanup := withTestEnvironment(t)
|
||||
defer cleanup()
|
||||
|
||||
datafile := filepath.Join("testdata", "backup-data.tar.gz")
|
||||
fd, err := os.Open(datafile)
|
||||
if os.IsNotExist(errors.Cause(err)) {
|
||||
t.Skipf("unable to find data file %q, skipping", datafile)
|
||||
return
|
||||
}
|
||||
rtest.OK(t, err)
|
||||
rtest.OK(t, fd.Close())
|
||||
|
||||
rtest.SetupTarTestFixture(t, env.testdata, datafile)
|
||||
|
||||
testRunInit(t, env.gopts)
|
||||
globalOptions.stderr = ioutil.Discard
|
||||
defer func() {
|
||||
globalOptions.stderr = os.Stderr
|
||||
}()
|
||||
|
||||
ranHook := false
|
||||
debug.Hook("pipe.walk1", func(context interface{}) {
|
||||
pathname := context.(string)
|
||||
|
||||
if pathname != filepath.Join("testdata", "0", "0", "9") {
|
||||
return
|
||||
}
|
||||
|
||||
t.Logf("in hook, removing test file testdata/0/0/9/37")
|
||||
ranHook = true
|
||||
|
||||
rtest.OK(t, os.Remove(filepath.Join(env.testdata, "0", "0", "9", "37")))
|
||||
})
|
||||
|
||||
opts := BackupOptions{}
|
||||
|
||||
testRunBackup(t, []string{env.testdata}, opts, env.gopts)
|
||||
testRunCheck(t, env.gopts)
|
||||
|
||||
rtest.Assert(t, ranHook, "hook did not run")
|
||||
debug.RemoveHook("pipe.walk1")
|
||||
}
|
||||
|
||||
func TestBackupMissingFile2(t *testing.T) {
|
||||
env, cleanup := withTestEnvironment(t)
|
||||
defer cleanup()
|
||||
|
||||
datafile := filepath.Join("testdata", "backup-data.tar.gz")
|
||||
fd, err := os.Open(datafile)
|
||||
if os.IsNotExist(errors.Cause(err)) {
|
||||
t.Skipf("unable to find data file %q, skipping", datafile)
|
||||
return
|
||||
}
|
||||
rtest.OK(t, err)
|
||||
rtest.OK(t, fd.Close())
|
||||
|
||||
rtest.SetupTarTestFixture(t, env.testdata, datafile)
|
||||
|
||||
testRunInit(t, env.gopts)
|
||||
|
||||
globalOptions.stderr = ioutil.Discard
|
||||
defer func() {
|
||||
globalOptions.stderr = os.Stderr
|
||||
}()
|
||||
|
||||
ranHook := false
|
||||
debug.Hook("pipe.walk2", func(context interface{}) {
|
||||
pathname := context.(string)
|
||||
|
||||
if pathname != filepath.Join("testdata", "0", "0", "9", "37") {
|
||||
return
|
||||
}
|
||||
|
||||
t.Logf("in hook, removing test file testdata/0/0/9/37")
|
||||
ranHook = true
|
||||
|
||||
rtest.OK(t, os.Remove(filepath.Join(env.testdata, "0", "0", "9", "37")))
|
||||
})
|
||||
|
||||
opts := BackupOptions{}
|
||||
|
||||
testRunBackup(t, []string{env.testdata}, opts, env.gopts)
|
||||
testRunCheck(t, env.gopts)
|
||||
|
||||
rtest.Assert(t, ranHook, "hook did not run")
|
||||
debug.RemoveHook("pipe.walk2")
|
||||
}
|
||||
|
||||
func TestBackupChangedFile(t *testing.T) {
|
||||
env, cleanup := withTestEnvironment(t)
|
||||
defer cleanup()
|
||||
|
||||
datafile := filepath.Join("testdata", "backup-data.tar.gz")
|
||||
fd, err := os.Open(datafile)
|
||||
if os.IsNotExist(errors.Cause(err)) {
|
||||
t.Skipf("unable to find data file %q, skipping", datafile)
|
||||
return
|
||||
}
|
||||
rtest.OK(t, err)
|
||||
rtest.OK(t, fd.Close())
|
||||
|
||||
rtest.SetupTarTestFixture(t, env.testdata, datafile)
|
||||
|
||||
testRunInit(t, env.gopts)
|
||||
|
||||
globalOptions.stderr = ioutil.Discard
|
||||
defer func() {
|
||||
globalOptions.stderr = os.Stderr
|
||||
}()
|
||||
|
||||
modFile := filepath.Join(env.testdata, "0", "0", "6", "18")
|
||||
|
||||
ranHook := false
|
||||
debug.Hook("archiver.SaveFile", func(context interface{}) {
|
||||
pathname := context.(string)
|
||||
|
||||
if pathname != modFile {
|
||||
return
|
||||
}
|
||||
|
||||
t.Logf("in hook, modifying test file %v", modFile)
|
||||
ranHook = true
|
||||
|
||||
rtest.OK(t, ioutil.WriteFile(modFile, []byte("modified"), 0600))
|
||||
})
|
||||
|
||||
opts := BackupOptions{}
|
||||
|
||||
testRunBackup(t, []string{env.testdata}, opts, env.gopts)
|
||||
testRunCheck(t, env.gopts)
|
||||
|
||||
rtest.Assert(t, ranHook, "hook did not run")
|
||||
debug.RemoveHook("archiver.SaveFile")
|
||||
}
|
||||
|
||||
func TestBackupDirectoryError(t *testing.T) {
|
||||
env, cleanup := withTestEnvironment(t)
|
||||
defer cleanup()
|
||||
|
||||
datafile := filepath.Join("testdata", "backup-data.tar.gz")
|
||||
fd, err := os.Open(datafile)
|
||||
if os.IsNotExist(errors.Cause(err)) {
|
||||
t.Skipf("unable to find data file %q, skipping", datafile)
|
||||
return
|
||||
}
|
||||
rtest.OK(t, err)
|
||||
rtest.OK(t, fd.Close())
|
||||
|
||||
rtest.SetupTarTestFixture(t, env.testdata, datafile)
|
||||
|
||||
testRunInit(t, env.gopts)
|
||||
|
||||
globalOptions.stderr = ioutil.Discard
|
||||
defer func() {
|
||||
globalOptions.stderr = os.Stderr
|
||||
}()
|
||||
|
||||
ranHook := false
|
||||
|
||||
testdir := filepath.Join(env.testdata, "0", "0", "9")
|
||||
|
||||
// install hook that removes the dir right before readdirnames()
|
||||
debug.Hook("pipe.readdirnames", func(context interface{}) {
|
||||
path := context.(string)
|
||||
|
||||
if path != testdir {
|
||||
return
|
||||
}
|
||||
|
||||
t.Logf("in hook, removing test file %v", testdir)
|
||||
ranHook = true
|
||||
|
||||
rtest.OK(t, os.RemoveAll(testdir))
|
||||
})
|
||||
|
||||
testRunBackup(t, []string{filepath.Join(env.testdata, "0", "0")}, BackupOptions{}, env.gopts)
|
||||
testRunCheck(t, env.gopts)
|
||||
|
||||
rtest.Assert(t, ranHook, "hook did not run")
|
||||
debug.RemoveHook("pipe.walk2")
|
||||
|
||||
snapshots := testRunList(t, "snapshots", env.gopts)
|
||||
rtest.Assert(t, len(snapshots) > 0,
|
||||
"no snapshots found in repo (%v)", datafile)
|
||||
|
||||
files := testRunLs(t, env.gopts, snapshots[0].String())
|
||||
|
||||
rtest.Assert(t, len(files) > 1, "snapshot is empty")
|
||||
testRunBackup(t, "", dirs, opts, env.gopts)
|
||||
}
|
||||
|
||||
func includes(haystack []string, needle string) bool {
|
||||
@@ -551,21 +384,21 @@ func TestBackupExclude(t *testing.T) {
|
||||
|
||||
opts := BackupOptions{}
|
||||
|
||||
testRunBackup(t, []string{datadir}, opts, env.gopts)
|
||||
testRunBackup(t, filepath.Dir(env.testdata), []string{"testdata"}, opts, env.gopts)
|
||||
snapshots, snapshotID := lastSnapshot(snapshots, loadSnapshotMap(t, env.gopts))
|
||||
files := testRunLs(t, env.gopts, snapshotID)
|
||||
rtest.Assert(t, includes(files, filepath.Join(string(filepath.Separator), "testdata", "foo.tar.gz")),
|
||||
"expected file %q in first snapshot, but it's not included", "foo.tar.gz")
|
||||
|
||||
opts.Excludes = []string{"*.tar.gz"}
|
||||
testRunBackup(t, []string{datadir}, opts, env.gopts)
|
||||
testRunBackup(t, filepath.Dir(env.testdata), []string{"testdata"}, opts, env.gopts)
|
||||
snapshots, snapshotID = lastSnapshot(snapshots, loadSnapshotMap(t, env.gopts))
|
||||
files = testRunLs(t, env.gopts, snapshotID)
|
||||
rtest.Assert(t, !includes(files, filepath.Join(string(filepath.Separator), "testdata", "foo.tar.gz")),
|
||||
"expected file %q not in first snapshot, but it's included", "foo.tar.gz")
|
||||
|
||||
opts.Excludes = []string{"*.tar.gz", "private/secret"}
|
||||
testRunBackup(t, []string{datadir}, opts, env.gopts)
|
||||
testRunBackup(t, filepath.Dir(env.testdata), []string{"testdata"}, opts, env.gopts)
|
||||
_, snapshotID = lastSnapshot(snapshots, loadSnapshotMap(t, env.gopts))
|
||||
files = testRunLs(t, env.gopts, snapshotID)
|
||||
rtest.Assert(t, !includes(files, filepath.Join(string(filepath.Separator), "testdata", "foo.tar.gz")),
|
||||
@@ -575,9 +408,9 @@ func TestBackupExclude(t *testing.T) {
|
||||
}
|
||||
|
||||
const (
|
||||
incrementalFirstWrite = 20 * 1042 * 1024
|
||||
incrementalSecondWrite = 12 * 1042 * 1024
|
||||
incrementalThirdWrite = 4 * 1042 * 1024
|
||||
incrementalFirstWrite = 10 * 1042 * 1024
|
||||
incrementalSecondWrite = 1 * 1042 * 1024
|
||||
incrementalThirdWrite = 1 * 1042 * 1024
|
||||
)
|
||||
|
||||
func appendRandomData(filename string, bytes uint) error {
|
||||
@@ -615,13 +448,13 @@ func TestIncrementalBackup(t *testing.T) {
|
||||
|
||||
opts := BackupOptions{}
|
||||
|
||||
testRunBackup(t, []string{datadir}, opts, env.gopts)
|
||||
testRunBackup(t, "", []string{datadir}, opts, env.gopts)
|
||||
testRunCheck(t, env.gopts)
|
||||
stat1 := dirStats(env.repo)
|
||||
|
||||
rtest.OK(t, appendRandomData(testfile, incrementalSecondWrite))
|
||||
|
||||
testRunBackup(t, []string{datadir}, opts, env.gopts)
|
||||
testRunBackup(t, "", []string{datadir}, opts, env.gopts)
|
||||
testRunCheck(t, env.gopts)
|
||||
stat2 := dirStats(env.repo)
|
||||
if stat2.size-stat1.size > incrementalFirstWrite {
|
||||
@@ -631,7 +464,7 @@ func TestIncrementalBackup(t *testing.T) {
|
||||
|
||||
rtest.OK(t, appendRandomData(testfile, incrementalThirdWrite))
|
||||
|
||||
testRunBackup(t, []string{datadir}, opts, env.gopts)
|
||||
testRunBackup(t, "", []string{datadir}, opts, env.gopts)
|
||||
testRunCheck(t, env.gopts)
|
||||
stat3 := dirStats(env.repo)
|
||||
if stat3.size-stat2.size > incrementalFirstWrite {
|
||||
@@ -650,7 +483,7 @@ func TestBackupTags(t *testing.T) {
|
||||
|
||||
opts := BackupOptions{}
|
||||
|
||||
testRunBackup(t, []string{env.testdata}, opts, env.gopts)
|
||||
testRunBackup(t, "", []string{env.testdata}, opts, env.gopts)
|
||||
testRunCheck(t, env.gopts)
|
||||
newest, _ := testRunSnapshots(t, env.gopts)
|
||||
rtest.Assert(t, newest != nil, "expected a new backup, got nil")
|
||||
@@ -659,7 +492,7 @@ func TestBackupTags(t *testing.T) {
|
||||
parent := newest
|
||||
|
||||
opts.Tags = []string{"NL"}
|
||||
testRunBackup(t, []string{env.testdata}, opts, env.gopts)
|
||||
testRunBackup(t, "", []string{env.testdata}, opts, env.gopts)
|
||||
testRunCheck(t, env.gopts)
|
||||
newest, _ = testRunSnapshots(t, env.gopts)
|
||||
rtest.Assert(t, newest != nil, "expected a new backup, got nil")
|
||||
@@ -682,7 +515,7 @@ func TestTag(t *testing.T) {
|
||||
testRunInit(t, env.gopts)
|
||||
rtest.SetupTarTestFixture(t, env.testdata, datafile)
|
||||
|
||||
testRunBackup(t, []string{env.testdata}, BackupOptions{}, env.gopts)
|
||||
testRunBackup(t, "", []string{env.testdata}, BackupOptions{}, env.gopts)
|
||||
testRunCheck(t, env.gopts)
|
||||
newest, _ := testRunSnapshots(t, env.gopts)
|
||||
rtest.Assert(t, newest != nil, "expected a new backup, got nil")
|
||||
@@ -858,7 +691,7 @@ func TestRestoreFilter(t *testing.T) {
|
||||
|
||||
opts := BackupOptions{}
|
||||
|
||||
testRunBackup(t, []string{env.testdata}, opts, env.gopts)
|
||||
testRunBackup(t, filepath.Dir(env.testdata), []string{filepath.Base(env.testdata)}, opts, env.gopts)
|
||||
testRunCheck(t, env.gopts)
|
||||
|
||||
snapshotID := testRunList(t, "snapshots", env.gopts)[0]
|
||||
@@ -893,12 +726,12 @@ func TestRestore(t *testing.T) {
|
||||
for i := 0; i < 10; i++ {
|
||||
p := filepath.Join(env.testdata, fmt.Sprintf("foo/bar/testfile%v", i))
|
||||
rtest.OK(t, os.MkdirAll(filepath.Dir(p), 0755))
|
||||
rtest.OK(t, appendRandomData(p, uint(mrand.Intn(5<<21))))
|
||||
rtest.OK(t, appendRandomData(p, uint(mrand.Intn(2<<21))))
|
||||
}
|
||||
|
||||
opts := BackupOptions{}
|
||||
|
||||
testRunBackup(t, []string{env.testdata}, opts, env.gopts)
|
||||
testRunBackup(t, filepath.Dir(env.testdata), []string{filepath.Base(env.testdata)}, opts, env.gopts)
|
||||
testRunCheck(t, env.gopts)
|
||||
|
||||
// Restore latest without any filters
|
||||
@@ -921,12 +754,22 @@ func TestRestoreLatest(t *testing.T) {
|
||||
|
||||
opts := BackupOptions{}
|
||||
|
||||
testRunBackup(t, []string{env.testdata}, opts, env.gopts)
|
||||
// chdir manually here so we can get the current directory. This is not the
|
||||
// same as the temp dir returned by ioutil.TempDir() on darwin.
|
||||
back := fs.TestChdir(t, filepath.Dir(env.testdata))
|
||||
defer back()
|
||||
|
||||
curdir, err := os.Getwd()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
testRunBackup(t, "", []string{filepath.Base(env.testdata)}, opts, env.gopts)
|
||||
testRunCheck(t, env.gopts)
|
||||
|
||||
os.Remove(p)
|
||||
rtest.OK(t, appendRandomData(p, 101))
|
||||
testRunBackup(t, []string{env.testdata}, opts, env.gopts)
|
||||
testRunBackup(t, "", []string{filepath.Base(env.testdata)}, opts, env.gopts)
|
||||
testRunCheck(t, env.gopts)
|
||||
|
||||
// Restore latest without any filters
|
||||
@@ -934,16 +777,18 @@ func TestRestoreLatest(t *testing.T) {
|
||||
rtest.OK(t, testFileSize(filepath.Join(env.base, "restore0", "testdata", "testfile.c"), int64(101)))
|
||||
|
||||
// Setup test files in different directories backed up in different snapshots
|
||||
p1 := filepath.Join(env.testdata, "p1/testfile.c")
|
||||
p1 := filepath.Join(curdir, filepath.FromSlash("p1/testfile.c"))
|
||||
|
||||
rtest.OK(t, os.MkdirAll(filepath.Dir(p1), 0755))
|
||||
rtest.OK(t, appendRandomData(p1, 102))
|
||||
testRunBackup(t, []string{filepath.Dir(p1)}, opts, env.gopts)
|
||||
testRunBackup(t, "", []string{"p1"}, opts, env.gopts)
|
||||
testRunCheck(t, env.gopts)
|
||||
|
||||
p2 := filepath.Join(env.testdata, "p2/testfile.c")
|
||||
p2 := filepath.Join(curdir, filepath.FromSlash("p2/testfile.c"))
|
||||
|
||||
rtest.OK(t, os.MkdirAll(filepath.Dir(p2), 0755))
|
||||
rtest.OK(t, appendRandomData(p2, 103))
|
||||
testRunBackup(t, []string{filepath.Dir(p2)}, opts, env.gopts)
|
||||
testRunBackup(t, "", []string{"p2"}, opts, env.gopts)
|
||||
testRunCheck(t, env.gopts)
|
||||
|
||||
p1rAbs := filepath.Join(env.base, "restore1", "p1/testfile.c")
|
||||
@@ -1016,7 +861,7 @@ func TestRestoreNoMetadataOnIgnoredIntermediateDirs(t *testing.T) {
|
||||
|
||||
opts := BackupOptions{}
|
||||
|
||||
testRunBackup(t, []string{env.testdata}, opts, env.gopts)
|
||||
testRunBackup(t, filepath.Dir(env.testdata), []string{filepath.Base(env.testdata)}, opts, env.gopts)
|
||||
testRunCheck(t, env.gopts)
|
||||
|
||||
snapshotID := testRunList(t, "snapshots", env.gopts)[0]
|
||||
@@ -1054,7 +899,7 @@ func TestFind(t *testing.T) {
|
||||
|
||||
opts := BackupOptions{}
|
||||
|
||||
testRunBackup(t, []string{env.testdata}, opts, env.gopts)
|
||||
testRunBackup(t, "", []string{env.testdata}, opts, env.gopts)
|
||||
testRunCheck(t, env.gopts)
|
||||
|
||||
results := testRunFind(t, false, env.gopts, "unexistingfile")
|
||||
@@ -1094,7 +939,7 @@ func TestFindJSON(t *testing.T) {
|
||||
|
||||
opts := BackupOptions{}
|
||||
|
||||
testRunBackup(t, []string{env.testdata}, opts, env.gopts)
|
||||
testRunBackup(t, "", []string{env.testdata}, opts, env.gopts)
|
||||
testRunCheck(t, env.gopts)
|
||||
|
||||
results := testRunFind(t, true, env.gopts, "unexistingfile")
|
||||
@@ -1197,13 +1042,13 @@ func TestPrune(t *testing.T) {
|
||||
rtest.SetupTarTestFixture(t, env.testdata, datafile)
|
||||
opts := BackupOptions{}
|
||||
|
||||
testRunBackup(t, []string{filepath.Join(env.testdata, "0", "0")}, opts, env.gopts)
|
||||
testRunBackup(t, "", []string{filepath.Join(env.testdata, "0", "0", "9")}, opts, env.gopts)
|
||||
firstSnapshot := testRunList(t, "snapshots", env.gopts)
|
||||
rtest.Assert(t, len(firstSnapshot) == 1,
|
||||
"expected one snapshot, got %v", firstSnapshot)
|
||||
|
||||
testRunBackup(t, []string{filepath.Join(env.testdata, "0", "0", "2")}, opts, env.gopts)
|
||||
testRunBackup(t, []string{filepath.Join(env.testdata, "0", "0", "3")}, opts, env.gopts)
|
||||
testRunBackup(t, "", []string{filepath.Join(env.testdata, "0", "0", "9", "2")}, opts, env.gopts)
|
||||
testRunBackup(t, "", []string{filepath.Join(env.testdata, "0", "0", "9", "3")}, opts, env.gopts)
|
||||
|
||||
snapshotIDs := testRunList(t, "snapshots", env.gopts)
|
||||
rtest.Assert(t, len(snapshotIDs) == 3,
|
||||
@@ -1237,7 +1082,7 @@ func TestHardLink(t *testing.T) {
|
||||
opts := BackupOptions{}
|
||||
|
||||
// first backup
|
||||
testRunBackup(t, []string{env.testdata}, opts, env.gopts)
|
||||
testRunBackup(t, filepath.Dir(env.testdata), []string{filepath.Base(env.testdata)}, opts, env.gopts)
|
||||
snapshotIDs := testRunList(t, "snapshots", env.gopts)
|
||||
rtest.Assert(t, len(snapshotIDs) == 1,
|
||||
"expected one snapshot, got %v", snapshotIDs)
|
||||
@@ -1311,3 +1156,38 @@ func linkEqual(source, dest []string) bool {
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func TestQuietBackup(t *testing.T) {
|
||||
env, cleanup := withTestEnvironment(t)
|
||||
defer cleanup()
|
||||
|
||||
datafile := filepath.Join("testdata", "backup-data.tar.gz")
|
||||
fd, err := os.Open(datafile)
|
||||
if os.IsNotExist(errors.Cause(err)) {
|
||||
t.Skipf("unable to find data file %q, skipping", datafile)
|
||||
return
|
||||
}
|
||||
rtest.OK(t, err)
|
||||
rtest.OK(t, fd.Close())
|
||||
|
||||
testRunInit(t, env.gopts)
|
||||
|
||||
rtest.SetupTarTestFixture(t, env.testdata, datafile)
|
||||
opts := BackupOptions{}
|
||||
|
||||
env.gopts.Quiet = false
|
||||
testRunBackup(t, "", []string{env.testdata}, opts, env.gopts)
|
||||
snapshotIDs := testRunList(t, "snapshots", env.gopts)
|
||||
rtest.Assert(t, len(snapshotIDs) == 1,
|
||||
"expected one snapshot, got %v", snapshotIDs)
|
||||
|
||||
testRunCheck(t, env.gopts)
|
||||
|
||||
env.gopts.Quiet = true
|
||||
testRunBackup(t, "", []string{env.testdata}, opts, env.gopts)
|
||||
snapshotIDs = testRunList(t, "snapshots", env.gopts)
|
||||
rtest.Assert(t, len(snapshotIDs) == 2,
|
||||
"expected two snapshots, got %v", snapshotIDs)
|
||||
|
||||
testRunCheck(t, env.gopts)
|
||||
}
|
||||
|
||||
@@ -29,14 +29,31 @@ directories in an encrypted repository stored on different backends.
|
||||
SilenceUsage: true,
|
||||
DisableAutoGenTag: true,
|
||||
|
||||
PersistentPreRunE: func(*cobra.Command, []string) error {
|
||||
PersistentPreRunE: func(c *cobra.Command, args []string) error {
|
||||
// set verbosity, default is one
|
||||
globalOptions.verbosity = 1
|
||||
if globalOptions.Quiet && (globalOptions.Verbose > 1) {
|
||||
return errors.Fatal("--quiet and --verbose cannot be specified at the same time")
|
||||
}
|
||||
|
||||
switch {
|
||||
case globalOptions.Verbose >= 2:
|
||||
globalOptions.verbosity = 3
|
||||
case globalOptions.Verbose > 0:
|
||||
globalOptions.verbosity = 2
|
||||
case globalOptions.Quiet:
|
||||
globalOptions.verbosity = 0
|
||||
}
|
||||
|
||||
// parse extended options
|
||||
opts, err := options.Parse(globalOptions.Options)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
globalOptions.extended = opts
|
||||
|
||||
if c.Name() == "version" {
|
||||
return nil
|
||||
}
|
||||
pwd, err := resolvePassword(globalOptions, "RESTIC_PASSWORD")
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Resolving password failed: %v\n", err)
|
||||
@@ -64,7 +81,7 @@ func init() {
|
||||
|
||||
func main() {
|
||||
debug.Log("main %#v", os.Args)
|
||||
debug.Log("restic %s, compiled with %v on %v/%v",
|
||||
debug.Log("restic %s compiled with %v on %v/%v",
|
||||
version, runtime.Version(), runtime.GOOS, runtime.GOARCH)
|
||||
err := cmdRoot.Execute()
|
||||
|
||||
|
||||
BIN
cmd/restic/testdata/backup-data.tar.gz
vendored
BIN
cmd/restic/testdata/backup-data.tar.gz
vendored
Binary file not shown.
@@ -14,3 +14,6 @@
|
||||
Introduction
|
||||
############
|
||||
|
||||
Restic is a fast and secure backup program. In the following sections, we will
|
||||
present typical workflows, starting with installing, preparing a new
|
||||
repository, and making the first backup.
|
||||
|
||||
@@ -17,6 +17,14 @@ Installation
|
||||
Packages
|
||||
********
|
||||
|
||||
Note that if at any point the package you’re trying to use is outdated, you
|
||||
always have the option to use an official binary from the restic project.
|
||||
|
||||
These are up to date binaries, built in a reproducible and verifiable way, that
|
||||
you can download and run without having to do additional installation work.
|
||||
|
||||
Please see the :ref:`official_binaries` section below for various downloads.
|
||||
|
||||
Mac OS X
|
||||
========
|
||||
|
||||
@@ -62,12 +70,70 @@ installed from the official repos, e.g. with ``apt-get``:
|
||||
.. warning:: Please be aware that, at the time of writing, Debian *stable*
|
||||
has ``restic`` version 0.3.3 which is very old. The *testing* and *unstable*
|
||||
branches have recent versions of ``restic``.
|
||||
|
||||
RHEL & CentOS
|
||||
=============
|
||||
|
||||
Pre-compiled Binary
|
||||
*******************
|
||||
restic can be installed via copr repository.
|
||||
|
||||
You can download the latest pre-compiled binary from the `restic release
|
||||
page <https://github.com/restic/restic/releases/latest>`__.
|
||||
.. code-block:: console
|
||||
|
||||
$ yum install yum-plugin-copr
|
||||
$ yum copr enable copart/restic
|
||||
$ yum install restic
|
||||
|
||||
Fedora
|
||||
======
|
||||
|
||||
restic can be installed via copr repository.
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ dnf install dnf-plugin-core
|
||||
$ dnf copr enable copart/restic
|
||||
$ dnf install restic
|
||||
|
||||
Solus
|
||||
=====
|
||||
|
||||
restic can be installed from the official repo of Solus via the ``eopkg`` package manager:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ eopkg install restic
|
||||
|
||||
OpenBSD
|
||||
=======
|
||||
|
||||
On OpenBSD 6.3 and greater, you can install restic using ``pkg_add``:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
# pkg_add restic
|
||||
|
||||
.. _official_binaries:
|
||||
|
||||
Official Binaries
|
||||
*****************
|
||||
|
||||
Stable Releases
|
||||
===============
|
||||
|
||||
You can download the latest stable release versions of restic from the `restic
|
||||
release page <https://github.com/restic/restic/releases/latest>`__. These builds
|
||||
are considered stable and releases are made regularly in a controlled manner.
|
||||
|
||||
There's both pre-compiled binaries for different platforms as well as the source
|
||||
code available for download. Just download and run the one matching your system.
|
||||
|
||||
Unstable Builds
|
||||
===============
|
||||
|
||||
Another option is to use the latest builds for the master branch, available on
|
||||
the `restic beta download site
|
||||
<https://beta.restic.net/?sort=time&order=desc>`__. These too are pre-compiled
|
||||
and ready to run, and a new version is built every time a push is made to the
|
||||
master branch.
|
||||
|
||||
Windows
|
||||
=======
|
||||
@@ -79,15 +145,23 @@ Admin rights.
|
||||
Docker Container
|
||||
****************
|
||||
|
||||
We're maintaining a bare docker container with just a few files and the restic
|
||||
binary, you can get it with `docker pull` like this:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ docker pull restic/restic
|
||||
|
||||
.. note::
|
||||
| A docker container is available as a contribution (Thank you!).
|
||||
| You can find it at https://github.com/Lobaro/restic-backup-docker
|
||||
| Another docker container which offers more configuration options is
|
||||
| available as a contribution (Thank you!). You can find it at
|
||||
| https://github.com/Lobaro/restic-backup-docker
|
||||
|
||||
From Source
|
||||
***********
|
||||
|
||||
restic is written in the Go programming language and you need at least
|
||||
Go version 1.8. Building restic may also work with older versions of Go,
|
||||
Go version 1.9. Building restic may also work with older versions of Go,
|
||||
but that's not supported. See the `Getting
|
||||
started <https://golang.org/doc/install>`__ guide of the Go project for
|
||||
instructions how to install Go.
|
||||
@@ -107,7 +181,7 @@ You can easily cross-compile restic for all supported platforms, just
|
||||
supply the target OS and platform via the command-line options like this
|
||||
(for Windows and FreeBSD respectively):
|
||||
|
||||
::
|
||||
.. code-block:: console
|
||||
|
||||
$ go run build.go --goos windows --goarch amd64
|
||||
|
||||
@@ -124,35 +198,27 @@ compiler. Building restic with gccgo may work, but is not supported.
|
||||
Autocompletion
|
||||
**************
|
||||
|
||||
Restic can write out a bash compatible autocompletion script:
|
||||
Restic can write out man pages and bash/zsh compatible autocompletion scripts:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ ./restic autocomplete --help
|
||||
The "autocomplete" command generates a shell autocompletion script.
|
||||
$ ./restic generate --help
|
||||
|
||||
NOTE: The current version supports Bash only.
|
||||
This should work for *nix systems with Bash installed.
|
||||
|
||||
By default, the file is written directly to ``/etc/bash_completion.d/``
|
||||
for convenience, and the command may need superuser rights, e.g.
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ sudo restic autocomplete
|
||||
The "generate" command writes automatically generated files like the man pages
|
||||
and the auto-completion files for bash and zsh).
|
||||
|
||||
Usage:
|
||||
restic autocomplete [flags]
|
||||
restic generate [command] [flags]
|
||||
|
||||
Flags:
|
||||
--completionfile string autocompletion file (default "/etc/bash_completion.d/restic.sh")
|
||||
--bash-completion file write bash completion file
|
||||
-h, --help help for generate
|
||||
--man directory write man pages to directory
|
||||
--zsh-completion file write zsh completion file
|
||||
|
||||
Global Flags:
|
||||
--json set output mode to JSON for commands that support it
|
||||
--no-lock do not lock the repo, this allows some operations on read-only repos
|
||||
-o, --option key=value set extended option (key=value, can be specified multiple times)
|
||||
-p, --password-file string read the repository password from a file
|
||||
-q, --quiet do not output comprehensive progress report
|
||||
-r, --repo string repository to backup to or restore from (default: $RESTIC_REPOSITORY)
|
||||
Example for using sudo to write a bash completion script directly to the system-wide location:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ sudo ./restic generate --bash-completion /etc/bash_completion.d/restic
|
||||
writing bash completion file to /etc/bash_completion.d/restic
|
||||
|
||||
@@ -15,20 +15,24 @@ Preparing a new repository
|
||||
##########################
|
||||
|
||||
The place where your backups will be saved at is called a "repository".
|
||||
This chapter explains how to create ("init") such a repository.
|
||||
This chapter explains how to create ("init") such a repository. The repository
|
||||
can be stored locally, or on some remote server or service. We'll first cover
|
||||
using a local repository, the remaining sections of this chapter cover all the
|
||||
other options. You can skip to the next chapter once you've read the relevant
|
||||
section here.
|
||||
|
||||
Local
|
||||
*****
|
||||
|
||||
In order to create a repository at ``/tmp/backup``, run the following
|
||||
In order to create a repository at ``/srv/restic-repo``, run the following
|
||||
command and enter the same password twice:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ restic init --repo /tmp/backup
|
||||
$ restic init --repo /srv/restic-repo
|
||||
enter password for new backend:
|
||||
enter password again:
|
||||
created restic backend 085b3c76b9 at /tmp/backup
|
||||
created restic backend 085b3c76b9 at /srv/restic-repo
|
||||
Please note that knowledge of your password is required to access the repository.
|
||||
Losing your password means that your data is irrecoverably lost.
|
||||
|
||||
@@ -55,10 +59,10 @@ simply be achieved by changing the URL scheme in the ``init`` command:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ restic -r sftp:user@host:/tmp/backup init
|
||||
$ restic -r sftp:user@host:/srv/restic-repo init
|
||||
enter password for new backend:
|
||||
enter password again:
|
||||
created restic backend f1c6108821 at sftp:user@host:/tmp/backup
|
||||
created restic backend f1c6108821 at sftp:user@host:/srv/restic-repo
|
||||
Please note that knowledge of your password is required to access the repository.
|
||||
Losing your password means that your data is irrecoverably lost.
|
||||
|
||||
@@ -87,7 +91,7 @@ specify the user name in this case):
|
||||
|
||||
::
|
||||
|
||||
$ restic -r sftp:foo:/tmp/backup init
|
||||
$ restic -r sftp:foo:/srv/restic-repo init
|
||||
|
||||
You can also add an entry with a special host name which does not exist,
|
||||
just for use with restic, and use the ``Hostname`` option to set the
|
||||
@@ -104,7 +108,7 @@ Then use it in the backend specification:
|
||||
|
||||
::
|
||||
|
||||
$ restic -r sftp:restic-backup-host:/tmp/backup init
|
||||
$ restic -r sftp:restic-backup-host:/srv/restic-repo init
|
||||
|
||||
Last, if you'd like to use an entirely different program to create the
|
||||
SFTP connection, you can specify the command to be run with the option
|
||||
@@ -139,7 +143,10 @@ If you use TLS, restic will use the system's CA certificates to verify the
|
||||
server certificate. When the verification fails, restic refuses to proceed and
|
||||
exits with an error. If you have your own self-signed certificate, or a custom
|
||||
CA certificate should be used for verification, you can pass restic the
|
||||
certificate filename via the `--cacert` option.
|
||||
certificate filename via the ``--cacert`` option. It will then verify that the
|
||||
server's certificate is contained in the file passed to this option, or signed
|
||||
by a CA certificate in the file. In this case, the system CA certificates are
|
||||
not considered at all.
|
||||
|
||||
REST server uses exactly the same directory structure as local backend,
|
||||
so you should be able to access it both locally and via HTTP, even
|
||||
@@ -306,8 +313,8 @@ bucket does not exist yet, it will be created:
|
||||
Please note that knowledge of your password is required to access the repository.
|
||||
Losing your password means that your data is irrecoverably lost.
|
||||
|
||||
The number of concurrent connections to the B2 service can be set with the `-o
|
||||
b2.connections=10`. By default, at most five parallel connections are
|
||||
The number of concurrent connections to the B2 service can be set with the ``-o
|
||||
b2.connections=10``. By default, at most five parallel connections are
|
||||
established.
|
||||
|
||||
Microsoft Azure Blob Storage
|
||||
@@ -321,7 +328,7 @@ account name and key as follows:
|
||||
$ export AZURE_ACCOUNT_NAME=<ACCOUNT_NAME>
|
||||
$ export AZURE_ACCOUNT_KEY=<SECRET_KEY>
|
||||
|
||||
Afterwards you can initialize a repository in a container called `foo` in the
|
||||
Afterwards you can initialize a repository in a container called ``foo`` in the
|
||||
root path like this:
|
||||
|
||||
.. code-block:: console
|
||||
@@ -334,7 +341,7 @@ root path like this:
|
||||
[...]
|
||||
|
||||
The number of concurrent connections to the Azure Blob Storage service can be set with the
|
||||
`-o azure.connections=10`. By default, at most five parallel connections are
|
||||
``-o azure.connections=10``. By default, at most five parallel connections are
|
||||
established.
|
||||
|
||||
Google Cloud Storage
|
||||
@@ -362,8 +369,14 @@ key file and the project ID as follows:
|
||||
$ export GOOGLE_PROJECT_ID=123123123123
|
||||
$ export GOOGLE_APPLICATION_CREDENTIALS=$HOME/.config/gs-secret-restic-key.json
|
||||
|
||||
Then you can use the ``gs:`` backend type to create a new repository in the
|
||||
bucket `foo` at the root path:
|
||||
Restic uses Google's client library to generate [default authentication
|
||||
material](https://developers.google.com/identity/protocols/application-default-credentials),
|
||||
which means if you're running in Google Container Engine or are otherwise
|
||||
located on an instance with default service accounts then these should work out
|
||||
the box.
|
||||
|
||||
Once authenticated, you can use the ``gs:`` backend type to create a new
|
||||
repository in the bucket ``foo`` at the root path:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
@@ -375,12 +388,117 @@ bucket `foo` at the root path:
|
||||
[...]
|
||||
|
||||
The number of concurrent connections to the GCS service can be set with the
|
||||
`-o gs.connections=10`. By default, at most five parallel connections are
|
||||
``-o gs.connections=10``. By default, at most five parallel connections are
|
||||
established.
|
||||
|
||||
.. _service account: https://cloud.google.com/storage/docs/authentication#service_accounts
|
||||
.. _create a service account key: https://cloud.google.com/storage/docs/authentication#generating-a-private-key
|
||||
|
||||
Other Services via rclone
|
||||
*************************
|
||||
|
||||
The program `rclone`_ can be used to access many other different services and
|
||||
store data there. First, you need to install and `configure`_ rclone. The
|
||||
general backend specification format is ``rclone:<remote>:<path>``, the
|
||||
``<remote>:<path>`` component will be directly passed to rclone. When you
|
||||
configure a remote named ``foo``, you can then call restic as follows to
|
||||
initiate a new repository in the path ``bar`` in the repo:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ restic -r rclone:foo:bar init
|
||||
|
||||
Restic takes care of starting and stopping rclone.
|
||||
|
||||
As a more concrete example, suppose you have configured a remote named
|
||||
``b2prod`` for Backblaze B2 with rclone, with a bucket called ``yggdrasil``.
|
||||
You can then use rclone to list files in the bucket like this:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ rclone ls b2prod:yggdrasil
|
||||
|
||||
In order to create a new repository in the root directory of the bucket, call
|
||||
restic like this:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ restic -r rclone:b2prod:yggdrasil init
|
||||
|
||||
If you want to use the path ``foo/bar/baz`` in the bucket instead, pass this to
|
||||
restic:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ restic -r rclone:b2prod:yggdrasil/foo/bar/baz init
|
||||
|
||||
Listing the files of an empty repository directly with rclone should return a
|
||||
listing similar to the following:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ rclone ls b2prod:yggdrasil/foo/bar/baz
|
||||
155 bar/baz/config
|
||||
448 bar/baz/keys/4bf9c78049de689d73a56ed0546f83b8416795295cda12ec7fb9465af3900b44
|
||||
|
||||
Rclone can be `configured with environment variables`_, so for instance
|
||||
configuring a bandwidth limit for rclone can be achieved by setting the
|
||||
``RCLONE_BWLIMIT`` environment variable:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ export RCLONE_BWLIMIT=1M
|
||||
|
||||
For debugging rclone, you can set the environment variable ``RCLONE_VERBOSE=2``.
|
||||
|
||||
The rclone backend has two additional options:
|
||||
|
||||
* ``-o rclone.program`` specifies the path to rclone, the default value is just ``rclone``
|
||||
* ``-o rclone.args`` allows setting the arguments passed to rclone, by default this is ``serve restic --stdio --b2-hard-delete --drive-use-trash=false``
|
||||
|
||||
The reason for the two last parameters (``--b2-hard-delete`` and
|
||||
``--drive-use-trash=false``) can be found in the corresponding GitHub `issue #1657`_.
|
||||
|
||||
In order to start rclone, restic will build a list of arguments by joining the
|
||||
following lists (in this order): ``rclone.program``, ``rclone.args`` and as the
|
||||
last parameter the value that follows the ``rclone:`` prefix of the repository
|
||||
specification.
|
||||
|
||||
So, calling restic like this
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ restic -o rclone.program="/path/to/rclone" \
|
||||
-o rclone.args="serve restic --stdio --bwlimit 1M --b2-hard-delete --verbose" \
|
||||
-r rclone:b2:foo/bar
|
||||
|
||||
runs rclone as follows:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ /path/to/rclone serve restic --stdio --bwlimit 1M --b2-hard-delete --verbose b2:foo/bar
|
||||
|
||||
Manually setting ``rclone.program`` also allows running a remote instance of
|
||||
rclone e.g. via SSH on a server, for example:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ restic -o rclone.program="ssh user@host rclone" -r rclone:b2:foo/bar
|
||||
|
||||
The rclone command may also be hard-coded in the SSH configuration or the
|
||||
user's public key, in this case it may be sufficient to just start the SSH
|
||||
connection (and it's irrelevant what's passed after ``rclone:`` in the
|
||||
repository specification):
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ restic -o rclone.program="ssh user@host" -r rclone:x
|
||||
|
||||
.. _rclone: https://rclone.org/
|
||||
.. _configure: https://rclone.org/docs/
|
||||
.. _configured with environment variables: https://rclone.org/docs/#environment-variables
|
||||
.. _issue #1657: https://github.com/restic/restic/pull/1657#issuecomment-377707486
|
||||
|
||||
Password prompt on Windows
|
||||
**************************
|
||||
|
||||
@@ -398,5 +516,5 @@ On MSYS2, you can install ``winpty`` as follows:
|
||||
.. code-block:: console
|
||||
|
||||
$ pacman -S winpty
|
||||
$ winpty restic -r /tmp/backup init
|
||||
$ winpty restic -r /srv/restic-repo init
|
||||
|
||||
|
||||
@@ -21,43 +21,88 @@ again:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ restic -r /tmp/backup backup ~/work
|
||||
$ restic -r /srv/restic-repo --verbose backup ~/work
|
||||
open repository
|
||||
enter password for repository:
|
||||
scan [/home/user/work]
|
||||
scanned 764 directories, 1816 files in 0:00
|
||||
[0:29] 100.00% 54.732 MiB/s 1.582 GiB / 1.582 GiB 2580 / 2580 items 0 errors ETA 0:00
|
||||
duration: 0:29, 54.47MiB/s
|
||||
password is correct
|
||||
lock repository
|
||||
load index files
|
||||
start scan
|
||||
start backup
|
||||
scan finished in 1.837s
|
||||
processed 1.720 GiB in 0:12
|
||||
Files: 5307 new, 0 changed, 0 unmodified
|
||||
Dirs: 1867 new, 0 changed, 0 unmodified
|
||||
Added: 1.700 GiB
|
||||
snapshot 40dc1520 saved
|
||||
|
||||
As you can see, restic created a backup of the directory and was pretty
|
||||
fast! The specific snapshot just created is identified by a sequence of
|
||||
hexadecimal characters, ``40dc1520`` in this case.
|
||||
|
||||
If you don't pass the ``--verbose`` option, restic will print less data (but
|
||||
you'll still get a nice live status display).
|
||||
|
||||
If you run the command again, restic will create another snapshot of
|
||||
your data, but this time it's even faster. This is de-duplication at
|
||||
work!
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ restic -r /tmp/backup backup ~/work
|
||||
$ restic -r /srv/restic-repo backup --verbose ~/work
|
||||
open repository
|
||||
enter password for repository:
|
||||
using parent snapshot 40dc1520aa6a07b7b3ae561786770a01951245d2367241e71e9485f18ae8228c
|
||||
scan [/home/user/work]
|
||||
scanned 764 directories, 1816 files in 0:00
|
||||
[0:00] 100.00% 0B/s 1.582 GiB / 1.582 GiB 2580 / 2580 items 0 errors ETA 0:00
|
||||
duration: 0:00, 6572.38MiB/s
|
||||
password is correct
|
||||
lock repository
|
||||
load index files
|
||||
using parent snapshot d875ae93
|
||||
start scan
|
||||
start backup
|
||||
scan finished in 1.881s
|
||||
processed 1.720 GiB in 0:03
|
||||
Files: 0 new, 0 changed, 5307 unmodified
|
||||
Dirs: 0 new, 0 changed, 1867 unmodified
|
||||
Added: 0 B
|
||||
snapshot 79766175 saved
|
||||
|
||||
You can even backup individual files in the same repository.
|
||||
You can even backup individual files in the same repository (not passing
|
||||
``--verbose`` means less output):
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ restic -r /tmp/backup backup ~/work.txt
|
||||
scan [/home/user/work.txt]
|
||||
scanned 0 directories, 1 files in 0:00
|
||||
[0:00] 100.00% 0B/s 220B / 220B 1 / 1 items 0 errors ETA 0:00
|
||||
duration: 0:00, 0.03MiB/s
|
||||
snapshot 31f7bd63 saved
|
||||
$ restic -r /srv/restic-repo backup ~/work.txt
|
||||
enter password for repository:
|
||||
password is correct
|
||||
snapshot 249d0210 saved
|
||||
|
||||
If you're interested in what restic does, pass ``--verbose`` twice (or
|
||||
``--verbose 2``) to display detailed information about each file and directory
|
||||
restic encounters:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ echo 'more data foo bar' >> ~/work.txt
|
||||
|
||||
$ restic -r /srv/restic-repo backup --verbose --verbose ~/work.txt
|
||||
open repository
|
||||
enter password for repository:
|
||||
password is correct
|
||||
lock repository
|
||||
load index files
|
||||
using parent snapshot f3f8d56b
|
||||
start scan
|
||||
start backup
|
||||
scan finished in 2.115s
|
||||
modified /home/user/work.txt, saved in 0.007s (22 B added)
|
||||
modified /home/user/, saved in 0.008s (0 B added, 378 B metadata)
|
||||
modified /home/, saved in 0.009s (0 B added, 375 B metadata)
|
||||
processed 22 B in 0:02
|
||||
Files: 0 new, 1 changed, 0 unmodified
|
||||
Dirs: 0 new, 2 changed, 0 unmodified
|
||||
Data Blobs: 1 new
|
||||
Tree Blobs: 3 new
|
||||
Added: 1.116 KiB
|
||||
snapshot 8dc503fc saved
|
||||
|
||||
In fact several hosts may use the same repository to backup directories
|
||||
and files leading to a greater de-duplication.
|
||||
@@ -75,6 +120,9 @@ Now is a good time to run ``restic check`` to verify that all data
|
||||
is properly stored in the repository. You should run this command regularly
|
||||
to make sure the internal structure of the repository is free of errors.
|
||||
|
||||
Including and Excluding Files
|
||||
*****************************
|
||||
|
||||
You can exclude folders and files by specifying exclude patterns, currently
|
||||
the exclude options are:
|
||||
|
||||
@@ -84,33 +132,54 @@ the exclude options are:
|
||||
- ``--exclude-if-present`` Specified one or more times to exclude a folders content
|
||||
if it contains a given file (optionally having a given header)
|
||||
|
||||
Basic example:
|
||||
Let's say we have a file called ``excludes.txt`` with the following content:
|
||||
|
||||
.. code-block:: console
|
||||
::
|
||||
|
||||
$ cat exclude
|
||||
# exclude go-files
|
||||
*.go
|
||||
# exclude foo/x/y/z/bar foo/x/bar foo/bar
|
||||
foo/**/bar
|
||||
$ restic -r /tmp/backup backup ~/work --exclude=*.c --exclude-file=exclude
|
||||
|
||||
It can be used like this:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ restic -r /srv/restic-repo backup ~/work --exclude="*.c" --exclude-file=excludes.txt
|
||||
|
||||
This instruct restic to exclude files matching the following criteria:
|
||||
|
||||
* All files matching ``*.go`` (second line in ``excludes.txt``)
|
||||
* All files and sub-directories named ``bar`` which reside somewhere below a directory called ``foo`` (fourth line in ``excludes.txt``)
|
||||
* All files matching ``*.c`` (parameter ``--exclude``)
|
||||
|
||||
Please see ``restic help backup`` for more specific information about each exclude option.
|
||||
|
||||
Patterns use `filepath.Glob <https://golang.org/pkg/path/filepath/#Glob>`__ internally,
|
||||
see `filepath.Match <https://golang.org/pkg/path/filepath/#Match>`__ for syntax.
|
||||
Patterns are tested against the full path of a file/dir to be saved, not only
|
||||
against the relative path below the argument given to restic backup.
|
||||
Patterns need to match on complete path components. (``foo`` matches
|
||||
``/dir1/foo/dir2/file`` and ``/dir/foo`` but does not match ``/dir/foobar`` or
|
||||
``barfoo``.) A trailing ``/`` is ignored. A leading ``/`` anchors the
|
||||
pattern at the root directory. (``/bin`` matches ``/bin/bash`` but does not
|
||||
match ``/usr/bin/restic``.) Regular wildcards cannot be used to match over the
|
||||
directory separator ``/``. (``b*ash`` matches ``/bin/bash`` but does not match
|
||||
``/bin/ash``.) However ``**`` matches arbitrary subdirectories. (``foo/**/bar``
|
||||
matches ``/dir1/foo/dir2/bar/file``, ``/foo/bar/file`` and ``/tmp/foo/bar``.)
|
||||
Environment-variables in exclude-files are expanded with
|
||||
`os.ExpandEnv <https://golang.org/pkg/os/#ExpandEnv>`__.
|
||||
see `filepath.Match <https://golang.org/pkg/path/filepath/#Match>`__ for
|
||||
syntax. Patterns are tested against the full path of a file/dir to be saved,
|
||||
even if restic is passed a relative path to save. Environment-variables in
|
||||
exclude-files are expanded with `os.ExpandEnv <https://golang.org/pkg/os/#ExpandEnv>`__.
|
||||
|
||||
Patterns need to match on complete path components. For example, the pattern ``foo``:
|
||||
|
||||
* matches ``/dir1/foo/dir2/file`` and ``/dir/foo``
|
||||
* does not match ``/dir/foobar`` or ``barfoo``
|
||||
|
||||
A trailing ``/`` is ignored, a leading ``/`` anchors the
|
||||
pattern at the root directory. This means, ``/bin`` matches ``/bin/bash`` but
|
||||
does not match ``/usr/bin/restic``.
|
||||
|
||||
Regular wildcards cannot be used to match over the
|
||||
directory separator ``/``. For example: ``b*ash`` matches ``/bin/bash`` but does not match
|
||||
``/bin/ash``.
|
||||
|
||||
For this, the special wildcard ``**`` can be used to match arbitrary
|
||||
sub-directories: The pattern ``foo/**/bar`` matches:
|
||||
|
||||
* ``/dir1/foo/dir2/bar/file``
|
||||
* ``/foo/bar/file``
|
||||
* ``/tmp/foo/bar``
|
||||
|
||||
By specifying the option ``--one-file-system`` you can instruct restic
|
||||
to only backup files from the file systems the initially specified files
|
||||
@@ -119,15 +188,15 @@ backup ``/sys`` or ``/dev`` on a Linux system:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ restic -r /tmp/backup backup --one-file-system /
|
||||
$ restic -r /srv/restic-repo backup --one-file-system /
|
||||
|
||||
By using the ``--files-from`` option you can read the files you want to
|
||||
backup from a file. This is especially useful if a lot of files have to
|
||||
be backed up that are not in the same folder or are maybe pre-filtered
|
||||
by other software.
|
||||
|
||||
or example maybe you want to backup files that have a certain filename
|
||||
in them:
|
||||
For example maybe you want to backup files which have a name that matches a
|
||||
certain pattern:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
@@ -137,14 +206,16 @@ You can then use restic to backup the filtered files:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ restic -r /tmp/backup backup --files-from /tmp/files_to_backup
|
||||
$ restic -r /srv/restic-repo backup --files-from /tmp/files_to_backup
|
||||
|
||||
Incidentally you can also combine ``--files-from`` with the normal files
|
||||
args:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ restic -r /tmp/backup backup --files-from /tmp/files_to_backup /tmp/some_additional_file
|
||||
$ restic -r /srv/restic-repo backup --files-from /tmp/files_to_backup /tmp/some_additional_file
|
||||
|
||||
Paths in the listing file can be absolute or relative.
|
||||
|
||||
Comparing Snapshots
|
||||
*******************
|
||||
@@ -154,7 +225,7 @@ and displays a small statistic, just pass the command two snapshot IDs:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ restic -r /tmp/backup diff 5845b002 2ab627a6
|
||||
$ restic -r /srv/restic-repo diff 5845b002 2ab627a6
|
||||
password is correct
|
||||
comparing snapshot ea657ce5 to 2ab627a6:
|
||||
|
||||
@@ -201,7 +272,7 @@ this mode of operation, just supply the option ``--stdin`` to the
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ mysqldump [...] | restic -r /tmp/backup backup --stdin
|
||||
$ mysqldump [...] | restic -r /srv/restic-repo backup --stdin
|
||||
|
||||
This creates a new snapshot of the output of ``mysqldump``. You can then
|
||||
use e.g. the fuse mounting option (see below) to mount the repository
|
||||
@@ -212,7 +283,7 @@ specified with ``--stdin-filename``, e.g. like this:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ mysqldump [...] | restic -r /tmp/backup backup --stdin --stdin-filename production.sql
|
||||
$ mysqldump [...] | restic -r /srv/restic-repo backup --stdin --stdin-filename production.sql
|
||||
|
||||
Tags for backup
|
||||
***************
|
||||
@@ -222,7 +293,7 @@ information. Just specify the tags for a snapshot one by one with ``--tag``:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ restic -r /tmp/backup backup --tag projectX --tag foo --tag bar ~/work
|
||||
$ restic -r /srv/restic-repo backup --tag projectX --tag foo --tag bar ~/work
|
||||
[...]
|
||||
|
||||
The tags can later be used to keep (or forget) snapshots with the ``forget``
|
||||
|
||||
@@ -22,7 +22,7 @@ Now, you can list all the snapshots stored in the repository:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ restic -r /tmp/backup snapshots
|
||||
$ restic -r /srv/restic-repo snapshots
|
||||
enter password for repository:
|
||||
ID Date Host Tags Directory
|
||||
----------------------------------------------------------------------
|
||||
@@ -36,7 +36,7 @@ You can filter the listing by directory path:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ restic -r /tmp/backup snapshots --path="/srv"
|
||||
$ restic -r /srv/restic-repo snapshots --path="/srv"
|
||||
enter password for repository:
|
||||
ID Date Host Tags Directory
|
||||
----------------------------------------------------------------------
|
||||
@@ -47,7 +47,7 @@ Or filter by host:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ restic -r /tmp/backup snapshots --host luigi
|
||||
$ restic -r /srv/restic-repo snapshots --host luigi
|
||||
enter password for repository:
|
||||
ID Date Host Tags Directory
|
||||
----------------------------------------------------------------------
|
||||
@@ -74,7 +74,7 @@ backup data is consistent and the integrity is unharmed:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ restic -r /tmp/backup check
|
||||
$ restic -r /srv/restic-repo check
|
||||
Load indexes
|
||||
ciphertext verification failed
|
||||
|
||||
@@ -83,7 +83,7 @@ yield the same error:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ restic -r /tmp/backup restore 79766175 --target /tmp/restore-work
|
||||
$ restic -r /srv/restic-repo restore 79766175 --target /tmp/restore-work
|
||||
Load indexes
|
||||
ciphertext verification failed
|
||||
|
||||
@@ -93,7 +93,7 @@ data files:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ restic -r /tmp/backup check --read-data
|
||||
$ restic -r /srv/restic-repo check --read-data
|
||||
load indexes
|
||||
check all packs
|
||||
check snapshots, trees and blobs
|
||||
@@ -107,9 +107,9 @@ commands check all repository data files over 5 separate invocations:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ restic -r /tmp/backup check --read-data-subset=1/5
|
||||
$ restic -r /tmp/backup check --read-data-subset=2/5
|
||||
$ restic -r /tmp/backup check --read-data-subset=3/5
|
||||
$ restic -r /tmp/backup check --read-data-subset=4/5
|
||||
$ restic -r /tmp/backup check --read-data-subset=5/5
|
||||
$ restic -r /srv/restic-repo check --read-data-subset=1/5
|
||||
$ restic -r /srv/restic-repo check --read-data-subset=2/5
|
||||
$ restic -r /srv/restic-repo check --read-data-subset=3/5
|
||||
$ restic -r /srv/restic-repo check --read-data-subset=4/5
|
||||
$ restic -r /srv/restic-repo check --read-data-subset=5/5
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ command to restore the contents of the latest snapshot to
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ restic -r /tmp/backup restore 79766175 --target /tmp/restore-work
|
||||
$ restic -r /srv/restic-repo restore 79766175 --target /tmp/restore-work
|
||||
enter password for repository:
|
||||
restoring <Snapshot of [/home/user/work] at 2015-05-08 21:40:19.884408621 +0200 CEST> to /tmp/restore-work
|
||||
|
||||
@@ -33,7 +33,7 @@ backup for a specific host, path or both.
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ restic -r /tmp/backup restore latest --target /tmp/restore-art --path "/home/art" --host luigi
|
||||
$ restic -r /srv/restic-repo restore latest --target /tmp/restore-art --path "/home/art" --host luigi
|
||||
enter password for repository:
|
||||
restoring <Snapshot of [/home/art] at 2015-05-08 21:45:17.884408621 +0200 CEST> to /tmp/restore-art
|
||||
|
||||
@@ -42,7 +42,7 @@ files in the snapshot. For example, to restore a single file:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ restic -r /tmp/backup restore 79766175 --target /tmp/restore-work --include /work/foo
|
||||
$ restic -r /srv/restic-repo restore 79766175 --target /tmp/restore-work --include /work/foo
|
||||
enter password for repository:
|
||||
restoring <Snapshot of [/home/user/work] at 2015-05-08 21:40:19.884408621 +0200 CEST> to /tmp/restore-work
|
||||
|
||||
@@ -58,12 +58,13 @@ command to serve the repository with FUSE:
|
||||
.. code-block:: console
|
||||
|
||||
$ mkdir /mnt/restic
|
||||
$ restic -r /tmp/backup mount /mnt/restic
|
||||
$ restic -r /srv/restic-repo mount /mnt/restic
|
||||
enter password for repository:
|
||||
Now serving /tmp/backup at /mnt/restic
|
||||
Now serving /srv/restic-repo at /mnt/restic
|
||||
Don't forget to umount after quitting!
|
||||
|
||||
Mounting repositories via FUSE is not possible on Windows and OpenBSD.
|
||||
Mounting repositories via FUSE is not possible on OpenBSD, Solaris/illumos
|
||||
and Windows.
|
||||
|
||||
Restic supports storage and preservation of hard links. However, since
|
||||
hard links exist in the scope of a filesystem by definition, restoring
|
||||
@@ -79,4 +80,4 @@ the data directly. This can be achieved by using the `dump` command, like this:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ restic -r /tmp/backup dump latest production.sql | mysql
|
||||
$ restic -r /srv/restic-repo dump latest production.sql | mysql
|
||||
|
||||
@@ -35,7 +35,7 @@ repository like this:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ restic -r /tmp/backup snapshots
|
||||
$ restic -r /srv/restic-repo snapshots
|
||||
enter password for repository:
|
||||
ID Date Host Tags Directory
|
||||
----------------------------------------------------------------------
|
||||
@@ -50,7 +50,7 @@ command and specify the snapshot ID on the command line:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ restic -r /tmp/backup forget bdbd3439
|
||||
$ restic -r /srv/restic-repo forget bdbd3439
|
||||
enter password for repository:
|
||||
removed snapshot d3f01f63
|
||||
|
||||
@@ -58,7 +58,7 @@ Afterwards this snapshot is removed:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ restic -r /tmp/backup snapshots
|
||||
$ restic -r /srv/restic-repo snapshots
|
||||
enter password for repository:
|
||||
ID Date Host Tags Directory
|
||||
----------------------------------------------------------------------
|
||||
@@ -73,7 +73,7 @@ command must be run:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ restic -r /tmp/backup prune
|
||||
$ restic -r /srv/restic-repo prune
|
||||
enter password for repository:
|
||||
|
||||
counting files in repo
|
||||
@@ -159,6 +159,10 @@ The ``forget`` command accepts the following parameters:
|
||||
snapshots, only keep the last one for that year.
|
||||
- ``--keep-tag`` keep all snapshots which have all tags specified by
|
||||
this option (can be specified multiple times).
|
||||
- ``--keep-within duration`` keep all snapshots which have been made within
|
||||
the duration of the latest snapshot. ``duration`` needs to be a number of
|
||||
years, months, and days, e.g. ``2y5m7d`` will keep all snapshots made in the
|
||||
two years, five months, and seven days before the latest snapshot.
|
||||
|
||||
Additionally, you can restrict removing snapshots to those which have a
|
||||
particular hostname with the ``--hostname`` parameter, or tags with the
|
||||
|
||||
@@ -16,8 +16,8 @@ Encryption
|
||||
|
||||
|
||||
*"The design might not be perfect, but it’s good. Encryption is a first-class feature,
|
||||
the implementation looks sane and I guess the deduplication trade-off is worth it. So… I’m going to use restic for
|
||||
my personal backups.*" `Filippo Valsorda`_
|
||||
the implementation looks sane and I guess the deduplication trade-off is worth
|
||||
it. So… I’m going to use restic for my personal backups.*" `Filippo Valsorda`_
|
||||
|
||||
.. _Filippo Valsorda: https://blog.filippo.io/restic-cryptography/
|
||||
|
||||
@@ -31,19 +31,19 @@ per repository. In fact, you can use the ``list``, ``add``, ``remove``, and
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ restic -r /tmp/backup key list
|
||||
$ restic -r /srv/restic-repo key list
|
||||
enter password for repository:
|
||||
ID User Host Created
|
||||
----------------------------------------------------------------------
|
||||
*eb78040b username kasimir 2015-08-12 13:29:57
|
||||
|
||||
$ restic -r /tmp/backup key add
|
||||
$ restic -r /srv/restic-repo key add
|
||||
enter password for repository:
|
||||
enter password for new key:
|
||||
enter password again:
|
||||
saved new key as <Key of username@kasimir, created on 2015-08-12 13:35:05.316831933 +0200 CEST>
|
||||
|
||||
$ restic -r backup key list
|
||||
$ restic -r /srv/restic-repo key list
|
||||
enter password for repository:
|
||||
ID User Host Created
|
||||
----------------------------------------------------------------------
|
||||
|
||||
39
doc/075_scripting.rst
Normal file
39
doc/075_scripting.rst
Normal file
@@ -0,0 +1,39 @@
|
||||
..
|
||||
Normally, there are no heading levels assigned to certain characters as the structure is
|
||||
determined from the succession of headings. However, this convention is used in Python’s
|
||||
Style Guide for documenting which you may follow:
|
||||
|
||||
# with overline, for parts
|
||||
* for chapters
|
||||
= for sections
|
||||
- for subsections
|
||||
^ for subsubsections
|
||||
" for paragraphs
|
||||
|
||||
#########################
|
||||
Scripting
|
||||
#########################
|
||||
|
||||
This is a list of how certain tasks may be accomplished when you use
|
||||
restic via scripts.
|
||||
|
||||
Check if a repository is already initialized
|
||||
********************************************
|
||||
|
||||
You may find a need to check if a repository is already initialized,
|
||||
perhaps to prevent your script from initializing a repository multiple
|
||||
times. The command ``snapshots`` may be used for this purpose:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ restic -r /srv/restic-repo snapshots
|
||||
Fatal: unable to open config file: Stat: stat /srv/restic-repo/config: no such file or directory
|
||||
Is there a repository at the following location?
|
||||
/srv/restic-repo
|
||||
|
||||
If a repository does not exist, restic will return a non-zero exit code
|
||||
and print an error message. Note that restic will also return a non-zero
|
||||
exit code if a different error is encountered (e.g.: incorrect password
|
||||
to ``snapshots``) and it may print a different error message. If there
|
||||
are no errors, restic will return a zero exit code and print all the
|
||||
snapshots.
|
||||
@@ -625,14 +625,15 @@ are deleted, the particular snapshot vanished and all snapshots
|
||||
depending on data that has been added in the snapshot cannot be restored
|
||||
completely. Restic is not designed to detect this attack.
|
||||
|
||||
******
|
||||
Local Cache
|
||||
===========
|
||||
******
|
||||
|
||||
In order to speed up certain operations, restic manages a local cache of data.
|
||||
This document describes the data structures for the local cache with version 1.
|
||||
|
||||
Versions
|
||||
--------
|
||||
========
|
||||
|
||||
The cache directory is selected according to the `XDG base dir specification
|
||||
<http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html>`__.
|
||||
@@ -646,12 +647,21 @@ a lower version number is found the cache is recreated with the current
|
||||
version. If a higher version number is found the cache is ignored and left as
|
||||
is.
|
||||
|
||||
Snapshots and Indexes
|
||||
---------------------
|
||||
Snapshots, Data and Indexes
|
||||
===========================
|
||||
|
||||
Snapshot, Data and Index files are cached in the sub-directories ``snapshots``,
|
||||
``data`` and ``index``, as read from the repository.
|
||||
|
||||
Expiry
|
||||
======
|
||||
|
||||
Whenever a cache directory for a repo is used, that directory's modification
|
||||
timestamp is updated to the current time. By looking at the modification
|
||||
timestamps of the repo cache directories it is easy to decide which directories
|
||||
are old and haven't been used in a long time. Those are probably stale and can
|
||||
be removed.
|
||||
|
||||
|
||||
************
|
||||
REST Backend
|
||||
@@ -672,8 +682,8 @@ The following values are valid for ``{type}``:
|
||||
The API version is selected via the ``Accept`` HTTP header in the request. The
|
||||
following values are defined:
|
||||
|
||||
* ``application/vnd.x.restic.rest.v1+json`` or empty: Select API version 1
|
||||
* ``application/vnd.x.restic.rest.v2+json``: Select API version 2
|
||||
* ``application/vnd.x.restic.rest.v1`` or empty: Select API version 1
|
||||
* ``application/vnd.x.restic.rest.v2``: Select API version 2
|
||||
|
||||
The server will respond with the value of the highest version it supports in
|
||||
the ``Content-Type`` HTTP response header for the HTTP requests which should
|
||||
@@ -681,7 +691,7 @@ return JSON. Any different value for this header means API version 1.
|
||||
|
||||
The placeholder ``{path}`` in this document is a path to the repository, so
|
||||
that multiple different repositories can be accessed. The default path is
|
||||
``/``.
|
||||
``/``. The path must end with a slash.
|
||||
|
||||
POST {path}?create=true
|
||||
=======================
|
||||
@@ -798,24 +808,3 @@ Returns "200 OK" if the blob with the given name and type has been
|
||||
deleted from the repository, an HTTP error otherwise.
|
||||
|
||||
|
||||
*****
|
||||
Talks
|
||||
*****
|
||||
|
||||
The following talks will be or have been given about restic:
|
||||
|
||||
- 2016-01-31: Lightning Talk at the Go Devroom at FOSDEM 2016,
|
||||
Brussels, Belgium
|
||||
- 2016-01-29: `restic - Backups mal
|
||||
richtig <https://media.ccc.de/v/c4.openchaos.2016.01.restic>`__:
|
||||
Public lecture in German at `CCC Cologne
|
||||
e.V. <https://koeln.ccc.de>`__ in Cologne, Germany
|
||||
- 2015-08-23: `A Solution to the Backup
|
||||
Inconvenience <https://programm.froscon.de/2015/events/1515.html>`__:
|
||||
Lecture at `FROSCON 2015 <https://www.froscon.de>`__ in Bonn, Germany
|
||||
- 2015-02-01: `Lightning Talk at FOSDEM
|
||||
2015 <https://www.youtube.com/watch?v=oM-MfeflUZ8&t=11m40s>`__: A
|
||||
short introduction (with slightly outdated command line)
|
||||
- 2015-01-27: `Talk about restic at CCC
|
||||
Aachen <https://videoag.fsmpi.rwth-aachen.de/?view=player&lectureid=4442#content>`__
|
||||
(in German)
|
||||
|
||||
34
doc/110_talks.rst
Normal file
34
doc/110_talks.rst
Normal file
@@ -0,0 +1,34 @@
|
||||
..
|
||||
Normally, there are no heading levels assigned to certain characters as the structure is
|
||||
determined from the succession of headings. However, this convention is used in Python’s
|
||||
Style Guide for documenting which you may follow:
|
||||
|
||||
# with overline, for parts
|
||||
* for chapters
|
||||
= for sections
|
||||
- for subsections
|
||||
^ for subsubsections
|
||||
" for paragraphs
|
||||
|
||||
|
||||
#####
|
||||
Talks
|
||||
#####
|
||||
|
||||
The following talks will be or have been given about restic:
|
||||
|
||||
- 2016-01-31: Lightning Talk at the Go Devroom at FOSDEM 2016,
|
||||
Brussels, Belgium
|
||||
- 2016-01-29: `restic - Backups mal
|
||||
richtig <https://media.ccc.de/v/c4.openchaos.2016.01.restic>`__:
|
||||
Public lecture in German at `CCC Cologne
|
||||
e.V. <https://koeln.ccc.de>`__ in Cologne, Germany
|
||||
- 2015-08-23: `A Solution to the Backup
|
||||
Inconvenience <https://programm.froscon.de/2015/events/1515.html>`__:
|
||||
Lecture at `FROSCON 2015 <https://www.froscon.de>`__ in Bonn, Germany
|
||||
- 2015-02-01: `Lightning Talk at FOSDEM
|
||||
2015 <https://www.youtube.com/watch?v=oM-MfeflUZ8&t=11m40s>`__: A
|
||||
short introduction (with slightly outdated command line)
|
||||
- 2015-01-27: `Talk about restic at CCC
|
||||
Aachen <https://videoag.fsmpi.rwth-aachen.de/?view=player&lectureid=4442#content>`__
|
||||
(in German)
|
||||
@@ -1,6 +1,6 @@
|
||||
# bash completion for restic -*- shell-script -*-
|
||||
|
||||
__debug()
|
||||
__restic_debug()
|
||||
{
|
||||
if [[ -n ${BASH_COMP_DEBUG_FILE} ]]; then
|
||||
echo "$*" >> "${BASH_COMP_DEBUG_FILE}"
|
||||
@@ -9,13 +9,13 @@ __debug()
|
||||
|
||||
# Homebrew on Macs have version 1.3 of bash-completion which doesn't include
|
||||
# _init_completion. This is a very minimal version of that function.
|
||||
__my_init_completion()
|
||||
__restic_init_completion()
|
||||
{
|
||||
COMPREPLY=()
|
||||
_get_comp_words_by_ref "$@" cur prev words cword
|
||||
}
|
||||
|
||||
__index_of_word()
|
||||
__restic_index_of_word()
|
||||
{
|
||||
local w word=$1
|
||||
shift
|
||||
@@ -27,7 +27,7 @@ __index_of_word()
|
||||
index=-1
|
||||
}
|
||||
|
||||
__contains_word()
|
||||
__restic_contains_word()
|
||||
{
|
||||
local w word=$1; shift
|
||||
for w in "$@"; do
|
||||
@@ -36,9 +36,9 @@ __contains_word()
|
||||
return 1
|
||||
}
|
||||
|
||||
__handle_reply()
|
||||
__restic_handle_reply()
|
||||
{
|
||||
__debug "${FUNCNAME[0]}"
|
||||
__restic_debug "${FUNCNAME[0]}"
|
||||
case $cur in
|
||||
-*)
|
||||
if [[ $(type -t compopt) = "builtin" ]]; then
|
||||
@@ -62,8 +62,8 @@ __handle_reply()
|
||||
fi
|
||||
|
||||
local index flag
|
||||
flag="${cur%%=*}"
|
||||
__index_of_word "${flag}" "${flags_with_completion[@]}"
|
||||
flag="${cur%=*}"
|
||||
__restic_index_of_word "${flag}" "${flags_with_completion[@]}"
|
||||
COMPREPLY=()
|
||||
if [[ ${index} -ge 0 ]]; then
|
||||
PREFIX=""
|
||||
@@ -81,7 +81,7 @@ __handle_reply()
|
||||
|
||||
# check if we are handling a flag with special work handling
|
||||
local index
|
||||
__index_of_word "${prev}" "${flags_with_completion[@]}"
|
||||
__restic_index_of_word "${prev}" "${flags_with_completion[@]}"
|
||||
if [[ ${index} -ge 0 ]]; then
|
||||
${flags_completion[${index}]}
|
||||
return
|
||||
@@ -114,24 +114,30 @@ __handle_reply()
|
||||
if declare -F __ltrim_colon_completions >/dev/null; then
|
||||
__ltrim_colon_completions "$cur"
|
||||
fi
|
||||
|
||||
# If there is only 1 completion and it is a flag with an = it will be completed
|
||||
# but we don't want a space after the =
|
||||
if [[ "${#COMPREPLY[@]}" -eq "1" ]] && [[ $(type -t compopt) = "builtin" ]] && [[ "${COMPREPLY[0]}" == --*= ]]; then
|
||||
compopt -o nospace
|
||||
fi
|
||||
}
|
||||
|
||||
# The arguments should be in the form "ext1|ext2|extn"
|
||||
__handle_filename_extension_flag()
|
||||
__restic_handle_filename_extension_flag()
|
||||
{
|
||||
local ext="$1"
|
||||
_filedir "@(${ext})"
|
||||
}
|
||||
|
||||
__handle_subdirs_in_dir_flag()
|
||||
__restic_handle_subdirs_in_dir_flag()
|
||||
{
|
||||
local dir="$1"
|
||||
pushd "${dir}" >/dev/null 2>&1 && _filedir -d && popd >/dev/null 2>&1
|
||||
}
|
||||
|
||||
__handle_flag()
|
||||
__restic_handle_flag()
|
||||
{
|
||||
__debug "${FUNCNAME[0]}: c is $c words[c] is ${words[c]}"
|
||||
__restic_debug "${FUNCNAME[0]}: c is $c words[c] is ${words[c]}"
|
||||
|
||||
# if a command required a flag, and we found it, unset must_have_one_flag()
|
||||
local flagname=${words[c]}
|
||||
@@ -139,30 +145,33 @@ __handle_flag()
|
||||
# if the word contained an =
|
||||
if [[ ${words[c]} == *"="* ]]; then
|
||||
flagvalue=${flagname#*=} # take in as flagvalue after the =
|
||||
flagname=${flagname%%=*} # strip everything after the =
|
||||
flagname=${flagname%=*} # strip everything after the =
|
||||
flagname="${flagname}=" # but put the = back
|
||||
fi
|
||||
__debug "${FUNCNAME[0]}: looking for ${flagname}"
|
||||
if __contains_word "${flagname}" "${must_have_one_flag[@]}"; then
|
||||
__restic_debug "${FUNCNAME[0]}: looking for ${flagname}"
|
||||
if __restic_contains_word "${flagname}" "${must_have_one_flag[@]}"; then
|
||||
must_have_one_flag=()
|
||||
fi
|
||||
|
||||
# if you set a flag which only applies to this command, don't show subcommands
|
||||
if __contains_word "${flagname}" "${local_nonpersistent_flags[@]}"; then
|
||||
if __restic_contains_word "${flagname}" "${local_nonpersistent_flags[@]}"; then
|
||||
commands=()
|
||||
fi
|
||||
|
||||
# keep flag value with flagname as flaghash
|
||||
if [ -n "${flagvalue}" ] ; then
|
||||
flaghash[${flagname}]=${flagvalue}
|
||||
elif [ -n "${words[ $((c+1)) ]}" ] ; then
|
||||
flaghash[${flagname}]=${words[ $((c+1)) ]}
|
||||
else
|
||||
flaghash[${flagname}]="true" # pad "true" for bool flag
|
||||
# flaghash variable is an associative array which is only supported in bash > 3.
|
||||
if [[ -z "${BASH_VERSION}" || "${BASH_VERSINFO[0]}" -gt 3 ]]; then
|
||||
if [ -n "${flagvalue}" ] ; then
|
||||
flaghash[${flagname}]=${flagvalue}
|
||||
elif [ -n "${words[ $((c+1)) ]}" ] ; then
|
||||
flaghash[${flagname}]=${words[ $((c+1)) ]}
|
||||
else
|
||||
flaghash[${flagname}]="true" # pad "true" for bool flag
|
||||
fi
|
||||
fi
|
||||
|
||||
# skip the argument to a two word flag
|
||||
if __contains_word "${words[c]}" "${two_word_flags[@]}"; then
|
||||
if __restic_contains_word "${words[c]}" "${two_word_flags[@]}"; then
|
||||
c=$((c+1))
|
||||
# if we are looking for a flags value, don't show commands
|
||||
if [[ $c -eq $cword ]]; then
|
||||
@@ -174,13 +183,13 @@ __handle_flag()
|
||||
|
||||
}
|
||||
|
||||
__handle_noun()
|
||||
__restic_handle_noun()
|
||||
{
|
||||
__debug "${FUNCNAME[0]}: c is $c words[c] is ${words[c]}"
|
||||
__restic_debug "${FUNCNAME[0]}: c is $c words[c] is ${words[c]}"
|
||||
|
||||
if __contains_word "${words[c]}" "${must_have_one_noun[@]}"; then
|
||||
if __restic_contains_word "${words[c]}" "${must_have_one_noun[@]}"; then
|
||||
must_have_one_noun=()
|
||||
elif __contains_word "${words[c]}" "${noun_aliases[@]}"; then
|
||||
elif __restic_contains_word "${words[c]}" "${noun_aliases[@]}"; then
|
||||
must_have_one_noun=()
|
||||
fi
|
||||
|
||||
@@ -188,42 +197,42 @@ __handle_noun()
|
||||
c=$((c+1))
|
||||
}
|
||||
|
||||
__handle_command()
|
||||
__restic_handle_command()
|
||||
{
|
||||
__debug "${FUNCNAME[0]}: c is $c words[c] is ${words[c]}"
|
||||
__restic_debug "${FUNCNAME[0]}: c is $c words[c] is ${words[c]}"
|
||||
|
||||
local next_command
|
||||
if [[ -n ${last_command} ]]; then
|
||||
next_command="_${last_command}_${words[c]//:/__}"
|
||||
else
|
||||
if [[ $c -eq 0 ]]; then
|
||||
next_command="_$(basename "${words[c]//:/__}")"
|
||||
next_command="_restic_root_command"
|
||||
else
|
||||
next_command="_${words[c]//:/__}"
|
||||
fi
|
||||
fi
|
||||
c=$((c+1))
|
||||
__debug "${FUNCNAME[0]}: looking for ${next_command}"
|
||||
__restic_debug "${FUNCNAME[0]}: looking for ${next_command}"
|
||||
declare -F "$next_command" >/dev/null && $next_command
|
||||
}
|
||||
|
||||
__handle_word()
|
||||
__restic_handle_word()
|
||||
{
|
||||
if [[ $c -ge $cword ]]; then
|
||||
__handle_reply
|
||||
__restic_handle_reply
|
||||
return
|
||||
fi
|
||||
__debug "${FUNCNAME[0]}: c is $c words[c] is ${words[c]}"
|
||||
__restic_debug "${FUNCNAME[0]}: c is $c words[c] is ${words[c]}"
|
||||
if [[ "${words[c]}" == -* ]]; then
|
||||
__handle_flag
|
||||
elif __contains_word "${words[c]}" "${commands[@]}"; then
|
||||
__handle_command
|
||||
elif [[ $c -eq 0 ]] && __contains_word "$(basename "${words[c]}")" "${commands[@]}"; then
|
||||
__handle_command
|
||||
__restic_handle_flag
|
||||
elif __restic_contains_word "${words[c]}" "${commands[@]}"; then
|
||||
__restic_handle_command
|
||||
elif [[ $c -eq 0 ]]; then
|
||||
__restic_handle_command
|
||||
else
|
||||
__handle_noun
|
||||
__restic_handle_noun
|
||||
fi
|
||||
__handle_word
|
||||
__restic_handle_word
|
||||
}
|
||||
|
||||
_restic_backup()
|
||||
@@ -288,6 +297,51 @@ _restic_backup()
|
||||
flags+=("--repo=")
|
||||
two_word_flags+=("-r")
|
||||
flags+=("--tls-client-cert=")
|
||||
flags+=("--verbose")
|
||||
flags+=("-v")
|
||||
|
||||
must_have_one_flag=()
|
||||
must_have_one_noun=()
|
||||
noun_aliases=()
|
||||
}
|
||||
|
||||
_restic_cache()
|
||||
{
|
||||
last_command="restic_cache"
|
||||
commands=()
|
||||
|
||||
flags=()
|
||||
two_word_flags=()
|
||||
local_nonpersistent_flags=()
|
||||
flags_with_completion=()
|
||||
flags_completion=()
|
||||
|
||||
flags+=("--cleanup")
|
||||
local_nonpersistent_flags+=("--cleanup")
|
||||
flags+=("--help")
|
||||
flags+=("-h")
|
||||
local_nonpersistent_flags+=("--help")
|
||||
flags+=("--max-age=")
|
||||
local_nonpersistent_flags+=("--max-age=")
|
||||
flags+=("--cacert=")
|
||||
flags+=("--cache-dir=")
|
||||
flags+=("--cleanup-cache")
|
||||
flags+=("--json")
|
||||
flags+=("--limit-download=")
|
||||
flags+=("--limit-upload=")
|
||||
flags+=("--no-cache")
|
||||
flags+=("--no-lock")
|
||||
flags+=("--option=")
|
||||
two_word_flags+=("-o")
|
||||
flags+=("--password-file=")
|
||||
two_word_flags+=("-p")
|
||||
flags+=("--quiet")
|
||||
flags+=("-q")
|
||||
flags+=("--repo=")
|
||||
two_word_flags+=("-r")
|
||||
flags+=("--tls-client-cert=")
|
||||
flags+=("--verbose")
|
||||
flags+=("-v")
|
||||
|
||||
must_have_one_flag=()
|
||||
must_have_one_noun=()
|
||||
@@ -325,6 +379,8 @@ _restic_cat()
|
||||
flags+=("--repo=")
|
||||
two_word_flags+=("-r")
|
||||
flags+=("--tls-client-cert=")
|
||||
flags+=("--verbose")
|
||||
flags+=("-v")
|
||||
|
||||
must_have_one_flag=()
|
||||
must_have_one_noun=()
|
||||
@@ -370,6 +426,8 @@ _restic_check()
|
||||
flags+=("--repo=")
|
||||
two_word_flags+=("-r")
|
||||
flags+=("--tls-client-cert=")
|
||||
flags+=("--verbose")
|
||||
flags+=("-v")
|
||||
|
||||
must_have_one_flag=()
|
||||
must_have_one_noun=()
|
||||
@@ -409,6 +467,8 @@ _restic_diff()
|
||||
flags+=("--repo=")
|
||||
two_word_flags+=("-r")
|
||||
flags+=("--tls-client-cert=")
|
||||
flags+=("--verbose")
|
||||
flags+=("-v")
|
||||
|
||||
must_have_one_flag=()
|
||||
must_have_one_noun=()
|
||||
@@ -453,6 +513,8 @@ _restic_dump()
|
||||
flags+=("--repo=")
|
||||
two_word_flags+=("-r")
|
||||
flags+=("--tls-client-cert=")
|
||||
flags+=("--verbose")
|
||||
flags+=("-v")
|
||||
|
||||
must_have_one_flag=()
|
||||
must_have_one_noun=()
|
||||
@@ -512,6 +574,8 @@ _restic_find()
|
||||
flags+=("--repo=")
|
||||
two_word_flags+=("-r")
|
||||
flags+=("--tls-client-cert=")
|
||||
flags+=("--verbose")
|
||||
flags+=("-v")
|
||||
|
||||
must_have_one_flag=()
|
||||
must_have_one_noun=()
|
||||
@@ -547,6 +611,8 @@ _restic_forget()
|
||||
flags+=("--keep-yearly=")
|
||||
two_word_flags+=("-y")
|
||||
local_nonpersistent_flags+=("--keep-yearly=")
|
||||
flags+=("--keep-within=")
|
||||
local_nonpersistent_flags+=("--keep-within=")
|
||||
flags+=("--keep-tag=")
|
||||
local_nonpersistent_flags+=("--keep-tag=")
|
||||
flags+=("--host=")
|
||||
@@ -588,6 +654,8 @@ _restic_forget()
|
||||
flags+=("--repo=")
|
||||
two_word_flags+=("-r")
|
||||
flags+=("--tls-client-cert=")
|
||||
flags+=("--verbose")
|
||||
flags+=("-v")
|
||||
|
||||
must_have_one_flag=()
|
||||
must_have_one_noun=()
|
||||
@@ -631,6 +699,8 @@ _restic_generate()
|
||||
flags+=("--repo=")
|
||||
two_word_flags+=("-r")
|
||||
flags+=("--tls-client-cert=")
|
||||
flags+=("--verbose")
|
||||
flags+=("-v")
|
||||
|
||||
must_have_one_flag=()
|
||||
must_have_one_noun=()
|
||||
@@ -668,6 +738,8 @@ _restic_init()
|
||||
flags+=("--repo=")
|
||||
two_word_flags+=("-r")
|
||||
flags+=("--tls-client-cert=")
|
||||
flags+=("--verbose")
|
||||
flags+=("-v")
|
||||
|
||||
must_have_one_flag=()
|
||||
must_have_one_noun=()
|
||||
@@ -688,6 +760,8 @@ _restic_key()
|
||||
flags+=("--help")
|
||||
flags+=("-h")
|
||||
local_nonpersistent_flags+=("--help")
|
||||
flags+=("--new-password-file=")
|
||||
local_nonpersistent_flags+=("--new-password-file=")
|
||||
flags+=("--cacert=")
|
||||
flags+=("--cache-dir=")
|
||||
flags+=("--cleanup-cache")
|
||||
@@ -705,6 +779,8 @@ _restic_key()
|
||||
flags+=("--repo=")
|
||||
two_word_flags+=("-r")
|
||||
flags+=("--tls-client-cert=")
|
||||
flags+=("--verbose")
|
||||
flags+=("-v")
|
||||
|
||||
must_have_one_flag=()
|
||||
must_have_one_noun=()
|
||||
@@ -742,6 +818,8 @@ _restic_list()
|
||||
flags+=("--repo=")
|
||||
two_word_flags+=("-r")
|
||||
flags+=("--tls-client-cert=")
|
||||
flags+=("--verbose")
|
||||
flags+=("-v")
|
||||
|
||||
must_have_one_flag=()
|
||||
must_have_one_noun=()
|
||||
@@ -789,6 +867,8 @@ _restic_ls()
|
||||
flags+=("--repo=")
|
||||
two_word_flags+=("-r")
|
||||
flags+=("--tls-client-cert=")
|
||||
flags+=("--verbose")
|
||||
flags+=("-v")
|
||||
|
||||
must_have_one_flag=()
|
||||
must_have_one_noun=()
|
||||
@@ -829,6 +909,8 @@ _restic_migrate()
|
||||
flags+=("--repo=")
|
||||
two_word_flags+=("-r")
|
||||
flags+=("--tls-client-cert=")
|
||||
flags+=("--verbose")
|
||||
flags+=("-v")
|
||||
|
||||
must_have_one_flag=()
|
||||
must_have_one_noun=()
|
||||
@@ -881,6 +963,8 @@ _restic_mount()
|
||||
flags+=("--repo=")
|
||||
two_word_flags+=("-r")
|
||||
flags+=("--tls-client-cert=")
|
||||
flags+=("--verbose")
|
||||
flags+=("-v")
|
||||
|
||||
must_have_one_flag=()
|
||||
must_have_one_noun=()
|
||||
@@ -918,6 +1002,8 @@ _restic_prune()
|
||||
flags+=("--repo=")
|
||||
two_word_flags+=("-r")
|
||||
flags+=("--tls-client-cert=")
|
||||
flags+=("--verbose")
|
||||
flags+=("-v")
|
||||
|
||||
must_have_one_flag=()
|
||||
must_have_one_noun=()
|
||||
@@ -955,6 +1041,8 @@ _restic_rebuild-index()
|
||||
flags+=("--repo=")
|
||||
two_word_flags+=("-r")
|
||||
flags+=("--tls-client-cert=")
|
||||
flags+=("--verbose")
|
||||
flags+=("-v")
|
||||
|
||||
must_have_one_flag=()
|
||||
must_have_one_noun=()
|
||||
@@ -1008,6 +1096,8 @@ _restic_restore()
|
||||
flags+=("--repo=")
|
||||
two_word_flags+=("-r")
|
||||
flags+=("--tls-client-cert=")
|
||||
flags+=("--verbose")
|
||||
flags+=("-v")
|
||||
|
||||
must_have_one_flag=()
|
||||
must_have_one_noun=()
|
||||
@@ -1057,6 +1147,8 @@ _restic_snapshots()
|
||||
flags+=("--repo=")
|
||||
two_word_flags+=("-r")
|
||||
flags+=("--tls-client-cert=")
|
||||
flags+=("--verbose")
|
||||
flags+=("-v")
|
||||
|
||||
must_have_one_flag=()
|
||||
must_have_one_noun=()
|
||||
@@ -1107,6 +1199,8 @@ _restic_tag()
|
||||
flags+=("--repo=")
|
||||
two_word_flags+=("-r")
|
||||
flags+=("--tls-client-cert=")
|
||||
flags+=("--verbose")
|
||||
flags+=("-v")
|
||||
|
||||
must_have_one_flag=()
|
||||
must_have_one_noun=()
|
||||
@@ -1146,6 +1240,8 @@ _restic_unlock()
|
||||
flags+=("--repo=")
|
||||
two_word_flags+=("-r")
|
||||
flags+=("--tls-client-cert=")
|
||||
flags+=("--verbose")
|
||||
flags+=("-v")
|
||||
|
||||
must_have_one_flag=()
|
||||
must_have_one_noun=()
|
||||
@@ -1183,17 +1279,20 @@ _restic_version()
|
||||
flags+=("--repo=")
|
||||
two_word_flags+=("-r")
|
||||
flags+=("--tls-client-cert=")
|
||||
flags+=("--verbose")
|
||||
flags+=("-v")
|
||||
|
||||
must_have_one_flag=()
|
||||
must_have_one_noun=()
|
||||
noun_aliases=()
|
||||
}
|
||||
|
||||
_restic()
|
||||
_restic_root_command()
|
||||
{
|
||||
last_command="restic"
|
||||
commands=()
|
||||
commands+=("backup")
|
||||
commands+=("cache")
|
||||
commands+=("cat")
|
||||
commands+=("check")
|
||||
commands+=("diff")
|
||||
@@ -1241,6 +1340,8 @@ _restic()
|
||||
flags+=("--repo=")
|
||||
two_word_flags+=("-r")
|
||||
flags+=("--tls-client-cert=")
|
||||
flags+=("--verbose")
|
||||
flags+=("-v")
|
||||
|
||||
must_have_one_flag=()
|
||||
must_have_one_noun=()
|
||||
@@ -1254,7 +1355,7 @@ __start_restic()
|
||||
if declare -F _init_completion >/dev/null 2>&1; then
|
||||
_init_completion -s || return
|
||||
else
|
||||
__my_init_completion -n "=" || return
|
||||
__restic_init_completion -n "=" || return
|
||||
fi
|
||||
|
||||
local c=0
|
||||
@@ -1269,7 +1370,7 @@ __start_restic()
|
||||
local last_command
|
||||
local nouns=()
|
||||
|
||||
__handle_word
|
||||
__restic_handle_word
|
||||
}
|
||||
|
||||
if [[ $(type -t compopt) = "builtin" ]]; then
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
Local Cache
|
||||
===========
|
||||
|
||||
In order to speed up certain operations, restic manages a local cache of data.
|
||||
This document describes the data structures for the local cache with version 1.
|
||||
|
||||
Versions
|
||||
--------
|
||||
|
||||
The cache directory is selected according to the `XDG base dir specification
|
||||
<http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html>`__.
|
||||
Each repository has its own cache sub-directory, consting of the repository ID
|
||||
which is chosen at ``init``. All cache directories for different repos are
|
||||
independent of each other.
|
||||
|
||||
The cache dir for a repo contains a file named ``version``, which contains a
|
||||
single ASCII integer line that stands for the current version of the cache. If
|
||||
a lower version number is found the cache is recreated with the current
|
||||
version. If a higher version number is found the cache is ignored and left as
|
||||
is.
|
||||
|
||||
Snapshots, Data and Indexes
|
||||
---------------------------
|
||||
|
||||
Snapshot, Data and Index files are cached in the sub-directories ``snapshots``,
|
||||
``data`` and ``index``, as read from the repository.
|
||||
|
||||
Expiry
|
||||
------
|
||||
|
||||
Whenever a cache directory for a repo is used, that directory's modification
|
||||
timestamp is updated to the current time. By looking at the modification
|
||||
timestamps of the repo cache directories it is easy to decide which directories
|
||||
are old and haven't been used in a long time. Those are probably stale and can
|
||||
be removed.
|
||||
|
||||
@@ -35,7 +35,7 @@ master_doc = 'index'
|
||||
|
||||
# General information about the project.
|
||||
project = 'restic'
|
||||
copyright = '2017, restic authors'
|
||||
copyright = '2018, restic authors'
|
||||
author = 'fd0'
|
||||
|
||||
# The version info for the project you're documenting, acts as replacement for
|
||||
|
||||
@@ -111,6 +111,10 @@ directory structure via sftp, so the path that needs to be specified is
|
||||
different than the directory structure on the device and maybe even as exposed
|
||||
via other protocols.
|
||||
|
||||
|
||||
Try removing the /volume1 prefix in your paths. If this does not work, use sftp
|
||||
and ls to explore the SFTP file system hierarchy on your NAS.
|
||||
|
||||
The following may work:
|
||||
|
||||
::
|
||||
|
||||
@@ -12,8 +12,10 @@ Restic Documentation
|
||||
050_restore
|
||||
060_forget
|
||||
070_encryption
|
||||
075_scripting
|
||||
080_examples
|
||||
090_participating
|
||||
100_references
|
||||
110_talks
|
||||
faq
|
||||
manual_rest
|
||||
|
||||
@@ -84,7 +84,7 @@ given as the arguments.
|
||||
.SH OPTIONS INHERITED FROM PARENT COMMANDS
|
||||
.PP
|
||||
\fB\-\-cacert\fP=[]
|
||||
path to load root certificates from (default: use system certificates)
|
||||
\fB\fCfile\fR to load root certificates from (default: use system certificates)
|
||||
|
||||
.PP
|
||||
\fB\-\-cache\-dir\fP=""
|
||||
@@ -134,6 +134,10 @@ given as the arguments.
|
||||
\fB\-\-tls\-client\-cert\fP=""
|
||||
path to a file containing PEM encoded TLS client certificate and private key
|
||||
|
||||
.PP
|
||||
\fB\-v\fP, \fB\-\-verbose\fP[=0]
|
||||
be verbose (specify \-\-verbose multiple times or level \fB\fCn\fR)
|
||||
|
||||
|
||||
.SH SEE ALSO
|
||||
.PP
|
||||
|
||||
95
doc/man/restic-cache.1
Normal file
95
doc/man/restic-cache.1
Normal file
@@ -0,0 +1,95 @@
|
||||
.TH "restic backup" "1" "Jan 2017" "generated by `restic generate`" ""
|
||||
.nh
|
||||
.ad l
|
||||
|
||||
|
||||
.SH NAME
|
||||
.PP
|
||||
restic\-cache \- Operate on local cache directories
|
||||
|
||||
|
||||
.SH SYNOPSIS
|
||||
.PP
|
||||
\fBrestic cache [flags]\fP
|
||||
|
||||
|
||||
.SH DESCRIPTION
|
||||
.PP
|
||||
The "cache" command allows listing and cleaning local cache directories.
|
||||
|
||||
|
||||
.SH OPTIONS
|
||||
.PP
|
||||
\fB\-\-cleanup\fP[=false]
|
||||
remove old cache directories
|
||||
|
||||
.PP
|
||||
\fB\-h\fP, \fB\-\-help\fP[=false]
|
||||
help for cache
|
||||
|
||||
.PP
|
||||
\fB\-\-max\-age\fP=30
|
||||
max age in \fB\fCdays\fR for cache directories to be considered old
|
||||
|
||||
|
||||
.SH OPTIONS INHERITED FROM PARENT COMMANDS
|
||||
.PP
|
||||
\fB\-\-cacert\fP=[]
|
||||
\fB\fCfile\fR to load root certificates from (default: use system certificates)
|
||||
|
||||
.PP
|
||||
\fB\-\-cache\-dir\fP=""
|
||||
set the cache directory
|
||||
|
||||
.PP
|
||||
\fB\-\-cleanup\-cache\fP[=false]
|
||||
auto remove old cache directories
|
||||
|
||||
.PP
|
||||
\fB\-\-json\fP[=false]
|
||||
set output mode to JSON for commands that support it
|
||||
|
||||
.PP
|
||||
\fB\-\-limit\-download\fP=0
|
||||
limits downloads to a maximum rate in KiB/s. (default: unlimited)
|
||||
|
||||
.PP
|
||||
\fB\-\-limit\-upload\fP=0
|
||||
limits uploads to a maximum rate in KiB/s. (default: unlimited)
|
||||
|
||||
.PP
|
||||
\fB\-\-no\-cache\fP[=false]
|
||||
do not use a local cache
|
||||
|
||||
.PP
|
||||
\fB\-\-no\-lock\fP[=false]
|
||||
do not lock the repo, this allows some operations on read\-only repos
|
||||
|
||||
.PP
|
||||
\fB\-o\fP, \fB\-\-option\fP=[]
|
||||
set extended option (\fB\fCkey=value\fR, can be specified multiple times)
|
||||
|
||||
.PP
|
||||
\fB\-p\fP, \fB\-\-password\-file\fP=""
|
||||
read the repository password from a file (default: $RESTIC\_PASSWORD\_FILE)
|
||||
|
||||
.PP
|
||||
\fB\-q\fP, \fB\-\-quiet\fP[=false]
|
||||
do not output comprehensive progress report
|
||||
|
||||
.PP
|
||||
\fB\-r\fP, \fB\-\-repo\fP=""
|
||||
repository to backup to or restore from (default: $RESTIC\_REPOSITORY)
|
||||
|
||||
.PP
|
||||
\fB\-\-tls\-client\-cert\fP=""
|
||||
path to a file containing PEM encoded TLS client certificate and private key
|
||||
|
||||
.PP
|
||||
\fB\-v\fP, \fB\-\-verbose\fP[=0]
|
||||
be verbose (specify \-\-verbose multiple times or level \fB\fCn\fR)
|
||||
|
||||
|
||||
.SH SEE ALSO
|
||||
.PP
|
||||
\fBrestic(1)\fP
|
||||
@@ -27,7 +27,7 @@ The "cat" command is used to print internal objects to stdout.
|
||||
.SH OPTIONS INHERITED FROM PARENT COMMANDS
|
||||
.PP
|
||||
\fB\-\-cacert\fP=[]
|
||||
path to load root certificates from (default: use system certificates)
|
||||
\fB\fCfile\fR to load root certificates from (default: use system certificates)
|
||||
|
||||
.PP
|
||||
\fB\-\-cache\-dir\fP=""
|
||||
@@ -77,6 +77,10 @@ The "cat" command is used to print internal objects to stdout.
|
||||
\fB\-\-tls\-client\-cert\fP=""
|
||||
path to a file containing PEM encoded TLS client certificate and private key
|
||||
|
||||
.PP
|
||||
\fB\-v\fP, \fB\-\-verbose\fP[=0]
|
||||
be verbose (specify \-\-verbose multiple times or level \fB\fCn\fR)
|
||||
|
||||
|
||||
.SH SEE ALSO
|
||||
.PP
|
||||
|
||||
@@ -48,7 +48,7 @@ repository and not use a local cache.
|
||||
.SH OPTIONS INHERITED FROM PARENT COMMANDS
|
||||
.PP
|
||||
\fB\-\-cacert\fP=[]
|
||||
path to load root certificates from (default: use system certificates)
|
||||
\fB\fCfile\fR to load root certificates from (default: use system certificates)
|
||||
|
||||
.PP
|
||||
\fB\-\-cache\-dir\fP=""
|
||||
@@ -98,6 +98,10 @@ repository and not use a local cache.
|
||||
\fB\-\-tls\-client\-cert\fP=""
|
||||
path to a file containing PEM encoded TLS client certificate and private key
|
||||
|
||||
.PP
|
||||
\fB\-v\fP, \fB\-\-verbose\fP[=0]
|
||||
be verbose (specify \-\-verbose multiple times or level \fB\fCn\fR)
|
||||
|
||||
|
||||
.SH SEE ALSO
|
||||
.PP
|
||||
|
||||
@@ -18,6 +18,8 @@ restic\-diff \- Show differences between two snapshots
|
||||
The "diff" command shows differences from the first to the second snapshot. The
|
||||
first characters in each line display what has happened to a particular file or
|
||||
directory:
|
||||
|
||||
.RS
|
||||
.IP \(bu 2
|
||||
The item was added
|
||||
.IP \(bu 2
|
||||
@@ -26,6 +28,8 @@ U The metadata (access mode, timestamps, ...) for the item was updated
|
||||
M The file's content was modified
|
||||
T The type was changed, e.g. a file was made a symlink
|
||||
|
||||
.RE
|
||||
|
||||
|
||||
.SH OPTIONS
|
||||
.PP
|
||||
@@ -40,7 +44,7 @@ T The type was changed, e.g. a file was made a symlink
|
||||
.SH OPTIONS INHERITED FROM PARENT COMMANDS
|
||||
.PP
|
||||
\fB\-\-cacert\fP=[]
|
||||
path to load root certificates from (default: use system certificates)
|
||||
\fB\fCfile\fR to load root certificates from (default: use system certificates)
|
||||
|
||||
.PP
|
||||
\fB\-\-cache\-dir\fP=""
|
||||
@@ -90,6 +94,10 @@ T The type was changed, e.g. a file was made a symlink
|
||||
\fB\-\-tls\-client\-cert\fP=""
|
||||
path to a file containing PEM encoded TLS client certificate and private key
|
||||
|
||||
.PP
|
||||
\fB\-v\fP, \fB\-\-verbose\fP[=0]
|
||||
be verbose (specify \-\-verbose multiple times or level \fB\fCn\fR)
|
||||
|
||||
|
||||
.SH SEE ALSO
|
||||
.PP
|
||||
|
||||
@@ -44,7 +44,7 @@ repository.
|
||||
.SH OPTIONS INHERITED FROM PARENT COMMANDS
|
||||
.PP
|
||||
\fB\-\-cacert\fP=[]
|
||||
path to load root certificates from (default: use system certificates)
|
||||
\fB\fCfile\fR to load root certificates from (default: use system certificates)
|
||||
|
||||
.PP
|
||||
\fB\-\-cache\-dir\fP=""
|
||||
@@ -94,6 +94,10 @@ repository.
|
||||
\fB\-\-tls\-client\-cert\fP=""
|
||||
path to a file containing PEM encoded TLS client certificate and private key
|
||||
|
||||
.PP
|
||||
\fB\-v\fP, \fB\-\-verbose\fP[=0]
|
||||
be verbose (specify \-\-verbose multiple times or level \fB\fCn\fR)
|
||||
|
||||
|
||||
.SH SEE ALSO
|
||||
.PP
|
||||
|
||||
@@ -60,7 +60,7 @@ repo.
|
||||
.SH OPTIONS INHERITED FROM PARENT COMMANDS
|
||||
.PP
|
||||
\fB\-\-cacert\fP=[]
|
||||
path to load root certificates from (default: use system certificates)
|
||||
\fB\fCfile\fR to load root certificates from (default: use system certificates)
|
||||
|
||||
.PP
|
||||
\fB\-\-cache\-dir\fP=""
|
||||
@@ -110,6 +110,10 @@ repo.
|
||||
\fB\-\-tls\-client\-cert\fP=""
|
||||
path to a file containing PEM encoded TLS client certificate and private key
|
||||
|
||||
.PP
|
||||
\fB\-v\fP, \fB\-\-verbose\fP[=0]
|
||||
be verbose (specify \-\-verbose multiple times or level \fB\fCn\fR)
|
||||
|
||||
|
||||
.SH SEE ALSO
|
||||
.PP
|
||||
|
||||
@@ -46,6 +46,10 @@ data after 'forget' was run successfully, see the 'prune' command.
|
||||
\fB\-y\fP, \fB\-\-keep\-yearly\fP=0
|
||||
keep the last \fB\fCn\fR yearly snapshots
|
||||
|
||||
.PP
|
||||
\fB\-\-keep\-within\fP=
|
||||
keep snapshots that were created within \fB\fCduration\fR before the newest (e.g. 1y5m7d)
|
||||
|
||||
.PP
|
||||
\fB\-\-keep\-tag\fP=[]
|
||||
keep snapshots with this \fB\fCtaglist\fR (can be specified multiple times)
|
||||
@@ -90,7 +94,7 @@ data after 'forget' was run successfully, see the 'prune' command.
|
||||
.SH OPTIONS INHERITED FROM PARENT COMMANDS
|
||||
.PP
|
||||
\fB\-\-cacert\fP=[]
|
||||
path to load root certificates from (default: use system certificates)
|
||||
\fB\fCfile\fR to load root certificates from (default: use system certificates)
|
||||
|
||||
.PP
|
||||
\fB\-\-cache\-dir\fP=""
|
||||
@@ -140,6 +144,10 @@ data after 'forget' was run successfully, see the 'prune' command.
|
||||
\fB\-\-tls\-client\-cert\fP=""
|
||||
path to a file containing PEM encoded TLS client certificate and private key
|
||||
|
||||
.PP
|
||||
\fB\-v\fP, \fB\-\-verbose\fP[=0]
|
||||
be verbose (specify \-\-verbose multiple times or level \fB\fCn\fR)
|
||||
|
||||
|
||||
.SH SEE ALSO
|
||||
.PP
|
||||
|
||||
@@ -40,7 +40,7 @@ and the auto\-completion files for bash and zsh).
|
||||
.SH OPTIONS INHERITED FROM PARENT COMMANDS
|
||||
.PP
|
||||
\fB\-\-cacert\fP=[]
|
||||
path to load root certificates from (default: use system certificates)
|
||||
\fB\fCfile\fR to load root certificates from (default: use system certificates)
|
||||
|
||||
.PP
|
||||
\fB\-\-cache\-dir\fP=""
|
||||
@@ -90,6 +90,10 @@ and the auto\-completion files for bash and zsh).
|
||||
\fB\-\-tls\-client\-cert\fP=""
|
||||
path to a file containing PEM encoded TLS client certificate and private key
|
||||
|
||||
.PP
|
||||
\fB\-v\fP, \fB\-\-verbose\fP[=0]
|
||||
be verbose (specify \-\-verbose multiple times or level \fB\fCn\fR)
|
||||
|
||||
|
||||
.SH SEE ALSO
|
||||
.PP
|
||||
|
||||
@@ -27,7 +27,7 @@ The "init" command initializes a new repository.
|
||||
.SH OPTIONS INHERITED FROM PARENT COMMANDS
|
||||
.PP
|
||||
\fB\-\-cacert\fP=[]
|
||||
path to load root certificates from (default: use system certificates)
|
||||
\fB\fCfile\fR to load root certificates from (default: use system certificates)
|
||||
|
||||
.PP
|
||||
\fB\-\-cache\-dir\fP=""
|
||||
@@ -77,6 +77,10 @@ The "init" command initializes a new repository.
|
||||
\fB\-\-tls\-client\-cert\fP=""
|
||||
path to a file containing PEM encoded TLS client certificate and private key
|
||||
|
||||
.PP
|
||||
\fB\-v\fP, \fB\-\-verbose\fP[=0]
|
||||
be verbose (specify \-\-verbose multiple times or level \fB\fCn\fR)
|
||||
|
||||
|
||||
.SH SEE ALSO
|
||||
.PP
|
||||
|
||||
@@ -23,11 +23,15 @@ The "key" command manages keys (passwords) for accessing the repository.
|
||||
\fB\-h\fP, \fB\-\-help\fP[=false]
|
||||
help for key
|
||||
|
||||
.PP
|
||||
\fB\-\-new\-password\-file\fP=""
|
||||
the file from which to load a new password
|
||||
|
||||
|
||||
.SH OPTIONS INHERITED FROM PARENT COMMANDS
|
||||
.PP
|
||||
\fB\-\-cacert\fP=[]
|
||||
path to load root certificates from (default: use system certificates)
|
||||
\fB\fCfile\fR to load root certificates from (default: use system certificates)
|
||||
|
||||
.PP
|
||||
\fB\-\-cache\-dir\fP=""
|
||||
@@ -77,6 +81,10 @@ The "key" command manages keys (passwords) for accessing the repository.
|
||||
\fB\-\-tls\-client\-cert\fP=""
|
||||
path to a file containing PEM encoded TLS client certificate and private key
|
||||
|
||||
.PP
|
||||
\fB\-v\fP, \fB\-\-verbose\fP[=0]
|
||||
be verbose (specify \-\-verbose multiple times or level \fB\fCn\fR)
|
||||
|
||||
|
||||
.SH SEE ALSO
|
||||
.PP
|
||||
|
||||
@@ -27,7 +27,7 @@ The "list" command allows listing objects in the repository based on type.
|
||||
.SH OPTIONS INHERITED FROM PARENT COMMANDS
|
||||
.PP
|
||||
\fB\-\-cacert\fP=[]
|
||||
path to load root certificates from (default: use system certificates)
|
||||
\fB\fCfile\fR to load root certificates from (default: use system certificates)
|
||||
|
||||
.PP
|
||||
\fB\-\-cache\-dir\fP=""
|
||||
@@ -77,6 +77,10 @@ The "list" command allows listing objects in the repository based on type.
|
||||
\fB\-\-tls\-client\-cert\fP=""
|
||||
path to a file containing PEM encoded TLS client certificate and private key
|
||||
|
||||
.PP
|
||||
\fB\-v\fP, \fB\-\-verbose\fP[=0]
|
||||
be verbose (specify \-\-verbose multiple times or level \fB\fCn\fR)
|
||||
|
||||
|
||||
.SH SEE ALSO
|
||||
.PP
|
||||
|
||||
@@ -46,7 +46,7 @@ The special snapshot\-ID "latest" can be used to list files and directories of t
|
||||
.SH OPTIONS INHERITED FROM PARENT COMMANDS
|
||||
.PP
|
||||
\fB\-\-cacert\fP=[]
|
||||
path to load root certificates from (default: use system certificates)
|
||||
\fB\fCfile\fR to load root certificates from (default: use system certificates)
|
||||
|
||||
.PP
|
||||
\fB\-\-cache\-dir\fP=""
|
||||
@@ -96,6 +96,10 @@ The special snapshot\-ID "latest" can be used to list files and directories of t
|
||||
\fB\-\-tls\-client\-cert\fP=""
|
||||
path to a file containing PEM encoded TLS client certificate and private key
|
||||
|
||||
.PP
|
||||
\fB\-v\fP, \fB\-\-verbose\fP[=0]
|
||||
be verbose (specify \-\-verbose multiple times or level \fB\fCn\fR)
|
||||
|
||||
|
||||
.SH SEE ALSO
|
||||
.PP
|
||||
|
||||
@@ -32,7 +32,7 @@ name is explicitly given, a list of migrations that can be applied is printed.
|
||||
.SH OPTIONS INHERITED FROM PARENT COMMANDS
|
||||
.PP
|
||||
\fB\-\-cacert\fP=[]
|
||||
path to load root certificates from (default: use system certificates)
|
||||
\fB\fCfile\fR to load root certificates from (default: use system certificates)
|
||||
|
||||
.PP
|
||||
\fB\-\-cache\-dir\fP=""
|
||||
@@ -82,6 +82,10 @@ name is explicitly given, a list of migrations that can be applied is printed.
|
||||
\fB\-\-tls\-client\-cert\fP=""
|
||||
path to a file containing PEM encoded TLS client certificate and private key
|
||||
|
||||
.PP
|
||||
\fB\-v\fP, \fB\-\-verbose\fP[=0]
|
||||
be verbose (specify \-\-verbose multiple times or level \fB\fCn\fR)
|
||||
|
||||
|
||||
.SH SEE ALSO
|
||||
.PP
|
||||
|
||||
@@ -88,7 +88,7 @@ For details please see the documentation for time.Format() at:
|
||||
.SH OPTIONS INHERITED FROM PARENT COMMANDS
|
||||
.PP
|
||||
\fB\-\-cacert\fP=[]
|
||||
path to load root certificates from (default: use system certificates)
|
||||
\fB\fCfile\fR to load root certificates from (default: use system certificates)
|
||||
|
||||
.PP
|
||||
\fB\-\-cache\-dir\fP=""
|
||||
@@ -138,6 +138,10 @@ For details please see the documentation for time.Format() at:
|
||||
\fB\-\-tls\-client\-cert\fP=""
|
||||
path to a file containing PEM encoded TLS client certificate and private key
|
||||
|
||||
.PP
|
||||
\fB\-v\fP, \fB\-\-verbose\fP[=0]
|
||||
be verbose (specify \-\-verbose multiple times or level \fB\fCn\fR)
|
||||
|
||||
|
||||
.SH SEE ALSO
|
||||
.PP
|
||||
|
||||
@@ -28,7 +28,7 @@ referenced and therefore not needed any more.
|
||||
.SH OPTIONS INHERITED FROM PARENT COMMANDS
|
||||
.PP
|
||||
\fB\-\-cacert\fP=[]
|
||||
path to load root certificates from (default: use system certificates)
|
||||
\fB\fCfile\fR to load root certificates from (default: use system certificates)
|
||||
|
||||
.PP
|
||||
\fB\-\-cache\-dir\fP=""
|
||||
@@ -78,6 +78,10 @@ referenced and therefore not needed any more.
|
||||
\fB\-\-tls\-client\-cert\fP=""
|
||||
path to a file containing PEM encoded TLS client certificate and private key
|
||||
|
||||
.PP
|
||||
\fB\-v\fP, \fB\-\-verbose\fP[=0]
|
||||
be verbose (specify \-\-verbose multiple times or level \fB\fCn\fR)
|
||||
|
||||
|
||||
.SH SEE ALSO
|
||||
.PP
|
||||
|
||||
@@ -28,7 +28,7 @@ repository.
|
||||
.SH OPTIONS INHERITED FROM PARENT COMMANDS
|
||||
.PP
|
||||
\fB\-\-cacert\fP=[]
|
||||
path to load root certificates from (default: use system certificates)
|
||||
\fB\fCfile\fR to load root certificates from (default: use system certificates)
|
||||
|
||||
.PP
|
||||
\fB\-\-cache\-dir\fP=""
|
||||
@@ -78,6 +78,10 @@ repository.
|
||||
\fB\-\-tls\-client\-cert\fP=""
|
||||
path to a file containing PEM encoded TLS client certificate and private key
|
||||
|
||||
.PP
|
||||
\fB\-v\fP, \fB\-\-verbose\fP[=0]
|
||||
be verbose (specify \-\-verbose multiple times or level \fB\fCn\fR)
|
||||
|
||||
|
||||
.SH SEE ALSO
|
||||
.PP
|
||||
|
||||
@@ -56,7 +56,7 @@ repository.
|
||||
.SH OPTIONS INHERITED FROM PARENT COMMANDS
|
||||
.PP
|
||||
\fB\-\-cacert\fP=[]
|
||||
path to load root certificates from (default: use system certificates)
|
||||
\fB\fCfile\fR to load root certificates from (default: use system certificates)
|
||||
|
||||
.PP
|
||||
\fB\-\-cache\-dir\fP=""
|
||||
@@ -106,6 +106,10 @@ repository.
|
||||
\fB\-\-tls\-client\-cert\fP=""
|
||||
path to a file containing PEM encoded TLS client certificate and private key
|
||||
|
||||
.PP
|
||||
\fB\-v\fP, \fB\-\-verbose\fP[=0]
|
||||
be verbose (specify \-\-verbose multiple times or level \fB\fCn\fR)
|
||||
|
||||
|
||||
.SH SEE ALSO
|
||||
.PP
|
||||
|
||||
@@ -47,7 +47,7 @@ The "snapshots" command lists all snapshots stored in the repository.
|
||||
.SH OPTIONS INHERITED FROM PARENT COMMANDS
|
||||
.PP
|
||||
\fB\-\-cacert\fP=[]
|
||||
path to load root certificates from (default: use system certificates)
|
||||
\fB\fCfile\fR to load root certificates from (default: use system certificates)
|
||||
|
||||
.PP
|
||||
\fB\-\-cache\-dir\fP=""
|
||||
@@ -97,6 +97,10 @@ The "snapshots" command lists all snapshots stored in the repository.
|
||||
\fB\-\-tls\-client\-cert\fP=""
|
||||
path to a file containing PEM encoded TLS client certificate and private key
|
||||
|
||||
.PP
|
||||
\fB\-v\fP, \fB\-\-verbose\fP[=0]
|
||||
be verbose (specify \-\-verbose multiple times or level \fB\fCn\fR)
|
||||
|
||||
|
||||
.SH SEE ALSO
|
||||
.PP
|
||||
|
||||
@@ -58,7 +58,7 @@ When no snapshot\-ID is given, all snapshots matching the host, tag and path fil
|
||||
.SH OPTIONS INHERITED FROM PARENT COMMANDS
|
||||
.PP
|
||||
\fB\-\-cacert\fP=[]
|
||||
path to load root certificates from (default: use system certificates)
|
||||
\fB\fCfile\fR to load root certificates from (default: use system certificates)
|
||||
|
||||
.PP
|
||||
\fB\-\-cache\-dir\fP=""
|
||||
@@ -108,6 +108,10 @@ When no snapshot\-ID is given, all snapshots matching the host, tag and path fil
|
||||
\fB\-\-tls\-client\-cert\fP=""
|
||||
path to a file containing PEM encoded TLS client certificate and private key
|
||||
|
||||
.PP
|
||||
\fB\-v\fP, \fB\-\-verbose\fP[=0]
|
||||
be verbose (specify \-\-verbose multiple times or level \fB\fCn\fR)
|
||||
|
||||
|
||||
.SH SEE ALSO
|
||||
.PP
|
||||
|
||||
@@ -31,7 +31,7 @@ The "unlock" command removes stale locks that have been created by other restic
|
||||
.SH OPTIONS INHERITED FROM PARENT COMMANDS
|
||||
.PP
|
||||
\fB\-\-cacert\fP=[]
|
||||
path to load root certificates from (default: use system certificates)
|
||||
\fB\fCfile\fR to load root certificates from (default: use system certificates)
|
||||
|
||||
.PP
|
||||
\fB\-\-cache\-dir\fP=""
|
||||
@@ -81,6 +81,10 @@ The "unlock" command removes stale locks that have been created by other restic
|
||||
\fB\-\-tls\-client\-cert\fP=""
|
||||
path to a file containing PEM encoded TLS client certificate and private key
|
||||
|
||||
.PP
|
||||
\fB\-v\fP, \fB\-\-verbose\fP[=0]
|
||||
be verbose (specify \-\-verbose multiple times or level \fB\fCn\fR)
|
||||
|
||||
|
||||
.SH SEE ALSO
|
||||
.PP
|
||||
|
||||
@@ -28,7 +28,7 @@ and the version of this software.
|
||||
.SH OPTIONS INHERITED FROM PARENT COMMANDS
|
||||
.PP
|
||||
\fB\-\-cacert\fP=[]
|
||||
path to load root certificates from (default: use system certificates)
|
||||
\fB\fCfile\fR to load root certificates from (default: use system certificates)
|
||||
|
||||
.PP
|
||||
\fB\-\-cache\-dir\fP=""
|
||||
@@ -78,6 +78,10 @@ and the version of this software.
|
||||
\fB\-\-tls\-client\-cert\fP=""
|
||||
path to a file containing PEM encoded TLS client certificate and private key
|
||||
|
||||
.PP
|
||||
\fB\-v\fP, \fB\-\-verbose\fP[=0]
|
||||
be verbose (specify \-\-verbose multiple times or level \fB\fCn\fR)
|
||||
|
||||
|
||||
.SH SEE ALSO
|
||||
.PP
|
||||
|
||||
@@ -22,7 +22,7 @@ directories in an encrypted repository stored on different backends.
|
||||
.SH OPTIONS
|
||||
.PP
|
||||
\fB\-\-cacert\fP=[]
|
||||
path to load root certificates from (default: use system certificates)
|
||||
\fB\fCfile\fR to load root certificates from (default: use system certificates)
|
||||
|
||||
.PP
|
||||
\fB\-\-cache\-dir\fP=""
|
||||
@@ -76,7 +76,11 @@ directories in an encrypted repository stored on different backends.
|
||||
\fB\-\-tls\-client\-cert\fP=""
|
||||
path to a file containing PEM encoded TLS client certificate and private key
|
||||
|
||||
.PP
|
||||
\fB\-v\fP, \fB\-\-verbose\fP[=0]
|
||||
be verbose (specify \-\-verbose multiple times or level \fB\fCn\fR)
|
||||
|
||||
|
||||
.SH SEE ALSO
|
||||
.PP
|
||||
\fBrestic\-backup(1)\fP, \fBrestic\-cat(1)\fP, \fBrestic\-check(1)\fP, \fBrestic\-diff(1)\fP, \fBrestic\-dump(1)\fP, \fBrestic\-find(1)\fP, \fBrestic\-forget(1)\fP, \fBrestic\-generate(1)\fP, \fBrestic\-init(1)\fP, \fBrestic\-key(1)\fP, \fBrestic\-list(1)\fP, \fBrestic\-ls(1)\fP, \fBrestic\-migrate(1)\fP, \fBrestic\-mount(1)\fP, \fBrestic\-prune(1)\fP, \fBrestic\-rebuild\-index(1)\fP, \fBrestic\-restore(1)\fP, \fBrestic\-snapshots(1)\fP, \fBrestic\-tag(1)\fP, \fBrestic\-unlock(1)\fP, \fBrestic\-version(1)\fP
|
||||
\fBrestic\-backup(1)\fP, \fBrestic\-cache(1)\fP, \fBrestic\-cat(1)\fP, \fBrestic\-check(1)\fP, \fBrestic\-diff(1)\fP, \fBrestic\-dump(1)\fP, \fBrestic\-find(1)\fP, \fBrestic\-forget(1)\fP, \fBrestic\-generate(1)\fP, \fBrestic\-init(1)\fP, \fBrestic\-key(1)\fP, \fBrestic\-list(1)\fP, \fBrestic\-ls(1)\fP, \fBrestic\-migrate(1)\fP, \fBrestic\-mount(1)\fP, \fBrestic\-prune(1)\fP, \fBrestic\-rebuild\-index(1)\fP, \fBrestic\-restore(1)\fP, \fBrestic\-snapshots(1)\fP, \fBrestic\-tag(1)\fP, \fBrestic\-unlock(1)\fP, \fBrestic\-version(1)\fP
|
||||
|
||||
@@ -17,8 +17,10 @@ Usage help is available:
|
||||
|
||||
Available Commands:
|
||||
backup Create a new backup of files and/or directories
|
||||
cache Operate on local cache directories
|
||||
cat Print internal objects to stdout
|
||||
check Check the repository for errors
|
||||
diff Show differences between two snapshots
|
||||
dump Print a backed-up file to stdout
|
||||
find Find a file or directory
|
||||
forget Remove snapshots from the repository
|
||||
@@ -39,24 +41,24 @@ Usage help is available:
|
||||
version Print version information
|
||||
|
||||
Flags:
|
||||
--cacert stringSlice path to load root certificates from (default: use system certificates)
|
||||
--cache-dir string set the cache directory
|
||||
-h, --help help for restic
|
||||
--json set output mode to JSON for commands that support it
|
||||
--limit-download int limits downloads to a maximum rate in KiB/s. (default: unlimited)
|
||||
--limit-upload int limits uploads to a maximum rate in KiB/s. (default: unlimited)
|
||||
--no-cache do not use a local cache
|
||||
--no-lock do not lock the repo, this allows some operations on read-only repos
|
||||
-o, --option key=value set extended option (key=value, can be specified multiple times)
|
||||
-p, --password-file string read the repository password from a file (default: $RESTIC_PASSWORD_FILE)
|
||||
-q, --quiet do not output comprehensive progress report
|
||||
-r, --repo string repository to backup to or restore from (default: $RESTIC_REPOSITORY)
|
||||
--cacert file file to load root certificates from (default: use system certificates)
|
||||
--cache-dir string set the cache directory
|
||||
--cleanup-cache auto remove old cache directories
|
||||
-h, --help help for restic
|
||||
--json set output mode to JSON for commands that support it
|
||||
--limit-download int limits downloads to a maximum rate in KiB/s. (default: unlimited)
|
||||
--limit-upload int limits uploads to a maximum rate in KiB/s. (default: unlimited)
|
||||
--no-cache do not use a local cache
|
||||
--no-lock do not lock the repo, this allows some operations on read-only repos
|
||||
-o, --option key=value set extended option (key=value, can be specified multiple times)
|
||||
-p, --password-file string read the repository password from a file (default: $RESTIC_PASSWORD_FILE)
|
||||
-q, --quiet do not output comprehensive progress report
|
||||
-r, --repo string repository to backup to or restore from (default: $RESTIC_REPOSITORY)
|
||||
--tls-client-cert string path to a file containing PEM encoded TLS client certificate and private key
|
||||
|
||||
-v, --verbose n[=-1] be verbose (specify --verbose multiple times or level n)
|
||||
|
||||
Use "restic [command] --help" for more information about a command.
|
||||
|
||||
|
||||
Similar to programs such as ``git``, restic has a number of
|
||||
sub-commands. You can see these commands in the listing above. Each
|
||||
sub-command may have own command-line options, and there is a help
|
||||
@@ -87,37 +89,41 @@ command:
|
||||
--stdin-filename string file name to use when reading from stdin (default "stdin")
|
||||
--tag tag add a tag for the new snapshot (can be specified multiple times)
|
||||
--time string time of the backup (ex. '2012-11-01 22:08:41') (default: now)
|
||||
--with-atime store the atime for all files and directories
|
||||
|
||||
Global Flags:
|
||||
--cacert stringSlice path to load root certificates from (default: use system certificates)
|
||||
--cache-dir string set the cache directory
|
||||
--json set output mode to JSON for commands that support it
|
||||
--limit-download int limits downloads to a maximum rate in KiB/s. (default: unlimited)
|
||||
--limit-upload int limits uploads to a maximum rate in KiB/s. (default: unlimited)
|
||||
--no-cache do not use a local cache
|
||||
--no-lock do not lock the repo, this allows some operations on read-only repos
|
||||
-o, --option key=value set extended option (key=value, can be specified multiple times)
|
||||
-p, --password-file string read the repository password from a file (default: $RESTIC_PASSWORD_FILE)
|
||||
-q, --quiet do not output comprehensive progress report
|
||||
-r, --repo string repository to backup to or restore from (default: $RESTIC_REPOSITORY)
|
||||
--tls-client-cert string path to a TLS client certificate
|
||||
--tls-client-key string path to a TLS client certificate key
|
||||
--cacert file file to load root certificates from (default: use system certificates)
|
||||
--cache-dir string set the cache directory
|
||||
--cleanup-cache auto remove old cache directories
|
||||
--json set output mode to JSON for commands that support it
|
||||
--limit-download int limits downloads to a maximum rate in KiB/s. (default: unlimited)
|
||||
--limit-upload int limits uploads to a maximum rate in KiB/s. (default: unlimited)
|
||||
--no-cache do not use a local cache
|
||||
--no-lock do not lock the repo, this allows some operations on read-only repos
|
||||
-o, --option key=value set extended option (key=value, can be specified multiple times)
|
||||
-p, --password-file string read the repository password from a file (default: $RESTIC_PASSWORD_FILE)
|
||||
-q, --quiet do not output comprehensive progress report
|
||||
-r, --repo string repository to backup to or restore from (default: $RESTIC_REPOSITORY)
|
||||
--tls-client-cert string path to a file containing PEM encoded TLS client certificate and private key
|
||||
-v, --verbose n[=-1] be verbose (specify --verbose multiple times or level n)
|
||||
|
||||
Subcommand that support showing progress information such as ``backup``,
|
||||
``check`` and ``prune`` will do so unless the quiet flag ``-q`` or
|
||||
``--quiet`` is set. When running from a non-interactive console progress
|
||||
reporting will be limited to once every 10 seconds to not fill your
|
||||
logs.
|
||||
logs. Use ``backup`` with the quiet flag ``-q`` or ``--quiet`` to skip
|
||||
the initial scan of the source directory, this may shorten the backup
|
||||
time needed for large directories.
|
||||
|
||||
Additionally on Unix systems if ``restic`` receives a SIGUSR1 signal the
|
||||
current progress will written to the standard output so you can check up
|
||||
current progress will be written to the standard output so you can check up
|
||||
on the status at will.
|
||||
|
||||
Manage tags
|
||||
-----------
|
||||
|
||||
Managing tags on snapshots is done with the ``tag`` command. The
|
||||
existing set of tags can be replaced completely, tags can be added to
|
||||
existing set of tags can be replaced completely, tags can be added or
|
||||
removed. The result is directly visible in the ``snapshots`` command.
|
||||
|
||||
Let's say we want to tag snapshot ``590c8fc8`` with the tags ``NL`` and
|
||||
@@ -126,7 +132,7 @@ command does that:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ restic -r /tmp/backup tag --set NL --set CH 590c8fc8
|
||||
$ restic -r /srv/restic-repo tag --set NL --set CH 590c8fc8
|
||||
create exclusive lock for repository
|
||||
modified tags on 1 snapshots
|
||||
|
||||
@@ -139,19 +145,19 @@ So we can add and remove tags incrementally like this:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ restic -r /tmp/backup tag --tag NL --remove CH
|
||||
$ restic -r /srv/restic-repo tag --tag NL --remove CH
|
||||
create exclusive lock for repository
|
||||
modified tags on 1 snapshots
|
||||
|
||||
$ restic -r /tmp/backup tag --tag NL --add UK
|
||||
$ restic -r /srv/restic-repo tag --tag NL --add UK
|
||||
create exclusive lock for repository
|
||||
modified tags on 1 snapshots
|
||||
|
||||
$ restic -r /tmp/backup tag --tag NL --remove NL
|
||||
$ restic -r /srv/restic-repo tag --tag NL --remove NL
|
||||
create exclusive lock for repository
|
||||
modified tags on 1 snapshots
|
||||
|
||||
$ restic -r /tmp/backup tag --tag NL --add SOMETHING
|
||||
$ restic -r /srv/restic-repo tag --tag NL --add SOMETHING
|
||||
no snapshots were modified
|
||||
|
||||
Under the hood
|
||||
@@ -168,7 +174,7 @@ locks with the following command:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ restic -r /tmp/backup list snapshots
|
||||
$ restic -r /srv/restic-repo list snapshots
|
||||
d369ccc7d126594950bf74f0a348d5d98d9e99f3215082eb69bf02dc9b3e464c
|
||||
|
||||
The ``find`` command searches for a given
|
||||
@@ -185,11 +191,11 @@ repository.
|
||||
-rw-r--r-- 501 20 5 2015-08-26 14:09:57 +0200 CEST path/to/test.txt
|
||||
|
||||
The ``cat`` command allows you to display the JSON representation of the
|
||||
objects or its raw content.
|
||||
objects or their raw content.
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ restic -r /tmp/backup cat snapshot d369ccc7d126594950bf74f0a348d5d98d9e99f3215082eb69bf02dc9b3e464c
|
||||
$ restic -r /srv/restic-repo cat snapshot d369ccc7d126594950bf74f0a348d5d98d9e99f3215082eb69bf02dc9b3e464c
|
||||
enter password for repository:
|
||||
{
|
||||
"time": "2015-08-12T12:52:44.091448856+02:00",
|
||||
@@ -240,7 +246,7 @@ lists all snapshots as JSON and uses ``jq`` to pretty-print the result:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ restic -r /tmp/backup snapshots --json | jq .
|
||||
$ restic -r /srv/restic-repo snapshots --json | jq .
|
||||
[
|
||||
{
|
||||
"time": "2017-03-11T09:57:43.26630619+01:00",
|
||||
@@ -281,7 +287,7 @@ instead of the default, set the environment variable like this:
|
||||
.. code-block:: console
|
||||
|
||||
$ export TMPDIR=/var/tmp/restic-tmp
|
||||
$ restic -r /tmp/backup backup ~/work
|
||||
$ restic -r /srv/restic-repo backup ~/work
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ case $state in
|
||||
level1)
|
||||
case $words[1] in
|
||||
restic)
|
||||
_arguments '1: :(backup cat check diff dump find forget generate help init key list ls migrate mount options prune rebuild-index restore snapshots tag unlock version)'
|
||||
_arguments '1: :(backup cache cat check diff dump find forget generate help init key list ls migrate mount options prune rebuild-index restore snapshots tag unlock version)'
|
||||
;;
|
||||
*)
|
||||
_arguments '*: :_files'
|
||||
|
||||
@@ -1,117 +0,0 @@
|
||||
package archiver
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"time"
|
||||
|
||||
"github.com/restic/restic/internal/debug"
|
||||
"github.com/restic/restic/internal/restic"
|
||||
|
||||
"github.com/restic/restic/internal/errors"
|
||||
|
||||
"github.com/restic/chunker"
|
||||
)
|
||||
|
||||
// Reader allows saving a stream of data to the repository.
|
||||
type Reader struct {
|
||||
restic.Repository
|
||||
|
||||
Tags []string
|
||||
Hostname string
|
||||
}
|
||||
|
||||
// Archive reads data from the reader and saves it to the repo.
|
||||
func (r *Reader) Archive(ctx context.Context, name string, rd io.Reader, p *restic.Progress) (*restic.Snapshot, restic.ID, error) {
|
||||
if name == "" {
|
||||
return nil, restic.ID{}, errors.New("no filename given")
|
||||
}
|
||||
|
||||
debug.Log("start archiving %s", name)
|
||||
sn, err := restic.NewSnapshot([]string{name}, r.Tags, r.Hostname, time.Now())
|
||||
if err != nil {
|
||||
return nil, restic.ID{}, err
|
||||
}
|
||||
|
||||
p.Start()
|
||||
defer p.Done()
|
||||
|
||||
repo := r.Repository
|
||||
chnker := chunker.New(rd, repo.Config().ChunkerPolynomial)
|
||||
|
||||
ids := restic.IDs{}
|
||||
var fileSize uint64
|
||||
|
||||
for {
|
||||
chunk, err := chnker.Next(getBuf())
|
||||
if errors.Cause(err) == io.EOF {
|
||||
break
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, restic.ID{}, errors.Wrap(err, "chunker.Next()")
|
||||
}
|
||||
|
||||
id := restic.Hash(chunk.Data)
|
||||
|
||||
if !repo.Index().Has(id, restic.DataBlob) {
|
||||
_, err := repo.SaveBlob(ctx, restic.DataBlob, chunk.Data, id)
|
||||
if err != nil {
|
||||
return nil, restic.ID{}, err
|
||||
}
|
||||
debug.Log("saved blob %v (%d bytes)\n", id, chunk.Length)
|
||||
} else {
|
||||
debug.Log("blob %v already saved in the repo\n", id)
|
||||
}
|
||||
|
||||
freeBuf(chunk.Data)
|
||||
|
||||
ids = append(ids, id)
|
||||
|
||||
p.Report(restic.Stat{Bytes: uint64(chunk.Length)})
|
||||
fileSize += uint64(chunk.Length)
|
||||
}
|
||||
|
||||
tree := &restic.Tree{
|
||||
Nodes: []*restic.Node{
|
||||
{
|
||||
Name: name,
|
||||
AccessTime: time.Now(),
|
||||
ModTime: time.Now(),
|
||||
Type: "file",
|
||||
Mode: 0644,
|
||||
Size: fileSize,
|
||||
UID: sn.UID,
|
||||
GID: sn.GID,
|
||||
User: sn.Username,
|
||||
Content: ids,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
treeID, err := repo.SaveTree(ctx, tree)
|
||||
if err != nil {
|
||||
return nil, restic.ID{}, err
|
||||
}
|
||||
sn.Tree = &treeID
|
||||
debug.Log("tree saved as %v", treeID)
|
||||
|
||||
id, err := repo.SaveJSONUnpacked(ctx, restic.SnapshotFile, sn)
|
||||
if err != nil {
|
||||
return nil, restic.ID{}, err
|
||||
}
|
||||
|
||||
debug.Log("snapshot saved as %v", id)
|
||||
|
||||
err = repo.Flush(ctx)
|
||||
if err != nil {
|
||||
return nil, restic.ID{}, err
|
||||
}
|
||||
|
||||
err = repo.SaveIndex(ctx)
|
||||
if err != nil {
|
||||
return nil, restic.ID{}, err
|
||||
}
|
||||
|
||||
return sn, id, nil
|
||||
}
|
||||
@@ -1,206 +0,0 @@
|
||||
package archiver
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"io"
|
||||
"math/rand"
|
||||
"testing"
|
||||
|
||||
"github.com/restic/restic/internal/checker"
|
||||
"github.com/restic/restic/internal/repository"
|
||||
"github.com/restic/restic/internal/restic"
|
||||
)
|
||||
|
||||
func loadBlob(t *testing.T, repo restic.Repository, id restic.ID, buf []byte) int {
|
||||
n, err := repo.LoadBlob(context.TODO(), restic.DataBlob, id, buf)
|
||||
if err != nil {
|
||||
t.Fatalf("LoadBlob(%v) returned error %v", id, err)
|
||||
}
|
||||
|
||||
return n
|
||||
}
|
||||
|
||||
func checkSavedFile(t *testing.T, repo restic.Repository, treeID restic.ID, name string, rd io.Reader) {
|
||||
tree, err := repo.LoadTree(context.TODO(), treeID)
|
||||
if err != nil {
|
||||
t.Fatalf("LoadTree() returned error %v", err)
|
||||
}
|
||||
|
||||
if len(tree.Nodes) != 1 {
|
||||
t.Fatalf("wrong number of nodes for tree, want %v, got %v", 1, len(tree.Nodes))
|
||||
}
|
||||
|
||||
node := tree.Nodes[0]
|
||||
if node.Name != "fakefile" {
|
||||
t.Fatalf("wrong filename, want %v, got %v", "fakefile", node.Name)
|
||||
}
|
||||
|
||||
if len(node.Content) == 0 {
|
||||
t.Fatalf("node.Content has length 0")
|
||||
}
|
||||
|
||||
// check blobs
|
||||
for i, id := range node.Content {
|
||||
size, found := repo.LookupBlobSize(id, restic.DataBlob)
|
||||
if !found {
|
||||
t.Fatal("Failed to find blob", id.Str())
|
||||
}
|
||||
|
||||
buf := restic.NewBlobBuffer(int(size))
|
||||
n := loadBlob(t, repo, id, buf)
|
||||
if n != len(buf) {
|
||||
t.Errorf("wrong number of bytes read, want %d, got %d", len(buf), n)
|
||||
}
|
||||
|
||||
buf2 := make([]byte, int(size))
|
||||
_, err := io.ReadFull(rd, buf2)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !bytes.Equal(buf, buf2) {
|
||||
t.Fatalf("blob %d (%v) is wrong", i, id.Str())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// fakeFile returns a reader which yields deterministic pseudo-random data.
|
||||
func fakeFile(t testing.TB, seed, size int64) io.Reader {
|
||||
return io.LimitReader(restic.NewRandReader(rand.New(rand.NewSource(seed))), size)
|
||||
}
|
||||
|
||||
func TestArchiveReader(t *testing.T) {
|
||||
repo, cleanup := repository.TestRepository(t)
|
||||
defer cleanup()
|
||||
|
||||
seed := rand.Int63()
|
||||
size := int64(rand.Intn(50*1024*1024) + 50*1024*1024)
|
||||
t.Logf("seed is 0x%016x, size is %v", seed, size)
|
||||
|
||||
f := fakeFile(t, seed, size)
|
||||
|
||||
r := &Reader{
|
||||
Repository: repo,
|
||||
Hostname: "localhost",
|
||||
Tags: []string{"test"},
|
||||
}
|
||||
|
||||
sn, id, err := r.Archive(context.TODO(), "fakefile", f, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("ArchiveReader() returned error %v", err)
|
||||
}
|
||||
|
||||
if id.IsNull() {
|
||||
t.Fatalf("ArchiveReader() returned null ID")
|
||||
}
|
||||
|
||||
t.Logf("snapshot saved as %v, tree is %v", id.Str(), sn.Tree.Str())
|
||||
|
||||
checkSavedFile(t, repo, *sn.Tree, "fakefile", fakeFile(t, seed, size))
|
||||
|
||||
checker.TestCheckRepo(t, repo)
|
||||
}
|
||||
|
||||
func TestArchiveReaderNull(t *testing.T) {
|
||||
repo, cleanup := repository.TestRepository(t)
|
||||
defer cleanup()
|
||||
|
||||
r := &Reader{
|
||||
Repository: repo,
|
||||
Hostname: "localhost",
|
||||
Tags: []string{"test"},
|
||||
}
|
||||
|
||||
sn, id, err := r.Archive(context.TODO(), "fakefile", bytes.NewReader(nil), nil)
|
||||
if err != nil {
|
||||
t.Fatalf("ArchiveReader() returned error %v", err)
|
||||
}
|
||||
|
||||
if id.IsNull() {
|
||||
t.Fatalf("ArchiveReader() returned null ID")
|
||||
}
|
||||
|
||||
t.Logf("snapshot saved as %v, tree is %v", id.Str(), sn.Tree.Str())
|
||||
|
||||
checker.TestCheckRepo(t, repo)
|
||||
}
|
||||
|
||||
type errReader string
|
||||
|
||||
func (e errReader) Read([]byte) (int, error) {
|
||||
return 0, errors.New(string(e))
|
||||
}
|
||||
|
||||
func countSnapshots(t testing.TB, repo restic.Repository) int {
|
||||
snapshots := 0
|
||||
err := repo.List(context.TODO(), restic.SnapshotFile, func(id restic.ID, size int64) error {
|
||||
snapshots++
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
return snapshots
|
||||
}
|
||||
|
||||
func TestArchiveReaderError(t *testing.T) {
|
||||
repo, cleanup := repository.TestRepository(t)
|
||||
defer cleanup()
|
||||
|
||||
r := &Reader{
|
||||
Repository: repo,
|
||||
Hostname: "localhost",
|
||||
Tags: []string{"test"},
|
||||
}
|
||||
|
||||
sn, id, err := r.Archive(context.TODO(), "fakefile", errReader("error returned by reading stdin"), nil)
|
||||
if err == nil {
|
||||
t.Errorf("expected error not returned")
|
||||
}
|
||||
|
||||
if sn != nil {
|
||||
t.Errorf("Snapshot should be nil, but isn't")
|
||||
}
|
||||
|
||||
if !id.IsNull() {
|
||||
t.Errorf("id should be null, but %v returned", id.Str())
|
||||
}
|
||||
|
||||
n := countSnapshots(t, repo)
|
||||
if n > 0 {
|
||||
t.Errorf("expected zero snapshots, but got %d", n)
|
||||
}
|
||||
|
||||
checker.TestCheckRepo(t, repo)
|
||||
}
|
||||
|
||||
func BenchmarkArchiveReader(t *testing.B) {
|
||||
repo, cleanup := repository.TestRepository(t)
|
||||
defer cleanup()
|
||||
|
||||
const size = 50 * 1024 * 1024
|
||||
|
||||
buf := make([]byte, size)
|
||||
_, err := io.ReadFull(fakeFile(t, 23, size), buf)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
r := &Reader{
|
||||
Repository: repo,
|
||||
Hostname: "localhost",
|
||||
Tags: []string{"test"},
|
||||
}
|
||||
|
||||
t.SetBytes(size)
|
||||
t.ResetTimer()
|
||||
|
||||
for i := 0; i < t.N; i++ {
|
||||
_, _, err := r.Archive(context.TODO(), "fakefile", bytes.NewReader(buf), nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user