mirror of
https://github.com/restic/restic.git
synced 2025-08-24 07:37:27 +00:00
Compare commits
274 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
cd9b2295f1 | ||
![]() |
a439cdeb05 | ||
![]() |
827f6d7b24 | ||
![]() |
77ab10d401 | ||
![]() |
3b0ad2e368 | ||
![]() |
2996c110f1 | ||
![]() |
4609b5c24d | ||
![]() |
830511460a | ||
![]() |
0dc3648416 | ||
![]() |
d71dba3788 | ||
![]() |
e482633943 | ||
![]() |
900621051a | ||
![]() |
1f246c5309 | ||
![]() |
e40805b002 | ||
![]() |
d65bea1b2a | ||
![]() |
3b68acf853 | ||
![]() |
82a70643a2 | ||
![]() |
0dd805421e | ||
![]() |
16b82f4b1d | ||
![]() |
7a6bfcd58c | ||
![]() |
de54618852 | ||
![]() |
98526b8dbe | ||
![]() |
0083680d33 | ||
![]() |
05222b7343 | ||
![]() |
d4ff5b6bf4 | ||
![]() |
cf0883e16c | ||
![]() |
a35a24b8b4 | ||
![]() |
df7f72cdde | ||
![]() |
3edc723bf0 | ||
![]() |
71891b340c | ||
![]() |
6f5c3e57f6 | ||
![]() |
56af0ce370 | ||
![]() |
c9745cd47e | ||
![]() |
2434ab2106 | ||
![]() |
1688713400 | ||
![]() |
00597284de | ||
![]() |
6dc7cca597 | ||
![]() |
d32c7c2aba | ||
![]() |
09e9b74cbd | ||
![]() |
d53595e43c | ||
![]() |
0de19cc87f | ||
![]() |
2c9ec07d0b | ||
![]() |
a7971a3ece | ||
![]() |
4ab0022da8 | ||
![]() |
4b3c054257 | ||
![]() |
7486bfea5b | ||
![]() |
c8fc72364a | ||
![]() |
987ef2f4a9 | ||
![]() |
5b95bb7059 | ||
![]() |
8471a359ee | ||
![]() |
f9422ff4c7 | ||
![]() |
c0572ca15f | ||
![]() |
a630d69e0c | ||
![]() |
20bcd281a3 | ||
![]() |
c012fccd22 | ||
![]() |
920727dd34 | ||
![]() |
157d365894 | ||
![]() |
bfa18ee8ec | ||
![]() |
890eebf151 | ||
![]() |
9310cd0cd6 | ||
![]() |
9f7ce7ce5a | ||
![]() |
0b600d6cef | ||
![]() |
3ae2a79bdf | ||
![]() |
f7c0893f76 | ||
![]() |
c3de301fc8 | ||
![]() |
944b446ac0 | ||
![]() |
b096fc7abf | ||
![]() |
d10754e2b4 | ||
![]() |
7ac683c360 | ||
![]() |
6caa9d38ac | ||
![]() |
19fd0f101f | ||
![]() |
8c91c51d1b | ||
![]() |
7e28bf7e97 | ||
![]() |
43d6e426c8 | ||
![]() |
26fc60e7cb | ||
![]() |
e5d7879622 | ||
![]() |
d2ee58f2e9 | ||
![]() |
3f25537a06 | ||
![]() |
d203ae37f4 | ||
![]() |
6eedd66c1a | ||
![]() |
e4b39ae553 | ||
![]() |
7cbcb6d318 | ||
![]() |
c0fca3f50a | ||
![]() |
4c2072d875 | ||
![]() |
92ecca1808 | ||
![]() |
7236635cc1 | ||
![]() |
21a3486ebb | ||
![]() |
bda8d7722e | ||
![]() |
c2bcb764cd | ||
![]() |
9e24154ec9 | ||
![]() |
9f3ca97ee8 | ||
![]() |
32d5ceba87 | ||
![]() |
e010f3b884 | ||
![]() |
941202c119 | ||
![]() |
c021ad2334 | ||
![]() |
2b3420820b | ||
![]() |
da57302fca | ||
![]() |
1869930d95 | ||
![]() |
1213d8fef4 | ||
![]() |
a432b42c81 | ||
![]() |
7d0f2eaf24 | ||
![]() |
41a4d67d93 | ||
![]() |
afde60e433 | ||
![]() |
d7baa67acb | ||
![]() |
167397c18c | ||
![]() |
be36c5f150 | ||
![]() |
9484a14ab2 | ||
![]() |
0f5fc8fb3d | ||
![]() |
a5b40e9372 | ||
![]() |
c5ec4efe91 | ||
![]() |
e64a0e0454 | ||
![]() |
8b5b031f90 | ||
![]() |
4a2134bbc5 | ||
![]() |
484844aa1a | ||
![]() |
4ed10239ad | ||
![]() |
c4896ed642 | ||
![]() |
29aaec383c | ||
![]() |
0cb241b7d3 | ||
![]() |
de4750b8e0 | ||
![]() |
7b91c40e21 | ||
![]() |
cc9bf02da1 | ||
![]() |
b7959c44d2 | ||
![]() |
277cba4b32 | ||
![]() |
ed651df19b | ||
![]() |
641dc65e6e | ||
![]() |
de9136b29f | ||
![]() |
b36345fd84 | ||
![]() |
03402c8a04 | ||
![]() |
966e5a5575 | ||
![]() |
5aa0deeff9 | ||
![]() |
af4d822380 | ||
![]() |
fd95b86894 | ||
![]() |
5dbef3712e | ||
![]() |
63647e93e4 | ||
![]() |
9b8deb51ba | ||
![]() |
2c4b0d975e | ||
![]() |
8ceda538ef | ||
![]() |
233596f4bc | ||
![]() |
6712ee8f92 | ||
![]() |
0916ff71bd | ||
![]() |
5971650f77 | ||
![]() |
19725954ee | ||
![]() |
b1e1b71bab | ||
![]() |
f1799de309 | ||
![]() |
585a5e3416 | ||
![]() |
b7eeeedc3f | ||
![]() |
a20d4bc6b0 | ||
![]() |
fb31d66951 | ||
![]() |
33dfbf5c38 | ||
![]() |
d1df3718b5 | ||
![]() |
e2da0a416c | ||
![]() |
0c0a8e3d2b | ||
![]() |
0882aca3a8 | ||
![]() |
cd41915e10 | ||
![]() |
2effacd444 | ||
![]() |
c6901ff908 | ||
![]() |
2f774acce3 | ||
![]() |
5f8658238c | ||
![]() |
2bb1be4d4e | ||
![]() |
40e0016403 | ||
![]() |
541d232f1c | ||
![]() |
6bc99ce451 | ||
![]() |
e42d2d1da8 | ||
![]() |
bd9022962e | ||
![]() |
91f1b40206 | ||
![]() |
d9b89eead0 | ||
![]() |
5399297de6 | ||
![]() |
96f7be5d9b | ||
![]() |
0922367308 | ||
![]() |
e2d9900d82 | ||
![]() |
1140950d7b | ||
![]() |
6d9c008900 | ||
![]() |
b617444158 | ||
![]() |
e588c42646 | ||
![]() |
14bb2a9005 | ||
![]() |
f04d347e7a | ||
![]() |
746182c526 | ||
![]() |
08beb7d84c | ||
![]() |
9795b00f51 | ||
![]() |
bfc1bc6ee6 | ||
![]() |
e9cdcf131c | ||
![]() |
35e9885e8b | ||
![]() |
16885529f7 | ||
![]() |
3c02eeb5a8 | ||
![]() |
9e9bb62ad4 | ||
![]() |
175e630717 | ||
![]() |
44f38ad049 | ||
![]() |
ca928aeae4 | ||
![]() |
27b60a05b4 | ||
![]() |
8af4b331ef | ||
![]() |
a5a46e4989 | ||
![]() |
e4cdb0eab3 | ||
![]() |
e9a764129f | ||
![]() |
65129bde5e | ||
![]() |
b4beaf807b | ||
![]() |
4734056583 | ||
![]() |
71e0408390 | ||
![]() |
1352a9d848 | ||
![]() |
e0f68ec2c0 | ||
![]() |
9c6e0c6eb9 | ||
![]() |
4cbc7c4467 | ||
![]() |
aaff8803ef | ||
![]() |
16e20676b6 | ||
![]() |
6cd5f8b7f5 | ||
![]() |
10c0b8080e | ||
![]() |
d31666d332 | ||
![]() |
6d53e767d5 | ||
![]() |
f1b0bb33dd | ||
![]() |
99ae913414 | ||
![]() |
df78896e59 | ||
![]() |
c896751ce2 | ||
![]() |
501189625e | ||
![]() |
a065ada46a | ||
![]() |
17d6d537e2 | ||
![]() |
5cc224e44a | ||
![]() |
896089976a | ||
![]() |
a563f87818 | ||
![]() |
de307ea2ab | ||
![]() |
4bc904a527 | ||
![]() |
5937b5b355 | ||
![]() |
76387b6cd0 | ||
![]() |
9aa36a37c7 | ||
![]() |
9fd3796d93 | ||
![]() |
93fa17b53f | ||
![]() |
15ad0e5bc7 | ||
![]() |
1f27d17c0d | ||
![]() |
8af918a1e4 | ||
![]() |
bb5425a1d8 | ||
![]() |
12246969db | ||
![]() |
9151eec24e | ||
![]() |
22475729ce | ||
![]() |
04c67d700d | ||
![]() |
d708d607fa | ||
![]() |
46f71f4c22 | ||
![]() |
48cc2f2188 | ||
![]() |
bd6e7c934c | ||
![]() |
7925217e25 | ||
![]() |
401a564486 | ||
![]() |
31176d212b | ||
![]() |
2d89311d49 | ||
![]() |
5a25ad1972 | ||
![]() |
79d3a18b31 | ||
![]() |
89f17847ad | ||
![]() |
1ab5703404 | ||
![]() |
49d95e9a50 | ||
![]() |
7dff1a08d0 | ||
![]() |
5fee36fa84 | ||
![]() |
b0211dff49 | ||
![]() |
0f6d21cf84 | ||
![]() |
10b5cf8f32 | ||
![]() |
ad5aec3f3b | ||
![]() |
6e1a3987b7 | ||
![]() |
9630398e3b | ||
![]() |
7e34de4c29 | ||
![]() |
ace5cc4ed3 | ||
![]() |
7f617cfd7f | ||
![]() |
0deb4e5994 | ||
![]() |
6b9dde3ce8 | ||
![]() |
c145b618d4 | ||
![]() |
b07bb3d8c3 | ||
![]() |
9b513312e2 | ||
![]() |
bf26a3ed57 | ||
![]() |
77a8d931b8 | ||
![]() |
11ce572894 | ||
![]() |
7a468d1226 | ||
![]() |
00e2fd8b5f | ||
![]() |
0f83fea007 | ||
![]() |
04f7c054cd | ||
![]() |
5dd0df0162 | ||
![]() |
abc923f693 | ||
![]() |
ac3bd6b2eb | ||
![]() |
156d85a29b | ||
![]() |
8c146eac4b | ||
![]() |
6f5b0f3622 |
27
.github/ISSUE_TEMPLATE.md
vendored
Normal file
27
.github/ISSUE_TEMPLATE.md
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
<!--
|
||||
|
||||
Welcome! If you have a question or are unsure if you should open an issue,
|
||||
please use the forum instead!
|
||||
|
||||
https://forum.restic.net
|
||||
|
||||
The forum is a better place for questions about restic or general suggestions
|
||||
and topics, e.g. usage or documentation questions! This issue tracker is mainly
|
||||
for tracking bugs and feature requests directly relating to the development of
|
||||
the software itself, rather than the project.
|
||||
|
||||
Thanks for understanding, and for contributing to the project!
|
||||
-->
|
||||
|
||||
|
||||
Output of `restic version`
|
||||
--------------------------
|
||||
|
||||
<!--
|
||||
Please add the version of restic you're currently using here, this helps us
|
||||
later to see what has changed in restic when we revisit this issue after some
|
||||
time.
|
||||
-->
|
||||
|
||||
Describe the issue
|
||||
------------------
|
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,3 +1,2 @@
|
||||
/restic
|
||||
/.vagrant
|
||||
/doc/_build
|
||||
|
26
.travis.yml
26
.travis.yml
@@ -6,15 +6,35 @@ matrix:
|
||||
- os: linux
|
||||
go: "1.9.x"
|
||||
env: RESTIC_TEST_FUSE=0 RESTIC_TEST_CLOUD_BACKENDS=0 RESTIC_BUILD_SOLARIS=0
|
||||
cache:
|
||||
directories:
|
||||
- $HOME/.cache/go-build
|
||||
- $HOME/gopath/pkg/mod
|
||||
|
||||
- os: linux
|
||||
go: "1.10.x"
|
||||
env: RESTIC_TEST_FUSE=0 RESTIC_TEST_CLOUD_BACKENDS=0
|
||||
cache:
|
||||
directories:
|
||||
- $HOME/.cache/go-build
|
||||
- $HOME/gopath/pkg/mod
|
||||
|
||||
# only run fuse and cloud backends tests on Travis for the latest Go on Linux
|
||||
- os: linux
|
||||
go: "1.10.x"
|
||||
go: "1.11.x"
|
||||
sudo: true
|
||||
cache:
|
||||
directories:
|
||||
- $HOME/.cache/go-build
|
||||
- $HOME/gopath/pkg/mod
|
||||
|
||||
- os: osx
|
||||
go: "1.10.x"
|
||||
go: "1.11.x"
|
||||
env: RESTIC_TEST_FUSE=0 RESTIC_TEST_CLOUD_BACKENDS=0
|
||||
cache:
|
||||
directories:
|
||||
- $HOME/Library/Caches/go-build
|
||||
- $HOME/gopath/pkg/mod
|
||||
|
||||
branches:
|
||||
only:
|
||||
@@ -38,4 +58,4 @@ script:
|
||||
- go run run_integration_tests.go
|
||||
|
||||
after_success:
|
||||
- bash <(curl -s https://codecov.io/bash) -f all.cov
|
||||
- test -r all.cov && bash <(curl -s https://codecov.io/bash) -f all.cov
|
||||
|
299
CHANGELOG.md
299
CHANGELOG.md
@@ -1,3 +1,302 @@
|
||||
Changelog for restic 0.9.4 (2019-01-06)
|
||||
=======================================
|
||||
|
||||
The following sections list the changes in restic 0.9.4 relevant to
|
||||
restic users. The changes are ordered by importance.
|
||||
|
||||
Summary
|
||||
-------
|
||||
|
||||
* Fix #1989: Google Cloud Storage: Respect bandwidth limit
|
||||
* Fix #2040: Add host name filter shorthand flag for `stats` command
|
||||
* Fix #2068: Correctly return error loading data
|
||||
* Fix #2095: Consistently use local time for snapshots times
|
||||
* Enh #1605: Concurrent restore
|
||||
* Enh #2089: Increase granularity of the "keep within" retention policy
|
||||
* Enh #2097: Add key hinting
|
||||
* Enh #2017: Mount: Enforce FUSE Unix permissions with allow-other
|
||||
* Enh #2070: Make all commands display timestamps in local time
|
||||
* Enh #2085: Allow --files-from to be specified multiple times
|
||||
* Enh #2094: Run command to get password
|
||||
|
||||
Details
|
||||
-------
|
||||
|
||||
* Bugfix #1989: Google Cloud Storage: Respect bandwidth limit
|
||||
|
||||
The GCS backend did not respect the bandwidth limit configured, a previous commit
|
||||
accidentally removed support for it.
|
||||
|
||||
https://github.com/restic/restic/issues/1989
|
||||
https://github.com/restic/restic/pull/2100
|
||||
|
||||
* Bugfix #2040: Add host name filter shorthand flag for `stats` command
|
||||
|
||||
The default value for `--host` flag was set to 'H' (the shorthand version of the flag), this
|
||||
caused the lookup for the latest snapshot to fail.
|
||||
|
||||
Add shorthand flag `-H` for `--host` (with empty default so if these flags are not specified the
|
||||
latest snapshot will not filter by host name).
|
||||
|
||||
Also add shorthand `-H` for `backup` command.
|
||||
|
||||
https://github.com/restic/restic/issues/2040
|
||||
|
||||
* Bugfix #2068: Correctly return error loading data
|
||||
|
||||
In one case during `prune` and `check`, an error loading data from the backend is not returned
|
||||
properly. This is now corrected.
|
||||
|
||||
https://github.com/restic/restic/issues/1999#issuecomment-433737921
|
||||
https://github.com/restic/restic/pull/2068
|
||||
|
||||
* Bugfix #2095: Consistently use local time for snapshots times
|
||||
|
||||
By default snapshots created with restic backup were set to local time, but when the --time flag
|
||||
was used the provided timestamp was parsed as UTC. With this change all snapshots times are set
|
||||
to local time.
|
||||
|
||||
https://github.com/restic/restic/pull/2095
|
||||
|
||||
* Enhancement #1605: Concurrent restore
|
||||
|
||||
This change significantly improves restore performance, especially when using
|
||||
high-latency remote repositories like B2.
|
||||
|
||||
The implementation now uses several concurrent threads to download and process multiple
|
||||
remote files concurrently. To further reduce restore time, each remote file is downloaded
|
||||
using a single repository request.
|
||||
|
||||
https://github.com/restic/restic/issues/1605
|
||||
https://github.com/restic/restic/pull/1719
|
||||
|
||||
* Enhancement #2089: Increase granularity of the "keep within" retention policy
|
||||
|
||||
The `keep-within` option of the `forget` command now accepts time ranges with an hourly
|
||||
granularity. For example, running `restic forget --keep-within 3d12h` will keep all the
|
||||
snapshots made within three days and twelve hours from the time of the latest snapshot.
|
||||
|
||||
https://github.com/restic/restic/issues/2089
|
||||
https://github.com/restic/restic/pull/2090
|
||||
|
||||
* Enhancement #2097: Add key hinting
|
||||
|
||||
Added a new option `--key-hint` and corresponding environment variable `RESTIC_KEY_HINT`.
|
||||
The key hint is a key ID to try decrypting first, before other keys in the repository.
|
||||
|
||||
This change will benefit repositories with many keys; if the correct key hint is supplied then
|
||||
restic only needs to check one key. If the key hint is incorrect (the key does not exist, or the
|
||||
password is incorrect) then restic will check all keys, as usual.
|
||||
|
||||
https://github.com/restic/restic/issues/2097
|
||||
|
||||
* Enhancement #2017: Mount: Enforce FUSE Unix permissions with allow-other
|
||||
|
||||
The fuse mount (`restic mount`) now lets the kernel check the permissions of the files within
|
||||
snapshots (this is done through the `DefaultPermissions` FUSE option) when the option
|
||||
`--allow-other` is specified.
|
||||
|
||||
To restore the old behavior, we've added the `--no-default-permissions` option. This allows
|
||||
all users that have access to the mount point to access all files within the snapshots.
|
||||
|
||||
https://github.com/restic/restic/pull/2017
|
||||
|
||||
* Enhancement #2070: Make all commands display timestamps in local time
|
||||
|
||||
Restic used to drop the timezone information from displayed timestamps, it now converts
|
||||
timestamps to local time before printing them so the times can be easily compared to.
|
||||
|
||||
https://github.com/restic/restic/pull/2070
|
||||
|
||||
* Enhancement #2085: Allow --files-from to be specified multiple times
|
||||
|
||||
Before, restic took only the last file specified with `--files-from` into account, this is now
|
||||
corrected.
|
||||
|
||||
https://github.com/restic/restic/issues/2085
|
||||
https://github.com/restic/restic/pull/2086
|
||||
|
||||
* Enhancement #2094: Run command to get password
|
||||
|
||||
We've added the `--password-command` option which allows specifying a command that restic
|
||||
runs every time the password for the repository is needed, so it can be integrated with a
|
||||
password manager or keyring. The option can also be set via the environment variable
|
||||
`$RESTIC_PASSWORD_COMMAND`.
|
||||
|
||||
https://github.com/restic/restic/pull/2094
|
||||
|
||||
|
||||
Changelog for restic 0.9.3 (2018-10-13)
|
||||
=======================================
|
||||
|
||||
The following sections list the changes in restic 0.9.3 relevant to
|
||||
restic users. The changes are ordered by importance.
|
||||
|
||||
Summary
|
||||
-------
|
||||
|
||||
* Fix #1935: Remove truncated files from cache
|
||||
* Fix #1978: Do not return an error when the scanner is slower than backup
|
||||
* Enh #1766: Restore: suppress lchown errors when not running as root
|
||||
* Enh #1909: Reject files/dirs by name first
|
||||
* Enh #1940: Add directory filter to ls command
|
||||
* Enh #1967: Use `--host` everywhere
|
||||
* Enh #2028: Display size of cache directories
|
||||
* Enh #1777: Improve the `find` command
|
||||
* Enh #1876: Display reason why forget keeps snapshots
|
||||
* Enh #1891: Accept glob in paths loaded via --files-from
|
||||
* Enh #1920: Vendor dependencies with Go 1.11 Modules
|
||||
* Enh #1949: Add new command `self-update`
|
||||
* Enh #1953: Ls: Add JSON output support for restic ls cmd
|
||||
* Enh #1962: Stream JSON output for ls command
|
||||
|
||||
Details
|
||||
-------
|
||||
|
||||
* Bugfix #1935: Remove truncated files from cache
|
||||
|
||||
When a file in the local cache is truncated, and restic tries to access data beyond the end of the
|
||||
(cached) file, it used to return an error "EOF". This is now fixed, such truncated files are
|
||||
removed and the data is fetched directly from the backend.
|
||||
|
||||
https://github.com/restic/restic/issues/1935
|
||||
|
||||
* Bugfix #1978: Do not return an error when the scanner is slower than backup
|
||||
|
||||
When restic makes a backup, there's a background task called "scanner" which collects
|
||||
information on how many files and directories are to be saved, in order to display progress
|
||||
information to the user. When the backup finishes faster than the scanner, it is aborted
|
||||
because the result is not needed any more. This logic contained a bug, where quitting the
|
||||
scanner process was treated as an error, and caused restic to print an unhelpful error message
|
||||
("context canceled").
|
||||
|
||||
https://github.com/restic/restic/issues/1978
|
||||
https://github.com/restic/restic/pull/1991
|
||||
|
||||
* Enhancement #1766: Restore: suppress lchown errors when not running as root
|
||||
|
||||
Like "cp" and "rsync" do, restic now only reports errors for changing the ownership of files
|
||||
during restore if it is run as root, on non-Windows operating systems. On Windows, the error
|
||||
is reported as usual.
|
||||
|
||||
https://github.com/restic/restic/issues/1766
|
||||
|
||||
* Enhancement #1909: Reject files/dirs by name first
|
||||
|
||||
The current scanner/archiver code had an architectural limitation: it always ran the
|
||||
`lstat()` system call on all files and directories before a decision to include/exclude the
|
||||
file/dir was made. This lead to a lot of unnecessary system calls for items that could have been
|
||||
rejected by their name or path only.
|
||||
|
||||
We've changed the archiver/scanner implementation so that it now first rejects by name/path,
|
||||
and only runs the system call on the remaining items. This reduces the number of `lstat()`
|
||||
system calls a lot (depending on the exclude settings).
|
||||
|
||||
https://github.com/restic/restic/issues/1909
|
||||
https://github.com/restic/restic/pull/1912
|
||||
|
||||
* Enhancement #1940: Add directory filter to ls command
|
||||
|
||||
The ls command can now be filtered by directories, so that only files in the given directories
|
||||
will be shown. If the --recursive flag is specified, then ls will traverse subfolders and list
|
||||
their files as well.
|
||||
|
||||
It used to be possible to specify multiple snapshots, but that has been replaced by only one
|
||||
snapshot and the possibility of specifying multiple directories.
|
||||
|
||||
Specifying directories constrains the walk, which can significantly speed up the listing.
|
||||
|
||||
https://github.com/restic/restic/issues/1940
|
||||
https://github.com/restic/restic/pull/1941
|
||||
|
||||
* Enhancement #1967: Use `--host` everywhere
|
||||
|
||||
We now use the flag `--host` for all commands which need a host name, using `--hostname` (e.g.
|
||||
for `restic backup`) still works, but will print a deprecation warning. Also, add the short
|
||||
option `-H` where possible.
|
||||
|
||||
https://github.com/restic/restic/issues/1967
|
||||
|
||||
* Enhancement #2028: Display size of cache directories
|
||||
|
||||
The `cache` command now by default shows the size of the individual cache directories. It can be
|
||||
disabled with `--no-size`.
|
||||
|
||||
https://github.com/restic/restic/issues/2028
|
||||
https://github.com/restic/restic/pull/2033
|
||||
|
||||
* Enhancement #1777: Improve the `find` command
|
||||
|
||||
We've updated the `find` command to support multiple patterns.
|
||||
|
||||
`restic find` is now able to list the snapshots containing a specific tree or blob, or even the
|
||||
snapshots that contain blobs belonging to a given pack. A list of IDs can be given, as long as they
|
||||
all have the same type.
|
||||
|
||||
The command `find` can also display the pack IDs the blobs belong to, if the `--show-pack-id`
|
||||
flag is provided.
|
||||
|
||||
https://github.com/restic/restic/issues/1777
|
||||
https://github.com/restic/restic/pull/1780
|
||||
|
||||
* Enhancement #1876: Display reason why forget keeps snapshots
|
||||
|
||||
We've added a column to the list of snapshots `forget` keeps which details the reasons to keep a
|
||||
particuliar snapshot. This makes debugging policies for forget much easier. Please remember
|
||||
to always try things out with `--dry-run`!
|
||||
|
||||
https://github.com/restic/restic/pull/1876
|
||||
|
||||
* Enhancement #1891: Accept glob in paths loaded via --files-from
|
||||
|
||||
Before that, behaviour was different if paths were appended to command line or from a file,
|
||||
because wild card characters were expanded by shell if appended to command line, but not
|
||||
expanded if loaded from file.
|
||||
|
||||
https://github.com/restic/restic/issues/1891
|
||||
|
||||
* Enhancement #1920: Vendor dependencies with Go 1.11 Modules
|
||||
|
||||
Until now, we've used `dep` for managing dependencies, we've now switch to using Go modules.
|
||||
For users this does not change much, only if you want to compile restic without downloading
|
||||
anything with Go 1.11, then you need to run: `go build -mod=vendor build.go`
|
||||
|
||||
https://github.com/restic/restic/pull/1920
|
||||
|
||||
* Enhancement #1949: Add new command `self-update`
|
||||
|
||||
We have added a new command called `self-update` which downloads the latest released version
|
||||
of restic from GitHub and replaces the current binary with it. It does not rely on any external
|
||||
program (so it'll work everywhere), but still verifies the GPG signature using the embedded
|
||||
GPG public key.
|
||||
|
||||
By default, the `self-update` command is hidden behind the `selfupdate` built tag, which is
|
||||
only set when restic is built using `build.go` (including official releases). The reason for
|
||||
this is that downstream distributions will then not include the command by default, so users
|
||||
are encouraged to use the platform-specific distribution mechanism.
|
||||
|
||||
https://github.com/restic/restic/pull/1949
|
||||
|
||||
* Enhancement #1953: Ls: Add JSON output support for restic ls cmd
|
||||
|
||||
We've implemented listing files in the repository with JSON as output, just pass `--json` as an
|
||||
option to `restic ls`. This makes the output of the command machine readable.
|
||||
|
||||
https://github.com/restic/restic/pull/1953
|
||||
|
||||
* Enhancement #1962: Stream JSON output for ls command
|
||||
|
||||
The `ls` command now supports JSON output with the global `--json` flag, and this change
|
||||
streams out JSON messages one object at a time rather than en entire array buffered in memory
|
||||
before encoding. The advantage is it allows large listings to be handled efficiently.
|
||||
|
||||
Two message types are printed: snapshots and nodes. A snapshot object will precede node
|
||||
objects which belong to that snapshot. The `struct_type` field can be used to determine which
|
||||
kind of message an object is.
|
||||
|
||||
https://github.com/restic/restic/pull/1962
|
||||
|
||||
|
||||
Changelog for restic 0.9.2 (2018-08-06)
|
||||
=======================================
|
||||
|
||||
|
@@ -46,12 +46,15 @@ Remember, the easier it is for us to reproduce the bug, the earlier it will be
|
||||
corrected!
|
||||
|
||||
In addition, you can compile restic with debug support by running
|
||||
`go run build.go -tags debug` and instructing it to create a debug log by
|
||||
setting the environment variable `DEBUG_LOG` to a file, e.g. like this:
|
||||
`go run -mod=vendor build.go -tags debug` and instructing it to create a debug
|
||||
log by setting the environment variable `DEBUG_LOG` to a file, e.g. like this:
|
||||
|
||||
$ export DEBUG_LOG=/tmp/restic-debug.log
|
||||
$ restic backup ~/work
|
||||
|
||||
For Go < 1.11, you need to remove the `-mod=vendor` option from the build
|
||||
command.
|
||||
|
||||
Please be aware that the debug log file will contain potentially sensitive
|
||||
things like file and directory names, so please either redact it before
|
||||
uploading it somewhere or post only the parts that are really relevant.
|
||||
@@ -60,9 +63,37 @@ uploading it somewhere or post only the parts that are really relevant.
|
||||
Development Environment
|
||||
=======================
|
||||
|
||||
In order to compile restic with the `go` tool directly, it needs to be checked
|
||||
out at the right path within a `GOPATH`. The concept of a `GOPATH` is explained
|
||||
in ["How to write Go code"](https://golang.org/doc/code.html).
|
||||
The repository contains several sets of directories with code: `cmd/` and
|
||||
`internal/` contain the code written for restic, whereas `vendor/` contains
|
||||
copies of libraries restic depends on. The libraries are managed with the
|
||||
command `go mod vendor`.
|
||||
|
||||
Go >= 1.11
|
||||
----------
|
||||
|
||||
For Go version 1.11 or later, you should clone the repo (without having
|
||||
`$GOPATH` set) and `cd` into the directory:
|
||||
|
||||
$ unset GOPATH
|
||||
$ git clone https://github.com/restic/restic
|
||||
$ cd restic
|
||||
|
||||
Then use the `go` tool to build restic:
|
||||
|
||||
$ go build ./cmd/restic
|
||||
$ ./restic version
|
||||
restic 0.9.2-dev (compiled manually) compiled with go1.11 on linux/amd64
|
||||
|
||||
You can run all tests with the following command:
|
||||
|
||||
$ go test ./...
|
||||
|
||||
Go < 1.11
|
||||
---------
|
||||
|
||||
In order to compile restic with Go before 1.11, it needs to be checked out at
|
||||
the right path within a `GOPATH`. The concept of a `GOPATH` is explained in
|
||||
["How to write Go code"](https://golang.org/doc/code.html).
|
||||
|
||||
If you do not have a directory with Go code yet, executing the following
|
||||
instructions in your shell will create one for you and check out the restic
|
||||
@@ -83,12 +114,7 @@ You can then build restic as follows:
|
||||
|
||||
The following commands can be used to run all the tests:
|
||||
|
||||
$ go test ./cmd/... ./internal/...
|
||||
|
||||
The repository contains two sets of directories with code: `cmd/` and
|
||||
`internal/` contain the code written for restic, whereas `vendor/` contains
|
||||
copies of libraries restic depends on. The libraries are managed with the
|
||||
[`dep`](https://github.com/golang/dep) tool.
|
||||
$ go test ./...
|
||||
|
||||
Providing Patches
|
||||
=================
|
||||
@@ -141,13 +167,14 @@ run
|
||||
|
||||
gofmt -w **/*.go
|
||||
|
||||
in the project root directory before committing. Installing the script
|
||||
`fmt-check` from https://github.com/edsrzf/gofmt-git-hook locally as a
|
||||
pre-commit hook checks formatting before committing automatically, just copy
|
||||
this script to `.git/hooks/pre-commit`.
|
||||
in the project root directory before committing. For each Pull Request, the
|
||||
formatting is tested with `gofmt` for the latest stable version of Go.
|
||||
Installing the script `fmt-check` from https://github.com/edsrzf/gofmt-git-hook
|
||||
locally as a pre-commit hook checks formatting before committing automatically,
|
||||
just copy this script to `.git/hooks/pre-commit`.
|
||||
|
||||
For each pull request, several different systems run the integration tests on
|
||||
Linux, OS X and Windows. We won't merge any code that does not pass all tests
|
||||
Linux, macOS and Windows. We won't merge any code that does not pass all tests
|
||||
for all systems, so when a tests fails, try to find out what's wrong and fix
|
||||
it. If you need help on this, please leave a comment in the pull request, and
|
||||
we'll be glad to assist. Having a PR with failing integration tests is nothing
|
||||
|
474
Gopkg.lock
generated
474
Gopkg.lock
generated
@@ -1,474 +0,0 @@
|
||||
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
|
||||
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:94e9caf404409a2990cfd22aca37d758494c098eff3e2c37fda1abed862e74dd"
|
||||
name = "bazil.org/fuse"
|
||||
packages = [
|
||||
".",
|
||||
"fs",
|
||||
"fuseutil",
|
||||
]
|
||||
pruneopts = "UT"
|
||||
revision = "65cc252bf6691cb3c7014bcb2c8dc29de91e3a7e"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:5c3894b2aa4d6bead0ceeea6831b305d62879c871780e7b76296ded1b004bc57"
|
||||
name = "cloud.google.com/go"
|
||||
packages = ["compute/metadata"]
|
||||
pruneopts = "UT"
|
||||
revision = "aad3f485ee528456e0768f20397b4d9dd941e755"
|
||||
version = "v0.25.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:46ea9487304f4b3c787f54483ecb13a338d686dcd670db0ab1a112ed0ae2128e"
|
||||
name = "github.com/Azure/azure-sdk-for-go"
|
||||
packages = [
|
||||
"storage",
|
||||
"version",
|
||||
]
|
||||
pruneopts = "UT"
|
||||
revision = "4e8cbbfb1aeab140cd0fa97fd16b64ee18c3ca6a"
|
||||
version = "v19.1.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:27d0cd1a78fc836f7c0f07749d029a5f7895c84ad066187b08b70e9d1830098e"
|
||||
name = "github.com/Azure/go-autorest"
|
||||
packages = [
|
||||
"autorest",
|
||||
"autorest/adal",
|
||||
"autorest/azure",
|
||||
"autorest/date",
|
||||
"logger",
|
||||
"version",
|
||||
]
|
||||
pruneopts = "UT"
|
||||
revision = "dd94e014aaf16d1df746762e392aa201c1b4c461"
|
||||
version = "v10.15.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:2209584c0f7c9b68c23374e659357ab546e1b70eec2761f03280f69a8fd23d77"
|
||||
name = "github.com/cenkalti/backoff"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "2ea60e5f094469f9e65adb9cd103795b73ae743e"
|
||||
version = "v2.0.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:7cb4fdca4c251b3ef8027c90ea35f70c7b661a593b9eeae34753c65499098bb1"
|
||||
name = "github.com/cpuguy83/go-md2man"
|
||||
packages = ["md2man"]
|
||||
pruneopts = "UT"
|
||||
revision = "20f5889cbdc3c73dbd2862796665e7c465ade7d1"
|
||||
version = "v1.0.8"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:76dc72490af7174349349838f2fe118996381b31ea83243812a97e5a0fd5ed55"
|
||||
name = "github.com/dgrijalva/jwt-go"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "06ea1031745cb8b3dab3f6a236daf2b0aa468b7e"
|
||||
version = "v3.2.0"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:6f9339c912bbdda81302633ad7e99a28dfa5a639c864061f1929510a9a64aa74"
|
||||
name = "github.com/dustin/go-humanize"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "9f541cc9db5d55bce703bd99987c9d5cb8eea45e"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:c7edfbb6320d6a93240d663dc52bca92bed4c116abe54c35679eec4e7cc2bd77"
|
||||
name = "github.com/elithrar/simple-scrypt"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "d150773194090feb6c897805a7bcea8d49544e2c"
|
||||
version = "v1.3.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:fe8a03a8222d5b913f256972933d26d24ad7c8286692a42943bc01633cc8fce3"
|
||||
name = "github.com/go-ini/ini"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "358ee7663966325963d4e8b2e1fbd570c5195153"
|
||||
version = "v1.38.1"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:15042ad3498153684d09f393bbaec6b216c8eec6d61f63dff711de7d64ed8861"
|
||||
name = "github.com/golang/protobuf"
|
||||
packages = ["proto"]
|
||||
pruneopts = "UT"
|
||||
revision = "b4deda0973fb4c70b50d226b1af49f3da59f5265"
|
||||
version = "v1.1.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:2e3c336fc7fde5c984d2841455a658a6d626450b1754a854b3b32e7a8f49a07a"
|
||||
name = "github.com/google/go-cmp"
|
||||
packages = [
|
||||
"cmp",
|
||||
"cmp/internal/diff",
|
||||
"cmp/internal/function",
|
||||
"cmp/internal/value",
|
||||
]
|
||||
pruneopts = "UT"
|
||||
revision = "3af367b6b30c263d47e8895973edcca9a49cf029"
|
||||
version = "v0.2.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:870d441fe217b8e689d7949fef6e43efbc787e50f200cb1e70dbca9204a1d6be"
|
||||
name = "github.com/inconshreveable/mousetrap"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "76626ae9c91c4f2a10f34cad8ce83ea42c93bb75"
|
||||
version = "v1.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:190ff84d9b2ed6589088f178cba8edb4b8ecb334df4572421fb016be1ac20463"
|
||||
name = "github.com/juju/ratelimit"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "59fac5042749a5afb9af70e813da1dd5474f0167"
|
||||
version = "1.0.1"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:9cedee824c21326bd26950bd9e1ffe9dc4e7ca03dc8634d0e6f954ee6a383172"
|
||||
name = "github.com/kr/fs"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "1455def202f6e05b95cc7bfc7e8ae67ae5141eba"
|
||||
version = "v0.1.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:1faa76bd9bffce9c25eaca0597afb67bd05a21ae57fe4378154ce8855ef163d1"
|
||||
name = "github.com/kurin/blazer"
|
||||
packages = [
|
||||
"b2",
|
||||
"base",
|
||||
"internal/b2assets",
|
||||
"internal/b2types",
|
||||
"internal/blog",
|
||||
"x/window",
|
||||
]
|
||||
pruneopts = "UT"
|
||||
revision = "caf65aa76491dc533bac68ad3243ce72fa4e0a0a"
|
||||
version = "v0.5.1"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:4e878df5f4e9fd625bf9c9aac77ef7cbfa4a74c01265505527c23470c0e40300"
|
||||
name = "github.com/marstr/guid"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "8bd9a64bf37eb297b492a4101fb28e80ac0b290f"
|
||||
version = "v1.1.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:d4d17353dbd05cb52a2a52b7fe1771883b682806f68db442b436294926bbfafb"
|
||||
name = "github.com/mattn/go-isatty"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "0360b2af4f38e8d38c7fce2a9f4e702702d73a39"
|
||||
version = "v0.0.3"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:95c73c666919be2843b955eafc83f58c136312b74f79c703152f4c4a95fd64dc"
|
||||
name = "github.com/minio/minio-go"
|
||||
packages = [
|
||||
".",
|
||||
"pkg/credentials",
|
||||
"pkg/encrypt",
|
||||
"pkg/s3signer",
|
||||
"pkg/s3utils",
|
||||
"pkg/set",
|
||||
]
|
||||
pruneopts = "UT"
|
||||
revision = "70799fe8dae6ecfb6c7d7e9e048fce27f23a1992"
|
||||
version = "v6.0.5"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:8eb17c2ec4df79193ae65b621cd1c0c4697db3bc317fe6afdc76d7f2746abd05"
|
||||
name = "github.com/mitchellh/go-homedir"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "3864e76763d94a6df2f9960b16a20a33da9f9a66"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:928de5172dd3563964d1b88a4ee3775cf72e16f1efabb482ab6d0e0bab86ee69"
|
||||
name = "github.com/ncw/swift"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "b2a7479cf26fa841ff90dd932d0221cb5c50782d"
|
||||
version = "v1.0.39"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:40e195917a951a8bf867cd05de2a46aaf1806c50cf92eebf4c16f78cd196f747"
|
||||
name = "github.com/pkg/errors"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "645ef00459ed84a119197bfb8d8205042c6df63d"
|
||||
version = "v0.8.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:cfa0d7741863a0e1d30e0ccdd4b48a96a471cdb47892303de8b92c3713af3e77"
|
||||
name = "github.com/pkg/profile"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "5b67d428864e92711fcbd2f8629456121a56d91f"
|
||||
version = "v1.2.1"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:23ed92ba5d90a2dfe817f3895027ccef796e79c30be5125d48e17afdcc395d73"
|
||||
name = "github.com/pkg/sftp"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "57673e38ea946592a59c26592b7e6fbda646975b"
|
||||
version = "v1.8.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:0d67664e93e366f072ac9672feea29bfc63c9f90f005e9e8a0df1954153f5a14"
|
||||
name = "github.com/pkg/xattr"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "ae385d07bb53f092fcc7daaf738d8513df084931"
|
||||
version = "v0.3.1"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:13ecc4000f49cf0aa3ee56fffcc93119c8edffacfff08674c80d2757d8c33a83"
|
||||
name = "github.com/restic/chunker"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "db83917be3b88cc307464b7d8a221c173e34a0db"
|
||||
version = "v0.2.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:8bc629776d035c003c7814d4369521afe67fdb8efc4b5f66540d29343b98cf23"
|
||||
name = "github.com/russross/blackfriday"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "55d61fa8aa702f59229e6cff85793c22e580eaf5"
|
||||
version = "v1.5.1"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:274f67cb6fed9588ea2521ecdac05a6d62a8c51c074c1fccc6a49a40ba80e925"
|
||||
name = "github.com/satori/go.uuid"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "f58768cc1a7a7e77a3bd49e98cdd21419399b6a3"
|
||||
version = "v1.2.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:d867dfa6751c8d7a435821ad3b736310c2ed68945d05b50fb9d23aee0540c8cc"
|
||||
name = "github.com/sirupsen/logrus"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "3e01752db0189b9157070a0e1668a620f9a85da2"
|
||||
version = "v1.0.6"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:e01b05ba901239c783dfe56450bcde607fc858908529868259c9a8765dc176d0"
|
||||
name = "github.com/spf13/cobra"
|
||||
packages = [
|
||||
".",
|
||||
"doc",
|
||||
]
|
||||
pruneopts = "UT"
|
||||
revision = "ef82de70bb3f60c65fb8eebacbb2d122ef517385"
|
||||
version = "v0.0.3"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:9424f440bba8f7508b69414634aef3b2b3a877e522d8a4624692412805407bb7"
|
||||
name = "github.com/spf13/pflag"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "583c0c0531f06d5278b7d917446061adc344b5cd"
|
||||
version = "v1.0.1"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:eefb1f49ec07e71206d4c9ea1a3e634cad331c2180733e4121b8ae39e8e92ecb"
|
||||
name = "golang.org/x/crypto"
|
||||
packages = [
|
||||
"argon2",
|
||||
"blake2b",
|
||||
"curve25519",
|
||||
"ed25519",
|
||||
"ed25519/internal/edwards25519",
|
||||
"internal/chacha20",
|
||||
"internal/subtle",
|
||||
"pbkdf2",
|
||||
"poly1305",
|
||||
"scrypt",
|
||||
"ssh",
|
||||
"ssh/terminal",
|
||||
]
|
||||
pruneopts = "UT"
|
||||
revision = "c126467f60eb25f8f27e5a981f32a87e3965053f"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:8356aa7bdcb10a210b814b64ff76d61de7c36ac4cb6263de3af5e3e2e546956d"
|
||||
name = "golang.org/x/net"
|
||||
packages = [
|
||||
"context",
|
||||
"context/ctxhttp",
|
||||
"http/httpguts",
|
||||
"http2",
|
||||
"http2/hpack",
|
||||
"idna",
|
||||
]
|
||||
pruneopts = "UT"
|
||||
revision = "32f9bdbd7df18e8641d215e7ea68be88b971feb0"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:bea0314c10bd362ab623af4880d853b5bad3b63d0ab9945c47e461b8d04203ed"
|
||||
name = "golang.org/x/oauth2"
|
||||
packages = [
|
||||
".",
|
||||
"google",
|
||||
"internal",
|
||||
"jws",
|
||||
"jwt",
|
||||
]
|
||||
pruneopts = "UT"
|
||||
revision = "3d292e4d0cdc3a0113e6d207bb137145ef1de42f"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:39ebcc2b11457b703ae9ee2e8cca0f68df21969c6102cb3b705f76cca0ea0239"
|
||||
name = "golang.org/x/sync"
|
||||
packages = ["errgroup"]
|
||||
pruneopts = "UT"
|
||||
revision = "1d60e4601c6fd243af51cc01ddf169918a5407ca"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:a220a85c72a6cb7339c412cb2b117019a7fd94007cdfffb3b5b1d058227a2bf8"
|
||||
name = "golang.org/x/sys"
|
||||
packages = [
|
||||
"cpu",
|
||||
"unix",
|
||||
"windows",
|
||||
]
|
||||
pruneopts = "UT"
|
||||
revision = "bd9dbc187b6e1dacfdd2722a87e83093c2d7bd6e"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:e5a8511f063c38c51ab9ab80e718e9149f692652aeb4e393a8c020dd1bf38ca2"
|
||||
name = "golang.org/x/text"
|
||||
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",
|
||||
]
|
||||
pruneopts = "UT"
|
||||
revision = "f21a4dfb5e38f5895301dc265a8def02365cc3d0"
|
||||
version = "v0.3.0"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:fb983acae7bd9c3ed9aadc1b1241d9e559ed21dbf84c17a0dda663ca169ccd69"
|
||||
name = "google.golang.org/api"
|
||||
packages = [
|
||||
"gensupport",
|
||||
"googleapi",
|
||||
"googleapi/internal/uritemplates",
|
||||
"storage/v1",
|
||||
]
|
||||
pruneopts = "UT"
|
||||
revision = "31ca0e01cd791f07750cb23fc99327721f753290"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:c8907869850adaa8bd7631887948d0684f3787d0912f1c01ab72581a6c34432e"
|
||||
name = "google.golang.org/appengine"
|
||||
packages = [
|
||||
".",
|
||||
"internal",
|
||||
"internal/app_identity",
|
||||
"internal/base",
|
||||
"internal/datastore",
|
||||
"internal/log",
|
||||
"internal/modules",
|
||||
"internal/remote_api",
|
||||
"internal/urlfetch",
|
||||
"urlfetch",
|
||||
]
|
||||
pruneopts = "UT"
|
||||
revision = "b1f26356af11148e710935ed1ac8a7f5702c7612"
|
||||
version = "v1.1.0"
|
||||
|
||||
[[projects]]
|
||||
branch = "v2"
|
||||
digest = "1:5bb148b78468350091db2ffbb2370f35cc6dcd74d9378a31b1c7b86ff7528f08"
|
||||
name = "gopkg.in/tomb.v2"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "d5d1b5820637886def9eef33e03a27a9f166942c"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:342378ac4dcb378a5448dd723f0784ae519383532f5e70ade24132c4c8693202"
|
||||
name = "gopkg.in/yaml.v2"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "5420a8b6744d3b0345ab293f6fcba19c978f1183"
|
||||
version = "v2.2.1"
|
||||
|
||||
[solve-meta]
|
||||
analyzer-name = "dep"
|
||||
analyzer-version = 1
|
||||
input-imports = [
|
||||
"bazil.org/fuse",
|
||||
"bazil.org/fuse/fs",
|
||||
"github.com/Azure/azure-sdk-for-go/storage",
|
||||
"github.com/cenkalti/backoff",
|
||||
"github.com/elithrar/simple-scrypt",
|
||||
"github.com/google/go-cmp/cmp",
|
||||
"github.com/juju/ratelimit",
|
||||
"github.com/kurin/blazer/b2",
|
||||
"github.com/mattn/go-isatty",
|
||||
"github.com/minio/minio-go",
|
||||
"github.com/minio/minio-go/pkg/credentials",
|
||||
"github.com/ncw/swift",
|
||||
"github.com/pkg/errors",
|
||||
"github.com/pkg/profile",
|
||||
"github.com/pkg/sftp",
|
||||
"github.com/pkg/xattr",
|
||||
"github.com/restic/chunker",
|
||||
"github.com/spf13/cobra",
|
||||
"github.com/spf13/cobra/doc",
|
||||
"github.com/spf13/pflag",
|
||||
"golang.org/x/crypto/poly1305",
|
||||
"golang.org/x/crypto/scrypt",
|
||||
"golang.org/x/crypto/ssh/terminal",
|
||||
"golang.org/x/net/context",
|
||||
"golang.org/x/net/context/ctxhttp",
|
||||
"golang.org/x/net/http2",
|
||||
"golang.org/x/oauth2/google",
|
||||
"golang.org/x/sync/errgroup",
|
||||
"golang.org/x/sys/unix",
|
||||
"golang.org/x/text/encoding/unicode",
|
||||
"google.golang.org/api/googleapi",
|
||||
"google.golang.org/api/storage/v1",
|
||||
"gopkg.in/tomb.v2",
|
||||
]
|
||||
solver-name = "gps-cdcl"
|
||||
solver-version = 1
|
25
Gopkg.toml
25
Gopkg.toml
@@ -1,25 +0,0 @@
|
||||
|
||||
# Gopkg.toml example
|
||||
#
|
||||
# Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md
|
||||
# for detailed Gopkg.toml documentation.
|
||||
#
|
||||
# required = ["github.com/user/thing/cmd/thing"]
|
||||
# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"]
|
||||
#
|
||||
# [[constraint]]
|
||||
# name = "github.com/user/project"
|
||||
# version = "1.0.0"
|
||||
#
|
||||
# [[constraint]]
|
||||
# name = "github.com/user/project2"
|
||||
# branch = "dev"
|
||||
# source = "github.com/myfork/project2"
|
||||
#
|
||||
# [[override]]
|
||||
# name = "github.com/x/y"
|
||||
# version = "2.4.0"
|
||||
|
||||
[prune]
|
||||
unused-packages = true
|
||||
go-tests = true
|
2
Makefile
2
Makefile
@@ -3,7 +3,7 @@
|
||||
all: restic
|
||||
|
||||
restic:
|
||||
go run build.go
|
||||
go run -mod=vendor build.go || go run build.go
|
||||
|
||||
clean:
|
||||
rm -f restic
|
||||
|
@@ -3,7 +3,7 @@
|
||||
Introduction
|
||||
------------
|
||||
|
||||
restic is a backup program that is fast, efficient and secure.
|
||||
restic is a backup program that is fast, efficient and secure. It supports the three major operating systems (Linux, macOS, Windows) and a few smaller ones (FreeBSD, OpenBSD).
|
||||
|
||||
For detailed usage and installation instructions check out the `documentation <https://restic.readthedocs.io/en/latest>`__.
|
||||
|
||||
|
@@ -7,6 +7,9 @@ branches:
|
||||
only:
|
||||
- master
|
||||
|
||||
cache:
|
||||
- '%LocalAppData%\go-build'
|
||||
|
||||
init:
|
||||
- ps: >-
|
||||
$app = Get-WmiObject -Class Win32_Product -Filter "Vendor = 'http://golang.org'"
|
||||
@@ -17,8 +20,8 @@ init:
|
||||
|
||||
install:
|
||||
- rmdir c:\go /s /q
|
||||
- appveyor DownloadFile https://dl.google.com/go/go1.10.windows-amd64.msi
|
||||
- msiexec /i go1.10.windows-amd64.msi /q
|
||||
- appveyor DownloadFile https://dl.google.com/go/go1.11.windows-amd64.msi
|
||||
- msiexec /i go1.11.windows-amd64.msi /q
|
||||
- go version
|
||||
- go env
|
||||
- appveyor DownloadFile http://sourceforge.netcologne.de/project/gnuwin32/tar/1.13-1/tar-1.13-1-bin.zip -FileName tar.zip
|
||||
@@ -26,4 +29,4 @@ install:
|
||||
- set PATH=bin/;%PATH%
|
||||
|
||||
build_script:
|
||||
- go run run_integration_tests.go
|
||||
- go run -mod=vendor run_integration_tests.go
|
||||
|
325
build.go
325
build.go
@@ -1,3 +1,18 @@
|
||||
// Description
|
||||
//
|
||||
// This program aims to make building Go programs for end users easier by just
|
||||
// calling it with `go run`, without having to setup a GOPATH.
|
||||
//
|
||||
// For Go < 1.11, it'll create a new GOPATH in a temporary directory, then run
|
||||
// `go build` on the package configured as Main in the Config struct.
|
||||
//
|
||||
// For Go >= 1.11 if the file go.mod is present, it'll use Go modules and not
|
||||
// setup a GOPATH. It builds the package configured as Main in the Config
|
||||
// struct with `go build -mod=vendor` to use the vendored dependencies.
|
||||
// The variable GOPROXY is set to `off` so that no network calls are made. All
|
||||
// files are copied to a temporary directory before `go build` is called within
|
||||
// that directory.
|
||||
|
||||
// BSD 2-Clause License
|
||||
//
|
||||
// Copyright (c) 2016-2018, Alexander Neumann <alexander@bumpern.de>
|
||||
@@ -37,7 +52,6 @@ import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strconv"
|
||||
@@ -46,23 +60,22 @@ import (
|
||||
|
||||
// config contains the configuration for the program to build.
|
||||
var config = Config{
|
||||
Name: "restic", // name of the program executable and directory
|
||||
Namespace: "github.com/restic/restic", // subdir of GOPATH, e.g. "github.com/foo/bar"
|
||||
Main: "github.com/restic/restic/cmd/restic", // package name for the main package
|
||||
Tests: []string{ // tests to run
|
||||
"github.com/restic/restic/internal/...",
|
||||
"github.com/restic/restic/cmd/...",
|
||||
},
|
||||
MinVersion: GoVersion{Major: 1, Minor: 9, Patch: 0}, // minimum Go version supported
|
||||
Name: "restic", // name of the program executable and directory
|
||||
Namespace: "github.com/restic/restic", // subdir of GOPATH, e.g. "github.com/foo/bar"
|
||||
Main: "./cmd/restic", // package name for the main package
|
||||
DefaultBuildTags: []string{"selfupdate"}, // specify build tags which are always used
|
||||
Tests: []string{"./..."}, // tests to run
|
||||
MinVersion: GoVersion{Major: 1, Minor: 9, Patch: 0}, // minimum Go version supported
|
||||
}
|
||||
|
||||
// Config configures the build.
|
||||
type Config struct {
|
||||
Name string
|
||||
Namespace string
|
||||
Main string
|
||||
Tests []string
|
||||
MinVersion GoVersion
|
||||
Name string
|
||||
Namespace string
|
||||
Main string
|
||||
DefaultBuildTags []string
|
||||
Tests []string
|
||||
MinVersion GoVersion
|
||||
}
|
||||
|
||||
var (
|
||||
@@ -71,41 +84,12 @@ var (
|
||||
runTests bool
|
||||
enableCGO bool
|
||||
enablePIE bool
|
||||
goVersion = ParseGoVersion(runtime.Version())
|
||||
)
|
||||
|
||||
// specialDir returns true if the file begins with a special character ('.' or '_').
|
||||
func specialDir(name string) bool {
|
||||
if name == "." {
|
||||
return false
|
||||
}
|
||||
|
||||
base := filepath.Base(name)
|
||||
if base == "vendor" || base[0] == '_' || base[0] == '.' {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// excludePath returns true if the file should not be copied to the new GOPATH.
|
||||
func excludePath(name string) bool {
|
||||
ext := path.Ext(name)
|
||||
if ext == ".go" || ext == ".s" || ext == ".h" {
|
||||
return false
|
||||
}
|
||||
|
||||
parentDir := filepath.Base(filepath.Dir(name))
|
||||
if parentDir == "testdata" {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// updateGopath builds a valid GOPATH at dst, with all Go files in src/ copied
|
||||
// to dst/prefix/, so calling
|
||||
// copy all Go files in src to dst, creating directories on the fly, so calling
|
||||
//
|
||||
// updateGopath("/tmp/gopath", "/home/u/restic", "github.com/restic/restic")
|
||||
// copy("/tmp/gopath/src/github.com/restic/restic", "/home/u/restic")
|
||||
//
|
||||
// with "/home/u/restic" containing the file "foo.go" yields the following tree
|
||||
// at "/tmp/gopath":
|
||||
@@ -116,19 +100,15 @@ func excludePath(name string) bool {
|
||||
// └── restic
|
||||
// └── restic
|
||||
// └── foo.go
|
||||
func updateGopath(dst, src, prefix string) error {
|
||||
verbosePrintf("copy contents of %v to %v\n", src, filepath.Join(dst, prefix))
|
||||
func copy(dst, src string) error {
|
||||
verbosePrintf("copy contents of %v to %v\n", src, dst)
|
||||
return filepath.Walk(src, func(name string, fi os.FileInfo, err error) error {
|
||||
if name == src {
|
||||
return err
|
||||
}
|
||||
|
||||
if specialDir(name) {
|
||||
if fi.IsDir() {
|
||||
return filepath.SkipDir
|
||||
}
|
||||
|
||||
return nil
|
||||
if name == ".git" {
|
||||
return filepath.SkipDir
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
@@ -139,17 +119,13 @@ func updateGopath(dst, src, prefix string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
if excludePath(name) {
|
||||
return nil
|
||||
}
|
||||
|
||||
intermediatePath, err := filepath.Rel(src, name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fileSrc := filepath.Join(src, intermediatePath)
|
||||
fileDst := filepath.Join(dst, "src", prefix, intermediatePath)
|
||||
fileDst := filepath.Join(dst, intermediatePath)
|
||||
|
||||
return copyFile(fileDst, fileSrc)
|
||||
})
|
||||
@@ -164,6 +140,15 @@ func directoryExists(dirname string) bool {
|
||||
return stat.IsDir()
|
||||
}
|
||||
|
||||
func fileExists(filename string) bool {
|
||||
stat, err := os.Stat(filename)
|
||||
if err != nil && os.IsNotExist(err) {
|
||||
return false
|
||||
}
|
||||
|
||||
return stat.Mode().IsRegular()
|
||||
}
|
||||
|
||||
// copyFile creates dst from src, preserving file attributes and timestamps.
|
||||
func copyFile(dst, src string) error {
|
||||
fi, err := os.Stat(src)
|
||||
@@ -183,30 +168,34 @@ func copyFile(dst, src string) error {
|
||||
|
||||
fdst, err := os.Create(dst)
|
||||
if err != nil {
|
||||
_ = fsrc.Close()
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err = io.Copy(fdst, fsrc); err != nil {
|
||||
_, err = io.Copy(fdst, fsrc)
|
||||
if err != nil {
|
||||
_ = fsrc.Close()
|
||||
_ = fdst.Close()
|
||||
return err
|
||||
}
|
||||
|
||||
if err == nil {
|
||||
err = fsrc.Close()
|
||||
err = fdst.Close()
|
||||
if err != nil {
|
||||
_ = fsrc.Close()
|
||||
return err
|
||||
}
|
||||
|
||||
if err == nil {
|
||||
err = fdst.Close()
|
||||
err = fsrc.Close()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err == nil {
|
||||
err = os.Chmod(dst, fi.Mode())
|
||||
err = os.Chmod(dst, fi.Mode())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err == nil {
|
||||
err = os.Chtimes(dst, fi.ModTime(), fi.ModTime())
|
||||
}
|
||||
|
||||
return nil
|
||||
return os.Chtimes(dst, fi.ModTime(), fi.ModTime())
|
||||
}
|
||||
|
||||
// die prints the message with fmt.Fprintf() to stderr and exits with an error
|
||||
@@ -222,7 +211,7 @@ func showUsage(output io.Writer) {
|
||||
fmt.Fprintf(output, "OPTIONS:\n")
|
||||
fmt.Fprintf(output, " -v --verbose output more messages\n")
|
||||
fmt.Fprintf(output, " -t --tags specify additional build tags\n")
|
||||
fmt.Fprintf(output, " -k --keep-gopath do not remove the GOPATH after build\n")
|
||||
fmt.Fprintf(output, " -k --keep-tempdir do not remove the temporary directory after build\n")
|
||||
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")
|
||||
@@ -241,11 +230,20 @@ func verbosePrintf(message string, args ...interface{}) {
|
||||
fmt.Printf("build: "+message, args...)
|
||||
}
|
||||
|
||||
// cleanEnv returns a clean environment with GOPATH and GOBIN removed (if
|
||||
// present).
|
||||
// cleanEnv returns a clean environment with GOPATH, GOBIN and GO111MODULE
|
||||
// removed (if present).
|
||||
func cleanEnv() (env []string) {
|
||||
removeKeys := map[string]struct{}{
|
||||
"GOPATH": struct{}{},
|
||||
"GOBIN": struct{}{},
|
||||
"GO111MODULE": struct{}{},
|
||||
}
|
||||
|
||||
for _, v := range os.Environ() {
|
||||
if strings.HasPrefix(v, "GOPATH=") || strings.HasPrefix(v, "GOBIN=") {
|
||||
data := strings.SplitN(v, "=", 2)
|
||||
name := data[0]
|
||||
|
||||
if _, ok := removeKeys[name]; ok {
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -256,17 +254,17 @@ func cleanEnv() (env []string) {
|
||||
}
|
||||
|
||||
// build runs "go build args..." with GOPATH set to gopath.
|
||||
func build(cwd string, ver GoVersion, goos, goarch, goarm, gopath string, args ...string) error {
|
||||
func build(cwd string, env map[string]string, args ...string) error {
|
||||
a := []string{"build"}
|
||||
|
||||
if ver.AtLeast(GoVersion{1, 10, 0}) {
|
||||
if goVersion.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))
|
||||
a = append(a, "-asmflags", fmt.Sprintf("all=-trimpath=%s", cwd))
|
||||
a = append(a, "-gcflags", fmt.Sprintf("all=-trimpath=%s", cwd))
|
||||
} else {
|
||||
a = append(a, "-asmflags", fmt.Sprintf("-trimpath=%s", gopath))
|
||||
a = append(a, "-gcflags", fmt.Sprintf("-trimpath=%s", gopath))
|
||||
a = append(a, "-asmflags", fmt.Sprintf("-trimpath=%s", cwd))
|
||||
a = append(a, "-gcflags", fmt.Sprintf("-trimpath=%s", cwd))
|
||||
}
|
||||
if enablePIE {
|
||||
a = append(a, "-buildmode=pie")
|
||||
@@ -274,9 +272,9 @@ func build(cwd string, ver GoVersion, goos, goarch, goarm, gopath string, args .
|
||||
|
||||
a = append(a, args...)
|
||||
cmd := exec.Command("go", a...)
|
||||
cmd.Env = append(cleanEnv(), "GOPATH="+gopath, "GOARCH="+goarch, "GOOS="+goos)
|
||||
if goarm != "" {
|
||||
cmd.Env = append(cmd.Env, "GOARM="+goarm)
|
||||
cmd.Env = append(cleanEnv(), "GOPROXY=off")
|
||||
for k, v := range env {
|
||||
cmd.Env = append(cmd.Env, k+"="+v)
|
||||
}
|
||||
if !enableCGO {
|
||||
cmd.Env = append(cmd.Env, "CGO_ENABLED=0")
|
||||
@@ -285,20 +283,30 @@ func build(cwd string, ver GoVersion, goos, goarch, goarm, gopath string, args .
|
||||
cmd.Dir = cwd
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
verbosePrintf("go %s\n", a)
|
||||
|
||||
verbosePrintf("chdir %q\n", cwd)
|
||||
verbosePrintf("go %q\n", a)
|
||||
|
||||
return cmd.Run()
|
||||
}
|
||||
|
||||
// test runs "go test args..." with GOPATH set to gopath.
|
||||
func test(cwd, gopath string, args ...string) error {
|
||||
args = append([]string{"test"}, args...)
|
||||
func test(cwd string, env map[string]string, args ...string) error {
|
||||
args = append([]string{"test", "-count", "1"}, args...)
|
||||
cmd := exec.Command("go", args...)
|
||||
cmd.Env = append(cleanEnv(), "GOPATH="+gopath)
|
||||
cmd.Env = append(cleanEnv(), "GOPROXY=off")
|
||||
for k, v := range env {
|
||||
cmd.Env = append(cmd.Env, k+"="+v)
|
||||
}
|
||||
if !enableCGO {
|
||||
cmd.Env = append(cmd.Env, "CGO_ENABLED=0")
|
||||
}
|
||||
cmd.Dir = cwd
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
verbosePrintf("go %s\n", args)
|
||||
|
||||
verbosePrintf("chdir %q\n", cwd)
|
||||
verbosePrintf("go %q\n", args)
|
||||
|
||||
return cmd.Run()
|
||||
}
|
||||
@@ -446,22 +454,24 @@ func (v GoVersion) String() string {
|
||||
}
|
||||
|
||||
func main() {
|
||||
ver := ParseGoVersion(runtime.Version())
|
||||
if !ver.AtLeast(config.MinVersion) {
|
||||
fmt.Fprintf(os.Stderr, "%s detected, this program requires at least %s\n", ver, config.MinVersion)
|
||||
if !goVersion.AtLeast(config.MinVersion) {
|
||||
fmt.Fprintf(os.Stderr, "%s detected, this program requires at least %s\n", goVersion, config.MinVersion)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
buildTags := []string{}
|
||||
buildTags := config.DefaultBuildTags
|
||||
|
||||
skipNext := false
|
||||
params := os.Args[1:]
|
||||
|
||||
targetGOOS := runtime.GOOS
|
||||
targetGOARCH := runtime.GOARCH
|
||||
targetGOARM := ""
|
||||
goEnv := map[string]string{}
|
||||
buildEnv := map[string]string{
|
||||
"GOOS": runtime.GOOS,
|
||||
"GOARCH": runtime.GOARCH,
|
||||
"GOARM": "",
|
||||
}
|
||||
|
||||
gopath := ""
|
||||
tempdir := ""
|
||||
|
||||
var outputFilename string
|
||||
|
||||
@@ -481,13 +491,13 @@ func main() {
|
||||
die("-t given but no tag specified")
|
||||
}
|
||||
skipNext = true
|
||||
buildTags = strings.Split(params[i+1], " ")
|
||||
buildTags = append(buildTags, strings.Split(params[i+1], " ")...)
|
||||
case "-o", "--output":
|
||||
skipNext = true
|
||||
outputFilename = params[i+1]
|
||||
case "--tempdir":
|
||||
skipNext = true
|
||||
gopath = params[i+1]
|
||||
tempdir = params[i+1]
|
||||
case "-T", "--test":
|
||||
runTests = true
|
||||
case "--enable-cgo":
|
||||
@@ -496,13 +506,13 @@ func main() {
|
||||
enablePIE = true
|
||||
case "--goos":
|
||||
skipNext = true
|
||||
targetGOOS = params[i+1]
|
||||
buildEnv["GOOS"] = params[i+1]
|
||||
case "--goarch":
|
||||
skipNext = true
|
||||
targetGOARCH = params[i+1]
|
||||
buildEnv["GOARCH"] = params[i+1]
|
||||
case "--goarm":
|
||||
skipNext = true
|
||||
targetGOARM = params[i+1]
|
||||
buildEnv["GOARM"] = params[i+1]
|
||||
case "-h":
|
||||
showUsage(os.Stdout)
|
||||
return
|
||||
@@ -513,12 +523,7 @@ func main() {
|
||||
}
|
||||
}
|
||||
|
||||
verbosePrintf("detected Go version %v\n", ver)
|
||||
|
||||
if len(buildTags) == 0 {
|
||||
verbosePrintf("adding build-tag release\n")
|
||||
buildTags = []string{"release"}
|
||||
}
|
||||
verbosePrintf("detected Go version %v\n", goVersion)
|
||||
|
||||
for i := range buildTags {
|
||||
buildTags[i] = strings.TrimSpace(buildTags[i])
|
||||
@@ -531,50 +536,16 @@ func main() {
|
||||
die("Getwd(): %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)
|
||||
if err = updateGopath(gopath, root, config.Namespace); err != nil {
|
||||
die("copying files from %v/src to %v/src failed: %v\n", root, gopath, err)
|
||||
}
|
||||
|
||||
vendor := filepath.Join(root, "vendor")
|
||||
if directoryExists(vendor) {
|
||||
if err = updateGopath(gopath, vendor, filepath.Join(config.Namespace, "vendor")); err != nil {
|
||||
die("copying files from %v to %v failed: %v\n", root, gopath, err)
|
||||
}
|
||||
}
|
||||
|
||||
defer func() {
|
||||
if !keepGopath {
|
||||
verbosePrintf("remove %v\n", gopath)
|
||||
if err = os.RemoveAll(gopath); err != nil {
|
||||
die("remove GOPATH at %s failed: %v\n", err)
|
||||
}
|
||||
} else {
|
||||
verbosePrintf("leaving temporary GOPATH at %v\n", gopath)
|
||||
}
|
||||
}()
|
||||
|
||||
if outputFilename == "" {
|
||||
outputFilename = config.Name
|
||||
if targetGOOS == "windows" {
|
||||
if buildEnv["GOOS"] == "windows" {
|
||||
outputFilename += ".exe"
|
||||
}
|
||||
}
|
||||
|
||||
cwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
die("Getwd() returned %v\n", err)
|
||||
}
|
||||
output := outputFilename
|
||||
if !filepath.IsAbs(output) {
|
||||
output = filepath.Join(cwd, output)
|
||||
output = filepath.Join(root, output)
|
||||
}
|
||||
|
||||
version := getVersion()
|
||||
@@ -585,13 +556,65 @@ func main() {
|
||||
ldflags := "-s -w " + constants.LDFlags()
|
||||
verbosePrintf("ldflags: %s\n", ldflags)
|
||||
|
||||
args := []string{
|
||||
"-tags", strings.Join(buildTags, " "),
|
||||
"-ldflags", ldflags,
|
||||
"-o", output, config.Main,
|
||||
var (
|
||||
buildArgs []string
|
||||
testArgs []string
|
||||
)
|
||||
|
||||
mainPackage := config.Main
|
||||
if strings.HasPrefix(mainPackage, config.Namespace) {
|
||||
mainPackage = strings.Replace(mainPackage, config.Namespace, "./", 1)
|
||||
}
|
||||
|
||||
err = build(filepath.Join(gopath, "src"), ver, targetGOOS, targetGOARCH, targetGOARM, gopath, args...)
|
||||
buildTarget := filepath.FromSlash(mainPackage)
|
||||
buildCWD := ""
|
||||
|
||||
if goVersion.AtLeast(GoVersion{1, 11, 0}) && fileExists("go.mod") {
|
||||
verbosePrintf("Go >= 1.11 and 'go.mod' found, building with modules\n")
|
||||
buildCWD = root
|
||||
|
||||
buildArgs = append(buildArgs, "-mod=vendor")
|
||||
testArgs = append(testArgs, "-mod=vendor")
|
||||
} else {
|
||||
if tempdir == "" {
|
||||
tempdir, err = ioutil.TempDir("", fmt.Sprintf("%v-build-", config.Name))
|
||||
if err != nil {
|
||||
die("TempDir(): %v\n", err)
|
||||
}
|
||||
}
|
||||
|
||||
verbosePrintf("Go < 1.11 or 'go.mod' not found, create GOPATH at %v\n", tempdir)
|
||||
targetdir := filepath.Join(tempdir, "src", filepath.FromSlash(config.Namespace))
|
||||
if err = copy(targetdir, root); err != nil {
|
||||
die("copying files from %v to %v/src failed: %v\n", root, tempdir, err)
|
||||
}
|
||||
|
||||
defer func() {
|
||||
if !keepGopath {
|
||||
verbosePrintf("remove %v\n", tempdir)
|
||||
if err = os.RemoveAll(tempdir); err != nil {
|
||||
die("remove GOPATH at %s failed: %v\n", tempdir, err)
|
||||
}
|
||||
} else {
|
||||
verbosePrintf("leaving temporary GOPATH at %v\n", tempdir)
|
||||
}
|
||||
}()
|
||||
|
||||
buildCWD = targetdir
|
||||
|
||||
goEnv["GOPATH"] = tempdir
|
||||
buildEnv["GOPATH"] = tempdir
|
||||
}
|
||||
|
||||
verbosePrintf("environment:\n go: %v\n build: %v\n", goEnv, buildEnv)
|
||||
|
||||
buildArgs = append(buildArgs,
|
||||
"-tags", strings.Join(buildTags, " "),
|
||||
"-ldflags", ldflags,
|
||||
"-o", output, buildTarget,
|
||||
)
|
||||
|
||||
err = build(buildCWD, buildEnv, buildArgs...)
|
||||
if err != nil {
|
||||
die("build failed: %v\n", err)
|
||||
}
|
||||
@@ -599,7 +622,9 @@ func main() {
|
||||
if runTests {
|
||||
verbosePrintf("running tests\n")
|
||||
|
||||
err = test(cwd, gopath, config.Tests...)
|
||||
testArgs = append(testArgs, config.Tests...)
|
||||
|
||||
err = test(buildCWD, goEnv, testArgs...)
|
||||
if err != nil {
|
||||
die("running tests failed: %v\n", err)
|
||||
}
|
||||
|
7
changelog/0.9.3_2018-10-13/issue-1766
Normal file
7
changelog/0.9.3_2018-10-13/issue-1766
Normal file
@@ -0,0 +1,7 @@
|
||||
Enhancement: restore: suppress lchown errors when not running as root
|
||||
|
||||
Like "cp" and "rsync" do, restic now only reports errors for changing
|
||||
the ownership of files during restore if it is run as root, on non-Windows
|
||||
operating systems. On Windows, the error is reported as usual.
|
||||
|
||||
https://github.com/restic/restic/issues/1766
|
14
changelog/0.9.3_2018-10-13/issue-1909
Normal file
14
changelog/0.9.3_2018-10-13/issue-1909
Normal file
@@ -0,0 +1,14 @@
|
||||
Enhancement: Reject files/dirs by name first
|
||||
|
||||
The current scanner/archiver code had an architectural limitation: it always
|
||||
ran the `lstat()` system call on all files and directories before a decision to
|
||||
include/exclude the file/dir was made. This lead to a lot of unnecessary system
|
||||
calls for items that could have been rejected by their name or path only.
|
||||
|
||||
We've changed the archiver/scanner implementation so that it now first rejects
|
||||
by name/path, and only runs the system call on the remaining items. This
|
||||
reduces the number of `lstat()` system calls a lot (depending on the exclude
|
||||
settings).
|
||||
|
||||
https://github.com/restic/restic/issues/1909
|
||||
https://github.com/restic/restic/pull/1912
|
8
changelog/0.9.3_2018-10-13/issue-1935
Normal file
8
changelog/0.9.3_2018-10-13/issue-1935
Normal file
@@ -0,0 +1,8 @@
|
||||
Bugfix: Remove truncated files from cache
|
||||
|
||||
When a file in the local cache is truncated, and restic tries to access data
|
||||
beyond the end of the (cached) file, it used to return an error "EOF". This is
|
||||
now fixed, such truncated files are removed and the data is fetched directly
|
||||
from the backend.
|
||||
|
||||
https://github.com/restic/restic/issues/1935
|
15
changelog/0.9.3_2018-10-13/issue-1941
Normal file
15
changelog/0.9.3_2018-10-13/issue-1941
Normal file
@@ -0,0 +1,15 @@
|
||||
Enhancement: Add directory filter to ls command
|
||||
|
||||
The ls command can now be filtered by directories, so that only files in the
|
||||
given directories will be shown. If the --recursive flag is specified, then
|
||||
ls will traverse subfolders and list their files as well.
|
||||
|
||||
It used to be possible to specify multiple snapshots, but that has been
|
||||
replaced by only one snapshot and the possibility of specifying multiple
|
||||
directories.
|
||||
|
||||
Specifying directories constrains the walk, which can significantly speed up
|
||||
the listing.
|
||||
|
||||
https://github.com/restic/restic/issues/1940
|
||||
https://github.com/restic/restic/pull/1941
|
7
changelog/0.9.3_2018-10-13/issue-1967
Normal file
7
changelog/0.9.3_2018-10-13/issue-1967
Normal file
@@ -0,0 +1,7 @@
|
||||
Enhancement: Use `--host` everywhere
|
||||
|
||||
We now use the flag `--host` for all commands which need a host name, using
|
||||
`--hostname` (e.g. for `restic backup`) still works, but will print a
|
||||
deprecation warning. Also, add the short option `-H` where possible.
|
||||
|
||||
https://github.com/restic/restic/issues/1967
|
12
changelog/0.9.3_2018-10-13/issue-1978
Normal file
12
changelog/0.9.3_2018-10-13/issue-1978
Normal file
@@ -0,0 +1,12 @@
|
||||
Bugfix: Do not return an error when the scanner is slower than backup
|
||||
|
||||
When restic makes a backup, there's a background task called "scanner" which
|
||||
collects information on how many files and directories are to be saved, in
|
||||
order to display progress information to the user. When the backup finishes
|
||||
faster than the scanner, it is aborted because the result is not needed any
|
||||
more. This logic contained a bug, where quitting the scanner process was
|
||||
treated as an error, and caused restic to print an unhelpful error message
|
||||
("context canceled").
|
||||
|
||||
https://github.com/restic/restic/issues/1978
|
||||
https://github.com/restic/restic/pull/1991
|
7
changelog/0.9.3_2018-10-13/issue-2028
Normal file
7
changelog/0.9.3_2018-10-13/issue-2028
Normal file
@@ -0,0 +1,7 @@
|
||||
Enhancement: Display size of cache directories
|
||||
|
||||
The `cache` command now by default shows the size of the individual cache
|
||||
directories. It can be disabled with `--no-size`.
|
||||
|
||||
https://github.com/restic/restic/issues/2028
|
||||
https://github.com/restic/restic/pull/2033
|
13
changelog/0.9.3_2018-10-13/pull-1780
Normal file
13
changelog/0.9.3_2018-10-13/pull-1780
Normal file
@@ -0,0 +1,13 @@
|
||||
Enhancement: Improve the `find` command
|
||||
|
||||
We've updated the `find` command to support multiple patterns.
|
||||
|
||||
`restic find` is now able to list the snapshots containing a specific tree
|
||||
or blob, or even the snapshots that contain blobs belonging to a given pack.
|
||||
A list of IDs can be given, as long as they all have the same type.
|
||||
|
||||
The command `find` can also display the pack IDs the blobs belong to, if
|
||||
the `--show-pack-id` flag is provided.
|
||||
|
||||
https://github.com/restic/restic/issues/1777
|
||||
https://github.com/restic/restic/pull/1780
|
7
changelog/0.9.3_2018-10-13/pull-1876
Normal file
7
changelog/0.9.3_2018-10-13/pull-1876
Normal file
@@ -0,0 +1,7 @@
|
||||
Enhancement: Display reason why forget keeps snapshots
|
||||
|
||||
We've added a column to the list of snapshots `forget` keeps which details the
|
||||
reasons to keep a particuliar snapshot. This makes debugging policies for
|
||||
forget much easier. Please remember to always try things out with `--dry-run`!
|
||||
|
||||
https://github.com/restic/restic/pull/1876
|
7
changelog/0.9.3_2018-10-13/pull-1891
Normal file
7
changelog/0.9.3_2018-10-13/pull-1891
Normal file
@@ -0,0 +1,7 @@
|
||||
Enhancement: Accept glob in paths loaded via --files-from
|
||||
|
||||
Before that, behaviour was different if paths were appended to command line or
|
||||
from a file, because wild card characters were expanded by shell if appended to
|
||||
command line, but not expanded if loaded from file.
|
||||
|
||||
https://github.com/restic/restic/issues/1891
|
8
changelog/0.9.3_2018-10-13/pull-1920
Normal file
8
changelog/0.9.3_2018-10-13/pull-1920
Normal file
@@ -0,0 +1,8 @@
|
||||
Enhancement: Vendor dependencies with Go 1.11 Modules
|
||||
|
||||
Until now, we've used `dep` for managing dependencies, we've now switch to
|
||||
using Go modules. For users this does not change much, only if you want to
|
||||
compile restic without downloading anything with Go 1.11, then you need to run:
|
||||
`go build -mod=vendor build.go`
|
||||
|
||||
https://github.com/restic/restic/pull/1920
|
15
changelog/0.9.3_2018-10-13/pull-1949
Normal file
15
changelog/0.9.3_2018-10-13/pull-1949
Normal file
@@ -0,0 +1,15 @@
|
||||
Enhancement: Add new command `self-update`
|
||||
|
||||
We have added a new command called `self-update` which downloads the
|
||||
latest released version of restic from GitHub and replaces the current
|
||||
binary with it. It does not rely on any external program (so it'll work
|
||||
everywhere), but still verifies the GPG signature using the embedded GPG
|
||||
public key.
|
||||
|
||||
By default, the `self-update` command is hidden behind the `selfupdate`
|
||||
built tag, which is only set when restic is built using `build.go` (including
|
||||
official releases). The reason for this is that downstream distributions will
|
||||
then not include the command by default, so users are encouraged to use the
|
||||
platform-specific distribution mechanism.
|
||||
|
||||
https://github.com/restic/restic/pull/1949
|
7
changelog/0.9.3_2018-10-13/pull-1953
Normal file
7
changelog/0.9.3_2018-10-13/pull-1953
Normal file
@@ -0,0 +1,7 @@
|
||||
Enhancement: ls: Add JSON output support for restic ls cmd
|
||||
|
||||
We've implemented listing files in the repository with JSON as output, just
|
||||
pass `--json` as an option to `restic ls`. This makes the output of the command
|
||||
machine readable.
|
||||
|
||||
https://github.com/restic/restic/pull/1953
|
13
changelog/0.9.3_2018-10-13/pull-1962
Normal file
13
changelog/0.9.3_2018-10-13/pull-1962
Normal file
@@ -0,0 +1,13 @@
|
||||
Enhancement: Stream JSON output for ls command
|
||||
|
||||
The `ls` command now supports JSON output with the global `--json`
|
||||
flag, and this change streams out JSON messages one object at a time
|
||||
rather than en entire array buffered in memory before encoding. The
|
||||
advantage is it allows large listings to be handled efficiently.
|
||||
|
||||
Two message types are printed: snapshots and nodes. A snapshot
|
||||
object will precede node objects which belong to that snapshot.
|
||||
The `struct_type` field can be used to determine which kind of
|
||||
message an object is.
|
||||
|
||||
https://github.com/restic/restic/pull/1962
|
11
changelog/0.9.4_2019-01-06/issue-1605
Normal file
11
changelog/0.9.4_2019-01-06/issue-1605
Normal file
@@ -0,0 +1,11 @@
|
||||
Enhancement: Concurrent restore
|
||||
|
||||
This change significantly improves restore performance, especially
|
||||
when using high-latency remote repositories like B2.
|
||||
|
||||
The implementation now uses several concurrent threads to download and process
|
||||
multiple remote files concurrently. To further reduce restore time, each remote
|
||||
file is downloaded using a single repository request.
|
||||
|
||||
https://github.com/restic/restic/issues/1605
|
||||
https://github.com/restic/restic/pull/1719
|
7
changelog/0.9.4_2019-01-06/issue-1989
Normal file
7
changelog/0.9.4_2019-01-06/issue-1989
Normal file
@@ -0,0 +1,7 @@
|
||||
Bugfix: Google Cloud Storage: Respect bandwidth limit
|
||||
|
||||
The GCS backend did not respect the bandwidth limit configured, a previous
|
||||
commit accidentally removed support for it.
|
||||
|
||||
https://github.com/restic/restic/issues/1989
|
||||
https://github.com/restic/restic/pull/2100
|
11
changelog/0.9.4_2019-01-06/issue-2040
Normal file
11
changelog/0.9.4_2019-01-06/issue-2040
Normal file
@@ -0,0 +1,11 @@
|
||||
Bugfix: Add host name filter shorthand flag for `stats` command
|
||||
|
||||
The default value for `--host` flag was set to 'H' (the shorthand version of
|
||||
the flag), this caused the lookup for the latest snapshot to fail.
|
||||
|
||||
Add shorthand flag `-H` for `--host` (with empty default so if these flags
|
||||
are not specified the latest snapshot will not filter by host name).
|
||||
|
||||
Also add shorthand `-H` for `backup` command.
|
||||
|
||||
https://github.com/restic/restic/issues/2040
|
9
changelog/0.9.4_2019-01-06/issue-2089
Normal file
9
changelog/0.9.4_2019-01-06/issue-2089
Normal file
@@ -0,0 +1,9 @@
|
||||
Enhancement: increase granularity of the "keep within" retention policy
|
||||
|
||||
The `keep-within` option of the `forget` command now accepts time ranges with
|
||||
an hourly granularity. For example, running `restic forget --keep-within 3d12h`
|
||||
will keep all the snapshots made within three days and twelve hours from the
|
||||
time of the latest snapshot.
|
||||
|
||||
https://github.com/restic/restic/issues/2089
|
||||
https://github.com/restic/restic/pull/2090
|
12
changelog/0.9.4_2019-01-06/issue-2097
Normal file
12
changelog/0.9.4_2019-01-06/issue-2097
Normal file
@@ -0,0 +1,12 @@
|
||||
Enhancement: Add key hinting
|
||||
|
||||
Added a new option `--key-hint` and corresponding environment variable
|
||||
`RESTIC_KEY_HINT`. The key hint is a key ID to try decrypting first, before
|
||||
other keys in the repository.
|
||||
|
||||
This change will benefit repositories with many keys; if the correct key hint
|
||||
is supplied then restic only needs to check one key. If the key hint is
|
||||
incorrect (the key does not exist, or the password is incorrect) then restic
|
||||
will check all keys, as usual.
|
||||
|
||||
https://github.com/restic/restic/issues/2097
|
11
changelog/0.9.4_2019-01-06/pull-2017
Normal file
11
changelog/0.9.4_2019-01-06/pull-2017
Normal file
@@ -0,0 +1,11 @@
|
||||
Enhancement: mount: Enforce FUSE Unix permissions with allow-other
|
||||
|
||||
The fuse mount (`restic mount`) now lets the kernel check the permissions of
|
||||
the files within snapshots (this is done through the `DefaultPermissions` FUSE
|
||||
option) when the option `--allow-other` is specified.
|
||||
|
||||
To restore the old behavior, we've added the `--no-default-permissions` option.
|
||||
This allows all users that have access to the mount point to access all
|
||||
files within the snapshots.
|
||||
|
||||
https://github.com/restic/restic/pull/2017
|
6
changelog/0.9.4_2019-01-06/pull-2068
Normal file
6
changelog/0.9.4_2019-01-06/pull-2068
Normal file
@@ -0,0 +1,6 @@
|
||||
Bugfix: Correctly return error loading data
|
||||
|
||||
In one case during `prune` and `check`, an error loading data from the backend is not returned properly. This is now corrected.
|
||||
|
||||
https://github.com/restic/restic/pull/2068
|
||||
https://github.com/restic/restic/issues/1999#issuecomment-433737921
|
7
changelog/0.9.4_2019-01-06/pull-2070
Normal file
7
changelog/0.9.4_2019-01-06/pull-2070
Normal file
@@ -0,0 +1,7 @@
|
||||
Enhancement: Make all commands display timestamps in local time
|
||||
|
||||
Restic used to drop the timezone information from displayed timestamps, it now
|
||||
converts timestamps to local time before printing them so the times can be
|
||||
easily compared to.
|
||||
|
||||
https://github.com/restic/restic/pull/2070
|
7
changelog/0.9.4_2019-01-06/pull-2086
Normal file
7
changelog/0.9.4_2019-01-06/pull-2086
Normal file
@@ -0,0 +1,7 @@
|
||||
Enhancement: Allow --files-from to be specified multiple times
|
||||
|
||||
Before, restic took only the last file specified with `--files-from` into
|
||||
account, this is now corrected.
|
||||
|
||||
https://github.com/restic/restic/issues/2085
|
||||
https://github.com/restic/restic/pull/2086
|
8
changelog/0.9.4_2019-01-06/pull-2094
Normal file
8
changelog/0.9.4_2019-01-06/pull-2094
Normal file
@@ -0,0 +1,8 @@
|
||||
Enhancement: Run command to get password
|
||||
|
||||
We've added the `--password-command` option which allows specifying a command
|
||||
that restic runs every time the password for the repository is needed, so it
|
||||
can be integrated with a password manager or keyring. The option can also be
|
||||
set via the environment variable `$RESTIC_PASSWORD_COMMAND`.
|
||||
|
||||
https://github.com/restic/restic/pull/2094
|
7
changelog/0.9.4_2019-01-06/pull-2095
Normal file
7
changelog/0.9.4_2019-01-06/pull-2095
Normal file
@@ -0,0 +1,7 @@
|
||||
Bugfix: consistently use local time for snapshots times
|
||||
|
||||
By default snapshots created with restic backup were set to local time,
|
||||
but when the --time flag was used the provided timestamp was parsed as
|
||||
UTC. With this change all snapshots times are set to local time.
|
||||
|
||||
https://github.com/restic/restic/pull/2095
|
@@ -4,8 +4,10 @@ import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -32,19 +34,23 @@ The "backup" command creates a new snapshot and saves the files and directories
|
||||
given as the arguments.
|
||||
`,
|
||||
PreRun: func(cmd *cobra.Command, args []string) {
|
||||
if backupOptions.Hostname == "" {
|
||||
if backupOptions.Host == "" {
|
||||
hostname, err := os.Hostname()
|
||||
if err != nil {
|
||||
debug.Log("os.Hostname() returned err: %v", err)
|
||||
return
|
||||
}
|
||||
backupOptions.Hostname = hostname
|
||||
backupOptions.Host = hostname
|
||||
}
|
||||
},
|
||||
DisableAutoGenTag: true,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
if backupOptions.Stdin && backupOptions.FilesFrom == "-" {
|
||||
return errors.Fatal("cannot use both `--stdin` and `--files-from -`")
|
||||
if backupOptions.Stdin {
|
||||
for _, filename := range backupOptions.FilesFrom {
|
||||
if filename == "-" {
|
||||
return errors.Fatal("cannot use both `--stdin` and `--files-from -`")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var t tomb.Tomb
|
||||
@@ -72,8 +78,8 @@ type BackupOptions struct {
|
||||
Stdin bool
|
||||
StdinFilename string
|
||||
Tags []string
|
||||
Hostname string
|
||||
FilesFrom string
|
||||
Host string
|
||||
FilesFrom []string
|
||||
TimeStamp string
|
||||
WithAtime bool
|
||||
}
|
||||
@@ -94,8 +100,12 @@ func init() {
|
||||
f.BoolVar(&backupOptions.Stdin, "stdin", false, "read backup from stdin")
|
||||
f.StringVar(&backupOptions.StdinFilename, "stdin-filename", "stdin", "file name to use when reading from stdin")
|
||||
f.StringArrayVar(&backupOptions.Tags, "tag", nil, "add a `tag` for the new snapshot (can be specified multiple times)")
|
||||
f.StringVar(&backupOptions.Hostname, "hostname", "", "set the `hostname` for the snapshot manually. To prevent an expensive rescan use the \"parent\" flag")
|
||||
f.StringVar(&backupOptions.FilesFrom, "files-from", "", "read the files to backup from file (can be combined with file args)")
|
||||
|
||||
f.StringVarP(&backupOptions.Host, "host", "H", "", "set the `hostname` for the snapshot manually. To prevent an expensive rescan use the \"parent\" flag")
|
||||
f.StringVar(&backupOptions.Host, "hostname", "", "set the `hostname` for the snapshot manually")
|
||||
f.MarkDeprecated("hostname", "use --host")
|
||||
|
||||
f.StringArrayVar(&backupOptions.FilesFrom, "files-from", nil, "read the files to backup from file (can be combined with file args/can be specified multiple times)")
|
||||
f.StringVar(&backupOptions.TimeStamp, "time", "", "time of the backup (ex. '2012-11-01 22:08:41') (default: now)")
|
||||
f.BoolVar(&backupOptions.WithAtime, "with-atime", false, "store the atime for all files and directories")
|
||||
}
|
||||
@@ -169,12 +179,16 @@ func readLinesFromFile(filename string) ([]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")
|
||||
if gopts.password == "" {
|
||||
for _, filename := range opts.FilesFrom {
|
||||
if filename == "-" {
|
||||
return errors.Fatal("unable to read password from stdin when data is to be read from stdin, use --password-file or $RESTIC_PASSWORD")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if opts.Stdin {
|
||||
if opts.FilesFrom != "" {
|
||||
if len(opts.FilesFrom) > 0 {
|
||||
return errors.Fatal("--stdin and --files-from cannot be used together")
|
||||
}
|
||||
|
||||
@@ -186,18 +200,9 @@ func (opts BackupOptions) Check(gopts GlobalOptions, args []string) error {
|
||||
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 && !opts.Stdin {
|
||||
f, err := rejectByDevice(targets)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fs = append(fs, f)
|
||||
}
|
||||
|
||||
// collectRejectByNameFuncs returns a list of all functions which may reject data
|
||||
// from being saved in a snapshot based on path only
|
||||
func collectRejectByNameFuncs(opts BackupOptions, repo *repository.Repository, targets []string) (fs []RejectByNameFunc, err error) {
|
||||
// exclude restic cache
|
||||
if repo.Cache != nil {
|
||||
f, err := rejectResticCache(repo)
|
||||
@@ -237,6 +242,21 @@ func collectRejectFuncs(opts BackupOptions, repo *repository.Repository, targets
|
||||
return fs, nil
|
||||
}
|
||||
|
||||
// collectRejectFuncs returns a list of all functions which may reject data
|
||||
// from being saved in a snapshot based on path and file info
|
||||
func collectRejectFuncs(opts BackupOptions, repo *repository.Repository, targets []string) (fs []RejectFunc, err error) {
|
||||
// allowed devices
|
||||
if opts.ExcludeOtherFS && !opts.Stdin {
|
||||
f, err := rejectByDevice(targets)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fs = append(fs, f)
|
||||
}
|
||||
|
||||
return fs, nil
|
||||
}
|
||||
|
||||
// readExcludePatternsFromFiles reads all exclude files and returns the list of
|
||||
// exclude patterns. For each line, leading and trailing white space is removed
|
||||
// and comment lines are ignored. For each remaining pattern, environment
|
||||
@@ -290,15 +310,31 @@ func collectTargets(opts BackupOptions, args []string) (targets []string, err er
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
fromfile, err := readLinesFromFile(opts.FilesFrom)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
var lines []string
|
||||
for _, file := range opts.FilesFrom {
|
||||
fromfile, err := readLinesFromFile(file)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// expand wildcards
|
||||
for _, line := range fromfile {
|
||||
var expanded []string
|
||||
expanded, err := filepath.Glob(line)
|
||||
if err != nil {
|
||||
return nil, errors.WithMessage(err, fmt.Sprintf("pattern: %s", line))
|
||||
}
|
||||
if len(expanded) == 0 {
|
||||
Warnf("pattern %q does not match any files, skipping\n", line)
|
||||
}
|
||||
lines = append(lines, expanded...)
|
||||
}
|
||||
}
|
||||
|
||||
// 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...)
|
||||
args = append(args, lines...)
|
||||
if len(args) == 0 && !opts.Stdin {
|
||||
return nil, errors.Fatal("nothing to backup, please specify target files/dirs")
|
||||
}
|
||||
@@ -327,7 +363,7 @@ func findParentSnapshot(ctx context.Context, repo restic.Repository, opts Backup
|
||||
|
||||
// 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)
|
||||
id, err := restic.FindLatestSnapshot(ctx, repo, targets, []restic.TagList{}, opts.Host)
|
||||
if err == nil {
|
||||
parentID = &id
|
||||
} else if err != restic.ErrNoSnapshotFound {
|
||||
@@ -351,7 +387,7 @@ func runBackup(opts BackupOptions, gopts GlobalOptions, term *termstatus.Termina
|
||||
|
||||
timeStamp := time.Now()
|
||||
if opts.TimeStamp != "" {
|
||||
timeStamp, err = time.Parse(TimeFormat, opts.TimeStamp)
|
||||
timeStamp, err = time.ParseInLocation(TimeFormat, opts.TimeStamp, time.Local)
|
||||
if err != nil {
|
||||
return errors.Fatalf("error in time option: %v\n", err)
|
||||
}
|
||||
@@ -359,6 +395,12 @@ func runBackup(opts BackupOptions, gopts GlobalOptions, term *termstatus.Termina
|
||||
|
||||
var t tomb.Tomb
|
||||
|
||||
term.Print("open repository\n")
|
||||
repo, err := OpenRepository(gopts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
p := ui.NewBackup(term, gopts.verbosity)
|
||||
|
||||
// use the terminal for stdout/stderr
|
||||
@@ -380,12 +422,6 @@ func runBackup(opts BackupOptions, gopts GlobalOptions, term *termstatus.Termina
|
||||
|
||||
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)
|
||||
@@ -393,7 +429,13 @@ func runBackup(opts BackupOptions, gopts GlobalOptions, term *termstatus.Termina
|
||||
return err
|
||||
}
|
||||
|
||||
// rejectFuncs collect functions that can reject items from the backup
|
||||
// rejectByNameFuncs collect functions that can reject items from the backup based on path only
|
||||
rejectByNameFuncs, err := collectRejectByNameFuncs(opts, repo, targets)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// rejectFuncs collect functions that can reject items from the backup based on path and file info
|
||||
rejectFuncs, err := collectRejectFuncs(opts, repo, targets)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -414,6 +456,15 @@ func runBackup(opts BackupOptions, gopts GlobalOptions, term *termstatus.Termina
|
||||
p.V("using parent snapshot %v\n", parentSnapshotID.Str())
|
||||
}
|
||||
|
||||
selectByNameFilter := func(item string) bool {
|
||||
for _, reject := range rejectByNameFuncs {
|
||||
if reject(item) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
selectFilter := func(item string, fi os.FileInfo) bool {
|
||||
for _, reject := range rejectFuncs {
|
||||
if reject(item, fi) {
|
||||
@@ -436,6 +487,7 @@ func runBackup(opts BackupOptions, gopts GlobalOptions, term *termstatus.Termina
|
||||
}
|
||||
|
||||
sc := archiver.NewScanner(targetFS)
|
||||
sc.SelectByName = selectByNameFilter
|
||||
sc.Select = selectFilter
|
||||
sc.Error = p.ScannerError
|
||||
sc.Result = p.ReportTotal
|
||||
@@ -444,6 +496,7 @@ func runBackup(opts BackupOptions, gopts GlobalOptions, term *termstatus.Termina
|
||||
t.Go(func() error { return sc.Scan(t.Context(gopts.ctx), targets) })
|
||||
|
||||
arch := archiver.New(repo, targetFS, archiver.Options{})
|
||||
arch.SelectByName = selectByNameFilter
|
||||
arch.Select = selectFilter
|
||||
arch.WithAtime = opts.WithAtime
|
||||
arch.Error = p.Error
|
||||
@@ -459,7 +512,7 @@ func runBackup(opts BackupOptions, gopts GlobalOptions, term *termstatus.Termina
|
||||
Excludes: opts.Excludes,
|
||||
Tags: opts.Tags,
|
||||
Time: timeStamp,
|
||||
Hostname: opts.Hostname,
|
||||
Hostname: opts.Host,
|
||||
ParentSnapshot: *parentSnapshotID,
|
||||
}
|
||||
|
||||
|
@@ -2,6 +2,7 @@ package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"time"
|
||||
@@ -9,6 +10,7 @@ import (
|
||||
"github.com/restic/restic/internal/cache"
|
||||
"github.com/restic/restic/internal/errors"
|
||||
"github.com/restic/restic/internal/fs"
|
||||
"github.com/restic/restic/internal/ui/table"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
@@ -28,6 +30,7 @@ The "cache" command allows listing and cleaning local cache directories.
|
||||
type CacheOptions struct {
|
||||
Cleanup bool
|
||||
MaxAge uint
|
||||
NoSize bool
|
||||
}
|
||||
|
||||
var cacheOptions CacheOptions
|
||||
@@ -38,6 +41,7 @@ func init() {
|
||||
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")
|
||||
f.BoolVar(&cacheOptions.NoSize, "no-size", false, "do not output the size of the cache directories")
|
||||
}
|
||||
|
||||
func runCache(opts CacheOptions, gopts GlobalOptions, args []string) error {
|
||||
@@ -85,9 +89,22 @@ func runCache(opts CacheOptions, gopts GlobalOptions, args []string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
tab := NewTable()
|
||||
tab.Header = fmt.Sprintf("%-14s %-16s %s", "Repository ID", "Last Used", "Old")
|
||||
tab.RowFormat = "%-14s %-16s %s"
|
||||
tab := table.New()
|
||||
|
||||
type data struct {
|
||||
ID string
|
||||
Last string
|
||||
Old string
|
||||
Size string
|
||||
}
|
||||
|
||||
tab.AddColumn("Repo ID", "{{ .ID }}")
|
||||
tab.AddColumn("Last Used", "{{ .Last }}")
|
||||
tab.AddColumn("Old", "{{ .Old }}")
|
||||
|
||||
if !opts.NoSize {
|
||||
tab.AddColumn("Size", "{{ .Size }}")
|
||||
}
|
||||
|
||||
dirs, err := cache.All(cachedir)
|
||||
if err != nil {
|
||||
@@ -109,14 +126,41 @@ func runCache(opts CacheOptions, gopts GlobalOptions, args []string) error {
|
||||
old = "yes"
|
||||
}
|
||||
|
||||
tab.Rows = append(tab.Rows, []interface{}{
|
||||
var size string
|
||||
if !opts.NoSize {
|
||||
bytes, err := dirSize(filepath.Join(cachedir, entry.Name()))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
size = fmt.Sprintf("%11s", formatBytes(uint64(bytes)))
|
||||
}
|
||||
|
||||
tab.AddRow(data{
|
||||
entry.Name()[:10],
|
||||
fmt.Sprintf("%d days ago", uint(time.Since(entry.ModTime()).Hours()/24)),
|
||||
old,
|
||||
size,
|
||||
})
|
||||
}
|
||||
|
||||
tab.Write(gopts.stdout)
|
||||
Printf("%d cache dirs in %s\n", len(dirs), cachedir)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func dirSize(path string) (int64, error) {
|
||||
var size int64
|
||||
err := filepath.Walk(path, func(_ string, info os.FileInfo, err error) error {
|
||||
if err != nil || info == nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !info.IsDir() {
|
||||
size += info.Size()
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
return size, err
|
||||
}
|
||||
|
@@ -21,11 +21,11 @@ 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:
|
||||
|
||||
+ The item was added
|
||||
- The item was removed
|
||||
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
|
||||
* + The item was added
|
||||
* - The item was removed
|
||||
* 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
|
||||
`,
|
||||
DisableAutoGenTag: true,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
|
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/restic/restic/internal/debug"
|
||||
@@ -47,12 +48,12 @@ func init() {
|
||||
flags.StringArrayVar(&dumpOptions.Paths, "path", nil, "only consider snapshots which include this (absolute) `path` for snapshot ID \"latest\"")
|
||||
}
|
||||
|
||||
func splitPath(path string) []string {
|
||||
d, f := filepath.Split(path)
|
||||
func splitPath(p string) []string {
|
||||
d, f := path.Split(p)
|
||||
if d == "" || d == "/" {
|
||||
return []string{f}
|
||||
}
|
||||
s := splitPath(filepath.Clean(d))
|
||||
s := splitPath(path.Clean(d))
|
||||
return append(s, f)
|
||||
}
|
||||
|
||||
|
@@ -16,27 +16,38 @@ import (
|
||||
)
|
||||
|
||||
var cmdFind = &cobra.Command{
|
||||
Use: "find [flags] PATTERN",
|
||||
Short: "Find a file or directory",
|
||||
Use: "find [flags] PATTERN...",
|
||||
Short: "Find a file, a directory or restic IDs",
|
||||
Long: `
|
||||
The "find" command searches for files or directories in snapshots stored in the
|
||||
repo. `,
|
||||
repo.
|
||||
It can also be used to search for restic blobs or trees for troubleshooting.`,
|
||||
Example: `restic find config.json
|
||||
restic find --json "*.yml" "*.json"
|
||||
restic find --json --blob 420f620f b46ebe8a ddd38656
|
||||
restic find --show-pack-id --blob 420f620f
|
||||
restic find --tree 577c2bc9 f81f2e22 a62827a9
|
||||
restic find --pack 025c1d06`,
|
||||
DisableAutoGenTag: true,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return runFind(findOptions, globalOptions, args)
|
||||
},
|
||||
}
|
||||
|
||||
const shortStr = 8 // Length of short IDs: 4 bytes as hex strings
|
||||
|
||||
// FindOptions bundles all options for the find command.
|
||||
type FindOptions struct {
|
||||
Oldest string
|
||||
Newest string
|
||||
Snapshots []string
|
||||
CaseInsensitive bool
|
||||
ListLong bool
|
||||
Host string
|
||||
Paths []string
|
||||
Tags restic.TagLists
|
||||
Oldest string
|
||||
Newest string
|
||||
Snapshots []string
|
||||
BlobID, TreeID bool
|
||||
PackID, ShowPackID bool
|
||||
CaseInsensitive bool
|
||||
ListLong bool
|
||||
Host string
|
||||
Paths []string
|
||||
Tags restic.TagLists
|
||||
}
|
||||
|
||||
var findOptions FindOptions
|
||||
@@ -48,6 +59,10 @@ func init() {
|
||||
f.StringVarP(&findOptions.Oldest, "oldest", "O", "", "oldest modification date/time")
|
||||
f.StringVarP(&findOptions.Newest, "newest", "N", "", "newest modification date/time")
|
||||
f.StringArrayVarP(&findOptions.Snapshots, "snapshot", "s", nil, "snapshot `id` to search in (can be given multiple times)")
|
||||
f.BoolVar(&findOptions.BlobID, "blob", false, "pattern is a blob-ID")
|
||||
f.BoolVar(&findOptions.TreeID, "tree", false, "pattern is a tree-ID")
|
||||
f.BoolVar(&findOptions.PackID, "pack", false, "pattern is a pack-ID")
|
||||
f.BoolVar(&findOptions.ShowPackID, "show-pack-id", false, "display the pack-ID the blobs belong to (with --blob)")
|
||||
f.BoolVarP(&findOptions.CaseInsensitive, "ignore-case", "i", false, "ignore case for pattern")
|
||||
f.BoolVarP(&findOptions.ListLong, "long", "l", false, "use a long listing format showing size and mode")
|
||||
|
||||
@@ -58,7 +73,7 @@ func init() {
|
||||
|
||||
type findPattern struct {
|
||||
oldest, newest time.Time
|
||||
pattern string
|
||||
pattern []string
|
||||
ignoreCase bool
|
||||
}
|
||||
|
||||
@@ -95,7 +110,7 @@ type statefulOutput struct {
|
||||
hits int
|
||||
}
|
||||
|
||||
func (s *statefulOutput) PrintJSON(path string, node *restic.Node) {
|
||||
func (s *statefulOutput) PrintPatternJSON(path string, node *restic.Node) {
|
||||
type findNode restic.Node
|
||||
b, err := json.Marshal(struct {
|
||||
// Add these attributes
|
||||
@@ -139,7 +154,7 @@ func (s *statefulOutput) PrintJSON(path string, node *restic.Node) {
|
||||
s.hits++
|
||||
}
|
||||
|
||||
func (s *statefulOutput) PrintNormal(path string, node *restic.Node) {
|
||||
func (s *statefulOutput) PrintPatternNormal(path string, node *restic.Node) {
|
||||
if s.newsn != s.oldsn {
|
||||
if s.oldsn != nil {
|
||||
Verbosef("\n")
|
||||
@@ -150,11 +165,62 @@ func (s *statefulOutput) PrintNormal(path string, node *restic.Node) {
|
||||
Printf(formatNode(path, node, s.ListLong) + "\n")
|
||||
}
|
||||
|
||||
func (s *statefulOutput) Print(path string, node *restic.Node) {
|
||||
func (s *statefulOutput) PrintPattern(path string, node *restic.Node) {
|
||||
if s.JSON {
|
||||
s.PrintJSON(path, node)
|
||||
s.PrintPatternJSON(path, node)
|
||||
} else {
|
||||
s.PrintNormal(path, node)
|
||||
s.PrintPatternNormal(path, node)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *statefulOutput) PrintObjectJSON(kind, id, nodepath, treeID string, sn *restic.Snapshot) {
|
||||
b, err := json.Marshal(struct {
|
||||
// Add these attributes
|
||||
ObjectType string `json:"object_type"`
|
||||
ID string `json:"id"`
|
||||
Path string `json:"path"`
|
||||
ParentTree string `json:"parent_tree,omitempty"`
|
||||
SnapshotID string `json:"snapshot"`
|
||||
Time time.Time `json:"time,omitempty"`
|
||||
}{
|
||||
ObjectType: kind,
|
||||
ID: id,
|
||||
Path: nodepath,
|
||||
SnapshotID: sn.ID().String(),
|
||||
ParentTree: treeID,
|
||||
Time: sn.Time,
|
||||
})
|
||||
if err != nil {
|
||||
Warnf("Marshall failed: %v\n", err)
|
||||
return
|
||||
}
|
||||
if !s.inuse {
|
||||
Printf("[")
|
||||
s.inuse = true
|
||||
}
|
||||
if s.hits > 0 {
|
||||
Printf(",")
|
||||
}
|
||||
Printf(string(b))
|
||||
s.hits++
|
||||
}
|
||||
|
||||
func (s *statefulOutput) PrintObjectNormal(kind, id, nodepath, treeID string, sn *restic.Snapshot) {
|
||||
Printf("Found %s %s\n", kind, id)
|
||||
if kind == "blob" {
|
||||
Printf(" ... in file %s\n", nodepath)
|
||||
Printf(" (tree %s)\n", treeID)
|
||||
} else {
|
||||
Printf(" ... path %s\n", nodepath)
|
||||
}
|
||||
Printf(" ... in snapshot %s (%s)\n", sn.ID().Str(), sn.Time.Local().Format(TimeFormat))
|
||||
}
|
||||
|
||||
func (s *statefulOutput) PrintObject(kind, id, nodepath, treeID string, sn *restic.Snapshot) {
|
||||
if s.JSON {
|
||||
s.PrintObjectJSON(kind, id, nodepath, treeID, sn)
|
||||
} else {
|
||||
s.PrintObjectNormal(kind, id, nodepath, treeID, sn)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -179,6 +245,9 @@ type Finder struct {
|
||||
pat findPattern
|
||||
out statefulOutput
|
||||
ignoreTrees restic.IDSet
|
||||
blobIDs map[string]struct{}
|
||||
treeIDs map[string]struct{}
|
||||
itemsFound int
|
||||
}
|
||||
|
||||
func (f *Finder) findInSnapshot(ctx context.Context, sn *restic.Snapshot) error {
|
||||
@@ -189,7 +258,7 @@ func (f *Finder) findInSnapshot(ctx context.Context, sn *restic.Snapshot) error
|
||||
}
|
||||
|
||||
f.out.newsn = sn
|
||||
return walker.Walk(ctx, f.repo, *sn.Tree, f.ignoreTrees, func(nodepath string, node *restic.Node, err error) (bool, error) {
|
||||
return walker.Walk(ctx, f.repo, *sn.Tree, f.ignoreTrees, func(_ restic.ID, nodepath string, node *restic.Node, err error) (bool, error) {
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
@@ -203,9 +272,17 @@ func (f *Finder) findInSnapshot(ctx context.Context, sn *restic.Snapshot) error
|
||||
normalizedNodepath = strings.ToLower(nodepath)
|
||||
}
|
||||
|
||||
foundMatch, err := filter.Match(f.pat.pattern, normalizedNodepath)
|
||||
if err != nil {
|
||||
return false, err
|
||||
var foundMatch bool
|
||||
|
||||
for _, pat := range f.pat.pattern {
|
||||
found, err := filter.Match(pat, normalizedNodepath)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if found {
|
||||
foundMatch = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
@@ -213,9 +290,16 @@ func (f *Finder) findInSnapshot(ctx context.Context, sn *restic.Snapshot) error
|
||||
errIfNoMatch error
|
||||
)
|
||||
if node.Type == "dir" {
|
||||
childMayMatch, err := filter.ChildMatch(f.pat.pattern, normalizedNodepath)
|
||||
if err != nil {
|
||||
return false, err
|
||||
var childMayMatch bool
|
||||
for _, pat := range f.pat.pattern {
|
||||
mayMatch, err := filter.ChildMatch(pat, normalizedNodepath)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if mayMatch {
|
||||
childMayMatch = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !childMayMatch {
|
||||
@@ -241,20 +325,158 @@ func (f *Finder) findInSnapshot(ctx context.Context, sn *restic.Snapshot) error
|
||||
}
|
||||
|
||||
debug.Log(" found match\n")
|
||||
f.out.Print(nodepath, node)
|
||||
f.out.PrintPattern(nodepath, node)
|
||||
return false, nil
|
||||
})
|
||||
}
|
||||
|
||||
func (f *Finder) findIDs(ctx context.Context, sn *restic.Snapshot) error {
|
||||
debug.Log("searching IDs in snapshot %s", sn.ID())
|
||||
|
||||
if sn.Tree == nil {
|
||||
return errors.Errorf("snapshot %v has no tree", sn.ID().Str())
|
||||
}
|
||||
|
||||
f.out.newsn = sn
|
||||
return walker.Walk(ctx, f.repo, *sn.Tree, f.ignoreTrees, func(parentTreeID restic.ID, nodepath string, node *restic.Node, err error) (bool, error) {
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if node == nil {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
if node.Type == "dir" && f.treeIDs != nil {
|
||||
treeID := node.Subtree
|
||||
found := false
|
||||
if _, ok := f.treeIDs[treeID.Str()]; ok {
|
||||
found = true
|
||||
} else if _, ok := f.treeIDs[treeID.String()]; ok {
|
||||
found = true
|
||||
}
|
||||
if found {
|
||||
f.out.PrintObject("tree", treeID.String(), nodepath, "", sn)
|
||||
f.itemsFound++
|
||||
// Terminate if we have found all trees (and we are not
|
||||
// looking for blobs)
|
||||
if f.itemsFound >= len(f.treeIDs) && f.blobIDs == nil {
|
||||
// Return an error to terminate the Walk
|
||||
return true, errors.New("OK")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if node.Type == "file" && f.blobIDs != nil {
|
||||
for _, id := range node.Content {
|
||||
idStr := id.String()
|
||||
if _, ok := f.blobIDs[idStr]; !ok {
|
||||
// Look for short ID form
|
||||
if _, ok := f.blobIDs[idStr[:shortStr]]; !ok {
|
||||
continue
|
||||
}
|
||||
// Replace the short ID with the long one
|
||||
f.blobIDs[idStr] = struct{}{}
|
||||
delete(f.blobIDs, idStr[:shortStr])
|
||||
}
|
||||
f.out.PrintObject("blob", idStr, nodepath, parentTreeID.String(), sn)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return false, nil
|
||||
})
|
||||
}
|
||||
|
||||
// packsToBlobs converts the list of pack IDs to a list of blob IDs that
|
||||
// belong to those packs.
|
||||
func (f *Finder) packsToBlobs(ctx context.Context, packs []string) error {
|
||||
packIDs := make(map[string]struct{})
|
||||
for _, p := range packs {
|
||||
packIDs[p] = struct{}{}
|
||||
}
|
||||
if f.blobIDs == nil {
|
||||
f.blobIDs = make(map[string]struct{})
|
||||
}
|
||||
|
||||
allPacksFound := false
|
||||
packsFound := 0
|
||||
|
||||
debug.Log("Looking for packs...")
|
||||
err := f.repo.List(ctx, restic.DataFile, func(id restic.ID, size int64) error {
|
||||
if allPacksFound {
|
||||
return nil
|
||||
}
|
||||
idStr := id.String()
|
||||
if _, ok := packIDs[idStr]; !ok {
|
||||
// Look for short ID form
|
||||
if _, ok := packIDs[idStr[:shortStr]]; !ok {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
debug.Log("Found pack %s", idStr)
|
||||
blobs, _, err := f.repo.ListPack(ctx, id, size)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, b := range blobs {
|
||||
f.blobIDs[b.ID.String()] = struct{}{}
|
||||
}
|
||||
// Stop searching when all packs have been found
|
||||
packsFound++
|
||||
if packsFound >= len(packIDs) {
|
||||
allPacksFound = true
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !allPacksFound {
|
||||
return errors.Fatal("unable to find all specified pack(s)")
|
||||
}
|
||||
|
||||
debug.Log("%d blobs found", len(f.blobIDs))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *Finder) findBlobsPacks(ctx context.Context) {
|
||||
idx := f.repo.Index()
|
||||
for i := range f.blobIDs {
|
||||
rid, err := restic.ParseID(i)
|
||||
if err != nil {
|
||||
Printf("Note: cannot find pack for blob '%s', unable to parse ID: %v\n", i, err)
|
||||
continue
|
||||
}
|
||||
|
||||
blobs, found := idx.Lookup(rid, restic.DataBlob)
|
||||
if !found {
|
||||
Printf("Blob %s not found in the index\n", rid.Str())
|
||||
continue
|
||||
}
|
||||
|
||||
for _, b := range blobs {
|
||||
if b.ID.Equal(rid) {
|
||||
Printf("Blob belongs to pack %s\n ... Pack %s: %s\n", b.PackID, b.PackID.Str(), b.String())
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func runFind(opts FindOptions, gopts GlobalOptions, args []string) error {
|
||||
if len(args) != 1 {
|
||||
if len(args) == 0 {
|
||||
return errors.Fatal("wrong number of arguments")
|
||||
}
|
||||
|
||||
var err error
|
||||
pat := findPattern{pattern: args[0]}
|
||||
pat := findPattern{pattern: args}
|
||||
if opts.CaseInsensitive {
|
||||
pat.pattern = strings.ToLower(pat.pattern)
|
||||
for i := range pat.pattern {
|
||||
pat.pattern[i] = strings.ToLower(pat.pattern[i])
|
||||
}
|
||||
pat.ignoreCase = true
|
||||
}
|
||||
|
||||
@@ -270,6 +492,14 @@ func runFind(opts FindOptions, gopts GlobalOptions, args []string) error {
|
||||
}
|
||||
}
|
||||
|
||||
// Check at most only one kind of IDs is provided: currently we
|
||||
// can't mix types
|
||||
if (opts.BlobID && opts.TreeID) ||
|
||||
(opts.BlobID && opts.PackID) ||
|
||||
(opts.TreeID && opts.PackID) {
|
||||
return errors.Fatal("cannot have several ID types")
|
||||
}
|
||||
|
||||
repo, err := OpenRepository(gopts)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -296,12 +526,40 @@ func runFind(opts FindOptions, gopts GlobalOptions, args []string) error {
|
||||
out: statefulOutput{ListLong: opts.ListLong, JSON: globalOptions.JSON},
|
||||
ignoreTrees: restic.NewIDSet(),
|
||||
}
|
||||
|
||||
if opts.BlobID {
|
||||
f.blobIDs = make(map[string]struct{})
|
||||
for _, pat := range f.pat.pattern {
|
||||
f.blobIDs[pat] = struct{}{}
|
||||
}
|
||||
}
|
||||
if opts.TreeID {
|
||||
f.treeIDs = make(map[string]struct{})
|
||||
for _, pat := range f.pat.pattern {
|
||||
f.treeIDs[pat] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
if opts.PackID {
|
||||
f.packsToBlobs(ctx, []string{f.pat.pattern[0]}) // TODO: support multiple packs
|
||||
}
|
||||
|
||||
for sn := range FindFilteredSnapshots(ctx, repo, opts.Host, opts.Tags, opts.Paths, opts.Snapshots) {
|
||||
if f.blobIDs != nil || f.treeIDs != nil {
|
||||
if err = f.findIDs(ctx, sn); err != nil && err.Error() != "OK" {
|
||||
return err
|
||||
}
|
||||
continue
|
||||
}
|
||||
if err = f.findInSnapshot(ctx, sn); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
f.out.Finish()
|
||||
|
||||
if opts.ShowPackID && f.blobIDs != nil {
|
||||
f.findBlobsPacks(ctx)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
@@ -59,14 +59,15 @@ 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.VarP(&forgetOptions.Within, "keep-within", "", "keep snapshots that are newer than `duration` (eg. 1y5m7d2h) relative to the latest snapshot")
|
||||
|
||||
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.
|
||||
f.StringVar(&forgetOptions.Host, "host", "", "only consider snapshots with the given `host`")
|
||||
// Deprecated since 2017-03-07.
|
||||
f.StringVar(&forgetOptions.Host, "hostname", "", "only consider snapshots with the given `hostname` (deprecated)")
|
||||
f.StringVar(&forgetOptions.Host, "hostname", "", "only consider snapshots with the given `hostname`")
|
||||
f.MarkDeprecated("hostname", "use --host")
|
||||
|
||||
f.Var(&forgetOptions.Tags, "tag", "only consider snapshots which include this `taglist` in the format `tag[,tag,...]` (can be specified multiple times)")
|
||||
|
||||
f.StringArrayVar(&forgetOptions.Paths, "path", nil, "only consider snapshots which include this (absolute) `path` (can be specified multiple times)")
|
||||
f.BoolVarP(&forgetOptions.Compact, "compact", "c", false, "use compact format")
|
||||
|
||||
@@ -206,17 +207,17 @@ func runForget(opts ForgetOptions, gopts GlobalOptions, args []string) error {
|
||||
}
|
||||
Verbosef(":\n\n")
|
||||
|
||||
keep, remove := restic.ApplyPolicy(snapshotGroup, policy)
|
||||
keep, remove, reasons := restic.ApplyPolicy(snapshotGroup, policy)
|
||||
|
||||
if len(keep) != 0 && !gopts.Quiet {
|
||||
Printf("keep %d snapshots:\n", len(keep))
|
||||
PrintSnapshots(globalOptions.stdout, keep, opts.Compact)
|
||||
PrintSnapshots(globalOptions.stdout, keep, reasons, opts.Compact)
|
||||
Printf("\n")
|
||||
}
|
||||
|
||||
if len(remove) != 0 && !gopts.Quiet {
|
||||
Printf("remove %d snapshots:\n", len(remove))
|
||||
PrintSnapshots(globalOptions.stdout, remove, opts.Compact)
|
||||
PrintSnapshots(globalOptions.stdout, remove, nil, opts.Compact)
|
||||
Printf("\n")
|
||||
}
|
||||
|
||||
|
@@ -3,7 +3,6 @@ package main
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strings"
|
||||
@@ -11,6 +10,7 @@ import (
|
||||
"github.com/restic/restic/internal/errors"
|
||||
"github.com/restic/restic/internal/repository"
|
||||
"github.com/restic/restic/internal/restic"
|
||||
"github.com/restic/restic/internal/ui/table"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
@@ -36,53 +36,18 @@ func init() {
|
||||
flags.StringVarP(&newPasswordFile, "new-password-file", "", "", "the file from which to load a new password")
|
||||
}
|
||||
|
||||
type keyInfo struct {
|
||||
Current bool `json:"current"`
|
||||
ID string `json:"id"`
|
||||
UserName string `json:"userName"`
|
||||
HostName string `json:"hostName"`
|
||||
Created string `json:"created"`
|
||||
}
|
||||
|
||||
func (ki keyInfo) CurrentStr() string {
|
||||
if ki.Current {
|
||||
return "*"
|
||||
}
|
||||
return " "
|
||||
}
|
||||
|
||||
func listKeys(ctx context.Context, s *repository.Repository, gopts GlobalOptions) error {
|
||||
var (
|
||||
appendKey func(keyInfo)
|
||||
printKeys func() error
|
||||
)
|
||||
|
||||
switch gopts.JSON {
|
||||
case true:
|
||||
var keys []keyInfo
|
||||
|
||||
appendKey = func(key keyInfo) {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
|
||||
printKeys = func() error {
|
||||
return json.NewEncoder(gopts.stdout).Encode(keys)
|
||||
}
|
||||
default:
|
||||
tab := NewTable()
|
||||
tab.Header = fmt.Sprintf(" %-10s %-10s %-10s %s", "ID", "User", "Host", "Created")
|
||||
tab.RowFormat = "%s%-10s %-10s %-10s %s"
|
||||
|
||||
appendKey = func(key keyInfo) {
|
||||
tab.Rows = append(tab.Rows, []interface{}{key.CurrentStr(), key.ID, key.UserName, key.HostName, key.Created})
|
||||
}
|
||||
|
||||
printKeys = func() error {
|
||||
return tab.Write(globalOptions.stdout)
|
||||
}
|
||||
type keyInfo struct {
|
||||
Current bool `json:"current"`
|
||||
ID string `json:"id"`
|
||||
UserName string `json:"userName"`
|
||||
HostName string `json:"hostName"`
|
||||
Created string `json:"created"`
|
||||
}
|
||||
|
||||
if err := s.List(ctx, restic.KeyFile, func(id restic.ID, size int64) error {
|
||||
var keys []keyInfo
|
||||
|
||||
err := s.List(ctx, restic.KeyFile, func(id restic.ID, size int64) error {
|
||||
k, err := repository.LoadKey(ctx, s, id.String())
|
||||
if err != nil {
|
||||
Warnf("LoadKey() failed: %v\n", err)
|
||||
@@ -94,17 +59,32 @@ func listKeys(ctx context.Context, s *repository.Repository, gopts GlobalOptions
|
||||
ID: id.Str(),
|
||||
UserName: k.Username,
|
||||
HostName: k.Hostname,
|
||||
Created: k.Created.Format(TimeFormat),
|
||||
Created: k.Created.Local().Format(TimeFormat),
|
||||
}
|
||||
|
||||
appendKey(key)
|
||||
|
||||
keys = append(keys, key)
|
||||
return nil
|
||||
}); err != nil {
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return printKeys()
|
||||
if gopts.JSON {
|
||||
return json.NewEncoder(globalOptions.stdout).Encode(keys)
|
||||
}
|
||||
|
||||
tab := table.New()
|
||||
tab.AddColumn(" ID", "{{if .Current}}*{{else}} {{end}}{{ .ID }}")
|
||||
tab.AddColumn("User", "{{ .UserName }}")
|
||||
tab.AddColumn("Host", "{{ .HostName }}")
|
||||
tab.AddColumn("Created", "{{ .Created }}")
|
||||
|
||||
for _, key := range keys {
|
||||
tab.AddRow(key)
|
||||
}
|
||||
|
||||
return tab.Write(globalOptions.stdout)
|
||||
}
|
||||
|
||||
// testKeyNewPassword is used to set a new password during integration testing.
|
||||
|
@@ -2,21 +2,37 @@ package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/restic/restic/internal/errors"
|
||||
"github.com/restic/restic/internal/fs"
|
||||
"github.com/restic/restic/internal/restic"
|
||||
"github.com/restic/restic/internal/walker"
|
||||
)
|
||||
|
||||
var cmdLs = &cobra.Command{
|
||||
Use: "ls [flags] [snapshot-ID ...]",
|
||||
Use: "ls [flags] [snapshotID] [dir...]",
|
||||
Short: "List files in a snapshot",
|
||||
Long: `
|
||||
The "ls" command allows listing files and directories in a snapshot.
|
||||
The "ls" command lists files and directories in a snapshot.
|
||||
|
||||
The special snapshot-ID "latest" can be used to list files and directories of the latest snapshot in the repository.
|
||||
The special snapshot ID "latest" can be used to list files and
|
||||
directories of the latest snapshot in the repository. The
|
||||
--host flag can be used in conjunction to select the latest
|
||||
snapshot originating from a certain host only.
|
||||
|
||||
File listings can optionally be filtered by directories. Any
|
||||
positional arguments after the snapshot ID are interpreted as
|
||||
absolute directory paths, and only files inside those directories
|
||||
will be listed. If the --recursive flag is used, then the filter
|
||||
will allow traversing into matching directories' subfolders.
|
||||
Any directory paths specified must be absolute (starting with
|
||||
a path separator); paths use the forward slash '/' as separator.
|
||||
`,
|
||||
DisableAutoGenTag: true,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
@@ -26,10 +42,11 @@ The special snapshot-ID "latest" can be used to list files and directories of th
|
||||
|
||||
// LsOptions collects all options for the ls command.
|
||||
type LsOptions struct {
|
||||
ListLong bool
|
||||
Host string
|
||||
Tags restic.TagLists
|
||||
Paths []string
|
||||
ListLong bool
|
||||
Host string
|
||||
Tags restic.TagLists
|
||||
Paths []string
|
||||
Recursive bool
|
||||
}
|
||||
|
||||
var lsOptions LsOptions
|
||||
@@ -39,10 +56,31 @@ func init() {
|
||||
|
||||
flags := cmdLs.Flags()
|
||||
flags.BoolVarP(&lsOptions.ListLong, "long", "l", false, "use a long listing format showing size and mode")
|
||||
|
||||
flags.StringVarP(&lsOptions.Host, "host", "H", "", "only consider snapshots for this `host`, when no snapshot ID is given")
|
||||
flags.Var(&lsOptions.Tags, "tag", "only consider snapshots which include this `taglist`, when no snapshot ID is given")
|
||||
flags.StringArrayVar(&lsOptions.Paths, "path", nil, "only consider snapshots which include this (absolute) `path`, when no snapshot ID is given")
|
||||
flags.BoolVar(&lsOptions.Recursive, "recursive", false, "include files in subfolders of the listed directories")
|
||||
}
|
||||
|
||||
type lsSnapshot struct {
|
||||
*restic.Snapshot
|
||||
ID *restic.ID `json:"id"`
|
||||
ShortID string `json:"short_id"`
|
||||
StructType string `json:"struct_type"` // "snapshot"
|
||||
}
|
||||
|
||||
type lsNode struct {
|
||||
Name string `json:"name"`
|
||||
Type string `json:"type"`
|
||||
Path string `json:"path"`
|
||||
UID uint32 `json:"uid"`
|
||||
GID uint32 `json:"gid"`
|
||||
Size uint64 `json:"size,omitempty"`
|
||||
Mode os.FileMode `json:"mode,omitempty"`
|
||||
ModTime time.Time `json:"mtime,omitempty"`
|
||||
AccessTime time.Time `json:"atime,omitempty"`
|
||||
ChangeTime time.Time `json:"ctime,omitempty"`
|
||||
StructType string `json:"struct_type"` // "node"
|
||||
}
|
||||
|
||||
func runLs(opts LsOptions, gopts GlobalOptions, args []string) error {
|
||||
@@ -50,6 +88,51 @@ func runLs(opts LsOptions, gopts GlobalOptions, args []string) error {
|
||||
return errors.Fatal("Invalid arguments, either give one or more snapshot IDs or set filters.")
|
||||
}
|
||||
|
||||
// extract any specific directories to walk
|
||||
var dirs []string
|
||||
if len(args) > 1 {
|
||||
dirs = args[1:]
|
||||
for _, dir := range dirs {
|
||||
if !strings.HasPrefix(dir, "/") {
|
||||
return errors.Fatal("All path filters must be absolute, starting with a forward slash '/'")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
withinDir := func(nodepath string) bool {
|
||||
if len(dirs) == 0 {
|
||||
return true
|
||||
}
|
||||
|
||||
for _, dir := range dirs {
|
||||
// we're within one of the selected dirs, example:
|
||||
// nodepath: "/test/foo"
|
||||
// dir: "/test"
|
||||
if fs.HasPathPrefix(dir, nodepath) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
approachingMatchingTree := func(nodepath string) bool {
|
||||
if len(dirs) == 0 {
|
||||
return true
|
||||
}
|
||||
|
||||
for _, dir := range dirs {
|
||||
// the current node path is a prefix for one of the
|
||||
// directories, so we're interested in something deeper in the
|
||||
// tree. Example:
|
||||
// nodepath: "/test"
|
||||
// dir: "/test/foo"
|
||||
if fs.HasPathPrefix(nodepath, dir) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
repo, err := OpenRepository(gopts)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -61,23 +144,88 @@ func runLs(opts LsOptions, gopts GlobalOptions, args []string) error {
|
||||
|
||||
ctx, cancel := context.WithCancel(gopts.ctx)
|
||||
defer cancel()
|
||||
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)
|
||||
|
||||
err := walker.Walk(ctx, repo, *sn.Tree, nil, func(nodepath string, node *restic.Node, err error) (bool, error) {
|
||||
var (
|
||||
printSnapshot func(sn *restic.Snapshot)
|
||||
printNode func(path string, node *restic.Node)
|
||||
)
|
||||
|
||||
if gopts.JSON {
|
||||
enc := json.NewEncoder(gopts.stdout)
|
||||
|
||||
printSnapshot = func(sn *restic.Snapshot) {
|
||||
enc.Encode(lsSnapshot{
|
||||
Snapshot: sn,
|
||||
ID: sn.ID(),
|
||||
ShortID: sn.ID().Str(),
|
||||
StructType: "snapshot",
|
||||
})
|
||||
}
|
||||
|
||||
printNode = func(path string, node *restic.Node) {
|
||||
enc.Encode(lsNode{
|
||||
Name: node.Name,
|
||||
Type: node.Type,
|
||||
Path: path,
|
||||
UID: node.UID,
|
||||
GID: node.GID,
|
||||
Size: node.Size,
|
||||
Mode: node.Mode,
|
||||
ModTime: node.ModTime,
|
||||
AccessTime: node.AccessTime,
|
||||
ChangeTime: node.ChangeTime,
|
||||
StructType: "node",
|
||||
})
|
||||
}
|
||||
} else {
|
||||
printSnapshot = func(sn *restic.Snapshot) {
|
||||
Verbosef("snapshot %s of %v filtered by %v at %s):\n", sn.ID().Str(), sn.Paths, dirs, sn.Time)
|
||||
}
|
||||
printNode = func(path string, node *restic.Node) {
|
||||
Printf("%s\n", formatNode(path, node, lsOptions.ListLong))
|
||||
}
|
||||
}
|
||||
|
||||
for sn := range FindFilteredSnapshots(ctx, repo, opts.Host, opts.Tags, opts.Paths, args[:1]) {
|
||||
printSnapshot(sn)
|
||||
|
||||
err := walker.Walk(ctx, repo, *sn.Tree, nil, func(_ restic.ID, nodepath string, node *restic.Node, err error) (bool, error) {
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if node == nil {
|
||||
return false, nil
|
||||
}
|
||||
Printf("%s\n", formatNode(nodepath, node, lsOptions.ListLong))
|
||||
|
||||
if withinDir(nodepath) {
|
||||
// if we're within a dir, print the node
|
||||
printNode(nodepath, node)
|
||||
|
||||
// if recursive listing is requested, signal the walker that it
|
||||
// should continue walking recursively
|
||||
if opts.Recursive {
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
|
||||
// if there's an upcoming match deeper in the tree (but we're not
|
||||
// there yet), signal the walker to descend into any subdirs
|
||||
if approachingMatchingTree(nodepath) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// otherwise, signal the walker to not walk recursively into any
|
||||
// subdirs
|
||||
if node.Type == "dir" {
|
||||
return false, walker.SkipNode
|
||||
}
|
||||
return false, nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
@@ -53,13 +53,14 @@ For details please see the documentation for time.Format() at:
|
||||
|
||||
// MountOptions collects all options for the mount command.
|
||||
type MountOptions struct {
|
||||
OwnerRoot bool
|
||||
AllowRoot bool
|
||||
AllowOther bool
|
||||
Host string
|
||||
Tags restic.TagLists
|
||||
Paths []string
|
||||
SnapshotTemplate string
|
||||
OwnerRoot bool
|
||||
AllowRoot bool
|
||||
AllowOther bool
|
||||
NoDefaultPermissions bool
|
||||
Host string
|
||||
Tags restic.TagLists
|
||||
Paths []string
|
||||
SnapshotTemplate string
|
||||
}
|
||||
|
||||
var mountOptions MountOptions
|
||||
@@ -71,6 +72,7 @@ func init() {
|
||||
mountFlags.BoolVar(&mountOptions.OwnerRoot, "owner-root", false, "use 'root' as the owner of files and dirs")
|
||||
mountFlags.BoolVar(&mountOptions.AllowRoot, "allow-root", false, "allow root user to access the data in the mounted directory")
|
||||
mountFlags.BoolVar(&mountOptions.AllowOther, "allow-other", false, "allow other users to access the data in the mounted directory")
|
||||
mountFlags.BoolVar(&mountOptions.NoDefaultPermissions, "no-default-permissions", false, "for 'allow-other', ignore Unix permissions and allow users to read all snapshot files")
|
||||
|
||||
mountFlags.StringVarP(&mountOptions.Host, "host", "H", "", `only consider snapshots for this host`)
|
||||
mountFlags.Var(&mountOptions.Tags, "tag", "only consider snapshots which include this `taglist`")
|
||||
@@ -118,6 +120,11 @@ func mount(opts MountOptions, gopts GlobalOptions, mountpoint string) error {
|
||||
|
||||
if opts.AllowOther {
|
||||
mountOptions = append(mountOptions, systemFuse.AllowOther())
|
||||
|
||||
// let the kernel check permissions unless it is explicitly disabled
|
||||
if !opts.NoDefaultPermissions {
|
||||
mountOptions = append(mountOptions, systemFuse.DefaultPermissions())
|
||||
}
|
||||
}
|
||||
|
||||
c, err := systemFuse.Mount(mountpoint, mountOptions...)
|
||||
|
@@ -149,8 +149,8 @@ func pruneRepository(gopts GlobalOptions, repo restic.Repository) error {
|
||||
len(idx.Packs), blobs, formatBytes(uint64(stats.bytes)))
|
||||
|
||||
blobCount := make(map[restic.BlobHandle]int)
|
||||
duplicateBlobs := 0
|
||||
duplicateBytes := 0
|
||||
var duplicateBlobs uint64
|
||||
var duplicateBytes uint64
|
||||
|
||||
// find duplicate blobs
|
||||
for _, p := range idx.Packs {
|
||||
@@ -161,7 +161,7 @@ func pruneRepository(gopts GlobalOptions, repo restic.Repository) error {
|
||||
|
||||
if blobCount[h] > 1 {
|
||||
duplicateBlobs++
|
||||
duplicateBytes += int(entry.Length)
|
||||
duplicateBytes += uint64(entry.Length)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -252,7 +252,7 @@ func pruneRepository(gopts GlobalOptions, repo restic.Repository) error {
|
||||
continue
|
||||
}
|
||||
|
||||
removeBytes += int(blob.Length)
|
||||
removeBytes += uint64(blob.Length)
|
||||
}
|
||||
|
||||
if hasActiveBlob {
|
||||
|
148
cmd/restic/cmd_recover.go
Normal file
148
cmd/restic/cmd_recover.go
Normal file
@@ -0,0 +1,148 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/restic/restic/internal/errors"
|
||||
"github.com/restic/restic/internal/restic"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var cmdRecover = &cobra.Command{
|
||||
Use: "recover [flags]",
|
||||
Short: "Recover data from the repository",
|
||||
Long: `
|
||||
The "recover" command build a new snapshot from all directories it can find in
|
||||
the raw data of the repository. It can be used if, for example, a snapshot has
|
||||
been removed by accident with "forget".
|
||||
`,
|
||||
DisableAutoGenTag: true,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return runRecover(globalOptions)
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
cmdRoot.AddCommand(cmdRecover)
|
||||
}
|
||||
|
||||
func runRecover(gopts GlobalOptions) error {
|
||||
hostname, err := os.Hostname()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
repo, err := OpenRepository(gopts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
lock, err := lockRepo(repo)
|
||||
defer unlockRepo(lock)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
Verbosef("load index files\n")
|
||||
if err = repo.LoadIndex(gopts.ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// trees maps a tree ID to whether or not it is referenced by a different
|
||||
// tree. If it is not referenced, we have a root tree.
|
||||
trees := make(map[restic.ID]bool)
|
||||
|
||||
for blob := range repo.Index().Each(gopts.ctx) {
|
||||
if blob.Blob.Type != restic.TreeBlob {
|
||||
continue
|
||||
}
|
||||
trees[blob.Blob.ID] = false
|
||||
}
|
||||
|
||||
cur := 0
|
||||
max := len(trees)
|
||||
Verbosef("load %d trees\n\n", len(trees))
|
||||
|
||||
for id := range trees {
|
||||
cur++
|
||||
Verbosef("\rtree (%v/%v)", cur, max)
|
||||
|
||||
if !trees[id] {
|
||||
trees[id] = false
|
||||
}
|
||||
|
||||
tree, err := repo.LoadTree(gopts.ctx, id)
|
||||
if err != nil {
|
||||
Warnf("unable to load tree %v: %v\n", id.Str(), err)
|
||||
continue
|
||||
}
|
||||
|
||||
for _, node := range tree.Nodes {
|
||||
if node.Type != "dir" || node.Subtree == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
subtree := *node.Subtree
|
||||
trees[subtree] = true
|
||||
}
|
||||
}
|
||||
Verbosef("\ndone\n")
|
||||
|
||||
roots := restic.NewIDSet()
|
||||
for id, seen := range trees {
|
||||
if seen {
|
||||
continue
|
||||
}
|
||||
|
||||
roots.Insert(id)
|
||||
}
|
||||
|
||||
Verbosef("found %d roots\n", len(roots))
|
||||
|
||||
tree := restic.NewTree()
|
||||
for id := range roots {
|
||||
var subtreeID = id
|
||||
node := restic.Node{
|
||||
Type: "dir",
|
||||
Name: id.Str(),
|
||||
Mode: 0755,
|
||||
Subtree: &subtreeID,
|
||||
AccessTime: time.Now(),
|
||||
ModTime: time.Now(),
|
||||
ChangeTime: time.Now(),
|
||||
}
|
||||
tree.Insert(&node)
|
||||
}
|
||||
|
||||
treeID, err := repo.SaveTree(gopts.ctx, tree)
|
||||
if err != nil {
|
||||
return errors.Fatalf("unable to save new tree to the repo: %v", err)
|
||||
}
|
||||
|
||||
err = repo.Flush(gopts.ctx)
|
||||
if err != nil {
|
||||
return errors.Fatalf("unable to save blobs to the repo: %v", err)
|
||||
}
|
||||
|
||||
err = repo.SaveIndex(gopts.ctx)
|
||||
if err != nil {
|
||||
return errors.Fatalf("unable to save new index to the repo: %v", err)
|
||||
}
|
||||
|
||||
sn, err := restic.NewSnapshot([]string{"/recover"}, []string{}, hostname, time.Now())
|
||||
if err != nil {
|
||||
return errors.Fatalf("unable to save snapshot: %v", err)
|
||||
}
|
||||
|
||||
sn.Tree = &treeID
|
||||
|
||||
id, err := repo.SaveJSONUnpacked(gopts.ctx, restic.SnapshotFile, sn)
|
||||
if err != nil {
|
||||
return errors.Fatalf("unable to save snapshot: %v", err)
|
||||
}
|
||||
|
||||
Printf("saved new snapshot %v\n", id.Str())
|
||||
|
||||
return nil
|
||||
}
|
@@ -113,8 +113,8 @@ func runRestore(opts RestoreOptions, gopts GlobalOptions, args []string) error {
|
||||
}
|
||||
|
||||
totalErrors := 0
|
||||
res.Error = func(dir string, node *restic.Node, err error) error {
|
||||
Warnf("ignoring error for %s: %s\n", dir, err)
|
||||
res.Error = func(location string, err error) error {
|
||||
Warnf("ignoring error for %s: %s\n", location, err)
|
||||
totalErrors++
|
||||
return nil
|
||||
}
|
||||
|
73
cmd/restic/cmd_self_update.go
Normal file
73
cmd/restic/cmd_self_update.go
Normal file
@@ -0,0 +1,73 @@
|
||||
// xbuild selfupdate
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/restic/restic/internal/errors"
|
||||
"github.com/restic/restic/internal/selfupdate"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var cmdSelfUpdate = &cobra.Command{
|
||||
Use: "self-update [flags]",
|
||||
Short: "Update the restic binary",
|
||||
Long: `
|
||||
The command "self-update" downloads the latest stable release of restic from
|
||||
GitHub and replaces the currently running binary. After download, the
|
||||
authenticity of the binary is verified using the GPG signature on the release
|
||||
files.
|
||||
`,
|
||||
DisableAutoGenTag: true,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return runSelfUpdate(selfUpdateOptions, globalOptions, args)
|
||||
},
|
||||
}
|
||||
|
||||
// SelfUpdateOptions collects all options for the update-restic command.
|
||||
type SelfUpdateOptions struct {
|
||||
Output string
|
||||
}
|
||||
|
||||
var selfUpdateOptions SelfUpdateOptions
|
||||
|
||||
func init() {
|
||||
cmdRoot.AddCommand(cmdSelfUpdate)
|
||||
|
||||
flags := cmdSelfUpdate.Flags()
|
||||
flags.StringVar(&selfUpdateOptions.Output, "output", "", "Save the downloaded file as `filename` (default: running binary itself)")
|
||||
}
|
||||
|
||||
func runSelfUpdate(opts SelfUpdateOptions, gopts GlobalOptions, args []string) error {
|
||||
if opts.Output == "" {
|
||||
file, err := os.Executable()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "unable to find executable")
|
||||
}
|
||||
|
||||
opts.Output = file
|
||||
}
|
||||
|
||||
fi, err := os.Lstat(opts.Output)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !fi.Mode().IsRegular() {
|
||||
return errors.Errorf("output file %v is not a normal file, use --output to specify a different file", opts.Output)
|
||||
}
|
||||
|
||||
Printf("writing restic to %v\n", opts.Output)
|
||||
|
||||
v, err := selfupdate.DownloadLatestStableRelease(gopts.ctx, opts.Output, version, Verbosef)
|
||||
if err != nil {
|
||||
return errors.Fatalf("unable to update restic: %v", err)
|
||||
}
|
||||
|
||||
if v != version {
|
||||
Printf("successfully updated restic to version %v\n", v)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
@@ -9,6 +9,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/restic/restic/internal/restic"
|
||||
"github.com/restic/restic/internal/ui/table"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
@@ -81,7 +82,7 @@ func runSnapshots(opts SnapshotOptions, gopts GlobalOptions, args []string) erro
|
||||
}
|
||||
return nil
|
||||
}
|
||||
PrintSnapshots(gopts.stdout, list, opts.Compact)
|
||||
PrintSnapshots(gopts.stdout, list, nil, opts.Compact)
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -123,7 +124,16 @@ func FilterLastSnapshots(list restic.Snapshots) restic.Snapshots {
|
||||
}
|
||||
|
||||
// PrintSnapshots prints a text table of the snapshots in list to stdout.
|
||||
func PrintSnapshots(stdout io.Writer, list restic.Snapshots, compact bool) {
|
||||
func PrintSnapshots(stdout io.Writer, list restic.Snapshots, reasons []restic.KeepReason, compact bool) {
|
||||
// keep the reasons a snasphot is being kept in a map, so that it doesn't
|
||||
// get lost when the list of snapshots is sorted
|
||||
keepReasons := make(map[restic.ID]restic.KeepReason, len(reasons))
|
||||
if len(reasons) > 0 {
|
||||
for i, sn := range list {
|
||||
id := sn.ID()
|
||||
keepReasons[*id] = reasons[i]
|
||||
}
|
||||
}
|
||||
|
||||
// always sort the snapshots so that the newer ones are listed last
|
||||
sort.SliceStable(list, func(i, j int) bool {
|
||||
@@ -143,71 +153,72 @@ func PrintSnapshots(stdout io.Writer, list restic.Snapshots, compact bool) {
|
||||
}
|
||||
}
|
||||
|
||||
tab := NewTable()
|
||||
if !compact {
|
||||
tab.Header = fmt.Sprintf("%-8s %-19s %-*s %-*s %-3s %s", "ID", "Date", -maxHost, "Host", -maxTag, "Tags", "", "Directory")
|
||||
tab.RowFormat = fmt.Sprintf("%%-8s %%-19s %%%ds %%%ds %%-3s %%s", -maxHost, -maxTag)
|
||||
tab := table.New()
|
||||
|
||||
if compact {
|
||||
tab.AddColumn("ID", "{{ .ID }}")
|
||||
tab.AddColumn("Time", "{{ .Timestamp }}")
|
||||
tab.AddColumn("Host", "{{ .Hostname }}")
|
||||
tab.AddColumn("Tags ", `{{ join .Tags "\n" }}`)
|
||||
} else {
|
||||
tab.Header = fmt.Sprintf("%-8s %-19s %-*s %-*s", "ID", "Date", -maxHost, "Host", -maxTag, "Tags")
|
||||
tab.RowFormat = fmt.Sprintf("%%-8s %%-19s %%%ds %%s", -maxHost)
|
||||
tab.AddColumn("ID", "{{ .ID }}")
|
||||
tab.AddColumn("Time", "{{ .Timestamp }}")
|
||||
tab.AddColumn("Host ", "{{ .Hostname }}")
|
||||
tab.AddColumn("Tags ", `{{ join .Tags "," }}`)
|
||||
if len(reasons) > 0 {
|
||||
tab.AddColumn("Reasons", `{{ join .Reasons "\n" }}`)
|
||||
}
|
||||
tab.AddColumn("Paths", `{{ join .Paths "\n" }}`)
|
||||
}
|
||||
|
||||
type snapshot struct {
|
||||
ID string
|
||||
Timestamp string
|
||||
Hostname string
|
||||
Tags []string
|
||||
Reasons []string
|
||||
Paths []string
|
||||
}
|
||||
|
||||
var multiline bool
|
||||
for _, sn := range list {
|
||||
if len(sn.Paths) == 0 {
|
||||
continue
|
||||
data := snapshot{
|
||||
ID: sn.ID().Str(),
|
||||
Timestamp: sn.Time.Local().Format(TimeFormat),
|
||||
Hostname: sn.Hostname,
|
||||
Tags: sn.Tags,
|
||||
Paths: sn.Paths,
|
||||
}
|
||||
|
||||
firstTag := ""
|
||||
if len(sn.Tags) > 0 {
|
||||
firstTag = sn.Tags[0]
|
||||
if len(reasons) > 0 {
|
||||
id := sn.ID()
|
||||
data.Reasons = keepReasons[*id].Matches
|
||||
}
|
||||
|
||||
rows := len(sn.Paths)
|
||||
if rows < len(sn.Tags) {
|
||||
rows = len(sn.Tags)
|
||||
if len(sn.Paths) > 1 && !compact {
|
||||
multiline = true
|
||||
}
|
||||
|
||||
treeElement := " "
|
||||
if rows != 1 {
|
||||
treeElement = "┌──"
|
||||
}
|
||||
|
||||
if !compact {
|
||||
tab.Rows = append(tab.Rows, []interface{}{sn.ID().Str(), sn.Time.Format(TimeFormat), sn.Hostname, firstTag, treeElement, sn.Paths[0]})
|
||||
} else {
|
||||
allTags := ""
|
||||
for _, tag := range sn.Tags {
|
||||
allTags += tag + " "
|
||||
}
|
||||
tab.Rows = append(tab.Rows, []interface{}{sn.ID().Str(), sn.Time.Format(TimeFormat), sn.Hostname, allTags})
|
||||
continue
|
||||
}
|
||||
|
||||
if len(sn.Tags) > rows {
|
||||
rows = len(sn.Tags)
|
||||
}
|
||||
|
||||
for i := 1; i < rows; i++ {
|
||||
path := ""
|
||||
if len(sn.Paths) > i {
|
||||
path = sn.Paths[i]
|
||||
}
|
||||
|
||||
tag := ""
|
||||
if len(sn.Tags) > i {
|
||||
tag = sn.Tags[i]
|
||||
}
|
||||
|
||||
treeElement := "│"
|
||||
if i == (rows - 1) {
|
||||
treeElement = "└──"
|
||||
}
|
||||
|
||||
tab.Rows = append(tab.Rows, []interface{}{"", "", "", tag, treeElement, path})
|
||||
}
|
||||
tab.AddRow(data)
|
||||
}
|
||||
|
||||
tab.Footer = fmt.Sprintf("%d snapshots", len(list))
|
||||
tab.AddFooter(fmt.Sprintf("%d snapshots", len(list)))
|
||||
|
||||
if multiline {
|
||||
// print an additional blank line between snapshots
|
||||
|
||||
var last int
|
||||
tab.PrintData = func(w io.Writer, idx int, s string) error {
|
||||
var err error
|
||||
if idx == last {
|
||||
_, err = fmt.Fprintf(w, "%s\n", s)
|
||||
} else {
|
||||
_, err = fmt.Fprintf(w, "\n%s\n", s)
|
||||
}
|
||||
last = idx
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
tab.Write(stdout)
|
||||
}
|
||||
|
@@ -8,6 +8,7 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/restic/restic/internal/errors"
|
||||
"github.com/restic/restic/internal/restic"
|
||||
"github.com/restic/restic/internal/walker"
|
||||
"github.com/spf13/cobra"
|
||||
@@ -29,17 +30,13 @@ to calculate.
|
||||
|
||||
The modes are:
|
||||
|
||||
restore-size: (default) Counts the size of the restored files.
|
||||
|
||||
files-by-contents: Counts total size of files, where a file is
|
||||
considered unique if it has unique contents.
|
||||
|
||||
raw-data: Counts the size of blobs in the repository, regardless
|
||||
of how many files reference them.
|
||||
|
||||
blobs-per-file: A combination of files-by-contents and raw-data.
|
||||
|
||||
Refer to the online manual for more details about each mode.
|
||||
* restore-size: (default) Counts the size of the restored files.
|
||||
* files-by-contents: Counts total size of files, where a file is
|
||||
considered unique if it has unique contents.
|
||||
* raw-data: Counts the size of blobs in the repository, regardless of
|
||||
how many files reference them.
|
||||
* blobs-per-file: A combination of files-by-contents and raw-data.
|
||||
* Refer to the online manual for more details about each mode.
|
||||
`,
|
||||
DisableAutoGenTag: true,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
@@ -51,7 +48,7 @@ func init() {
|
||||
cmdRoot.AddCommand(cmdStats)
|
||||
f := cmdStats.Flags()
|
||||
f.StringVar(&countMode, "mode", countModeRestoreSize, "counting mode: restore-size (default), files-by-contents, blobs-per-file, or raw-data")
|
||||
f.StringVar(&snapshotByHost, "host", "", "filter latest snapshot by this hostname")
|
||||
f.StringVarP(&snapshotByHost, "host", "H", "", "filter latest snapshot by this hostname")
|
||||
}
|
||||
|
||||
func runStats(gopts GlobalOptions, args []string) error {
|
||||
@@ -80,6 +77,10 @@ func runStats(gopts GlobalOptions, args []string) error {
|
||||
}
|
||||
}
|
||||
|
||||
if !gopts.JSON {
|
||||
Printf("scanning...\n")
|
||||
}
|
||||
|
||||
// create a container for the stats (and other needed state)
|
||||
stats := &statsContainer{
|
||||
uniqueFiles: make(map[fileID]struct{}),
|
||||
@@ -95,18 +96,18 @@ func runStats(gopts GlobalOptions, args []string) error {
|
||||
if snapshotIDString == "latest" {
|
||||
sID, err = restic.FindLatestSnapshot(ctx, repo, []string{}, []restic.TagList{}, snapshotByHost)
|
||||
if err != nil {
|
||||
Exitf(1, "latest snapshot for criteria not found: %v", err)
|
||||
return errors.Fatalf("latest snapshot for criteria not found: %v", err)
|
||||
}
|
||||
} else {
|
||||
sID, err = restic.FindSnapshot(repo, snapshotIDString)
|
||||
if err != nil {
|
||||
return err
|
||||
return errors.Fatalf("error loading snapshot: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
snapshot, err := restic.LoadSnapshot(ctx, repo, sID)
|
||||
if err != nil {
|
||||
return err
|
||||
return errors.Fatalf("error loading snapshot from repo: %v", err)
|
||||
}
|
||||
|
||||
err = statsWalkSnapshot(ctx, snapshot, repo, stats)
|
||||
@@ -144,6 +145,15 @@ func runStats(gopts GlobalOptions, args []string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// inform the user what was scanned and how it was scanned
|
||||
snapshotsScanned := snapshotIDString
|
||||
if snapshotsScanned == "latest" {
|
||||
snapshotsScanned = "the latest snapshot"
|
||||
} else if snapshotsScanned == "" {
|
||||
snapshotsScanned = "all snapshots"
|
||||
}
|
||||
Printf("Stats for %s in %s mode:\n", snapshotsScanned, countMode)
|
||||
|
||||
if stats.TotalBlobCount > 0 {
|
||||
Printf(" Total Blob Count: %d\n", stats.TotalBlobCount)
|
||||
}
|
||||
@@ -174,7 +184,7 @@ func statsWalkSnapshot(ctx context.Context, snapshot *restic.Snapshot, repo rest
|
||||
}
|
||||
|
||||
func statsWalkTree(repo restic.Repository, stats *statsContainer) walker.WalkFunc {
|
||||
return func(npath string, node *restic.Node, nodeErr error) (bool, error) {
|
||||
return func(_ restic.ID, npath string, node *restic.Node, nodeErr error) (bool, error) {
|
||||
if nodeErr != nil {
|
||||
return true, nodeErr
|
||||
}
|
||||
@@ -286,7 +296,7 @@ type statsContainer struct {
|
||||
// blobs that have been seen as a part of the file
|
||||
fileBlobs map[string]restic.IDSet
|
||||
|
||||
// blobs and blobsSeen are used to count indiviudal
|
||||
// blobs and blobsSeen are used to count individual
|
||||
// unique blobs, independent of references to files
|
||||
blobs, blobsSeen restic.BlobSet
|
||||
}
|
||||
|
@@ -60,15 +60,20 @@ func (rc *rejectionCache) Store(dir string, rejected bool) {
|
||||
rc.m[dir] = rejected
|
||||
}
|
||||
|
||||
// RejectByNameFunc is a function that takes a filename of a
|
||||
// file that would be included in the backup. The function returns true if it
|
||||
// should be excluded (rejected) from the backup.
|
||||
type RejectByNameFunc func(path string) bool
|
||||
|
||||
// RejectFunc is a function that takes a filename and os.FileInfo of a
|
||||
// file that would be included in the backup. The function returns true if it
|
||||
// should be excluded (rejected) from the backup.
|
||||
type RejectFunc func(path string, fi os.FileInfo) bool
|
||||
|
||||
// rejectByPattern returns a RejectFunc which rejects files that match
|
||||
// rejectByPattern returns a RejectByNameFunc which rejects files that match
|
||||
// one of the patterns.
|
||||
func rejectByPattern(patterns []string) RejectFunc {
|
||||
return func(item string, fi os.FileInfo) bool {
|
||||
func rejectByPattern(patterns []string) RejectByNameFunc {
|
||||
return func(item string) bool {
|
||||
matched, _, err := filter.List(patterns, item)
|
||||
if err != nil {
|
||||
Warnf("error for exclude pattern: %v", err)
|
||||
@@ -83,14 +88,14 @@ func rejectByPattern(patterns []string) RejectFunc {
|
||||
}
|
||||
}
|
||||
|
||||
// rejectIfPresent returns a RejectFunc which itself returns whether a path
|
||||
// should be excluded. The RejectFunc considers a file to be excluded when
|
||||
// rejectIfPresent returns a RejectByNameFunc which itself returns whether a path
|
||||
// should be excluded. The RejectByNameFunc considers a file to be excluded when
|
||||
// it resides in a directory with an exclusion file, that is specified by
|
||||
// excludeFileSpec in the form "filename[:content]". The returned error is
|
||||
// non-nil if the filename component of excludeFileSpec is empty. If rc is
|
||||
// non-nil, it is going to be used in the RejectFunc to expedite the evaluation
|
||||
// non-nil, it is going to be used in the RejectByNameFunc to expedite the evaluation
|
||||
// of a directory based on previous visits.
|
||||
func rejectIfPresent(excludeFileSpec string) (RejectFunc, error) {
|
||||
func rejectIfPresent(excludeFileSpec string) (RejectByNameFunc, error) {
|
||||
if excludeFileSpec == "" {
|
||||
return nil, errors.New("name for exclusion tagfile is empty")
|
||||
}
|
||||
@@ -107,7 +112,7 @@ func rejectIfPresent(excludeFileSpec string) (RejectFunc, error) {
|
||||
}
|
||||
debug.Log("using %q as exclusion tagfile", tf)
|
||||
rc := &rejectionCache{}
|
||||
fn := func(filename string, _ os.FileInfo) bool {
|
||||
fn := func(filename string) bool {
|
||||
return isExcludedByFile(filename, tf, tc, rc)
|
||||
}
|
||||
return fn, nil
|
||||
@@ -252,11 +257,11 @@ func rejectByDevice(samples []string) (RejectFunc, error) {
|
||||
}, nil
|
||||
}
|
||||
|
||||
// rejectResticCache returns a RejectFunc that rejects the restic cache
|
||||
// rejectResticCache returns a RejectByNameFunc that rejects the restic cache
|
||||
// directory (if set).
|
||||
func rejectResticCache(repo *repository.Repository) (RejectFunc, error) {
|
||||
func rejectResticCache(repo *repository.Repository) (RejectByNameFunc, error) {
|
||||
if repo.Cache == nil {
|
||||
return func(string, os.FileInfo) bool {
|
||||
return func(string) bool {
|
||||
return false
|
||||
}, nil
|
||||
}
|
||||
@@ -266,7 +271,7 @@ func rejectResticCache(repo *repository.Repository) (RejectFunc, error) {
|
||||
return nil, errors.New("cacheBase is empty string")
|
||||
}
|
||||
|
||||
return func(item string, _ os.FileInfo) bool {
|
||||
return func(item string) bool {
|
||||
if fs.HasPathPrefix(cacheBase, item) {
|
||||
debug.Log("rejecting restic cache directory %v", item)
|
||||
return true
|
||||
|
@@ -27,7 +27,7 @@ func TestRejectByPattern(t *testing.T) {
|
||||
for _, tc := range tests {
|
||||
t.Run("", func(t *testing.T) {
|
||||
reject := rejectByPattern(patterns)
|
||||
res := reject(tc.filename, nil)
|
||||
res := reject(tc.filename)
|
||||
if res != tc.reject {
|
||||
t.Fatalf("wrong result for filename %v: want %v, got %v",
|
||||
tc.filename, tc.reject, res)
|
||||
@@ -140,8 +140,8 @@ func TestMultipleIsExcludedByFile(t *testing.T) {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
excludedByFoo := fooExclude(p, fi)
|
||||
excludedByBar := barExclude(p, fi)
|
||||
excludedByFoo := fooExclude(p)
|
||||
excludedByBar := barExclude(p)
|
||||
excluded := excludedByFoo || excludedByBar
|
||||
// the log message helps debugging in case the test fails
|
||||
t.Logf("%q: %v || %v = %v", p, excludedByFoo, excludedByBar, excluded)
|
||||
|
@@ -21,7 +21,7 @@ func formatBytes(c uint64) string {
|
||||
case c > 1<<10:
|
||||
return fmt.Sprintf("%.3f KiB", b/(1<<10))
|
||||
default:
|
||||
return fmt.Sprintf("%dB", c)
|
||||
return fmt.Sprintf("%d B", c)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -90,6 +90,6 @@ func formatNode(path 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), path,
|
||||
n.ModTime.Local().Format(TimeFormat), path,
|
||||
target)
|
||||
}
|
||||
|
@@ -34,23 +34,29 @@ import (
|
||||
"github.com/restic/restic/internal/errors"
|
||||
|
||||
"golang.org/x/crypto/ssh/terminal"
|
||||
"os/exec"
|
||||
)
|
||||
|
||||
var version = "compiled manually"
|
||||
var version = "0.9.4"
|
||||
|
||||
// TimeFormat is the format used for all timestamps printed by restic.
|
||||
const TimeFormat = "2006-01-02 15:04:05"
|
||||
|
||||
// GlobalOptions hold all global options for restic.
|
||||
type GlobalOptions struct {
|
||||
Repo string
|
||||
PasswordFile string
|
||||
Quiet bool
|
||||
Verbose int
|
||||
NoLock bool
|
||||
JSON bool
|
||||
CacheDir string
|
||||
NoCache bool
|
||||
CACerts []string
|
||||
TLSClientCert string
|
||||
CleanupCache bool
|
||||
Repo string
|
||||
PasswordFile string
|
||||
PasswordCommand string
|
||||
KeyHint string
|
||||
Quiet bool
|
||||
Verbose int
|
||||
NoLock bool
|
||||
JSON bool
|
||||
CacheDir string
|
||||
NoCache bool
|
||||
CACerts []string
|
||||
TLSClientCert string
|
||||
CleanupCache bool
|
||||
|
||||
LimitUploadKb int
|
||||
LimitDownloadKb int
|
||||
@@ -64,7 +70,7 @@ type GlobalOptions struct {
|
||||
// 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
|
||||
// 3 means: print very detailed debug messages, this is used when --verbose 2 is specified
|
||||
verbosity uint
|
||||
|
||||
Options []string
|
||||
@@ -88,11 +94,13 @@ func init() {
|
||||
f := cmdRoot.PersistentFlags()
|
||||
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.StringVarP(&globalOptions.KeyHint, "key-hint", "", os.Getenv("RESTIC_KEY_HINT"), "key ID of key to try decrypting first (default: $RESTIC_KEY_HINT)")
|
||||
f.StringVarP(&globalOptions.PasswordCommand, "password-command", "", os.Getenv("RESTIC_PASSWORD_COMMAND"), "specify a shell command to obtain a password (default: $RESTIC_PASSWORD_COMMAND)")
|
||||
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.StringVar(&globalOptions.CacheDir, "cache-dir", "", "set the cache directory. (default: use system default cache directory)")
|
||||
f.BoolVar(&globalOptions.NoCache, "no-cache", false, "do not use a local cache")
|
||||
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")
|
||||
@@ -176,7 +184,6 @@ func Printf(format string, args ...interface{}) {
|
||||
_, err := fmt.Fprintf(globalOptions.stdout, format, args...)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "unable to write to stdout: %v\n", err)
|
||||
Exit(100)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -217,7 +224,6 @@ func Warnf(format string, args ...interface{}) {
|
||||
_, err := fmt.Fprintf(globalOptions.stderr, format, args...)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "unable to write to stderr: %v\n", err)
|
||||
Exit(100)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -233,7 +239,23 @@ 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) {
|
||||
func resolvePassword(opts GlobalOptions) (string, error) {
|
||||
if opts.PasswordFile != "" && opts.PasswordCommand != "" {
|
||||
return "", errors.Fatalf("Password file and command are mutually exclusive options")
|
||||
}
|
||||
if opts.PasswordCommand != "" {
|
||||
args, err := backend.SplitShellStrings(opts.PasswordCommand)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
cmd := exec.Command(args[0], args[1:]...)
|
||||
cmd.Stderr = os.Stderr
|
||||
output, err := cmd.Output()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return (strings.TrimSpace(string(output))), nil
|
||||
}
|
||||
if opts.PasswordFile != "" {
|
||||
s, err := textfile.Read(opts.PasswordFile)
|
||||
if os.IsNotExist(errors.Cause(err)) {
|
||||
@@ -242,7 +264,7 @@ func resolvePassword(opts GlobalOptions, env string) (string, error) {
|
||||
return strings.TrimSpace(string(s)), errors.Wrap(err, "Readfile")
|
||||
}
|
||||
|
||||
if pwd := os.Getenv(env); pwd != "" {
|
||||
if pwd := os.Getenv("RESTIC_PASSWORD"); pwd != "" {
|
||||
return pwd, nil
|
||||
}
|
||||
|
||||
@@ -350,7 +372,7 @@ func OpenRepository(opts GlobalOptions) (*repository.Repository, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = s.SearchKey(opts.ctx, opts.password, maxKeys)
|
||||
err = s.SearchKey(opts.ctx, opts.password, maxKeys, opts.KeyHint)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -360,7 +382,9 @@ func OpenRepository(opts GlobalOptions) (*repository.Repository, error) {
|
||||
if len(id) > 8 {
|
||||
id = id[:8]
|
||||
}
|
||||
Verbosef("repository %v opened successfully, password is correct\n", id)
|
||||
if !opts.JSON {
|
||||
Verbosef("repository %v opened successfully, password is correct\n", id)
|
||||
}
|
||||
}
|
||||
|
||||
if opts.NoCache {
|
||||
@@ -373,6 +397,10 @@ func OpenRepository(opts GlobalOptions) (*repository.Repository, error) {
|
||||
return s, nil
|
||||
}
|
||||
|
||||
if c.Created && !opts.JSON {
|
||||
Verbosef("created new cache in %v\n", c.Base)
|
||||
}
|
||||
|
||||
// start using the cache
|
||||
s.UseCache(c)
|
||||
|
||||
|
@@ -54,7 +54,7 @@ directories in an encrypted repository stored on different backends.
|
||||
if c.Name() == "version" {
|
||||
return nil
|
||||
}
|
||||
pwd, err := resolvePassword(globalOptions, "RESTIC_PASSWORD")
|
||||
pwd, err := resolvePassword(globalOptions)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Resolving password failed: %v\n", err)
|
||||
Exit(1)
|
||||
|
@@ -1,63 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Table contains data for a table to be printed.
|
||||
type Table struct {
|
||||
Header string
|
||||
Rows [][]interface{}
|
||||
Footer string
|
||||
|
||||
RowFormat string
|
||||
}
|
||||
|
||||
// NewTable initializes a new Table.
|
||||
func NewTable() Table {
|
||||
return Table{
|
||||
Rows: [][]interface{}{},
|
||||
}
|
||||
}
|
||||
|
||||
func (t Table) printSeparationLine(w io.Writer) error {
|
||||
_, err := fmt.Fprintln(w, strings.Repeat("-", 70))
|
||||
return err
|
||||
}
|
||||
|
||||
// Write prints the table to w.
|
||||
func (t Table) Write(w io.Writer) error {
|
||||
_, err := fmt.Fprintln(w, t.Header)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = t.printSeparationLine(w)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, row := range t.Rows {
|
||||
_, err = fmt.Fprintf(w, t.RowFormat+"\n", row...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
err = t.printSeparationLine(w)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = fmt.Fprintln(w, t.Footer)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// TimeFormat is the format used for all timestamps printed by restic.
|
||||
const TimeFormat = "2006-01-02 15:04:05"
|
72
contrib/restic.spec
Normal file
72
contrib/restic.spec
Normal file
@@ -0,0 +1,72 @@
|
||||
Name: restic
|
||||
Version: 0.9.2git.20180812
|
||||
Release: 4%{?dist}
|
||||
Summary: restic is a backup program that is fast, efficient and secure.
|
||||
|
||||
%global debug_package %{nil}
|
||||
|
||||
Group: Applications/Archiving
|
||||
License: BSD 2-Clause
|
||||
URL: https://restic.net/
|
||||
Source0: %{name}-%{version}.tar.gz
|
||||
|
||||
BuildRequires: golang
|
||||
Requires: /bin/bash
|
||||
|
||||
%description
|
||||
restic is a program that does backups right. The design goals are:
|
||||
|
||||
Easy: Doing backups should be a frictionless process, otherwise you are tempted to skip it. Restic should be easy to configure and use, so that in the unlikely event of a data loss you can just restore it. Likewise, restoring data should not be complicated.
|
||||
|
||||
Fast: Backing up your data with restic should only be limited by your network or hard disk bandwidth so that you can backup your files every day. Nobody does backups if it takes too much time. Restoring backups should only transfer data that is needed for the files that are to be restored, so that this process is also fast.
|
||||
|
||||
Verifiable: Much more important than backup is restore, so restic enables you to easily verify that all data can be restored.
|
||||
|
||||
Secure: Restic uses cryptography to guarantee confidentiality and integrity of your data. The location where the backup data is stored is assumed to be an untrusted environment (e.g. a shared space where others like system administrators are able to access your backups). Restic is built to secure your data against such attackers, by encrypting it with AES-256 in counter mode and authenticating it using Poly1305-AES.
|
||||
|
||||
Efficient: With the growth of data, additional snapshots should only take the storage of the actual increment. Even more, duplicate data should be de-duplicated before it is actually written to the storage backend to save precious backup space.
|
||||
|
||||
Versatile storage: Users can provide many different places to store the backups. Local, SFTP, Restics REST-Server, Amazon S3, Minio, Openstack Swift, Backblaze B2, Microsoft Azure Blob Storage, Google Cloud Storage and more by the usage of rclone.
|
||||
|
||||
Free: restic is free software and licensed under the BSD 2-Clause License and actively developed on GitHub.
|
||||
|
||||
%prep
|
||||
%setup -q
|
||||
|
||||
|
||||
%build
|
||||
make %{?_smp_mflags}
|
||||
|
||||
|
||||
%install
|
||||
mkdir -p %{buildroot}%{_bindir}
|
||||
mkdir -p %{buildroot}%{_mandir}/man1
|
||||
mkdir -p %{buildroot}%{_datarootdir}/zsh/site-functions
|
||||
mkdir -p %{buildroot}%{_datarootdir}/bash-completion/completions
|
||||
install -p -m 644 doc/man/* %{buildroot}%{_mandir}/man1/
|
||||
install -p -m 644 doc/zsh-completion.zsh %{buildroot}%{_datarootdir}/zsh/site-functions/_restic
|
||||
install -p -m 644 doc/bash-completion.sh %{buildroot}%{_datarootdir}/bash-completion/completions/restic
|
||||
install -p -m 755 %{name} %{buildroot}%{_bindir}
|
||||
|
||||
%files
|
||||
%doc LICENSE
|
||||
%doc README.rst
|
||||
%{_bindir}/%{name}
|
||||
%dir %{_datadir}/zsh/site-functions
|
||||
%{_datadir}/zsh/site-functions/_restic
|
||||
%dir %{_datadir}/bash-completion/
|
||||
%dir %{_datadir}/bash-completion/completions
|
||||
%{_datadir}/bash-completion/completions/restic
|
||||
%{_mandir}/man1/restic*.*
|
||||
|
||||
|
||||
%changelog
|
||||
* Sun Aug 12 2018 Luc De Louw <luc@delouw.ch> - 0.9.2git.20180812-4
|
||||
- %license does not work with RHEL6, using %doc instead
|
||||
* Sun Aug 12 2018 Luc De Louw <luc@delouw.ch> - 0.9.2git.20180812-3
|
||||
- Better description
|
||||
* Sun Aug 12 2018 Luc De Louw <luc@delouw.ch> - 0.9.2git.20180812-2
|
||||
- Initial RPM build
|
||||
* Sun Aug 12 2018 Luc De Louw <luc@delouw.ch> - 0.9.2git.20180812-1
|
||||
- Initial RPM build
|
||||
|
11
doc.go
Normal file
11
doc.go
Normal file
@@ -0,0 +1,11 @@
|
||||
// Package restic gives a (very brief) introduction to the structure of source code.
|
||||
//
|
||||
// Overview
|
||||
//
|
||||
// The packages are structured so that cmd/ contains the main package for the
|
||||
// restic binary, and internal/ contains almost all code in library form. We've
|
||||
// chosen to use the internal/ path so that the packages cannot be imported by
|
||||
// other programs. This was done on purpose, at the moment restic is a
|
||||
// command-line program and not a library. This may be revisited at a later
|
||||
// point in time.
|
||||
package restic
|
2
doc/.gitignore
vendored
Normal file
2
doc/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
_build
|
||||
.doctrees
|
@@ -24,37 +24,18 @@ 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
|
||||
========
|
||||
|
||||
If you are using Mac OS X, you can install restic using the
|
||||
`homebrew <http://brew.sh/>`__ package manager:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ brew install restic
|
||||
Official binaries can be updated in place by using the ``restic self-update``
|
||||
command.
|
||||
|
||||
Arch Linux
|
||||
==========
|
||||
|
||||
On `Arch Linux <https://www.archlinux.org/>`__, there is a package called ``restic-git``
|
||||
which can be installed from AUR, e.g. with ``pacaur``:
|
||||
On `Arch Linux <https://www.archlinux.org/>`__, there is a package called ``restic``
|
||||
installed from the official community repos, e.g. with ``pacman -S``:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ pacaur -S restic-git
|
||||
|
||||
Nix & NixOS
|
||||
===========
|
||||
|
||||
If you are using `Nix <https://nixos.org/nix/>`__ or `NixOS <https://nixos.org/>`__
|
||||
there is a package available named ``restic``.
|
||||
It can be installed uisng ``nix-env``:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ nix-env --install restic
|
||||
$ pacman -S restic
|
||||
|
||||
Debian
|
||||
======
|
||||
@@ -70,7 +51,62 @@ 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``.
|
||||
|
||||
|
||||
Fedora
|
||||
======
|
||||
|
||||
restic can be installed using ``dnf``:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ dnf install restic
|
||||
|
||||
If you used restic from copr previously, remove the copr repo as follows to
|
||||
avoid any conflicts:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ dnf copr remove copart/restic
|
||||
|
||||
macOS
|
||||
=====
|
||||
|
||||
If you are using macOS, you can install restic using the
|
||||
`homebrew <https://brew.sh/>`__ package manager:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ brew install restic
|
||||
|
||||
Nix & NixOS
|
||||
===========
|
||||
|
||||
If you are using `Nix <https://nixos.org/nix/>`__ or `NixOS <https://nixos.org/>`__
|
||||
there is a package available named ``restic``.
|
||||
It can be installed using ``nix-env``:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ nix-env --install restic
|
||||
|
||||
OpenBSD
|
||||
=======
|
||||
|
||||
On OpenBSD 6.3 and greater, you can install restic using ``pkg_add``:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
# pkg_add restic
|
||||
|
||||
FreeBSD
|
||||
=======
|
||||
|
||||
On FreeBSD (11 and probably later versions), you can install restic using ``pkg install``:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
# pkg install restic
|
||||
|
||||
RHEL & CentOS
|
||||
=============
|
||||
|
||||
@@ -94,17 +130,6 @@ For CentOS7 use:
|
||||
|
||||
$ yum-config-manager --add-repo https://copr.fedorainfracloud.org/coprs/copart/restic/repo/epel-7/copart-restic-epel-7.repo
|
||||
|
||||
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
|
||||
=====
|
||||
|
||||
@@ -113,15 +138,19 @@ restic can be installed from the official repo of Solus via the ``eopkg`` packag
|
||||
.. code-block:: console
|
||||
|
||||
$ eopkg install restic
|
||||
|
||||
OpenBSD
|
||||
|
||||
Windows
|
||||
=======
|
||||
|
||||
On OpenBSD 6.3 and greater, you can install restic using ``pkg_add``:
|
||||
restic can be installed using `Scoop <https://scoop.sh/>`__:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
# pkg_add restic
|
||||
scoop install restic
|
||||
|
||||
Using this installation method, ``restic.exe`` will automatically be available
|
||||
in the ``PATH``. It can be called from cmd.exe or PowerShell by typing ``restic``.
|
||||
|
||||
|
||||
.. _official_binaries:
|
||||
|
||||
@@ -138,6 +167,38 @@ 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.
|
||||
|
||||
The official binaries can be updated in place using the ``restic self-update``
|
||||
command:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ restic version
|
||||
restic 0.9.1 compiled with go1.10.3 on linux/amd64
|
||||
|
||||
$ restic self-update
|
||||
find latest release of restic at GitHub
|
||||
latest version is 0.9.2
|
||||
download file SHA256SUMS
|
||||
download SHA256SUMS
|
||||
download file SHA256SUMS
|
||||
download SHA256SUMS.asc
|
||||
GPG signature verification succeeded
|
||||
download restic_0.9.2_linux_amd64.bz2
|
||||
downloaded restic_0.9.2_linux_amd64.bz2
|
||||
saved 12115904 bytes in ./restic
|
||||
successfully updated restic to version 0.9.2
|
||||
|
||||
$ restic version
|
||||
restic 0.9.2 compiled with go1.10.3 on linux/amd64
|
||||
|
||||
The ``self-update`` command uses the GPG signature on the files uploaded to
|
||||
GitHub to verify their authenticity. No external programs are necessary.
|
||||
|
||||
.. note:: Please be aware that the user executing the ``restic self-update``
|
||||
command must have the permission to replace the restic binary.
|
||||
If you want to save the downloaded restic binary into a different file, pass
|
||||
the file name via the option ``--output``.
|
||||
|
||||
Unstable Builds
|
||||
===============
|
||||
|
||||
@@ -187,6 +248,13 @@ In order to build restic from source, execute the following steps:
|
||||
|
||||
$ cd restic
|
||||
|
||||
$ go run -mod=vendor build.go
|
||||
|
||||
For Go versions < 1.11, the option ``-mod=vendor`` needs to be removed, like
|
||||
this:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ go run build.go
|
||||
|
||||
You can easily cross-compile restic for all supported platforms, just
|
||||
@@ -195,12 +263,14 @@ supply the target OS and platform via the command-line options like this
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ go run build.go --goos windows --goarch amd64
|
||||
$ go run -mod=vendor build.go --goos windows --goarch amd64
|
||||
|
||||
$ go run build.go --goos freebsd --goarch 386
|
||||
$ go run -mod=vendor build.go --goos freebsd --goarch 386
|
||||
|
||||
$ go run -mod=vendor build.go --goos linux --goarch arm --goarm 6
|
||||
|
||||
Again, for Go < 1.11 ``-mod=vendor`` needs to be removed.
|
||||
|
||||
$ go run build.go --goos linux --goarch arm --goarm 6
|
||||
|
||||
The resulting binary is statically linked and does not require any
|
||||
libraries.
|
||||
|
||||
|
@@ -21,6 +21,19 @@ 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.
|
||||
|
||||
For automated backups, restic accepts the repository location in the
|
||||
environment variable ``RESTIC_REPOSITORY``. For the password, several options
|
||||
exist:
|
||||
|
||||
* Setting the environment variable ``RESTIC_PASSWORD``
|
||||
|
||||
* Specifying the path to a file with the password via the option
|
||||
``--password-file`` or the environment variable ``RESTIC_PASSWORD_FILE``
|
||||
|
||||
* Configuring a program to be called when the password is needed via the
|
||||
option ``--password-command`` or the environment variable
|
||||
``RESTIC_PASSWORD_COMMAND``
|
||||
|
||||
Local
|
||||
*****
|
||||
|
||||
@@ -41,11 +54,6 @@ command and enter the same password twice:
|
||||
Remembering your password is important! If you lose it, you won't be
|
||||
able to access data stored in the repository.
|
||||
|
||||
For automated backups, restic accepts the repository location in the
|
||||
environment variable ``RESTIC_REPOSITORY``. The password can be read
|
||||
from a file (via the option ``--password-file`` or the environment variable
|
||||
``RESTIC_PASSWORD_FILE``) or the environment variable ``RESTIC_PASSWORD``.
|
||||
|
||||
SFTP
|
||||
****
|
||||
|
||||
@@ -298,10 +306,10 @@ dashboard in on the "Buckets" page when signed into your B2 account:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ export B2_ACCOUNT_ID=<MY_ACCOUNT_ID>
|
||||
$ export B2_ACCOUNT_ID=<MY_APPLICATION_KEY_ID>
|
||||
$ export B2_ACCOUNT_KEY=<MY_SECRET_ACCOUNT_KEY>
|
||||
|
||||
.. note:: In case you want to use Backblaze Application Keys replace <MY_ACCOUNT_ID> and <MY_SECRET_ACCOUNT_KEY> with <applicationKeyId> and <applicationKey> respectively.
|
||||
.. note:: In case you want to use Backblaze Application Keys replace <MY_APPLICATION_KEY_ID> and <MY_SECRET_ACCOUNT_KEY> with <applicationKeyId> and <applicationKey> respectively.
|
||||
|
||||
You can then initialize a repository stored at Backblaze B2. If the
|
||||
bucket does not exist yet and the credentials you passed to restic have the
|
||||
|
@@ -33,18 +33,26 @@ again:
|
||||
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
|
||||
Added: 1.200 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. You'll still get a nice live status display. Be aware that the live status shows the processed files and not the transferred data. Transferred volume might be lower (due to deduplication) or higher.
|
||||
You can see that restic tells us it processed 1.720 GiB of data, this is the
|
||||
size of the files and directories in ``~/work`` on the local file system. It
|
||||
also tells us that only 1.200 GiB was added to the repository. This means that
|
||||
some of the data was duplicate and restic was able to efficiently reduce it.
|
||||
|
||||
If you don't pass the ``--verbose`` option, restic will print less data. You'll
|
||||
still get a nice live status display. Be aware that the live status shows the
|
||||
processed files and not the transferred data. Transferred volume might be lower
|
||||
(due to de-duplication) or higher.
|
||||
|
||||
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!
|
||||
your data, but this time it's even faster and no new data was added to the
|
||||
repository (since all data is already there). This is de-duplication at work!
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
@@ -113,13 +121,18 @@ can compute which parts of the files need to be saved. When you backup
|
||||
the same directory again (maybe with new or changed files) restic will
|
||||
find the old snapshot in the repo and by default only reads those files
|
||||
that are new or have been modified since the last snapshot. This is
|
||||
decided based on the modify date of the file in the file system.
|
||||
decided based on the following attributes of the file in the file system:
|
||||
|
||||
* Type (file, symlink, or directory?)
|
||||
* Modification time
|
||||
* Size
|
||||
* Inode number (internal number used to reference a file in a file system)
|
||||
|
||||
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
|
||||
Including and Excluding Files
|
||||
*****************************
|
||||
|
||||
You can exclude folders and files by specifying exclude patterns, currently
|
||||
@@ -179,7 +192,7 @@ 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``
|
||||
* ``/foo/bar/file``
|
||||
* ``/tmp/foo/bar``
|
||||
|
||||
By specifying the option ``--one-file-system`` you can instruct restic
|
||||
@@ -191,9 +204,12 @@ backup ``/sys`` or ``/dev`` on a Linux system:
|
||||
|
||||
$ restic -r /srv/restic-repo backup --one-file-system /
|
||||
|
||||
.. note:: ``--one-file-system`` is currently unsupported on Windows, and will
|
||||
cause the backup to immediately fail with an error.
|
||||
|
||||
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
|
||||
backup from one or more files. 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.
|
||||
|
||||
For example maybe you want to backup files which have a name that matches a
|
||||
@@ -314,3 +330,51 @@ some additional data in the repository, but the snapshot will never be
|
||||
created as it would only be written at the very (successful) end of
|
||||
the backup operation. Previous snapshots will still be there and will still
|
||||
work.
|
||||
|
||||
|
||||
Environment Variables
|
||||
*********************
|
||||
|
||||
In addition to command-line options, restic supports passing various options in
|
||||
environment variables. The following list of environment variables:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
RESTIC_REPOSITORY Location of repository (replaces -r)
|
||||
RESTIC_PASSWORD_FILE Location of password file (replaces --password-file)
|
||||
RESTIC_PASSWORD The actual password for the repository
|
||||
|
||||
AWS_ACCESS_KEY_ID Amazon S3 access key ID
|
||||
AWS_SECRET_ACCESS_KEY Amazon S3 secret access key
|
||||
|
||||
ST_AUTH Auth URL for keystone v1 authentication
|
||||
ST_USER Username for keystone v1 authentication
|
||||
ST_KEY Password for keystone v1 authentication
|
||||
|
||||
OS_AUTH_URL Auth URL for keystone authentication
|
||||
OS_REGION_NAME Region name for keystone authentication
|
||||
OS_USERNAME Username for keystone authentication
|
||||
OS_PASSWORD Password for keystone authentication
|
||||
OS_TENANT_ID Tenant ID for keystone v2 authentication
|
||||
OS_TENANT_NAME Tenant name for keystone v2 authentication
|
||||
|
||||
OS_USER_DOMAIN_NAME User domain name for keystone authentication
|
||||
OS_PROJECT_NAME Project name for keystone authentication
|
||||
OS_PROJECT_DOMAIN_NAME PRoject domain name for keystone authentication
|
||||
|
||||
OS_STORAGE_URL Storage URL for token authentication
|
||||
OS_AUTH_TOKEN Auth token for token authentication
|
||||
|
||||
B2_ACCOUNT_ID Account ID or applicationKeyId for Backblaze B2
|
||||
B2_ACCOUNT_KEY Account Key or applicationKey for Backblaze B2
|
||||
|
||||
AZURE_ACCOUNT_NAME Account name for Azure
|
||||
AZURE_ACCOUNT_KEY Account key for Azure
|
||||
|
||||
GOOGLE_PROJECT_ID Project ID for Google Cloud Storage
|
||||
GOOGLE_APPLICATION_CREDENTIALS Application Credentials for Google Cloud Storage (e.g. $HOME/.config/gs-secret-restic-key.json)
|
||||
|
||||
RCLONE_BWLIMIT rclone bandwidth limit
|
||||
|
||||
|
||||
|
||||
|
@@ -66,7 +66,7 @@ backup with the intention to make you restore malicious data:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ sudo echo "boom" >> backup/index/d795ffa99a8ab8f8e42cec1f814df4e48b8f49129360fb57613df93739faee97
|
||||
$ echo "boom" >> backup/index/d795ffa99a8ab8f8e42cec1f814df4e48b8f49129360fb57613df93739faee97
|
||||
|
||||
In order to detect these things, it is a good idea to regularly use the
|
||||
``check`` command to test whether everything is alright, your precious
|
||||
|
@@ -68,7 +68,9 @@ command to serve the repository with FUSE:
|
||||
Don't forget to umount after quitting!
|
||||
|
||||
Mounting repositories via FUSE is not possible on OpenBSD, Solaris/illumos
|
||||
and Windows.
|
||||
and Windows. For Linux, the ``fuse`` kernel module needs to be loaded. For
|
||||
FreeBSD, you may need to install FUSE and load the kernel module (``kldload
|
||||
fuse``).
|
||||
|
||||
Restic supports storage and preservation of hard links. However, since
|
||||
hard links exist in the scope of a filesystem by definition, restoring
|
||||
|
@@ -168,8 +168,9 @@ The ``forget`` command accepts the following parameters:
|
||||
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.
|
||||
years, months, days, and hours, e.g. ``2y5m7d3h`` will keep all snapshots
|
||||
made in the two years, five months, seven days, and three hours before the
|
||||
latest snapshot.
|
||||
|
||||
Multiple policies will be ORed together so as to be as inclusive as possible
|
||||
for keeping snapshots.
|
||||
|
@@ -22,7 +22,9 @@ The program can be built with debug support like this:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ go run build.go -tags debug
|
||||
$ go run build.go -mod=vendor -tags debug
|
||||
|
||||
For Go < 1.11, the option ``-mod=vendor`` needs to be removed.
|
||||
|
||||
Afterwards, extensive debug messages are written to the file in
|
||||
environment variable ``DEBUG_LOG``, e.g.:
|
||||
@@ -83,7 +85,7 @@ back end is contained in `Design <https://restic.readthedocs.io/en/latest/design
|
||||
If you'd like to start contributing to restic, but don't know exactly
|
||||
what do to, have a look at this great article by Dave Cheney:
|
||||
`Suggestions for contributing to an Open Source
|
||||
project <http://dave.cheney.net/2016/03/12/suggestions-for-contributing-to-an-open-source-project>`__
|
||||
project <https://dave.cheney.net/2016/03/12/suggestions-for-contributing-to-an-open-source-project>`__
|
||||
A few issues have been tagged with the label ``help wanted``, you can
|
||||
start looking at those:
|
||||
https://github.com/restic/restic/labels/help%20wanted
|
||||
@@ -111,7 +113,7 @@ Compatibility
|
||||
|
||||
Backward compatibility for backups is important so that our users are
|
||||
always able to restore saved data. Therefore restic follows `Semantic
|
||||
Versioning <http://semver.org>`__ to clearly define which versions are
|
||||
Versioning <https://semver.org>`__ to clearly define which versions are
|
||||
compatible. The repository and data structures contained therein are
|
||||
considered the "Public API" in the sense of Semantic Versioning. This
|
||||
goes for all released versions of restic, this may not be the case for
|
||||
@@ -126,7 +128,7 @@ prior versions.
|
||||
Building documentation
|
||||
**********************
|
||||
|
||||
The restic documentation is built with `Sphinx <http://sphinx-doc.org>`__,
|
||||
The restic documentation is built with `Sphinx <https://www.sphinx-doc.org>`__,
|
||||
therefore building it locally requires a recent Python version and requirements listed in ``doc/requirements.txt``.
|
||||
This example will guide you through the process using `virtualenv <https://virtualenv.pypa.io>`__:
|
||||
|
||||
|
@@ -17,4 +17,9 @@ help:
|
||||
# Catch-all target: route all unknown targets to Sphinx using the new
|
||||
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
|
||||
%: Makefile
|
||||
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
||||
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
||||
|
||||
.PHONY: autobuild
|
||||
|
||||
autobuild:
|
||||
sphinx-autobuild -b html -i '.doctrees/*' . _build
|
||||
|
@@ -274,8 +274,9 @@ _restic_backup()
|
||||
flags+=("--help")
|
||||
flags+=("-h")
|
||||
local_nonpersistent_flags+=("--help")
|
||||
flags+=("--hostname=")
|
||||
local_nonpersistent_flags+=("--hostname=")
|
||||
flags+=("--host=")
|
||||
two_word_flags+=("-H")
|
||||
local_nonpersistent_flags+=("--host=")
|
||||
flags+=("--one-file-system")
|
||||
flags+=("-x")
|
||||
local_nonpersistent_flags+=("--one-file-system")
|
||||
@@ -295,12 +296,14 @@ _restic_backup()
|
||||
flags+=("--cache-dir=")
|
||||
flags+=("--cleanup-cache")
|
||||
flags+=("--json")
|
||||
flags+=("--key-hint=")
|
||||
flags+=("--limit-download=")
|
||||
flags+=("--limit-upload=")
|
||||
flags+=("--no-cache")
|
||||
flags+=("--no-lock")
|
||||
flags+=("--option=")
|
||||
two_word_flags+=("-o")
|
||||
flags+=("--password-command=")
|
||||
flags+=("--password-file=")
|
||||
two_word_flags+=("-p")
|
||||
flags+=("--quiet")
|
||||
@@ -337,16 +340,20 @@ _restic_cache()
|
||||
local_nonpersistent_flags+=("--help")
|
||||
flags+=("--max-age=")
|
||||
local_nonpersistent_flags+=("--max-age=")
|
||||
flags+=("--no-size")
|
||||
local_nonpersistent_flags+=("--no-size")
|
||||
flags+=("--cacert=")
|
||||
flags+=("--cache-dir=")
|
||||
flags+=("--cleanup-cache")
|
||||
flags+=("--json")
|
||||
flags+=("--key-hint=")
|
||||
flags+=("--limit-download=")
|
||||
flags+=("--limit-upload=")
|
||||
flags+=("--no-cache")
|
||||
flags+=("--no-lock")
|
||||
flags+=("--option=")
|
||||
two_word_flags+=("-o")
|
||||
flags+=("--password-command=")
|
||||
flags+=("--password-file=")
|
||||
two_word_flags+=("-p")
|
||||
flags+=("--quiet")
|
||||
@@ -383,12 +390,14 @@ _restic_cat()
|
||||
flags+=("--cache-dir=")
|
||||
flags+=("--cleanup-cache")
|
||||
flags+=("--json")
|
||||
flags+=("--key-hint=")
|
||||
flags+=("--limit-download=")
|
||||
flags+=("--limit-upload=")
|
||||
flags+=("--no-cache")
|
||||
flags+=("--no-lock")
|
||||
flags+=("--option=")
|
||||
two_word_flags+=("-o")
|
||||
flags+=("--password-command=")
|
||||
flags+=("--password-file=")
|
||||
two_word_flags+=("-p")
|
||||
flags+=("--quiet")
|
||||
@@ -433,12 +442,14 @@ _restic_check()
|
||||
flags+=("--cache-dir=")
|
||||
flags+=("--cleanup-cache")
|
||||
flags+=("--json")
|
||||
flags+=("--key-hint=")
|
||||
flags+=("--limit-download=")
|
||||
flags+=("--limit-upload=")
|
||||
flags+=("--no-cache")
|
||||
flags+=("--no-lock")
|
||||
flags+=("--option=")
|
||||
two_word_flags+=("-o")
|
||||
flags+=("--password-command=")
|
||||
flags+=("--password-file=")
|
||||
two_word_flags+=("-p")
|
||||
flags+=("--quiet")
|
||||
@@ -477,12 +488,14 @@ _restic_diff()
|
||||
flags+=("--cache-dir=")
|
||||
flags+=("--cleanup-cache")
|
||||
flags+=("--json")
|
||||
flags+=("--key-hint=")
|
||||
flags+=("--limit-download=")
|
||||
flags+=("--limit-upload=")
|
||||
flags+=("--no-cache")
|
||||
flags+=("--no-lock")
|
||||
flags+=("--option=")
|
||||
two_word_flags+=("-o")
|
||||
flags+=("--password-command=")
|
||||
flags+=("--password-file=")
|
||||
two_word_flags+=("-p")
|
||||
flags+=("--quiet")
|
||||
@@ -526,12 +539,14 @@ _restic_dump()
|
||||
flags+=("--cache-dir=")
|
||||
flags+=("--cleanup-cache")
|
||||
flags+=("--json")
|
||||
flags+=("--key-hint=")
|
||||
flags+=("--limit-download=")
|
||||
flags+=("--limit-upload=")
|
||||
flags+=("--no-cache")
|
||||
flags+=("--no-lock")
|
||||
flags+=("--option=")
|
||||
two_word_flags+=("-o")
|
||||
flags+=("--password-command=")
|
||||
flags+=("--password-file=")
|
||||
two_word_flags+=("-p")
|
||||
flags+=("--quiet")
|
||||
@@ -561,6 +576,8 @@ _restic_find()
|
||||
flags_with_completion=()
|
||||
flags_completion=()
|
||||
|
||||
flags+=("--blob")
|
||||
local_nonpersistent_flags+=("--blob")
|
||||
flags+=("--help")
|
||||
flags+=("-h")
|
||||
local_nonpersistent_flags+=("--help")
|
||||
@@ -579,23 +596,31 @@ _restic_find()
|
||||
flags+=("--oldest=")
|
||||
two_word_flags+=("-O")
|
||||
local_nonpersistent_flags+=("--oldest=")
|
||||
flags+=("--pack")
|
||||
local_nonpersistent_flags+=("--pack")
|
||||
flags+=("--path=")
|
||||
local_nonpersistent_flags+=("--path=")
|
||||
flags+=("--show-pack-id")
|
||||
local_nonpersistent_flags+=("--show-pack-id")
|
||||
flags+=("--snapshot=")
|
||||
two_word_flags+=("-s")
|
||||
local_nonpersistent_flags+=("--snapshot=")
|
||||
flags+=("--tag=")
|
||||
local_nonpersistent_flags+=("--tag=")
|
||||
flags+=("--tree")
|
||||
local_nonpersistent_flags+=("--tree")
|
||||
flags+=("--cacert=")
|
||||
flags+=("--cache-dir=")
|
||||
flags+=("--cleanup-cache")
|
||||
flags+=("--json")
|
||||
flags+=("--key-hint=")
|
||||
flags+=("--limit-download=")
|
||||
flags+=("--limit-upload=")
|
||||
flags+=("--no-cache")
|
||||
flags+=("--no-lock")
|
||||
flags+=("--option=")
|
||||
two_word_flags+=("-o")
|
||||
flags+=("--password-command=")
|
||||
flags+=("--password-file=")
|
||||
two_word_flags+=("-p")
|
||||
flags+=("--quiet")
|
||||
@@ -649,8 +674,6 @@ _restic_forget()
|
||||
local_nonpersistent_flags+=("--keep-tag=")
|
||||
flags+=("--host=")
|
||||
local_nonpersistent_flags+=("--host=")
|
||||
flags+=("--hostname=")
|
||||
local_nonpersistent_flags+=("--hostname=")
|
||||
flags+=("--tag=")
|
||||
local_nonpersistent_flags+=("--tag=")
|
||||
flags+=("--path=")
|
||||
@@ -673,12 +696,14 @@ _restic_forget()
|
||||
flags+=("--cache-dir=")
|
||||
flags+=("--cleanup-cache")
|
||||
flags+=("--json")
|
||||
flags+=("--key-hint=")
|
||||
flags+=("--limit-download=")
|
||||
flags+=("--limit-upload=")
|
||||
flags+=("--no-cache")
|
||||
flags+=("--no-lock")
|
||||
flags+=("--option=")
|
||||
two_word_flags+=("-o")
|
||||
flags+=("--password-command=")
|
||||
flags+=("--password-file=")
|
||||
two_word_flags+=("-p")
|
||||
flags+=("--quiet")
|
||||
@@ -721,12 +746,14 @@ _restic_generate()
|
||||
flags+=("--cache-dir=")
|
||||
flags+=("--cleanup-cache")
|
||||
flags+=("--json")
|
||||
flags+=("--key-hint=")
|
||||
flags+=("--limit-download=")
|
||||
flags+=("--limit-upload=")
|
||||
flags+=("--no-cache")
|
||||
flags+=("--no-lock")
|
||||
flags+=("--option=")
|
||||
two_word_flags+=("-o")
|
||||
flags+=("--password-command=")
|
||||
flags+=("--password-file=")
|
||||
two_word_flags+=("-p")
|
||||
flags+=("--quiet")
|
||||
@@ -763,12 +790,14 @@ _restic_init()
|
||||
flags+=("--cache-dir=")
|
||||
flags+=("--cleanup-cache")
|
||||
flags+=("--json")
|
||||
flags+=("--key-hint=")
|
||||
flags+=("--limit-download=")
|
||||
flags+=("--limit-upload=")
|
||||
flags+=("--no-cache")
|
||||
flags+=("--no-lock")
|
||||
flags+=("--option=")
|
||||
two_word_flags+=("-o")
|
||||
flags+=("--password-command=")
|
||||
flags+=("--password-file=")
|
||||
two_word_flags+=("-p")
|
||||
flags+=("--quiet")
|
||||
@@ -807,12 +836,14 @@ _restic_key()
|
||||
flags+=("--cache-dir=")
|
||||
flags+=("--cleanup-cache")
|
||||
flags+=("--json")
|
||||
flags+=("--key-hint=")
|
||||
flags+=("--limit-download=")
|
||||
flags+=("--limit-upload=")
|
||||
flags+=("--no-cache")
|
||||
flags+=("--no-lock")
|
||||
flags+=("--option=")
|
||||
two_word_flags+=("-o")
|
||||
flags+=("--password-command=")
|
||||
flags+=("--password-file=")
|
||||
two_word_flags+=("-p")
|
||||
flags+=("--quiet")
|
||||
@@ -849,12 +880,14 @@ _restic_list()
|
||||
flags+=("--cache-dir=")
|
||||
flags+=("--cleanup-cache")
|
||||
flags+=("--json")
|
||||
flags+=("--key-hint=")
|
||||
flags+=("--limit-download=")
|
||||
flags+=("--limit-upload=")
|
||||
flags+=("--no-cache")
|
||||
flags+=("--no-lock")
|
||||
flags+=("--option=")
|
||||
two_word_flags+=("-o")
|
||||
flags+=("--password-command=")
|
||||
flags+=("--password-file=")
|
||||
two_word_flags+=("-p")
|
||||
flags+=("--quiet")
|
||||
@@ -895,18 +928,22 @@ _restic_ls()
|
||||
local_nonpersistent_flags+=("--long")
|
||||
flags+=("--path=")
|
||||
local_nonpersistent_flags+=("--path=")
|
||||
flags+=("--recursive")
|
||||
local_nonpersistent_flags+=("--recursive")
|
||||
flags+=("--tag=")
|
||||
local_nonpersistent_flags+=("--tag=")
|
||||
flags+=("--cacert=")
|
||||
flags+=("--cache-dir=")
|
||||
flags+=("--cleanup-cache")
|
||||
flags+=("--json")
|
||||
flags+=("--key-hint=")
|
||||
flags+=("--limit-download=")
|
||||
flags+=("--limit-upload=")
|
||||
flags+=("--no-cache")
|
||||
flags+=("--no-lock")
|
||||
flags+=("--option=")
|
||||
two_word_flags+=("-o")
|
||||
flags+=("--password-command=")
|
||||
flags+=("--password-file=")
|
||||
two_word_flags+=("-p")
|
||||
flags+=("--quiet")
|
||||
@@ -946,12 +983,14 @@ _restic_migrate()
|
||||
flags+=("--cache-dir=")
|
||||
flags+=("--cleanup-cache")
|
||||
flags+=("--json")
|
||||
flags+=("--key-hint=")
|
||||
flags+=("--limit-download=")
|
||||
flags+=("--limit-upload=")
|
||||
flags+=("--no-cache")
|
||||
flags+=("--no-lock")
|
||||
flags+=("--option=")
|
||||
two_word_flags+=("-o")
|
||||
flags+=("--password-command=")
|
||||
flags+=("--password-file=")
|
||||
two_word_flags+=("-p")
|
||||
flags+=("--quiet")
|
||||
@@ -991,6 +1030,8 @@ _restic_mount()
|
||||
flags+=("--host=")
|
||||
two_word_flags+=("-H")
|
||||
local_nonpersistent_flags+=("--host=")
|
||||
flags+=("--no-default-permissions")
|
||||
local_nonpersistent_flags+=("--no-default-permissions")
|
||||
flags+=("--owner-root")
|
||||
local_nonpersistent_flags+=("--owner-root")
|
||||
flags+=("--path=")
|
||||
@@ -1003,12 +1044,14 @@ _restic_mount()
|
||||
flags+=("--cache-dir=")
|
||||
flags+=("--cleanup-cache")
|
||||
flags+=("--json")
|
||||
flags+=("--key-hint=")
|
||||
flags+=("--limit-download=")
|
||||
flags+=("--limit-upload=")
|
||||
flags+=("--no-cache")
|
||||
flags+=("--no-lock")
|
||||
flags+=("--option=")
|
||||
two_word_flags+=("-o")
|
||||
flags+=("--password-command=")
|
||||
flags+=("--password-file=")
|
||||
two_word_flags+=("-p")
|
||||
flags+=("--quiet")
|
||||
@@ -1045,12 +1088,14 @@ _restic_prune()
|
||||
flags+=("--cache-dir=")
|
||||
flags+=("--cleanup-cache")
|
||||
flags+=("--json")
|
||||
flags+=("--key-hint=")
|
||||
flags+=("--limit-download=")
|
||||
flags+=("--limit-upload=")
|
||||
flags+=("--no-cache")
|
||||
flags+=("--no-lock")
|
||||
flags+=("--option=")
|
||||
two_word_flags+=("-o")
|
||||
flags+=("--password-command=")
|
||||
flags+=("--password-file=")
|
||||
two_word_flags+=("-p")
|
||||
flags+=("--quiet")
|
||||
@@ -1087,12 +1132,58 @@ _restic_rebuild-index()
|
||||
flags+=("--cache-dir=")
|
||||
flags+=("--cleanup-cache")
|
||||
flags+=("--json")
|
||||
flags+=("--key-hint=")
|
||||
flags+=("--limit-download=")
|
||||
flags+=("--limit-upload=")
|
||||
flags+=("--no-cache")
|
||||
flags+=("--no-lock")
|
||||
flags+=("--option=")
|
||||
two_word_flags+=("-o")
|
||||
flags+=("--password-command=")
|
||||
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=()
|
||||
noun_aliases=()
|
||||
}
|
||||
|
||||
_restic_recover()
|
||||
{
|
||||
last_command="restic_recover"
|
||||
|
||||
command_aliases=()
|
||||
|
||||
commands=()
|
||||
|
||||
flags=()
|
||||
two_word_flags=()
|
||||
local_nonpersistent_flags=()
|
||||
flags_with_completion=()
|
||||
flags_completion=()
|
||||
|
||||
flags+=("--help")
|
||||
flags+=("-h")
|
||||
local_nonpersistent_flags+=("--help")
|
||||
flags+=("--cacert=")
|
||||
flags+=("--cache-dir=")
|
||||
flags+=("--cleanup-cache")
|
||||
flags+=("--json")
|
||||
flags+=("--key-hint=")
|
||||
flags+=("--limit-download=")
|
||||
flags+=("--limit-upload=")
|
||||
flags+=("--no-cache")
|
||||
flags+=("--no-lock")
|
||||
flags+=("--option=")
|
||||
two_word_flags+=("-o")
|
||||
flags+=("--password-command=")
|
||||
flags+=("--password-file=")
|
||||
two_word_flags+=("-p")
|
||||
flags+=("--quiet")
|
||||
@@ -1147,12 +1238,60 @@ _restic_restore()
|
||||
flags+=("--cache-dir=")
|
||||
flags+=("--cleanup-cache")
|
||||
flags+=("--json")
|
||||
flags+=("--key-hint=")
|
||||
flags+=("--limit-download=")
|
||||
flags+=("--limit-upload=")
|
||||
flags+=("--no-cache")
|
||||
flags+=("--no-lock")
|
||||
flags+=("--option=")
|
||||
two_word_flags+=("-o")
|
||||
flags+=("--password-command=")
|
||||
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=()
|
||||
noun_aliases=()
|
||||
}
|
||||
|
||||
_restic_self-update()
|
||||
{
|
||||
last_command="restic_self-update"
|
||||
|
||||
command_aliases=()
|
||||
|
||||
commands=()
|
||||
|
||||
flags=()
|
||||
two_word_flags=()
|
||||
local_nonpersistent_flags=()
|
||||
flags_with_completion=()
|
||||
flags_completion=()
|
||||
|
||||
flags+=("--help")
|
||||
flags+=("-h")
|
||||
local_nonpersistent_flags+=("--help")
|
||||
flags+=("--output=")
|
||||
local_nonpersistent_flags+=("--output=")
|
||||
flags+=("--cacert=")
|
||||
flags+=("--cache-dir=")
|
||||
flags+=("--cleanup-cache")
|
||||
flags+=("--json")
|
||||
flags+=("--key-hint=")
|
||||
flags+=("--limit-download=")
|
||||
flags+=("--limit-upload=")
|
||||
flags+=("--no-cache")
|
||||
flags+=("--no-lock")
|
||||
flags+=("--option=")
|
||||
two_word_flags+=("-o")
|
||||
flags+=("--password-command=")
|
||||
flags+=("--password-file=")
|
||||
two_word_flags+=("-p")
|
||||
flags+=("--quiet")
|
||||
@@ -1201,12 +1340,14 @@ _restic_snapshots()
|
||||
flags+=("--cache-dir=")
|
||||
flags+=("--cleanup-cache")
|
||||
flags+=("--json")
|
||||
flags+=("--key-hint=")
|
||||
flags+=("--limit-download=")
|
||||
flags+=("--limit-upload=")
|
||||
flags+=("--no-cache")
|
||||
flags+=("--no-lock")
|
||||
flags+=("--option=")
|
||||
two_word_flags+=("-o")
|
||||
flags+=("--password-command=")
|
||||
flags+=("--password-file=")
|
||||
two_word_flags+=("-p")
|
||||
flags+=("--quiet")
|
||||
@@ -1240,6 +1381,7 @@ _restic_stats()
|
||||
flags+=("-h")
|
||||
local_nonpersistent_flags+=("--help")
|
||||
flags+=("--host=")
|
||||
two_word_flags+=("-H")
|
||||
local_nonpersistent_flags+=("--host=")
|
||||
flags+=("--mode=")
|
||||
local_nonpersistent_flags+=("--mode=")
|
||||
@@ -1247,12 +1389,14 @@ _restic_stats()
|
||||
flags+=("--cache-dir=")
|
||||
flags+=("--cleanup-cache")
|
||||
flags+=("--json")
|
||||
flags+=("--key-hint=")
|
||||
flags+=("--limit-download=")
|
||||
flags+=("--limit-upload=")
|
||||
flags+=("--no-cache")
|
||||
flags+=("--no-lock")
|
||||
flags+=("--option=")
|
||||
two_word_flags+=("-o")
|
||||
flags+=("--password-command=")
|
||||
flags+=("--password-file=")
|
||||
two_word_flags+=("-p")
|
||||
flags+=("--quiet")
|
||||
@@ -1302,12 +1446,14 @@ _restic_tag()
|
||||
flags+=("--cache-dir=")
|
||||
flags+=("--cleanup-cache")
|
||||
flags+=("--json")
|
||||
flags+=("--key-hint=")
|
||||
flags+=("--limit-download=")
|
||||
flags+=("--limit-upload=")
|
||||
flags+=("--no-cache")
|
||||
flags+=("--no-lock")
|
||||
flags+=("--option=")
|
||||
two_word_flags+=("-o")
|
||||
flags+=("--password-command=")
|
||||
flags+=("--password-file=")
|
||||
two_word_flags+=("-p")
|
||||
flags+=("--quiet")
|
||||
@@ -1346,12 +1492,14 @@ _restic_unlock()
|
||||
flags+=("--cache-dir=")
|
||||
flags+=("--cleanup-cache")
|
||||
flags+=("--json")
|
||||
flags+=("--key-hint=")
|
||||
flags+=("--limit-download=")
|
||||
flags+=("--limit-upload=")
|
||||
flags+=("--no-cache")
|
||||
flags+=("--no-lock")
|
||||
flags+=("--option=")
|
||||
two_word_flags+=("-o")
|
||||
flags+=("--password-command=")
|
||||
flags+=("--password-file=")
|
||||
two_word_flags+=("-p")
|
||||
flags+=("--quiet")
|
||||
@@ -1388,12 +1536,14 @@ _restic_version()
|
||||
flags+=("--cache-dir=")
|
||||
flags+=("--cleanup-cache")
|
||||
flags+=("--json")
|
||||
flags+=("--key-hint=")
|
||||
flags+=("--limit-download=")
|
||||
flags+=("--limit-upload=")
|
||||
flags+=("--no-cache")
|
||||
flags+=("--no-lock")
|
||||
flags+=("--option=")
|
||||
two_word_flags+=("-o")
|
||||
flags+=("--password-command=")
|
||||
flags+=("--password-file=")
|
||||
two_word_flags+=("-p")
|
||||
flags+=("--quiet")
|
||||
@@ -1433,7 +1583,9 @@ _restic_root_command()
|
||||
commands+=("mount")
|
||||
commands+=("prune")
|
||||
commands+=("rebuild-index")
|
||||
commands+=("recover")
|
||||
commands+=("restore")
|
||||
commands+=("self-update")
|
||||
commands+=("snapshots")
|
||||
commands+=("stats")
|
||||
commands+=("tag")
|
||||
@@ -1453,12 +1605,14 @@ _restic_root_command()
|
||||
flags+=("-h")
|
||||
local_nonpersistent_flags+=("--help")
|
||||
flags+=("--json")
|
||||
flags+=("--key-hint=")
|
||||
flags+=("--limit-download=")
|
||||
flags+=("--limit-upload=")
|
||||
flags+=("--no-cache")
|
||||
flags+=("--no-lock")
|
||||
flags+=("--option=")
|
||||
two_word_flags+=("-o")
|
||||
flags+=("--password-command=")
|
||||
flags+=("--password-file=")
|
||||
two_word_flags+=("-p")
|
||||
flags+=("--quiet")
|
||||
|
@@ -9,8 +9,8 @@ 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
|
||||
<https://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html>`__.
|
||||
Each repository has its own cache sub-directory, consisting of the repository ID
|
||||
which is chosen at ``init``. All cache directories for different repos are
|
||||
independent of each other.
|
||||
|
||||
|
@@ -276,7 +276,7 @@ the IV for counter mode and the nonce for Poly1305. This operation needs
|
||||
three keys: A 32 byte for AES-256 for encryption, a 16 byte AES key and
|
||||
a 16 byte key for Poly1305. For details see the original paper `The
|
||||
Poly1305-AES message-authentication
|
||||
code <http://cr.yp.to/mac/poly1305-20050329.pdf>`__ by Dan Bernstein.
|
||||
code <https://cr.yp.to/mac/poly1305-20050329.pdf>`__ by Dan Bernstein.
|
||||
The data is then encrypted with AES-256 and afterwards a message
|
||||
authentication code (MAC) is computed over the ciphertext, everything is
|
||||
then stored as IV \|\| CIPHERTEXT \|\| MAC.
|
||||
|
123
doc/developer_information.rst
Normal file
123
doc/developer_information.rst
Normal file
@@ -0,0 +1,123 @@
|
||||
Developer Information
|
||||
#####################
|
||||
|
||||
Reproducible Builds
|
||||
*******************
|
||||
|
||||
This section describes how to reproduce the official released binaries for
|
||||
restic for version 0.9.3 and later. The binary produced depends on the
|
||||
following things:
|
||||
|
||||
* The source code for the release
|
||||
* The exact version of the official `Go compiler <https://golang.org>`__ used to produce the binaries (running ``restic version`` will print this)
|
||||
* The architecture and operating system the Go compiler runs on (Linux, ``amd64``)
|
||||
* The path where the source code is extracted to (``/restic``)
|
||||
* The path to the Go compiler (``/usr/local/go``)
|
||||
* The build tags (for official binaries, it's the tag ``selfupdate``)
|
||||
* The environment variables (mostly ``$GOOS``, ``$GOARCH``, ``$CGO_ENABLED``)
|
||||
|
||||
In addition, The compressed ZIP files for Windows depends on the modification
|
||||
timestamp of the binary contained in it. In order to reproduce the exact same
|
||||
ZIP file every time, we update the timestamp of the file ``VERSION`` in the
|
||||
source code archive and set the timezone to Europe/Berlin.
|
||||
|
||||
In the following example, we'll use the file ``restic-0.9.3.tar.gz`` and Go
|
||||
1.11.1 to reproduce the released binaries.
|
||||
|
||||
1. Download and extract the Go compiler into ``/usr/local/go``:
|
||||
|
||||
.. code::
|
||||
|
||||
$ cd /usr/local
|
||||
$ curl -L https://dl.google.com/go/go1.11.1.linux-amd64.tar.gz | tar xz
|
||||
|
||||
2. Extract the restic source code into ``/restic``
|
||||
|
||||
.. code::
|
||||
|
||||
$ mkdir /restic
|
||||
$ cd /restic
|
||||
$ TZ=Europe/Berlin curl -L https://github.com/restic/restic/releases/download/v0.9.3/restic-0.9.3.tar.gz | tar xz --strip-components=1
|
||||
|
||||
3. Build the binaries for Windows and Linux:
|
||||
|
||||
.. code::
|
||||
|
||||
$ export PATH=/usr/local/go/bin:$PATH
|
||||
$ go version
|
||||
go version go1.11.1 linux/amd64
|
||||
|
||||
$ GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -mod=vendor -ldflags "-s -w" -tags selfupdate -o restic_linux_amd64 ./cmd/restic
|
||||
$ bzip2 restic_linux_amd64
|
||||
|
||||
$ GOOS=windows GOARCH=amd64 CGO_ENABLED=0 go build -mod=vendor -ldflags "-s -w" -tags selfupdate -o restic_windows_amd64.exe ./cmd/restic
|
||||
$ touch --reference VERSION restic_windows_amd64.exe
|
||||
$ TZ=Europe/Berlin zip -q -X restic_windows_amd64.zip restic_windows_amd64.exe
|
||||
|
||||
Building the Official Binaries
|
||||
******************************
|
||||
|
||||
The released binaries for restic are built using a Docker container. You can
|
||||
find it on `Docker Hub <https://hub.docker.com/r/restic/builder>`__ as
|
||||
``restic/builder``, the ``Dockerfile`` and instructions on how to build the
|
||||
container can be found in the `GitHub repository
|
||||
<https://github.com/restic/builder>`__
|
||||
|
||||
The container serves the following goals:
|
||||
* Have a very controlled environment which is independent from the local system
|
||||
* Make it easy to have the correct version of the Go compiler at the right path
|
||||
* Make it easy to pass in the source code to build at a well-defined path
|
||||
|
||||
The following steps are necessary to build the binaries:
|
||||
|
||||
1. Either build the container (see the instructions in the `repository's README <https://github.com/restic/builder>`__). Alternatively, download the container from the hub:
|
||||
|
||||
.. code::
|
||||
|
||||
docker pull restic/builder
|
||||
|
||||
2. Extract the source code somewhere:
|
||||
|
||||
.. code::
|
||||
|
||||
tar xvzf restic-0.9.3.tar.gz
|
||||
|
||||
3. Create a directory to place the resulting binaries in:
|
||||
|
||||
.. code::
|
||||
|
||||
mkdir output
|
||||
|
||||
3. Mount the source code and the output directory in the container and run the default command, which starts ``helpers/build-release-binaries/main.go``:
|
||||
|
||||
.. code::
|
||||
|
||||
docker run --rm \
|
||||
--volume "$PWD/restic-0.9.3:/restic" \
|
||||
--volume "$PWD/output:/output" \
|
||||
restic/builder
|
||||
|
||||
4. If anything goes wrong, you can enable debug output by specifying the call to ``helpers/build-release-binaries/main.go`` like this:
|
||||
|
||||
.. code::
|
||||
|
||||
docker run --rm \
|
||||
--volume "$PWD/restic-0.9.3:/restic" \
|
||||
--volume "$PWD/output:/output" \
|
||||
restic/builder \
|
||||
go run -mod=vendor helpers/build-release-binaries/main.go --verbose
|
||||
|
||||
Prepare a New Release
|
||||
*********************
|
||||
|
||||
Publishing a new release of restic requires many different steps. We've
|
||||
automated this in the Go program ``helpers/prepare-release/main.go`` which also
|
||||
includes checking that e.g. the changelog is correctly generated. The only
|
||||
required argument is the new version number (in `Semantic Versioning
|
||||
<https://semver.org/>`__ format ``MAJOR.MINOR.PATCH``):
|
||||
|
||||
.. code::
|
||||
|
||||
go run -mod=vendor helpers/prepare-release/main.go 0.9.3
|
||||
|
||||
Checks can be skipped on demand via flags, please see ``--help`` for details.
|
50
doc/faq.rst
50
doc/faq.rst
@@ -27,6 +27,54 @@ strictly necessary. With high probability this is duplicate data. In
|
||||
order to clean it up, the command ``restic prune`` can be used. The
|
||||
cause of this bug is not yet known.
|
||||
|
||||
I ran a ``restic`` command but it is not working as intended, what do I do now?
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
If you are running a restic command and it is not working as you hoped it would,
|
||||
there is an easy way of checking how your shell interpreted the command you are trying to run.
|
||||
|
||||
Here is an example of a mistake in a backup command that results in the command not working as expected.
|
||||
A user wants to run the following ``restic backup`` command
|
||||
|
||||
::
|
||||
|
||||
$ restic backup --exclude "~/documents" ~
|
||||
|
||||
.. important:: This command contains an intentional user error described in this paragraph.
|
||||
|
||||
This command will result in a complete backup of the current logged in user's home directory and it won't exclude the folder ``~/documents/`` - which is not what the user wanted to achieve.
|
||||
The problem is how the path to ``~/documents`` is passed to restic.
|
||||
|
||||
In order to spot an issue like this, you can make use of the following ruby command preceding your restic command.
|
||||
|
||||
::
|
||||
|
||||
$ ruby -e 'puts ARGV.inspect' restic backup --exclude "~/documents" ~
|
||||
["restic", "backup", "--exclude", "~/documents", "/home/john"]
|
||||
|
||||
As you can see, the command outputs every argument you have passed to the shell. This is what restic sees when you run your command.
|
||||
The error here is that the tilde ``~`` in ``"~/documents"`` didn't get expanded as it is quoted.
|
||||
|
||||
::
|
||||
|
||||
$ echo ~/documents
|
||||
/home/john/documents
|
||||
|
||||
$ echo "~/documents"
|
||||
~/document
|
||||
|
||||
$ echo "$HOME/documents"
|
||||
/home/john/documents
|
||||
|
||||
Restic handles globbing and expansion in the following ways:
|
||||
|
||||
- Globbing is only expanded for lines read via ``--files-from``
|
||||
- Environment variables are not expanded in the file read via ``--files-from``
|
||||
- ``*`` is expanded for paths read via ``--files-from``
|
||||
- e.g. For backup targets given to restic as arguments on the shell, neither glob expansion nor shell variable replacement is done. If restic is called as ``restic backup '*' '$HOME'``, it will try to backup the literal file(s)/dir(s) ``*`` and ``$HOME``
|
||||
- Double-asterisk ``**`` only works in exclude patterns as this is a custom extension built into restic; the shell must not expand it
|
||||
|
||||
|
||||
How can I specify encryption passwords automatically?
|
||||
-----------------------------------------------------
|
||||
|
||||
@@ -124,4 +172,4 @@ The following may work:
|
||||
Why does restic perform so poorly on Windows?
|
||||
---------------------------------------------
|
||||
|
||||
In some cases the realtime protection of antivirus software can interfere with restic's operations. If you are experiencing bad performace you can try to temporarily disable your antivirus software to find out if it is the cause for your performance problems.
|
||||
In some cases the real-time protection of antivirus software can interfere with restic's operations. If you are experiencing bad performance you can try to temporarily disable your antivirus software to find out if it is the cause for your performance problems.
|
||||
|
@@ -19,3 +19,4 @@ Restic Documentation
|
||||
110_talks
|
||||
faq
|
||||
manual_rest
|
||||
developer_information
|
||||
|
@@ -37,8 +37,8 @@ given as the arguments.
|
||||
takes filename[:header], exclude contents of directories containing filename (except filename itself) if header of that file is as provided (can be specified multiple times)
|
||||
|
||||
.PP
|
||||
\fB\-\-files\-from\fP=""
|
||||
read the files to backup from file (can be combined with file args)
|
||||
\fB\-\-files\-from\fP=[]
|
||||
read the files to backup from file (can be combined with file args/can be specified multiple times)
|
||||
|
||||
.PP
|
||||
\fB\-f\fP, \fB\-\-force\fP[=false]
|
||||
@@ -49,7 +49,7 @@ given as the arguments.
|
||||
help for backup
|
||||
|
||||
.PP
|
||||
\fB\-\-hostname\fP=""
|
||||
\fB\-H\fP, \fB\-\-host\fP=""
|
||||
set the \fB\fChostname\fR for the snapshot manually. To prevent an expensive rescan use the "parent" flag
|
||||
|
||||
.PP
|
||||
@@ -88,7 +88,7 @@ given as the arguments.
|
||||
|
||||
.PP
|
||||
\fB\-\-cache\-dir\fP=""
|
||||
set the cache directory
|
||||
set the cache directory. (default: use system default cache directory)
|
||||
|
||||
.PP
|
||||
\fB\-\-cleanup\-cache\fP[=false]
|
||||
@@ -98,6 +98,10 @@ given as the arguments.
|
||||
\fB\-\-json\fP[=false]
|
||||
set output mode to JSON for commands that support it
|
||||
|
||||
.PP
|
||||
\fB\-\-key\-hint\fP=""
|
||||
key ID of key to try decrypting first (default: $RESTIC\_KEY\_HINT)
|
||||
|
||||
.PP
|
||||
\fB\-\-limit\-download\fP=0
|
||||
limits downloads to a maximum rate in KiB/s. (default: unlimited)
|
||||
@@ -118,6 +122,10 @@ given as the arguments.
|
||||
\fB\-o\fP, \fB\-\-option\fP=[]
|
||||
set extended option (\fB\fCkey=value\fR, can be specified multiple times)
|
||||
|
||||
.PP
|
||||
\fB\-\-password\-command\fP=""
|
||||
specify a shell command to obtain a password (default: $RESTIC\_PASSWORD\_COMMAND)
|
||||
|
||||
.PP
|
||||
\fB\-p\fP, \fB\-\-password\-file\fP=""
|
||||
read the repository password from a file (default: $RESTIC\_PASSWORD\_FILE)
|
||||
|
@@ -31,6 +31,10 @@ The "cache" command allows listing and cleaning local cache directories.
|
||||
\fB\-\-max\-age\fP=30
|
||||
max age in \fB\fCdays\fR for cache directories to be considered old
|
||||
|
||||
.PP
|
||||
\fB\-\-no\-size\fP[=false]
|
||||
do not output the size of the cache directories
|
||||
|
||||
|
||||
.SH OPTIONS INHERITED FROM PARENT COMMANDS
|
||||
.PP
|
||||
@@ -39,7 +43,7 @@ The "cache" command allows listing and cleaning local cache directories.
|
||||
|
||||
.PP
|
||||
\fB\-\-cache\-dir\fP=""
|
||||
set the cache directory
|
||||
set the cache directory. (default: use system default cache directory)
|
||||
|
||||
.PP
|
||||
\fB\-\-cleanup\-cache\fP[=false]
|
||||
@@ -49,6 +53,10 @@ The "cache" command allows listing and cleaning local cache directories.
|
||||
\fB\-\-json\fP[=false]
|
||||
set output mode to JSON for commands that support it
|
||||
|
||||
.PP
|
||||
\fB\-\-key\-hint\fP=""
|
||||
key ID of key to try decrypting first (default: $RESTIC\_KEY\_HINT)
|
||||
|
||||
.PP
|
||||
\fB\-\-limit\-download\fP=0
|
||||
limits downloads to a maximum rate in KiB/s. (default: unlimited)
|
||||
@@ -69,6 +77,10 @@ The "cache" command allows listing and cleaning local cache directories.
|
||||
\fB\-o\fP, \fB\-\-option\fP=[]
|
||||
set extended option (\fB\fCkey=value\fR, can be specified multiple times)
|
||||
|
||||
.PP
|
||||
\fB\-\-password\-command\fP=""
|
||||
specify a shell command to obtain a password (default: $RESTIC\_PASSWORD\_COMMAND)
|
||||
|
||||
.PP
|
||||
\fB\-p\fP, \fB\-\-password\-file\fP=""
|
||||
read the repository password from a file (default: $RESTIC\_PASSWORD\_FILE)
|
||||
|
@@ -31,7 +31,7 @@ The "cat" command is used to print internal objects to stdout.
|
||||
|
||||
.PP
|
||||
\fB\-\-cache\-dir\fP=""
|
||||
set the cache directory
|
||||
set the cache directory. (default: use system default cache directory)
|
||||
|
||||
.PP
|
||||
\fB\-\-cleanup\-cache\fP[=false]
|
||||
@@ -41,6 +41,10 @@ The "cat" command is used to print internal objects to stdout.
|
||||
\fB\-\-json\fP[=false]
|
||||
set output mode to JSON for commands that support it
|
||||
|
||||
.PP
|
||||
\fB\-\-key\-hint\fP=""
|
||||
key ID of key to try decrypting first (default: $RESTIC\_KEY\_HINT)
|
||||
|
||||
.PP
|
||||
\fB\-\-limit\-download\fP=0
|
||||
limits downloads to a maximum rate in KiB/s. (default: unlimited)
|
||||
@@ -61,6 +65,10 @@ The "cat" command is used to print internal objects to stdout.
|
||||
\fB\-o\fP, \fB\-\-option\fP=[]
|
||||
set extended option (\fB\fCkey=value\fR, can be specified multiple times)
|
||||
|
||||
.PP
|
||||
\fB\-\-password\-command\fP=""
|
||||
specify a shell command to obtain a password (default: $RESTIC\_PASSWORD\_COMMAND)
|
||||
|
||||
.PP
|
||||
\fB\-p\fP, \fB\-\-password\-file\fP=""
|
||||
read the repository password from a file (default: $RESTIC\_PASSWORD\_FILE)
|
||||
|
@@ -52,7 +52,7 @@ repository and not use a local cache.
|
||||
|
||||
.PP
|
||||
\fB\-\-cache\-dir\fP=""
|
||||
set the cache directory
|
||||
set the cache directory. (default: use system default cache directory)
|
||||
|
||||
.PP
|
||||
\fB\-\-cleanup\-cache\fP[=false]
|
||||
@@ -62,6 +62,10 @@ repository and not use a local cache.
|
||||
\fB\-\-json\fP[=false]
|
||||
set output mode to JSON for commands that support it
|
||||
|
||||
.PP
|
||||
\fB\-\-key\-hint\fP=""
|
||||
key ID of key to try decrypting first (default: $RESTIC\_KEY\_HINT)
|
||||
|
||||
.PP
|
||||
\fB\-\-limit\-download\fP=0
|
||||
limits downloads to a maximum rate in KiB/s. (default: unlimited)
|
||||
@@ -82,6 +86,10 @@ repository and not use a local cache.
|
||||
\fB\-o\fP, \fB\-\-option\fP=[]
|
||||
set extended option (\fB\fCkey=value\fR, can be specified multiple times)
|
||||
|
||||
.PP
|
||||
\fB\-\-password\-command\fP=""
|
||||
specify a shell command to obtain a password (default: $RESTIC\_PASSWORD\_COMMAND)
|
||||
|
||||
.PP
|
||||
\fB\-p\fP, \fB\-\-password\-file\fP=""
|
||||
read the repository password from a file (default: $RESTIC\_PASSWORD\_FILE)
|
||||
|
@@ -21,11 +21,14 @@ directory:
|
||||
|
||||
.RS
|
||||
.IP \(bu 2
|
||||
The item was added
|
||||
+ The item was added
|
||||
.IP \(bu 2
|
||||
\- The item was removed
|
||||
.IP \(bu 2
|
||||
The item was removed
|
||||
U The metadata (access mode, timestamps, ...) for the item was updated
|
||||
.IP \(bu 2
|
||||
M The file's content was modified
|
||||
.IP \(bu 2
|
||||
T The type was changed, e.g. a file was made a symlink
|
||||
|
||||
.RE
|
||||
@@ -48,7 +51,7 @@ T The type was changed, e.g. a file was made a symlink
|
||||
|
||||
.PP
|
||||
\fB\-\-cache\-dir\fP=""
|
||||
set the cache directory
|
||||
set the cache directory. (default: use system default cache directory)
|
||||
|
||||
.PP
|
||||
\fB\-\-cleanup\-cache\fP[=false]
|
||||
@@ -58,6 +61,10 @@ T The type was changed, e.g. a file was made a symlink
|
||||
\fB\-\-json\fP[=false]
|
||||
set output mode to JSON for commands that support it
|
||||
|
||||
.PP
|
||||
\fB\-\-key\-hint\fP=""
|
||||
key ID of key to try decrypting first (default: $RESTIC\_KEY\_HINT)
|
||||
|
||||
.PP
|
||||
\fB\-\-limit\-download\fP=0
|
||||
limits downloads to a maximum rate in KiB/s. (default: unlimited)
|
||||
@@ -78,6 +85,10 @@ T The type was changed, e.g. a file was made a symlink
|
||||
\fB\-o\fP, \fB\-\-option\fP=[]
|
||||
set extended option (\fB\fCkey=value\fR, can be specified multiple times)
|
||||
|
||||
.PP
|
||||
\fB\-\-password\-command\fP=""
|
||||
specify a shell command to obtain a password (default: $RESTIC\_PASSWORD\_COMMAND)
|
||||
|
||||
.PP
|
||||
\fB\-p\fP, \fB\-\-password\-file\fP=""
|
||||
read the repository password from a file (default: $RESTIC\_PASSWORD\_FILE)
|
||||
|
@@ -48,7 +48,7 @@ repository.
|
||||
|
||||
.PP
|
||||
\fB\-\-cache\-dir\fP=""
|
||||
set the cache directory
|
||||
set the cache directory. (default: use system default cache directory)
|
||||
|
||||
.PP
|
||||
\fB\-\-cleanup\-cache\fP[=false]
|
||||
@@ -58,6 +58,10 @@ repository.
|
||||
\fB\-\-json\fP[=false]
|
||||
set output mode to JSON for commands that support it
|
||||
|
||||
.PP
|
||||
\fB\-\-key\-hint\fP=""
|
||||
key ID of key to try decrypting first (default: $RESTIC\_KEY\_HINT)
|
||||
|
||||
.PP
|
||||
\fB\-\-limit\-download\fP=0
|
||||
limits downloads to a maximum rate in KiB/s. (default: unlimited)
|
||||
@@ -78,6 +82,10 @@ repository.
|
||||
\fB\-o\fP, \fB\-\-option\fP=[]
|
||||
set extended option (\fB\fCkey=value\fR, can be specified multiple times)
|
||||
|
||||
.PP
|
||||
\fB\-\-password\-command\fP=""
|
||||
specify a shell command to obtain a password (default: $RESTIC\_PASSWORD\_COMMAND)
|
||||
|
||||
.PP
|
||||
\fB\-p\fP, \fB\-\-password\-file\fP=""
|
||||
read the repository password from a file (default: $RESTIC\_PASSWORD\_FILE)
|
||||
|
@@ -5,21 +5,26 @@
|
||||
|
||||
.SH NAME
|
||||
.PP
|
||||
restic\-find \- Find a file or directory
|
||||
restic\-find \- Find a file, a directory or restic IDs
|
||||
|
||||
|
||||
.SH SYNOPSIS
|
||||
.PP
|
||||
\fBrestic find [flags] PATTERN\fP
|
||||
\fBrestic find [flags] PATTERN...\fP
|
||||
|
||||
|
||||
.SH DESCRIPTION
|
||||
.PP
|
||||
The "find" command searches for files or directories in snapshots stored in the
|
||||
repo.
|
||||
It can also be used to search for restic blobs or trees for troubleshooting.
|
||||
|
||||
|
||||
.SH OPTIONS
|
||||
.PP
|
||||
\fB\-\-blob\fP[=false]
|
||||
pattern is a blob\-ID
|
||||
|
||||
.PP
|
||||
\fB\-h\fP, \fB\-\-help\fP[=false]
|
||||
help for find
|
||||
@@ -44,10 +49,18 @@ repo.
|
||||
\fB\-O\fP, \fB\-\-oldest\fP=""
|
||||
oldest modification date/time
|
||||
|
||||
.PP
|
||||
\fB\-\-pack\fP[=false]
|
||||
pattern is a pack\-ID
|
||||
|
||||
.PP
|
||||
\fB\-\-path\fP=[]
|
||||
only consider snapshots which include this (absolute) \fB\fCpath\fR, when no snapshot\-ID is given
|
||||
|
||||
.PP
|
||||
\fB\-\-show\-pack\-id\fP[=false]
|
||||
display the pack\-ID the blobs belong to (with \-\-blob)
|
||||
|
||||
.PP
|
||||
\fB\-s\fP, \fB\-\-snapshot\fP=[]
|
||||
snapshot \fB\fCid\fR to search in (can be given multiple times)
|
||||
@@ -56,6 +69,10 @@ repo.
|
||||
\fB\-\-tag\fP=[]
|
||||
only consider snapshots which include this \fB\fCtaglist\fR, when no snapshot\-ID is given
|
||||
|
||||
.PP
|
||||
\fB\-\-tree\fP[=false]
|
||||
pattern is a tree\-ID
|
||||
|
||||
|
||||
.SH OPTIONS INHERITED FROM PARENT COMMANDS
|
||||
.PP
|
||||
@@ -64,7 +81,7 @@ repo.
|
||||
|
||||
.PP
|
||||
\fB\-\-cache\-dir\fP=""
|
||||
set the cache directory
|
||||
set the cache directory. (default: use system default cache directory)
|
||||
|
||||
.PP
|
||||
\fB\-\-cleanup\-cache\fP[=false]
|
||||
@@ -74,6 +91,10 @@ repo.
|
||||
\fB\-\-json\fP[=false]
|
||||
set output mode to JSON for commands that support it
|
||||
|
||||
.PP
|
||||
\fB\-\-key\-hint\fP=""
|
||||
key ID of key to try decrypting first (default: $RESTIC\_KEY\_HINT)
|
||||
|
||||
.PP
|
||||
\fB\-\-limit\-download\fP=0
|
||||
limits downloads to a maximum rate in KiB/s. (default: unlimited)
|
||||
@@ -94,6 +115,10 @@ repo.
|
||||
\fB\-o\fP, \fB\-\-option\fP=[]
|
||||
set extended option (\fB\fCkey=value\fR, can be specified multiple times)
|
||||
|
||||
.PP
|
||||
\fB\-\-password\-command\fP=""
|
||||
specify a shell command to obtain a password (default: $RESTIC\_PASSWORD\_COMMAND)
|
||||
|
||||
.PP
|
||||
\fB\-p\fP, \fB\-\-password\-file\fP=""
|
||||
read the repository password from a file (default: $RESTIC\_PASSWORD\_FILE)
|
||||
@@ -115,6 +140,22 @@ repo.
|
||||
be verbose (specify \-\-verbose multiple times or level \fB\fCn\fR)
|
||||
|
||||
|
||||
.SH EXAMPLE
|
||||
.PP
|
||||
.RS
|
||||
|
||||
.nf
|
||||
restic find config.json
|
||||
restic find \-\-json "*.yml" "*.json"
|
||||
restic find \-\-json \-\-blob 420f620f b46ebe8a ddd38656
|
||||
restic find \-\-show\-pack\-id \-\-blob 420f620f
|
||||
restic find \-\-tree 577c2bc9 f81f2e22 a62827a9
|
||||
restic find \-\-pack 025c1d06
|
||||
|
||||
.fi
|
||||
.RE
|
||||
|
||||
|
||||
.SH SEE ALSO
|
||||
.PP
|
||||
\fBrestic(1)\fP
|
||||
|
@@ -48,7 +48,7 @@ data after 'forget' was run successfully, see the 'prune' command.
|
||||
|
||||
.PP
|
||||
\fB\-\-keep\-within\fP=
|
||||
keep snapshots that were created within \fB\fCduration\fR before the newest (e.g. 1y5m7d)
|
||||
keep snapshots that are newer than \fB\fCduration\fR (eg. 1y5m7d2h) relative to the latest snapshot
|
||||
|
||||
.PP
|
||||
\fB\-\-keep\-tag\fP=[]
|
||||
@@ -58,10 +58,6 @@ data after 'forget' was run successfully, see the 'prune' command.
|
||||
\fB\-\-host\fP=""
|
||||
only consider snapshots with the given \fB\fChost\fR
|
||||
|
||||
.PP
|
||||
\fB\-\-hostname\fP=""
|
||||
only consider snapshots with the given \fB\fChostname\fR (deprecated)
|
||||
|
||||
.PP
|
||||
\fB\-\-tag\fP=[]
|
||||
only consider snapshots which include this \fB\fCtaglist\fR in the format \fB\fCtag[,tag,...]\fR (can be specified multiple times)
|
||||
@@ -98,7 +94,7 @@ data after 'forget' was run successfully, see the 'prune' command.
|
||||
|
||||
.PP
|
||||
\fB\-\-cache\-dir\fP=""
|
||||
set the cache directory
|
||||
set the cache directory. (default: use system default cache directory)
|
||||
|
||||
.PP
|
||||
\fB\-\-cleanup\-cache\fP[=false]
|
||||
@@ -108,6 +104,10 @@ data after 'forget' was run successfully, see the 'prune' command.
|
||||
\fB\-\-json\fP[=false]
|
||||
set output mode to JSON for commands that support it
|
||||
|
||||
.PP
|
||||
\fB\-\-key\-hint\fP=""
|
||||
key ID of key to try decrypting first (default: $RESTIC\_KEY\_HINT)
|
||||
|
||||
.PP
|
||||
\fB\-\-limit\-download\fP=0
|
||||
limits downloads to a maximum rate in KiB/s. (default: unlimited)
|
||||
@@ -128,6 +128,10 @@ data after 'forget' was run successfully, see the 'prune' command.
|
||||
\fB\-o\fP, \fB\-\-option\fP=[]
|
||||
set extended option (\fB\fCkey=value\fR, can be specified multiple times)
|
||||
|
||||
.PP
|
||||
\fB\-\-password\-command\fP=""
|
||||
specify a shell command to obtain a password (default: $RESTIC\_PASSWORD\_COMMAND)
|
||||
|
||||
.PP
|
||||
\fB\-p\fP, \fB\-\-password\-file\fP=""
|
||||
read the repository password from a file (default: $RESTIC\_PASSWORD\_FILE)
|
||||
|
@@ -44,7 +44,7 @@ and the auto\-completion files for bash and zsh).
|
||||
|
||||
.PP
|
||||
\fB\-\-cache\-dir\fP=""
|
||||
set the cache directory
|
||||
set the cache directory. (default: use system default cache directory)
|
||||
|
||||
.PP
|
||||
\fB\-\-cleanup\-cache\fP[=false]
|
||||
@@ -54,6 +54,10 @@ and the auto\-completion files for bash and zsh).
|
||||
\fB\-\-json\fP[=false]
|
||||
set output mode to JSON for commands that support it
|
||||
|
||||
.PP
|
||||
\fB\-\-key\-hint\fP=""
|
||||
key ID of key to try decrypting first (default: $RESTIC\_KEY\_HINT)
|
||||
|
||||
.PP
|
||||
\fB\-\-limit\-download\fP=0
|
||||
limits downloads to a maximum rate in KiB/s. (default: unlimited)
|
||||
@@ -74,6 +78,10 @@ and the auto\-completion files for bash and zsh).
|
||||
\fB\-o\fP, \fB\-\-option\fP=[]
|
||||
set extended option (\fB\fCkey=value\fR, can be specified multiple times)
|
||||
|
||||
.PP
|
||||
\fB\-\-password\-command\fP=""
|
||||
specify a shell command to obtain a password (default: $RESTIC\_PASSWORD\_COMMAND)
|
||||
|
||||
.PP
|
||||
\fB\-p\fP, \fB\-\-password\-file\fP=""
|
||||
read the repository password from a file (default: $RESTIC\_PASSWORD\_FILE)
|
||||
|
@@ -31,7 +31,7 @@ The "init" command initializes a new repository.
|
||||
|
||||
.PP
|
||||
\fB\-\-cache\-dir\fP=""
|
||||
set the cache directory
|
||||
set the cache directory. (default: use system default cache directory)
|
||||
|
||||
.PP
|
||||
\fB\-\-cleanup\-cache\fP[=false]
|
||||
@@ -41,6 +41,10 @@ The "init" command initializes a new repository.
|
||||
\fB\-\-json\fP[=false]
|
||||
set output mode to JSON for commands that support it
|
||||
|
||||
.PP
|
||||
\fB\-\-key\-hint\fP=""
|
||||
key ID of key to try decrypting first (default: $RESTIC\_KEY\_HINT)
|
||||
|
||||
.PP
|
||||
\fB\-\-limit\-download\fP=0
|
||||
limits downloads to a maximum rate in KiB/s. (default: unlimited)
|
||||
@@ -61,6 +65,10 @@ The "init" command initializes a new repository.
|
||||
\fB\-o\fP, \fB\-\-option\fP=[]
|
||||
set extended option (\fB\fCkey=value\fR, can be specified multiple times)
|
||||
|
||||
.PP
|
||||
\fB\-\-password\-command\fP=""
|
||||
specify a shell command to obtain a password (default: $RESTIC\_PASSWORD\_COMMAND)
|
||||
|
||||
.PP
|
||||
\fB\-p\fP, \fB\-\-password\-file\fP=""
|
||||
read the repository password from a file (default: $RESTIC\_PASSWORD\_FILE)
|
||||
|
@@ -35,7 +35,7 @@ The "key" command manages keys (passwords) for accessing the repository.
|
||||
|
||||
.PP
|
||||
\fB\-\-cache\-dir\fP=""
|
||||
set the cache directory
|
||||
set the cache directory. (default: use system default cache directory)
|
||||
|
||||
.PP
|
||||
\fB\-\-cleanup\-cache\fP[=false]
|
||||
@@ -45,6 +45,10 @@ The "key" command manages keys (passwords) for accessing the repository.
|
||||
\fB\-\-json\fP[=false]
|
||||
set output mode to JSON for commands that support it
|
||||
|
||||
.PP
|
||||
\fB\-\-key\-hint\fP=""
|
||||
key ID of key to try decrypting first (default: $RESTIC\_KEY\_HINT)
|
||||
|
||||
.PP
|
||||
\fB\-\-limit\-download\fP=0
|
||||
limits downloads to a maximum rate in KiB/s. (default: unlimited)
|
||||
@@ -65,6 +69,10 @@ The "key" command manages keys (passwords) for accessing the repository.
|
||||
\fB\-o\fP, \fB\-\-option\fP=[]
|
||||
set extended option (\fB\fCkey=value\fR, can be specified multiple times)
|
||||
|
||||
.PP
|
||||
\fB\-\-password\-command\fP=""
|
||||
specify a shell command to obtain a password (default: $RESTIC\_PASSWORD\_COMMAND)
|
||||
|
||||
.PP
|
||||
\fB\-p\fP, \fB\-\-password\-file\fP=""
|
||||
read the repository password from a file (default: $RESTIC\_PASSWORD\_FILE)
|
||||
|
@@ -31,7 +31,7 @@ The "list" command allows listing objects in the repository based on type.
|
||||
|
||||
.PP
|
||||
\fB\-\-cache\-dir\fP=""
|
||||
set the cache directory
|
||||
set the cache directory. (default: use system default cache directory)
|
||||
|
||||
.PP
|
||||
\fB\-\-cleanup\-cache\fP[=false]
|
||||
@@ -41,6 +41,10 @@ The "list" command allows listing objects in the repository based on type.
|
||||
\fB\-\-json\fP[=false]
|
||||
set output mode to JSON for commands that support it
|
||||
|
||||
.PP
|
||||
\fB\-\-key\-hint\fP=""
|
||||
key ID of key to try decrypting first (default: $RESTIC\_KEY\_HINT)
|
||||
|
||||
.PP
|
||||
\fB\-\-limit\-download\fP=0
|
||||
limits downloads to a maximum rate in KiB/s. (default: unlimited)
|
||||
@@ -61,6 +65,10 @@ The "list" command allows listing objects in the repository based on type.
|
||||
\fB\-o\fP, \fB\-\-option\fP=[]
|
||||
set extended option (\fB\fCkey=value\fR, can be specified multiple times)
|
||||
|
||||
.PP
|
||||
\fB\-\-password\-command\fP=""
|
||||
specify a shell command to obtain a password (default: $RESTIC\_PASSWORD\_COMMAND)
|
||||
|
||||
.PP
|
||||
\fB\-p\fP, \fB\-\-password\-file\fP=""
|
||||
read the repository password from a file (default: $RESTIC\_PASSWORD\_FILE)
|
||||
|
@@ -10,15 +10,27 @@ restic\-ls \- List files in a snapshot
|
||||
|
||||
.SH SYNOPSIS
|
||||
.PP
|
||||
\fBrestic ls [flags] [snapshot\-ID ...]\fP
|
||||
\fBrestic ls [flags] [snapshotID] [dir...]\fP
|
||||
|
||||
|
||||
.SH DESCRIPTION
|
||||
.PP
|
||||
The "ls" command allows listing files and directories in a snapshot.
|
||||
The "ls" command lists files and directories in a snapshot.
|
||||
|
||||
.PP
|
||||
The special snapshot\-ID "latest" can be used to list files and directories of the latest snapshot in the repository.
|
||||
The special snapshot ID "latest" can be used to list files and
|
||||
directories of the latest snapshot in the repository. The
|
||||
\-\-host flag can be used in conjunction to select the latest
|
||||
snapshot originating from a certain host only.
|
||||
|
||||
.PP
|
||||
File listings can optionally be filtered by directories. Any
|
||||
positional arguments after the snapshot ID are interpreted as
|
||||
absolute directory paths, and only files inside those directories
|
||||
will be listed. If the \-\-recursive flag is used, then the filter
|
||||
will allow traversing into matching directories' subfolders.
|
||||
Any directory paths specified must be absolute (starting with
|
||||
a path separator); paths use the forward slash '/' as separator.
|
||||
|
||||
|
||||
.SH OPTIONS
|
||||
@@ -38,6 +50,10 @@ The special snapshot\-ID "latest" can be used to list files and directories of t
|
||||
\fB\-\-path\fP=[]
|
||||
only consider snapshots which include this (absolute) \fB\fCpath\fR, when no snapshot ID is given
|
||||
|
||||
.PP
|
||||
\fB\-\-recursive\fP[=false]
|
||||
include files in subfolders of the listed directories
|
||||
|
||||
.PP
|
||||
\fB\-\-tag\fP=[]
|
||||
only consider snapshots which include this \fB\fCtaglist\fR, when no snapshot ID is given
|
||||
@@ -50,7 +66,7 @@ The special snapshot\-ID "latest" can be used to list files and directories of t
|
||||
|
||||
.PP
|
||||
\fB\-\-cache\-dir\fP=""
|
||||
set the cache directory
|
||||
set the cache directory. (default: use system default cache directory)
|
||||
|
||||
.PP
|
||||
\fB\-\-cleanup\-cache\fP[=false]
|
||||
@@ -60,6 +76,10 @@ The special snapshot\-ID "latest" can be used to list files and directories of t
|
||||
\fB\-\-json\fP[=false]
|
||||
set output mode to JSON for commands that support it
|
||||
|
||||
.PP
|
||||
\fB\-\-key\-hint\fP=""
|
||||
key ID of key to try decrypting first (default: $RESTIC\_KEY\_HINT)
|
||||
|
||||
.PP
|
||||
\fB\-\-limit\-download\fP=0
|
||||
limits downloads to a maximum rate in KiB/s. (default: unlimited)
|
||||
@@ -80,6 +100,10 @@ The special snapshot\-ID "latest" can be used to list files and directories of t
|
||||
\fB\-o\fP, \fB\-\-option\fP=[]
|
||||
set extended option (\fB\fCkey=value\fR, can be specified multiple times)
|
||||
|
||||
.PP
|
||||
\fB\-\-password\-command\fP=""
|
||||
specify a shell command to obtain a password (default: $RESTIC\_PASSWORD\_COMMAND)
|
||||
|
||||
.PP
|
||||
\fB\-p\fP, \fB\-\-password\-file\fP=""
|
||||
read the repository password from a file (default: $RESTIC\_PASSWORD\_FILE)
|
||||
|
@@ -36,7 +36,7 @@ name is explicitly given, a list of migrations that can be applied is printed.
|
||||
|
||||
.PP
|
||||
\fB\-\-cache\-dir\fP=""
|
||||
set the cache directory
|
||||
set the cache directory. (default: use system default cache directory)
|
||||
|
||||
.PP
|
||||
\fB\-\-cleanup\-cache\fP[=false]
|
||||
@@ -46,6 +46,10 @@ name is explicitly given, a list of migrations that can be applied is printed.
|
||||
\fB\-\-json\fP[=false]
|
||||
set output mode to JSON for commands that support it
|
||||
|
||||
.PP
|
||||
\fB\-\-key\-hint\fP=""
|
||||
key ID of key to try decrypting first (default: $RESTIC\_KEY\_HINT)
|
||||
|
||||
.PP
|
||||
\fB\-\-limit\-download\fP=0
|
||||
limits downloads to a maximum rate in KiB/s. (default: unlimited)
|
||||
@@ -66,6 +70,10 @@ name is explicitly given, a list of migrations that can be applied is printed.
|
||||
\fB\-o\fP, \fB\-\-option\fP=[]
|
||||
set extended option (\fB\fCkey=value\fR, can be specified multiple times)
|
||||
|
||||
.PP
|
||||
\fB\-\-password\-command\fP=""
|
||||
specify a shell command to obtain a password (default: $RESTIC\_PASSWORD\_COMMAND)
|
||||
|
||||
.PP
|
||||
\fB\-p\fP, \fB\-\-password\-file\fP=""
|
||||
read the repository password from a file (default: $RESTIC\_PASSWORD\_FILE)
|
||||
|
@@ -68,6 +68,10 @@ For details please see the documentation for time.Format() at:
|
||||
\fB\-H\fP, \fB\-\-host\fP=""
|
||||
only consider snapshots for this host
|
||||
|
||||
.PP
|
||||
\fB\-\-no\-default\-permissions\fP[=false]
|
||||
for 'allow\-other', ignore Unix permissions and allow users to read all snapshot files
|
||||
|
||||
.PP
|
||||
\fB\-\-owner\-root\fP[=false]
|
||||
use 'root' as the owner of files and dirs
|
||||
@@ -92,7 +96,7 @@ For details please see the documentation for time.Format() at:
|
||||
|
||||
.PP
|
||||
\fB\-\-cache\-dir\fP=""
|
||||
set the cache directory
|
||||
set the cache directory. (default: use system default cache directory)
|
||||
|
||||
.PP
|
||||
\fB\-\-cleanup\-cache\fP[=false]
|
||||
@@ -102,6 +106,10 @@ For details please see the documentation for time.Format() at:
|
||||
\fB\-\-json\fP[=false]
|
||||
set output mode to JSON for commands that support it
|
||||
|
||||
.PP
|
||||
\fB\-\-key\-hint\fP=""
|
||||
key ID of key to try decrypting first (default: $RESTIC\_KEY\_HINT)
|
||||
|
||||
.PP
|
||||
\fB\-\-limit\-download\fP=0
|
||||
limits downloads to a maximum rate in KiB/s. (default: unlimited)
|
||||
@@ -122,6 +130,10 @@ For details please see the documentation for time.Format() at:
|
||||
\fB\-o\fP, \fB\-\-option\fP=[]
|
||||
set extended option (\fB\fCkey=value\fR, can be specified multiple times)
|
||||
|
||||
.PP
|
||||
\fB\-\-password\-command\fP=""
|
||||
specify a shell command to obtain a password (default: $RESTIC\_PASSWORD\_COMMAND)
|
||||
|
||||
.PP
|
||||
\fB\-p\fP, \fB\-\-password\-file\fP=""
|
||||
read the repository password from a file (default: $RESTIC\_PASSWORD\_FILE)
|
||||
|
@@ -32,7 +32,7 @@ referenced and therefore not needed any more.
|
||||
|
||||
.PP
|
||||
\fB\-\-cache\-dir\fP=""
|
||||
set the cache directory
|
||||
set the cache directory. (default: use system default cache directory)
|
||||
|
||||
.PP
|
||||
\fB\-\-cleanup\-cache\fP[=false]
|
||||
@@ -42,6 +42,10 @@ referenced and therefore not needed any more.
|
||||
\fB\-\-json\fP[=false]
|
||||
set output mode to JSON for commands that support it
|
||||
|
||||
.PP
|
||||
\fB\-\-key\-hint\fP=""
|
||||
key ID of key to try decrypting first (default: $RESTIC\_KEY\_HINT)
|
||||
|
||||
.PP
|
||||
\fB\-\-limit\-download\fP=0
|
||||
limits downloads to a maximum rate in KiB/s. (default: unlimited)
|
||||
@@ -62,6 +66,10 @@ referenced and therefore not needed any more.
|
||||
\fB\-o\fP, \fB\-\-option\fP=[]
|
||||
set extended option (\fB\fCkey=value\fR, can be specified multiple times)
|
||||
|
||||
.PP
|
||||
\fB\-\-password\-command\fP=""
|
||||
specify a shell command to obtain a password (default: $RESTIC\_PASSWORD\_COMMAND)
|
||||
|
||||
.PP
|
||||
\fB\-p\fP, \fB\-\-password\-file\fP=""
|
||||
read the repository password from a file (default: $RESTIC\_PASSWORD\_FILE)
|
||||
|
@@ -32,7 +32,7 @@ repository.
|
||||
|
||||
.PP
|
||||
\fB\-\-cache\-dir\fP=""
|
||||
set the cache directory
|
||||
set the cache directory. (default: use system default cache directory)
|
||||
|
||||
.PP
|
||||
\fB\-\-cleanup\-cache\fP[=false]
|
||||
@@ -42,6 +42,10 @@ repository.
|
||||
\fB\-\-json\fP[=false]
|
||||
set output mode to JSON for commands that support it
|
||||
|
||||
.PP
|
||||
\fB\-\-key\-hint\fP=""
|
||||
key ID of key to try decrypting first (default: $RESTIC\_KEY\_HINT)
|
||||
|
||||
.PP
|
||||
\fB\-\-limit\-download\fP=0
|
||||
limits downloads to a maximum rate in KiB/s. (default: unlimited)
|
||||
@@ -62,6 +66,10 @@ repository.
|
||||
\fB\-o\fP, \fB\-\-option\fP=[]
|
||||
set extended option (\fB\fCkey=value\fR, can be specified multiple times)
|
||||
|
||||
.PP
|
||||
\fB\-\-password\-command\fP=""
|
||||
specify a shell command to obtain a password (default: $RESTIC\_PASSWORD\_COMMAND)
|
||||
|
||||
.PP
|
||||
\fB\-p\fP, \fB\-\-password\-file\fP=""
|
||||
read the repository password from a file (default: $RESTIC\_PASSWORD\_FILE)
|
||||
|
97
doc/man/restic-recover.1
Normal file
97
doc/man/restic-recover.1
Normal file
@@ -0,0 +1,97 @@
|
||||
.TH "restic backup" "1" "Jan 2017" "generated by `restic generate`" ""
|
||||
.nh
|
||||
.ad l
|
||||
|
||||
|
||||
.SH NAME
|
||||
.PP
|
||||
restic\-recover \- Recover data from the repository
|
||||
|
||||
|
||||
.SH SYNOPSIS
|
||||
.PP
|
||||
\fBrestic recover [flags]\fP
|
||||
|
||||
|
||||
.SH DESCRIPTION
|
||||
.PP
|
||||
The "recover" command build a new snapshot from all directories it can find in
|
||||
the raw data of the repository. It can be used if, for example, a snapshot has
|
||||
been removed by accident with "forget".
|
||||
|
||||
|
||||
.SH OPTIONS
|
||||
.PP
|
||||
\fB\-h\fP, \fB\-\-help\fP[=false]
|
||||
help for recover
|
||||
|
||||
|
||||
.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. (default: use system default 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\-\-key\-hint\fP=""
|
||||
key ID of key to try decrypting first (default: $RESTIC\_KEY\_HINT)
|
||||
|
||||
.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\-\-password\-command\fP=""
|
||||
specify a shell command to obtain a password (default: $RESTIC\_PASSWORD\_COMMAND)
|
||||
|
||||
.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
|
@@ -64,7 +64,7 @@ repository.
|
||||
|
||||
.PP
|
||||
\fB\-\-cache\-dir\fP=""
|
||||
set the cache directory
|
||||
set the cache directory. (default: use system default cache directory)
|
||||
|
||||
.PP
|
||||
\fB\-\-cleanup\-cache\fP[=false]
|
||||
@@ -74,6 +74,10 @@ repository.
|
||||
\fB\-\-json\fP[=false]
|
||||
set output mode to JSON for commands that support it
|
||||
|
||||
.PP
|
||||
\fB\-\-key\-hint\fP=""
|
||||
key ID of key to try decrypting first (default: $RESTIC\_KEY\_HINT)
|
||||
|
||||
.PP
|
||||
\fB\-\-limit\-download\fP=0
|
||||
limits downloads to a maximum rate in KiB/s. (default: unlimited)
|
||||
@@ -94,6 +98,10 @@ repository.
|
||||
\fB\-o\fP, \fB\-\-option\fP=[]
|
||||
set extended option (\fB\fCkey=value\fR, can be specified multiple times)
|
||||
|
||||
.PP
|
||||
\fB\-\-password\-command\fP=""
|
||||
specify a shell command to obtain a password (default: $RESTIC\_PASSWORD\_COMMAND)
|
||||
|
||||
.PP
|
||||
\fB\-p\fP, \fB\-\-password\-file\fP=""
|
||||
read the repository password from a file (default: $RESTIC\_PASSWORD\_FILE)
|
||||
|
102
doc/man/restic-self-update.1
Normal file
102
doc/man/restic-self-update.1
Normal file
@@ -0,0 +1,102 @@
|
||||
.TH "restic backup" "1" "Jan 2017" "generated by `restic generate`" ""
|
||||
.nh
|
||||
.ad l
|
||||
|
||||
|
||||
.SH NAME
|
||||
.PP
|
||||
restic\-self\-update \- Update the restic binary
|
||||
|
||||
|
||||
.SH SYNOPSIS
|
||||
.PP
|
||||
\fBrestic self\-update [flags]\fP
|
||||
|
||||
|
||||
.SH DESCRIPTION
|
||||
.PP
|
||||
The command "self\-update" downloads the latest stable release of restic from
|
||||
GitHub and replaces the currently running binary. After download, the
|
||||
authenticity of the binary is verified using the GPG signature on the release
|
||||
files.
|
||||
|
||||
|
||||
.SH OPTIONS
|
||||
.PP
|
||||
\fB\-h\fP, \fB\-\-help\fP[=false]
|
||||
help for self\-update
|
||||
|
||||
.PP
|
||||
\fB\-\-output\fP=""
|
||||
Save the downloaded file as \fB\fCfilename\fR (default: running binary itself)
|
||||
|
||||
|
||||
.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. (default: use system default 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\-\-key\-hint\fP=""
|
||||
key ID of key to try decrypting first (default: $RESTIC\_KEY\_HINT)
|
||||
|
||||
.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\-\-password\-command\fP=""
|
||||
specify a shell command to obtain a password (default: $RESTIC\_PASSWORD\_COMMAND)
|
||||
|
||||
.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
|
@@ -51,7 +51,7 @@ The "snapshots" command lists all snapshots stored in the repository.
|
||||
|
||||
.PP
|
||||
\fB\-\-cache\-dir\fP=""
|
||||
set the cache directory
|
||||
set the cache directory. (default: use system default cache directory)
|
||||
|
||||
.PP
|
||||
\fB\-\-cleanup\-cache\fP[=false]
|
||||
@@ -61,6 +61,10 @@ The "snapshots" command lists all snapshots stored in the repository.
|
||||
\fB\-\-json\fP[=false]
|
||||
set output mode to JSON for commands that support it
|
||||
|
||||
.PP
|
||||
\fB\-\-key\-hint\fP=""
|
||||
key ID of key to try decrypting first (default: $RESTIC\_KEY\_HINT)
|
||||
|
||||
.PP
|
||||
\fB\-\-limit\-download\fP=0
|
||||
limits downloads to a maximum rate in KiB/s. (default: unlimited)
|
||||
@@ -81,6 +85,10 @@ The "snapshots" command lists all snapshots stored in the repository.
|
||||
\fB\-o\fP, \fB\-\-option\fP=[]
|
||||
set extended option (\fB\fCkey=value\fR, can be specified multiple times)
|
||||
|
||||
.PP
|
||||
\fB\-\-password\-command\fP=""
|
||||
specify a shell command to obtain a password (default: $RESTIC\_PASSWORD\_COMMAND)
|
||||
|
||||
.PP
|
||||
\fB\-p\fP, \fB\-\-password\-file\fP=""
|
||||
read the repository password from a file (default: $RESTIC\_PASSWORD\_FILE)
|
||||
|
@@ -29,23 +29,22 @@ to calculate.
|
||||
.PP
|
||||
The modes are:
|
||||
|
||||
.PP
|
||||
.RS
|
||||
.IP \(bu 2
|
||||
restore\-size: (default) Counts the size of the restored files.
|
||||
|
||||
.PP
|
||||
.IP \(bu 2
|
||||
files\-by\-contents: Counts total size of files, where a file is
|
||||
considered unique if it has unique contents.
|
||||
|
||||
.PP
|
||||
raw\-data: Counts the size of blobs in the repository, regardless
|
||||
of how many files reference them.
|
||||
|
||||
.PP
|
||||
considered unique if it has unique contents.
|
||||
.IP \(bu 2
|
||||
raw\-data: Counts the size of blobs in the repository, regardless of
|
||||
how many files reference them.
|
||||
.IP \(bu 2
|
||||
blobs\-per\-file: A combination of files\-by\-contents and raw\-data.
|
||||
|
||||
.PP
|
||||
.IP \(bu 2
|
||||
Refer to the online manual for more details about each mode.
|
||||
|
||||
.RE
|
||||
|
||||
|
||||
.SH OPTIONS
|
||||
.PP
|
||||
@@ -53,7 +52,7 @@ Refer to the online manual for more details about each mode.
|
||||
help for stats
|
||||
|
||||
.PP
|
||||
\fB\-\-host\fP=""
|
||||
\fB\-H\fP, \fB\-\-host\fP=""
|
||||
filter latest snapshot by this hostname
|
||||
|
||||
.PP
|
||||
@@ -68,7 +67,7 @@ Refer to the online manual for more details about each mode.
|
||||
|
||||
.PP
|
||||
\fB\-\-cache\-dir\fP=""
|
||||
set the cache directory
|
||||
set the cache directory. (default: use system default cache directory)
|
||||
|
||||
.PP
|
||||
\fB\-\-cleanup\-cache\fP[=false]
|
||||
@@ -78,6 +77,10 @@ Refer to the online manual for more details about each mode.
|
||||
\fB\-\-json\fP[=false]
|
||||
set output mode to JSON for commands that support it
|
||||
|
||||
.PP
|
||||
\fB\-\-key\-hint\fP=""
|
||||
key ID of key to try decrypting first (default: $RESTIC\_KEY\_HINT)
|
||||
|
||||
.PP
|
||||
\fB\-\-limit\-download\fP=0
|
||||
limits downloads to a maximum rate in KiB/s. (default: unlimited)
|
||||
@@ -98,6 +101,10 @@ Refer to the online manual for more details about each mode.
|
||||
\fB\-o\fP, \fB\-\-option\fP=[]
|
||||
set extended option (\fB\fCkey=value\fR, can be specified multiple times)
|
||||
|
||||
.PP
|
||||
\fB\-\-password\-command\fP=""
|
||||
specify a shell command to obtain a password (default: $RESTIC\_PASSWORD\_COMMAND)
|
||||
|
||||
.PP
|
||||
\fB\-p\fP, \fB\-\-password\-file\fP=""
|
||||
read the repository password from a file (default: $RESTIC\_PASSWORD\_FILE)
|
||||
|
@@ -62,7 +62,7 @@ When no snapshot\-ID is given, all snapshots matching the host, tag and path fil
|
||||
|
||||
.PP
|
||||
\fB\-\-cache\-dir\fP=""
|
||||
set the cache directory
|
||||
set the cache directory. (default: use system default cache directory)
|
||||
|
||||
.PP
|
||||
\fB\-\-cleanup\-cache\fP[=false]
|
||||
@@ -72,6 +72,10 @@ When no snapshot\-ID is given, all snapshots matching the host, tag and path fil
|
||||
\fB\-\-json\fP[=false]
|
||||
set output mode to JSON for commands that support it
|
||||
|
||||
.PP
|
||||
\fB\-\-key\-hint\fP=""
|
||||
key ID of key to try decrypting first (default: $RESTIC\_KEY\_HINT)
|
||||
|
||||
.PP
|
||||
\fB\-\-limit\-download\fP=0
|
||||
limits downloads to a maximum rate in KiB/s. (default: unlimited)
|
||||
@@ -92,6 +96,10 @@ When no snapshot\-ID is given, all snapshots matching the host, tag and path fil
|
||||
\fB\-o\fP, \fB\-\-option\fP=[]
|
||||
set extended option (\fB\fCkey=value\fR, can be specified multiple times)
|
||||
|
||||
.PP
|
||||
\fB\-\-password\-command\fP=""
|
||||
specify a shell command to obtain a password (default: $RESTIC\_PASSWORD\_COMMAND)
|
||||
|
||||
.PP
|
||||
\fB\-p\fP, \fB\-\-password\-file\fP=""
|
||||
read the repository password from a file (default: $RESTIC\_PASSWORD\_FILE)
|
||||
|
@@ -35,7 +35,7 @@ The "unlock" command removes stale locks that have been created by other restic
|
||||
|
||||
.PP
|
||||
\fB\-\-cache\-dir\fP=""
|
||||
set the cache directory
|
||||
set the cache directory. (default: use system default cache directory)
|
||||
|
||||
.PP
|
||||
\fB\-\-cleanup\-cache\fP[=false]
|
||||
@@ -45,6 +45,10 @@ The "unlock" command removes stale locks that have been created by other restic
|
||||
\fB\-\-json\fP[=false]
|
||||
set output mode to JSON for commands that support it
|
||||
|
||||
.PP
|
||||
\fB\-\-key\-hint\fP=""
|
||||
key ID of key to try decrypting first (default: $RESTIC\_KEY\_HINT)
|
||||
|
||||
.PP
|
||||
\fB\-\-limit\-download\fP=0
|
||||
limits downloads to a maximum rate in KiB/s. (default: unlimited)
|
||||
@@ -65,6 +69,10 @@ The "unlock" command removes stale locks that have been created by other restic
|
||||
\fB\-o\fP, \fB\-\-option\fP=[]
|
||||
set extended option (\fB\fCkey=value\fR, can be specified multiple times)
|
||||
|
||||
.PP
|
||||
\fB\-\-password\-command\fP=""
|
||||
specify a shell command to obtain a password (default: $RESTIC\_PASSWORD\_COMMAND)
|
||||
|
||||
.PP
|
||||
\fB\-p\fP, \fB\-\-password\-file\fP=""
|
||||
read the repository password from a file (default: $RESTIC\_PASSWORD\_FILE)
|
||||
|
@@ -32,7 +32,7 @@ and the version of this software.
|
||||
|
||||
.PP
|
||||
\fB\-\-cache\-dir\fP=""
|
||||
set the cache directory
|
||||
set the cache directory. (default: use system default cache directory)
|
||||
|
||||
.PP
|
||||
\fB\-\-cleanup\-cache\fP[=false]
|
||||
@@ -42,6 +42,10 @@ and the version of this software.
|
||||
\fB\-\-json\fP[=false]
|
||||
set output mode to JSON for commands that support it
|
||||
|
||||
.PP
|
||||
\fB\-\-key\-hint\fP=""
|
||||
key ID of key to try decrypting first (default: $RESTIC\_KEY\_HINT)
|
||||
|
||||
.PP
|
||||
\fB\-\-limit\-download\fP=0
|
||||
limits downloads to a maximum rate in KiB/s. (default: unlimited)
|
||||
@@ -62,6 +66,10 @@ and the version of this software.
|
||||
\fB\-o\fP, \fB\-\-option\fP=[]
|
||||
set extended option (\fB\fCkey=value\fR, can be specified multiple times)
|
||||
|
||||
.PP
|
||||
\fB\-\-password\-command\fP=""
|
||||
specify a shell command to obtain a password (default: $RESTIC\_PASSWORD\_COMMAND)
|
||||
|
||||
.PP
|
||||
\fB\-p\fP, \fB\-\-password\-file\fP=""
|
||||
read the repository password from a file (default: $RESTIC\_PASSWORD\_FILE)
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user