mirror of
https://github.com/restic/restic.git
synced 2025-08-24 22:47:30 +00:00
Compare commits
212 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
303210aa08 | ||
![]() |
c029881379 | ||
![]() |
6e89963c21 | ||
![]() |
1ac560181b | ||
![]() |
18ec27a0da | ||
![]() |
b40dea29ad | ||
![]() |
0561155963 | ||
![]() |
1aafc17212 | ||
![]() |
f11789c437 | ||
![]() |
8cab0c121d | ||
![]() |
5979414bcd | ||
![]() |
cc8b690b52 | ||
![]() |
a164dc9391 | ||
![]() |
9a26be4e5b | ||
![]() |
733519d895 | ||
![]() |
3d5a0c799b | ||
![]() |
c4475ac58f | ||
![]() |
c9fd9b5275 | ||
![]() |
cadcab5a19 | ||
![]() |
5ac9c1157a | ||
![]() |
5715517e29 | ||
![]() |
ecc2458de8 | ||
![]() |
2c6ba5d9ac | ||
![]() |
0cc3647e51 | ||
![]() |
6b700d02f5 | ||
![]() |
2b09a10234 | ||
![]() |
1c87d01bad | ||
![]() |
78a3ffcfb9 | ||
![]() |
4d77c0c21c | ||
![]() |
fb064afa34 | ||
![]() |
7304738872 | ||
![]() |
66efa425bf | ||
![]() |
d51e9d1b98 | ||
![]() |
e046428c94 | ||
![]() |
75906edef5 | ||
![]() |
203d775190 | ||
![]() |
ecd7ee85e8 | ||
![]() |
2022355800 | ||
![]() |
36f22a0feb | ||
![]() |
f58a44b911 | ||
![]() |
fe886a6439 | ||
![]() |
be23313072 | ||
![]() |
3c112d9cae | ||
![]() |
2970e38d92 | ||
![]() |
870e7583a1 | ||
![]() |
db1c835c37 | ||
![]() |
190bed9908 | ||
![]() |
85f4c826db | ||
![]() |
5da4b0fc7d | ||
![]() |
c1058005c3 | ||
![]() |
ca73808649 | ||
![]() |
f2ea91df38 | ||
![]() |
15cc4d74b2 | ||
![]() |
bf9a507148 | ||
![]() |
65b476ead9 | ||
![]() |
aaa1cc2c26 | ||
![]() |
95434cff16 | ||
![]() |
1b94ae1c00 | ||
![]() |
d138b38f28 | ||
![]() |
db8f5864fc | ||
![]() |
1d8b21cdad | ||
![]() |
3865b59716 | ||
![]() |
7b8d1dc040 | ||
![]() |
d19a29f79e | ||
![]() |
449c049ce9 | ||
![]() |
9f436d80e1 | ||
![]() |
e277a92a2f | ||
![]() |
d9e22c2df1 | ||
![]() |
4b0fb5af36 | ||
![]() |
7519c73987 | ||
![]() |
45a48eb4a8 | ||
![]() |
a2f30cde4c | ||
![]() |
6ebcfe7c18 | ||
![]() |
0022926eba | ||
![]() |
3e3a0220ec | ||
![]() |
c125fb763d | ||
![]() |
b9f0f031b6 | ||
![]() |
aa7043151a | ||
![]() |
ebf22a35f4 | ||
![]() |
3f069ac404 | ||
![]() |
56e5467096 | ||
![]() |
5ee932a124 | ||
![]() |
fed25714a4 | ||
![]() |
8906d85ab8 | ||
![]() |
97aafc1eec | ||
![]() |
6a5c9f57c2 | ||
![]() |
6cf13483b5 | ||
![]() |
f645306a18 | ||
![]() |
186e10e0cb | ||
![]() |
29a5bd5b30 | ||
![]() |
06a01bc016 | ||
![]() |
cdc287a7f6 | ||
![]() |
deedc38129 | ||
![]() |
1107eef215 | ||
![]() |
60c7020bcb | ||
![]() |
b96ef48562 | ||
![]() |
cd9b2295f1 | ||
![]() |
a439cdeb05 | ||
![]() |
827f6d7b24 | ||
![]() |
77ab10d401 | ||
![]() |
3b0ad2e368 | ||
![]() |
2996c110f1 | ||
![]() |
4609b5c24d | ||
![]() |
830511460a | ||
![]() |
0dc3648416 | ||
![]() |
d71dba3788 | ||
![]() |
e482633943 | ||
![]() |
900621051a | ||
![]() |
1f246c5309 | ||
![]() |
e40805b002 | ||
![]() |
6f69ae1b8d | ||
![]() |
c4fbf2c779 | ||
![]() |
7c084014fa | ||
![]() |
d65bea1b2a | ||
![]() |
3b68acf853 | ||
![]() |
82a70643a2 | ||
![]() |
0dd805421e | ||
![]() |
16b82f4b1d | ||
![]() |
7a6bfcd58c | ||
![]() |
de54618852 | ||
![]() |
98526b8dbe | ||
![]() |
0083680d33 | ||
![]() |
05222b7343 | ||
![]() |
d4ff5b6bf4 | ||
![]() |
cf0883e16c | ||
![]() |
a35a24b8b4 | ||
![]() |
df7f72cdde | ||
![]() |
3edc723bf0 | ||
![]() |
71891b340c | ||
![]() |
6f5c3e57f6 | ||
![]() |
56af0ce370 | ||
![]() |
c9745cd47e | ||
![]() |
2434ab2106 | ||
![]() |
1688713400 | ||
![]() |
00597284de | ||
![]() |
879f6e0c81 | ||
![]() |
8a97bb8661 | ||
![]() |
5fe6de219d | ||
![]() |
c13f79da02 | ||
![]() |
db82e6b80c | ||
![]() |
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 | ||
![]() |
9151eec24e | ||
![]() |
22475729ce | ||
![]() |
04c67d700d |
20
.travis.yml
20
.travis.yml
@@ -3,14 +3,6 @@ sudo: false
|
||||
|
||||
matrix:
|
||||
include:
|
||||
- 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
|
||||
@@ -19,9 +11,17 @@ matrix:
|
||||
- $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.11.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.12.x"
|
||||
sudo: true
|
||||
cache:
|
||||
directories:
|
||||
@@ -29,7 +29,7 @@ matrix:
|
||||
- $HOME/gopath/pkg/mod
|
||||
|
||||
- os: osx
|
||||
go: "1.11.x"
|
||||
go: "1.12.x"
|
||||
env: RESTIC_TEST_FUSE=0 RESTIC_TEST_CLOUD_BACKENDS=0
|
||||
cache:
|
||||
directories:
|
||||
|
287
CHANGELOG.md
287
CHANGELOG.md
@@ -1,3 +1,273 @@
|
||||
Changelog for restic 0.9.5 (2019-04-23)
|
||||
=======================================
|
||||
|
||||
The following sections list the changes in restic 0.9.5 relevant to
|
||||
restic users. The changes are ordered by importance.
|
||||
|
||||
Summary
|
||||
-------
|
||||
|
||||
* Fix #2135: Return error when no bytes could be read from stdin
|
||||
* Fix #2181: Don't cancel timeout after 30 seconds for self-update
|
||||
* Fix #2203: Fix reading passwords from stdin
|
||||
* Fix #2224: Don't abort the find command when a tree can't be loaded
|
||||
* Enh #1895: Add case insensitive include & exclude options
|
||||
* Enh #1937: Support streaming JSON output for backup
|
||||
* Enh #2155: Add Openstack application credential auth for Swift
|
||||
* Enh #2184: Add --json support to forget command
|
||||
* Enh #2037: Add group-by option to snapshots command
|
||||
* Enh #2124: Ability to dump folders to tar via stdout
|
||||
* Enh #2139: Return error if no bytes could be read for `backup --stdin`
|
||||
* Enh #2205: Add --ignore-inode option to backup cmd
|
||||
* Enh #2220: Add config option to set S3 storage class
|
||||
|
||||
Details
|
||||
-------
|
||||
|
||||
* Bugfix #2135: Return error when no bytes could be read from stdin
|
||||
|
||||
We assume that users reading backup data from stdin want to know when no data could be read, so now
|
||||
restic returns an error when `backup --stdin` is called but no bytes could be read. Usually,
|
||||
this means that an earlier command in a pipe has failed. The documentation was amended and now
|
||||
recommends setting the `pipefail` option (`set -o pipefail`).
|
||||
|
||||
https://github.com/restic/restic/pull/2135
|
||||
https://github.com/restic/restic/pull/2139
|
||||
|
||||
* Bugfix #2181: Don't cancel timeout after 30 seconds for self-update
|
||||
|
||||
https://github.com/restic/restic/issues/2181
|
||||
|
||||
* Bugfix #2203: Fix reading passwords from stdin
|
||||
|
||||
Passwords for the `init`, `key add`, and `key passwd` commands can now be read from
|
||||
non-terminal stdin.
|
||||
|
||||
https://github.com/restic/restic/issues/2203
|
||||
|
||||
* Bugfix #2224: Don't abort the find command when a tree can't be loaded
|
||||
|
||||
Change the find command so that missing trees don't result in a crash. Instead, the error is
|
||||
logged to the debug log, and the tree ID is displayed along with the snapshot it belongs to. This
|
||||
makes it possible to recover repositories that are missing trees by forgetting the snapshots
|
||||
they are used in.
|
||||
|
||||
https://github.com/restic/restic/issues/2224
|
||||
|
||||
* Enhancement #1895: Add case insensitive include & exclude options
|
||||
|
||||
The backup and restore commands now have --iexclude and --iinclude flags as case insensitive
|
||||
variants of --exclude and --include.
|
||||
|
||||
https://github.com/restic/restic/issues/1895
|
||||
https://github.com/restic/restic/pull/2032
|
||||
|
||||
* Enhancement #1937: Support streaming JSON output for backup
|
||||
|
||||
We've added support for getting machine-readable status output during backup, just pass the
|
||||
flag `--json` for `restic backup` and restic will output a stream of JSON objects which contain
|
||||
the current progress.
|
||||
|
||||
https://github.com/restic/restic/issues/1937
|
||||
https://github.com/restic/restic/pull/1944
|
||||
|
||||
* Enhancement #2155: Add Openstack application credential auth for Swift
|
||||
|
||||
Since Openstack Queens Identity (auth V3) service supports an application credential auth
|
||||
method. It allows to create a technical account with the limited roles. This commit adds an
|
||||
application credential authentication method for the Swift backend.
|
||||
|
||||
https://github.com/restic/restic/issues/2155
|
||||
|
||||
* Enhancement #2184: Add --json support to forget command
|
||||
|
||||
The forget command now supports the --json argument, outputting the information about what is
|
||||
(or would-be) kept and removed from the repository.
|
||||
|
||||
https://github.com/restic/restic/issues/2184
|
||||
https://github.com/restic/restic/pull/2185
|
||||
|
||||
* Enhancement #2037: Add group-by option to snapshots command
|
||||
|
||||
We have added an option to group the output of the snapshots command, similar to the output of the
|
||||
forget command. The option has been called "--group-by" and accepts any combination of the
|
||||
values "host", "paths" and "tags", separated by commas. Default behavior (not specifying
|
||||
--group-by) has not been changed. We have added support of the grouping to the JSON output.
|
||||
|
||||
https://github.com/restic/restic/issues/2037
|
||||
https://github.com/restic/restic/pull/2087
|
||||
|
||||
* Enhancement #2124: Ability to dump folders to tar via stdout
|
||||
|
||||
We've added the ability to dump whole folders to stdout via the `dump` command. Restic now
|
||||
requires at least Go 1.10 due to a limitation of the standard library for Go <= 1.9.
|
||||
|
||||
https://github.com/restic/restic/issues/2123
|
||||
https://github.com/restic/restic/pull/2124
|
||||
|
||||
* Enhancement #2139: Return error if no bytes could be read for `backup --stdin`
|
||||
|
||||
When restic is used to backup the output of a program, like `mysqldump | restic backup --stdin`,
|
||||
it now returns an error if no bytes could be read at all. This catches the failure case when
|
||||
`mysqldump` failed for some reason and did not output any data to stdout.
|
||||
|
||||
https://github.com/restic/restic/pull/2139
|
||||
|
||||
* Enhancement #2205: Add --ignore-inode option to backup cmd
|
||||
|
||||
This option handles backup of virtual filesystems that do not keep fixed inodes for files, like
|
||||
Fuse-based, pCloud, etc. Ignoring inode changes allows to consider the file as unchanged if
|
||||
last modification date and size are unchanged.
|
||||
|
||||
https://github.com/restic/restic/issues/1631
|
||||
https://github.com/restic/restic/pull/2205
|
||||
https://github.com/restic/restic/pull/2047
|
||||
|
||||
* Enhancement #2220: Add config option to set S3 storage class
|
||||
|
||||
The `s3.storage-class` option can be passed to restic (using `-o`) to specify the storage
|
||||
class to be used for S3 objects created by restic.
|
||||
|
||||
The storage class is passed as-is to S3, so it needs to be understood by the API. On AWS, it can be
|
||||
one of `STANDARD`, `STANDARD_IA`, `ONEZONE_IA`, `INTELLIGENT_TIERING` and
|
||||
`REDUCED_REDUNDANCY`. If unspecified, the default storage class is used (`STANDARD` on
|
||||
AWS).
|
||||
|
||||
You can mix storage classes in the same bucket, and the setting isn't stored in the restic
|
||||
repository, so be sure to specify it with each command that writes to S3.
|
||||
|
||||
https://github.com/restic/restic/issues/706
|
||||
https://github.com/restic/restic/pull/2220
|
||||
|
||||
|
||||
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)
|
||||
=======================================
|
||||
|
||||
@@ -8,7 +278,7 @@ Summary
|
||||
-------
|
||||
|
||||
* Fix #1935: Remove truncated files from cache
|
||||
* Fix #1978: Do not return an error when the scanner is faster than backup
|
||||
* 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
|
||||
@@ -20,6 +290,7 @@ Summary
|
||||
* 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
|
||||
-------
|
||||
@@ -32,7 +303,7 @@ Details
|
||||
|
||||
https://github.com/restic/restic/issues/1935
|
||||
|
||||
* Bugfix #1978: Do not return an error when the scanner is faster than backup
|
||||
* 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
|
||||
@@ -155,6 +426,18 @@ Details
|
||||
|
||||
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)
|
||||
=======================================
|
||||
|
@@ -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>`__.
|
||||
|
||||
|
@@ -20,8 +20,8 @@ init:
|
||||
|
||||
install:
|
||||
- rmdir c:\go /s /q
|
||||
- appveyor DownloadFile https://dl.google.com/go/go1.11.windows-amd64.msi
|
||||
- msiexec /i go1.11.windows-amd64.msi /q
|
||||
- appveyor DownloadFile https://dl.google.com/go/go1.12.1.windows-amd64.msi
|
||||
- msiexec /i go1.12.1.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
|
||||
|
12
build.go
12
build.go
@@ -60,12 +60,12 @@ 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: "./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
|
||||
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: 10, Patch: 0}, // minimum Go version supported
|
||||
}
|
||||
|
||||
// Config configures the build.
|
||||
|
@@ -1,4 +1,4 @@
|
||||
Bugfix: Do not return an error when the scanner is faster than backup
|
||||
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
|
||||
|
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
|
7
changelog/0.9.5_2019-04-23/issue-1895
Normal file
7
changelog/0.9.5_2019-04-23/issue-1895
Normal file
@@ -0,0 +1,7 @@
|
||||
Enhancement: Add case insensitive include & exclude options
|
||||
|
||||
The backup and restore commands now have --iexclude and --iinclude flags
|
||||
as case insensitive variants of --exclude and --include.
|
||||
|
||||
https://github.com/restic/restic/issues/1895
|
||||
https://github.com/restic/restic/pull/2032
|
8
changelog/0.9.5_2019-04-23/issue-1937
Normal file
8
changelog/0.9.5_2019-04-23/issue-1937
Normal file
@@ -0,0 +1,8 @@
|
||||
Enhancement: Support streaming JSON output for backup
|
||||
|
||||
We've added support for getting machine-readable status output during backup,
|
||||
just pass the flag `--json` for `restic backup` and restic will output a stream
|
||||
of JSON objects which contain the current progress.
|
||||
|
||||
https://github.com/restic/restic/issues/1937
|
||||
https://github.com/restic/restic/pull/1944
|
10
changelog/0.9.5_2019-04-23/issue-2135
Normal file
10
changelog/0.9.5_2019-04-23/issue-2135
Normal file
@@ -0,0 +1,10 @@
|
||||
Bugfix: Return error when no bytes could be read from stdin
|
||||
|
||||
We assume that users reading backup data from stdin want to know when no data
|
||||
could be read, so now restic returns an error when `backup --stdin` is called
|
||||
but no bytes could be read. Usually, this means that an earlier command in a
|
||||
pipe has failed. The documentation was amended and now recommends setting the
|
||||
`pipefail` option (`set -o pipefail`).
|
||||
|
||||
https://github.com/restic/restic/pull/2135
|
||||
https://github.com/restic/restic/pull/2139
|
8
changelog/0.9.5_2019-04-23/issue-2155
Normal file
8
changelog/0.9.5_2019-04-23/issue-2155
Normal file
@@ -0,0 +1,8 @@
|
||||
Enhancement: add Openstack application credential auth for Swift
|
||||
|
||||
Since Openstack Queens Identity (auth V3) service supports an application
|
||||
credential auth method. It allows to create a technical account with the
|
||||
limited roles. This commit adds an application credential authentication
|
||||
method for the Swift backend.
|
||||
|
||||
https://github.com/restic/restic/issues/2155
|
3
changelog/0.9.5_2019-04-23/issue-2181
Normal file
3
changelog/0.9.5_2019-04-23/issue-2181
Normal file
@@ -0,0 +1,3 @@
|
||||
Bugfix: Don't cancel timeout after 30 seconds for self-update
|
||||
|
||||
https://github.com/restic/restic/issues/2181
|
8
changelog/0.9.5_2019-04-23/issue-2184
Normal file
8
changelog/0.9.5_2019-04-23/issue-2184
Normal file
@@ -0,0 +1,8 @@
|
||||
Enhancement: Add --json support to forget command
|
||||
|
||||
The forget command now supports the --json argument, outputting the
|
||||
information about what is (or would-be) kept and removed from the
|
||||
repository.
|
||||
|
||||
https://github.com/restic/restic/issues/2184
|
||||
https://github.com/restic/restic/pull/2185
|
6
changelog/0.9.5_2019-04-23/issue-2203
Normal file
6
changelog/0.9.5_2019-04-23/issue-2203
Normal file
@@ -0,0 +1,6 @@
|
||||
Bugfix: Fix reading passwords from stdin
|
||||
|
||||
Passwords for the `init`, `key add`, and `key passwd` commands can now be read from
|
||||
non-terminal stdin.
|
||||
|
||||
https://github.com/restic/restic/issues/2203
|
9
changelog/0.9.5_2019-04-23/issue-2224
Normal file
9
changelog/0.9.5_2019-04-23/issue-2224
Normal file
@@ -0,0 +1,9 @@
|
||||
Bugfix: Don't abort the find command when a tree can't be loaded
|
||||
|
||||
Change the find command so that missing trees don't result in a crash.
|
||||
Instead, the error is logged to the debug log, and the tree ID is displayed
|
||||
along with the snapshot it belongs to. This makes it possible to recover
|
||||
repositories that are missing trees by forgetting the snapshots they are used
|
||||
in.
|
||||
|
||||
https://github.com/restic/restic/issues/2224
|
10
changelog/0.9.5_2019-04-23/pull-2087
Normal file
10
changelog/0.9.5_2019-04-23/pull-2087
Normal file
@@ -0,0 +1,10 @@
|
||||
Enhancement: Add group-by option to snapshots command
|
||||
|
||||
We have added an option to group the output of the snapshots command, similar
|
||||
to the output of the forget command. The option has been called "--group-by"
|
||||
and accepts any combination of the values "host", "paths" and "tags", separated
|
||||
by commas. Default behavior (not specifying --group-by) has not been changed.
|
||||
We have added support of the grouping to the JSON output.
|
||||
|
||||
https://github.com/restic/restic/issues/2037
|
||||
https://github.com/restic/restic/pull/2087
|
8
changelog/0.9.5_2019-04-23/pull-2124
Normal file
8
changelog/0.9.5_2019-04-23/pull-2124
Normal file
@@ -0,0 +1,8 @@
|
||||
Enhancement: Ability to dump folders to tar via stdout
|
||||
|
||||
We've added the ability to dump whole folders to stdout via the `dump` command.
|
||||
Restic now requires at least Go 1.10 due to a limitation of the standard
|
||||
library for Go <= 1.9.
|
||||
|
||||
https://github.com/restic/restic/pull/2124
|
||||
https://github.com/restic/restic/issues/2123
|
8
changelog/0.9.5_2019-04-23/pull-2139
Normal file
8
changelog/0.9.5_2019-04-23/pull-2139
Normal file
@@ -0,0 +1,8 @@
|
||||
Enhancement: Return error if no bytes could be read for `backup --stdin`
|
||||
|
||||
When restic is used to backup the output of a program, like `mysqldump | restic
|
||||
backup --stdin`, it now returns an error if no bytes could be read at all. This
|
||||
catches the failure case when `mysqldump` failed for some reason and did not
|
||||
output any data to stdout.
|
||||
|
||||
https://github.com/restic/restic/pull/2139
|
10
changelog/0.9.5_2019-04-23/pull-2205
Normal file
10
changelog/0.9.5_2019-04-23/pull-2205
Normal file
@@ -0,0 +1,10 @@
|
||||
Enhancement: Add --ignore-inode option to backup cmd
|
||||
|
||||
This option handles backup of virtual filesystems that do not keep fixed
|
||||
inodes for files, like Fuse-based, pCloud, etc. Ignoring inode changes allows
|
||||
to consider the file as unchanged if last modification date and size
|
||||
are unchanged.
|
||||
|
||||
https://github.com/restic/restic/pull/2205
|
||||
https://github.com/restic/restic/pull/2047
|
||||
https://github.com/restic/restic/issues/1631
|
16
changelog/0.9.5_2019-04-23/pull-2220
Normal file
16
changelog/0.9.5_2019-04-23/pull-2220
Normal file
@@ -0,0 +1,16 @@
|
||||
Enhancement: Add config option to set S3 storage class
|
||||
|
||||
The `s3.storage-class` option can be passed to restic (using `-o`) to
|
||||
specify the storage class to be used for S3 objects created by restic.
|
||||
|
||||
The storage class is passed as-is to S3, so it needs to be understood by
|
||||
the API. On AWS, it can be one of `STANDARD`, `STANDARD_IA`,
|
||||
`ONEZONE_IA`, `INTELLIGENT_TIERING` and `REDUCED_REDUNDANCY`. If
|
||||
unspecified, the default storage class is used (`STANDARD` on AWS).
|
||||
|
||||
You can mix storage classes in the same bucket, and the setting isn't
|
||||
stored in the restic repository, so be sure to specify it with each
|
||||
command that writes to S3.
|
||||
|
||||
https://github.com/restic/restic/pull/2220
|
||||
https://github.com/restic/restic/issues/706
|
131
cmd/restic/acl.go
Normal file
131
cmd/restic/acl.go
Normal file
@@ -0,0 +1,131 @@
|
||||
package main
|
||||
|
||||
// Adapted from https://github.com/maxymania/go-system/blob/master/posix_acl/posix_acl.go
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
const (
|
||||
aclUserOwner = 0x0001
|
||||
aclUser = 0x0002
|
||||
aclGroupOwner = 0x0004
|
||||
aclGroup = 0x0008
|
||||
aclMask = 0x0010
|
||||
aclOthers = 0x0020
|
||||
)
|
||||
|
||||
type aclSID uint64
|
||||
|
||||
type aclElem struct {
|
||||
Tag uint16
|
||||
Perm uint16
|
||||
ID uint32
|
||||
}
|
||||
|
||||
type acl struct {
|
||||
Version uint32
|
||||
List []aclElement
|
||||
}
|
||||
|
||||
type aclElement struct {
|
||||
aclSID
|
||||
Perm uint16
|
||||
}
|
||||
|
||||
func (a *aclSID) setUID(uid uint32) {
|
||||
*a = aclSID(uid) | (aclUser << 32)
|
||||
}
|
||||
func (a *aclSID) setGID(gid uint32) {
|
||||
*a = aclSID(gid) | (aclGroup << 32)
|
||||
}
|
||||
|
||||
func (a *aclSID) setType(tp int) {
|
||||
*a = aclSID(tp) << 32
|
||||
}
|
||||
|
||||
func (a aclSID) getType() int {
|
||||
return int(a >> 32)
|
||||
}
|
||||
func (a aclSID) getID() uint32 {
|
||||
return uint32(a & 0xffffffff)
|
||||
}
|
||||
func (a aclSID) String() string {
|
||||
switch a >> 32 {
|
||||
case aclUserOwner:
|
||||
return "user::"
|
||||
case aclUser:
|
||||
return fmt.Sprintf("user:%v:", a.getID())
|
||||
case aclGroupOwner:
|
||||
return "group::"
|
||||
case aclGroup:
|
||||
return fmt.Sprintf("group:%v:", a.getID())
|
||||
case aclMask:
|
||||
return "mask::"
|
||||
case aclOthers:
|
||||
return "other::"
|
||||
}
|
||||
return "?:"
|
||||
}
|
||||
|
||||
func (a aclElement) String() string {
|
||||
str := ""
|
||||
if (a.Perm & 4) != 0 {
|
||||
str += "r"
|
||||
} else {
|
||||
str += "-"
|
||||
}
|
||||
if (a.Perm & 2) != 0 {
|
||||
str += "w"
|
||||
} else {
|
||||
str += "-"
|
||||
}
|
||||
if (a.Perm & 1) != 0 {
|
||||
str += "x"
|
||||
} else {
|
||||
str += "-"
|
||||
}
|
||||
return fmt.Sprintf("%v%v", a.aclSID, str)
|
||||
}
|
||||
|
||||
func (a *acl) decode(xattr []byte) {
|
||||
var elem aclElement
|
||||
ae := new(aclElem)
|
||||
nr := bytes.NewReader(xattr)
|
||||
e := binary.Read(nr, binary.LittleEndian, &a.Version)
|
||||
if e != nil {
|
||||
a.Version = 0
|
||||
return
|
||||
}
|
||||
if len(a.List) > 0 {
|
||||
a.List = a.List[:0]
|
||||
}
|
||||
for binary.Read(nr, binary.LittleEndian, ae) == nil {
|
||||
elem.aclSID = (aclSID(ae.Tag) << 32) | aclSID(ae.ID)
|
||||
elem.Perm = ae.Perm
|
||||
a.List = append(a.List, elem)
|
||||
}
|
||||
}
|
||||
|
||||
func (a *acl) encode() []byte {
|
||||
buf := new(bytes.Buffer)
|
||||
ae := new(aclElem)
|
||||
binary.Write(buf, binary.LittleEndian, &a.Version)
|
||||
for _, elem := range a.List {
|
||||
ae.Tag = uint16(elem.getType())
|
||||
ae.Perm = elem.Perm
|
||||
ae.ID = elem.getID()
|
||||
binary.Write(buf, binary.LittleEndian, ae)
|
||||
}
|
||||
return buf.Bytes()
|
||||
}
|
||||
|
||||
func (a *acl) String() string {
|
||||
var finalacl string
|
||||
for _, acl := range a.List {
|
||||
finalacl += acl.String() + "\n"
|
||||
}
|
||||
return finalacl
|
||||
}
|
96
cmd/restic/acl_test.go
Normal file
96
cmd/restic/acl_test.go
Normal file
@@ -0,0 +1,96 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func Test_acl_decode(t *testing.T) {
|
||||
type args struct {
|
||||
xattr []byte
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want string
|
||||
}{
|
||||
{
|
||||
name: "decode string",
|
||||
args: args{
|
||||
xattr: []byte{2, 0, 0, 0, 1, 0, 6, 0, 255, 255, 255, 255, 2, 0, 7, 0, 0, 0, 0, 0, 2, 0, 7, 0, 254, 255, 0, 0, 4, 0, 7, 0, 255, 255, 255, 255, 16, 0, 7, 0, 255, 255, 255, 255, 32, 0, 4, 0, 255, 255, 255, 255},
|
||||
},
|
||||
want: "user::rw-\nuser:0:rwx\nuser:65534:rwx\ngroup::rwx\nmask::rwx\nother::r--\n",
|
||||
},
|
||||
{
|
||||
name: "decode fail",
|
||||
args: args{
|
||||
xattr: []byte("abctest"),
|
||||
},
|
||||
want: "",
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
a := &acl{}
|
||||
a.decode(tt.args.xattr)
|
||||
if tt.want != a.String() {
|
||||
t.Errorf("acl.decode() = %v, want: %v", a.String(), tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_acl_encode(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
want []byte
|
||||
args []aclElement
|
||||
}{
|
||||
{
|
||||
name: "encode values",
|
||||
want: []byte{2, 0, 0, 0, 1, 0, 6, 0, 255, 255, 255, 255, 2, 0, 7, 0, 0, 0, 0, 0, 2, 0, 7, 0, 254, 255, 0, 0, 4, 0, 7, 0, 255, 255, 255, 255, 16, 0, 7, 0, 255, 255, 255, 255, 32, 0, 4, 0, 255, 255, 255, 255},
|
||||
args: []aclElement{
|
||||
{
|
||||
aclSID: 8589934591,
|
||||
Perm: 6,
|
||||
},
|
||||
{
|
||||
aclSID: 8589934592,
|
||||
Perm: 7,
|
||||
},
|
||||
{
|
||||
aclSID: 8590000126,
|
||||
Perm: 7,
|
||||
},
|
||||
{
|
||||
aclSID: 21474836479,
|
||||
Perm: 7,
|
||||
},
|
||||
{
|
||||
aclSID: 73014444031,
|
||||
Perm: 7,
|
||||
},
|
||||
{
|
||||
aclSID: 141733920767,
|
||||
Perm: 4,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "encode fail",
|
||||
want: []byte{2, 0, 0, 0},
|
||||
args: []aclElement{},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
a := &acl{
|
||||
Version: 2,
|
||||
List: tt.args,
|
||||
}
|
||||
if got := a.encode(); !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("acl.encode() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
@@ -5,6 +5,7 @@ import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
@@ -23,6 +24,7 @@ import (
|
||||
"github.com/restic/restic/internal/restic"
|
||||
"github.com/restic/restic/internal/textfile"
|
||||
"github.com/restic/restic/internal/ui"
|
||||
"github.com/restic/restic/internal/ui/jsonstatus"
|
||||
"github.com/restic/restic/internal/ui/termstatus"
|
||||
)
|
||||
|
||||
@@ -45,8 +47,12 @@ given as the arguments.
|
||||
},
|
||||
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
|
||||
@@ -64,20 +70,22 @@ given as the arguments.
|
||||
|
||||
// BackupOptions bundles all options for the backup command.
|
||||
type BackupOptions struct {
|
||||
Parent string
|
||||
Force bool
|
||||
Excludes []string
|
||||
ExcludeFiles []string
|
||||
ExcludeOtherFS bool
|
||||
ExcludeIfPresent []string
|
||||
ExcludeCaches bool
|
||||
Stdin bool
|
||||
StdinFilename string
|
||||
Tags []string
|
||||
Host string
|
||||
FilesFrom string
|
||||
TimeStamp string
|
||||
WithAtime bool
|
||||
Parent string
|
||||
Force bool
|
||||
Excludes []string
|
||||
InsensitiveExcludes []string
|
||||
ExcludeFiles []string
|
||||
ExcludeOtherFS bool
|
||||
ExcludeIfPresent []string
|
||||
ExcludeCaches bool
|
||||
Stdin bool
|
||||
StdinFilename string
|
||||
Tags []string
|
||||
Host string
|
||||
FilesFrom []string
|
||||
TimeStamp string
|
||||
WithAtime bool
|
||||
IgnoreInode bool
|
||||
}
|
||||
|
||||
var backupOptions BackupOptions
|
||||
@@ -89,21 +97,23 @@ func init() {
|
||||
f.StringVar(&backupOptions.Parent, "parent", "", "use this parent snapshot (default: last snapshot in the repo that has the same target files/directories)")
|
||||
f.BoolVarP(&backupOptions.Force, "force", "f", false, `force re-reading the target files/directories (overrides the "parent" flag)`)
|
||||
f.StringArrayVarP(&backupOptions.Excludes, "exclude", "e", nil, "exclude a `pattern` (can be specified multiple times)")
|
||||
f.StringArrayVar(&backupOptions.InsensitiveExcludes, "iexclude", nil, "same as `--exclude` but ignores the casing of filenames")
|
||||
f.StringArrayVar(&backupOptions.ExcludeFiles, "exclude-file", nil, "read exclude patterns from a `file` (can be specified multiple times)")
|
||||
f.BoolVarP(&backupOptions.ExcludeOtherFS, "one-file-system", "x", false, "exclude other file systems")
|
||||
f.StringArrayVar(&backupOptions.ExcludeIfPresent, "exclude-if-present", nil, "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)")
|
||||
f.BoolVar(&backupOptions.ExcludeCaches, "exclude-caches", false, `excludes cache directories that are marked with a CACHEDIR.TAG file`)
|
||||
f.BoolVar(&backupOptions.ExcludeCaches, "exclude-caches", false, `excludes cache directories that are marked with a CACHEDIR.TAG file. See http://bford.info/cachedir/spec.html for the Cache Directory Tagging Standard`)
|
||||
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.Host, "host", "H", "set the `hostname` for the snapshot manually. To prevent an expensive rescan use the \"parent\" flag")
|
||||
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.StringVar(&backupOptions.FilesFrom, "files-from", "", "read the files to backup from file (can be combined with file args)")
|
||||
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")
|
||||
f.BoolVar(&backupOptions.IgnoreInode, "ignore-inode", false, "ignore inode number changes when checking for modified files")
|
||||
}
|
||||
|
||||
// filterExisting returns a slice of all existing items, or an error if no
|
||||
@@ -175,12 +185,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")
|
||||
}
|
||||
|
||||
@@ -214,6 +228,10 @@ func collectRejectByNameFuncs(opts BackupOptions, repo *repository.Repository, t
|
||||
opts.Excludes = append(opts.Excludes, excludes...)
|
||||
}
|
||||
|
||||
if len(opts.InsensitiveExcludes) > 0 {
|
||||
fs = append(fs, rejectByInsensitivePattern(opts.InsensitiveExcludes))
|
||||
}
|
||||
|
||||
if len(opts.Excludes) > 0 {
|
||||
fs = append(fs, rejectByPattern(opts.Excludes))
|
||||
}
|
||||
@@ -302,20 +320,25 @@ func collectTargets(opts BackupOptions, args []string) (targets []string, err er
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
fromfile, err := readLinesFromFile(opts.FilesFrom)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// expand wildcards
|
||||
var lines []string
|
||||
for _, line := range fromfile {
|
||||
var expanded []string
|
||||
expanded, err := filepath.Glob(line)
|
||||
for _, file := range opts.FilesFrom {
|
||||
fromfile, err := readLinesFromFile(file)
|
||||
if err != nil {
|
||||
return nil, errors.WithMessage(err, fmt.Sprintf("pattern: %s", line))
|
||||
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...)
|
||||
}
|
||||
lines = append(lines, expanded...)
|
||||
}
|
||||
|
||||
// merge files from files-from into normal args so we can reuse the normal
|
||||
@@ -374,7 +397,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)
|
||||
}
|
||||
@@ -382,7 +405,43 @@ func runBackup(opts BackupOptions, gopts GlobalOptions, term *termstatus.Termina
|
||||
|
||||
var t tomb.Tomb
|
||||
|
||||
p := ui.NewBackup(term, gopts.verbosity)
|
||||
if gopts.verbosity >= 2 && !gopts.JSON {
|
||||
term.Print("open repository\n")
|
||||
}
|
||||
|
||||
repo, err := OpenRepository(gopts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
type ArchiveProgressReporter interface {
|
||||
CompleteItem(item string, previous, current *restic.Node, s archiver.ItemStats, d time.Duration)
|
||||
StartFile(filename string)
|
||||
CompleteBlob(filename string, bytes uint64)
|
||||
ScannerError(item string, fi os.FileInfo, err error) error
|
||||
ReportTotal(item string, s archiver.ScanStats)
|
||||
SetMinUpdatePause(d time.Duration)
|
||||
Run(ctx context.Context) error
|
||||
Error(item string, fi os.FileInfo, err error) error
|
||||
Finish(snapshotID restic.ID)
|
||||
|
||||
// ui.StdioWrapper
|
||||
Stdout() io.WriteCloser
|
||||
Stderr() io.WriteCloser
|
||||
|
||||
// ui.Message
|
||||
E(msg string, args ...interface{})
|
||||
P(msg string, args ...interface{})
|
||||
V(msg string, args ...interface{})
|
||||
VV(msg string, args ...interface{})
|
||||
}
|
||||
|
||||
var p ArchiveProgressReporter
|
||||
if gopts.JSON {
|
||||
p = jsonstatus.NewBackup(term, gopts.verbosity)
|
||||
} else {
|
||||
p = ui.NewBackup(term, gopts.verbosity)
|
||||
}
|
||||
|
||||
// use the terminal for stdout/stderr
|
||||
prevStdout, prevStderr := gopts.stdout, gopts.stderr
|
||||
@@ -397,19 +456,15 @@ func runBackup(opts BackupOptions, gopts GlobalOptions, term *termstatus.Termina
|
||||
if fps > 60 {
|
||||
fps = 60
|
||||
}
|
||||
p.MinUpdatePause = time.Second / time.Duration(fps)
|
||||
p.SetMinUpdatePause(time.Second / time.Duration(fps))
|
||||
}
|
||||
}
|
||||
|
||||
t.Go(func() error { return p.Run(t.Context(gopts.ctx)) })
|
||||
|
||||
p.V("open repository")
|
||||
repo, err := OpenRepository(gopts)
|
||||
if err != nil {
|
||||
return err
|
||||
if !gopts.JSON {
|
||||
p.V("lock repository")
|
||||
}
|
||||
|
||||
p.V("lock repository")
|
||||
lock, err := lockRepo(repo)
|
||||
defer unlockRepo(lock)
|
||||
if err != nil {
|
||||
@@ -428,7 +483,9 @@ func runBackup(opts BackupOptions, gopts GlobalOptions, term *termstatus.Termina
|
||||
return err
|
||||
}
|
||||
|
||||
p.V("load index files")
|
||||
if !gopts.JSON {
|
||||
p.V("load index files")
|
||||
}
|
||||
err = repo.LoadIndex(gopts.ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -439,7 +496,7 @@ func runBackup(opts BackupOptions, gopts GlobalOptions, term *termstatus.Termina
|
||||
return err
|
||||
}
|
||||
|
||||
if parentSnapshotID != nil {
|
||||
if !gopts.JSON && parentSnapshotID != nil {
|
||||
p.V("using parent snapshot %v\n", parentSnapshotID.Str())
|
||||
}
|
||||
|
||||
@@ -463,7 +520,9 @@ func runBackup(opts BackupOptions, gopts GlobalOptions, term *termstatus.Termina
|
||||
|
||||
var targetFS fs.FS = fs.Local{}
|
||||
if opts.Stdin {
|
||||
p.V("read data from stdin")
|
||||
if !gopts.JSON {
|
||||
p.V("read data from stdin")
|
||||
}
|
||||
targetFS = &fs.Reader{
|
||||
ModTime: timeStamp,
|
||||
Name: opts.StdinFilename,
|
||||
@@ -479,7 +538,9 @@ func runBackup(opts BackupOptions, gopts GlobalOptions, term *termstatus.Termina
|
||||
sc.Error = p.ScannerError
|
||||
sc.Result = p.ReportTotal
|
||||
|
||||
p.V("start scan on %v", targets)
|
||||
if !gopts.JSON {
|
||||
p.V("start scan on %v", targets)
|
||||
}
|
||||
t.Go(func() error { return sc.Scan(t.Context(gopts.ctx), targets) })
|
||||
|
||||
arch := archiver.New(repo, targetFS, archiver.Options{})
|
||||
@@ -487,9 +548,10 @@ func runBackup(opts BackupOptions, gopts GlobalOptions, term *termstatus.Termina
|
||||
arch.Select = selectFilter
|
||||
arch.WithAtime = opts.WithAtime
|
||||
arch.Error = p.Error
|
||||
arch.CompleteItem = p.CompleteItemFn
|
||||
arch.CompleteItem = p.CompleteItem
|
||||
arch.StartFile = p.StartFile
|
||||
arch.CompleteBlob = p.CompleteBlob
|
||||
arch.IgnoreInode = opts.IgnoreInode
|
||||
|
||||
if parentSnapshotID == nil {
|
||||
parentSnapshotID = &restic.ID{}
|
||||
@@ -506,10 +568,14 @@ func runBackup(opts BackupOptions, gopts GlobalOptions, term *termstatus.Termina
|
||||
uploader := archiver.IndexUploader{
|
||||
Repository: repo,
|
||||
Start: func() {
|
||||
p.VV("uploading intermediate index")
|
||||
if !gopts.JSON {
|
||||
p.VV("uploading intermediate index")
|
||||
}
|
||||
},
|
||||
Complete: func(id restic.ID) {
|
||||
p.V("uploaded intermediate index %v", id.Str())
|
||||
if !gopts.JSON {
|
||||
p.V("uploaded intermediate index %v", id.Str())
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
@@ -517,14 +583,18 @@ func runBackup(opts BackupOptions, gopts GlobalOptions, term *termstatus.Termina
|
||||
return uploader.Upload(gopts.ctx, t.Context(gopts.ctx), 30*time.Second)
|
||||
})
|
||||
|
||||
p.V("start backup on %v", targets)
|
||||
if !gopts.JSON {
|
||||
p.V("start backup on %v", targets)
|
||||
}
|
||||
_, id, err := arch.Snapshot(gopts.ctx, targets, snapshotOpts)
|
||||
if err != nil {
|
||||
return errors.Fatalf("unable to save snapshot: %v", err)
|
||||
}
|
||||
|
||||
p.Finish()
|
||||
p.P("snapshot %s saved\n", id.Str())
|
||||
p.Finish(id)
|
||||
if !gopts.JSON {
|
||||
p.P("snapshot %s saved\n", id.Str())
|
||||
}
|
||||
|
||||
// cleanly shutdown all running goroutines
|
||||
t.Kill(nil)
|
||||
|
@@ -74,7 +74,7 @@ func runCat(gopts GlobalOptions, args []string) error {
|
||||
fmt.Println(string(buf))
|
||||
return nil
|
||||
case "index":
|
||||
buf, err := repo.LoadAndDecrypt(gopts.ctx, restic.IndexFile, id)
|
||||
buf, err := repo.LoadAndDecrypt(gopts.ctx, nil, restic.IndexFile, id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -99,7 +99,7 @@ func runCat(gopts GlobalOptions, args []string) error {
|
||||
return nil
|
||||
case "key":
|
||||
h := restic.Handle{Type: restic.KeyFile, Name: id.String()}
|
||||
buf, err := backend.LoadAll(gopts.ctx, repo.Backend(), h)
|
||||
buf, err := backend.LoadAll(gopts.ctx, nil, repo.Backend(), h)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -150,7 +150,7 @@ func runCat(gopts GlobalOptions, args []string) error {
|
||||
switch tpe {
|
||||
case "pack":
|
||||
h := restic.Handle{Type: restic.DataFile, Name: id.String()}
|
||||
buf, err := backend.LoadAll(gopts.ctx, repo.Backend(), h)
|
||||
buf, err := backend.LoadAll(gopts.ctx, nil, repo.Backend(), h)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@@ -1,14 +1,19 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"archive/tar"
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/restic/restic/internal/debug"
|
||||
"github.com/restic/restic/internal/errors"
|
||||
"github.com/restic/restic/internal/restic"
|
||||
"github.com/restic/restic/internal/walker"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
@@ -47,43 +52,20 @@ 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)
|
||||
if d == "" || d == "/" {
|
||||
func splitPath(p string) []string {
|
||||
d, f := path.Split(p)
|
||||
if d == "" {
|
||||
return []string{f}
|
||||
}
|
||||
s := splitPath(filepath.Clean(d))
|
||||
if d == "/" {
|
||||
return []string{d}
|
||||
}
|
||||
s := splitPath(path.Clean(d))
|
||||
return append(s, f)
|
||||
}
|
||||
|
||||
func dumpNode(ctx context.Context, repo restic.Repository, node *restic.Node) error {
|
||||
var buf []byte
|
||||
for _, id := range node.Content {
|
||||
size, found := repo.LookupBlobSize(id, restic.DataBlob)
|
||||
if !found {
|
||||
return errors.Errorf("id %v not found in repository", id)
|
||||
}
|
||||
func printFromTree(ctx context.Context, tree *restic.Tree, repo restic.Repository, prefix string, pathComponents []string, pathToPrint string) error {
|
||||
|
||||
buf = buf[:cap(buf)]
|
||||
if len(buf) < restic.CiphertextLength(int(size)) {
|
||||
buf = restic.NewBlobBuffer(int(size))
|
||||
}
|
||||
|
||||
n, err := repo.LoadBlob(ctx, restic.DataBlob, id, buf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
buf = buf[:n]
|
||||
|
||||
_, err = os.Stdout.Write(buf)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "Write")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func printFromTree(ctx context.Context, tree *restic.Tree, repo restic.Repository, prefix string, pathComponents []string) error {
|
||||
if tree == nil {
|
||||
return fmt.Errorf("called with a nil tree")
|
||||
}
|
||||
@@ -96,16 +78,19 @@ func printFromTree(ctx context.Context, tree *restic.Tree, repo restic.Repositor
|
||||
}
|
||||
item := filepath.Join(prefix, pathComponents[0])
|
||||
for _, node := range tree.Nodes {
|
||||
if node.Name == pathComponents[0] {
|
||||
if node.Name == pathComponents[0] || pathComponents[0] == "/" {
|
||||
switch {
|
||||
case l == 1 && node.Type == "file":
|
||||
return dumpNode(ctx, repo, node)
|
||||
return getNodeData(ctx, os.Stdout, repo, node)
|
||||
case l > 1 && node.Type == "dir":
|
||||
subtree, err := repo.LoadTree(ctx, *node.Subtree)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "cannot load subtree for %q", item)
|
||||
}
|
||||
return printFromTree(ctx, subtree, repo, item, pathComponents[1:])
|
||||
return printFromTree(ctx, subtree, repo, item, pathComponents[1:], pathToPrint)
|
||||
case node.Type == "dir":
|
||||
node.Path = pathToPrint
|
||||
return tarTree(ctx, repo, node, pathToPrint)
|
||||
case l > 1:
|
||||
return fmt.Errorf("%q should be a dir, but s a %q", item, node.Type)
|
||||
case node.Type != "file":
|
||||
@@ -128,7 +113,7 @@ func runDump(opts DumpOptions, gopts GlobalOptions, args []string) error {
|
||||
|
||||
debug.Log("dump file %q from %q", pathToPrint, snapshotIDString)
|
||||
|
||||
splittedPath := splitPath(pathToPrint)
|
||||
splittedPath := splitPath(path.Clean(pathToPrint))
|
||||
|
||||
repo, err := OpenRepository(gopts)
|
||||
if err != nil {
|
||||
@@ -172,10 +157,143 @@ func runDump(opts DumpOptions, gopts GlobalOptions, args []string) error {
|
||||
Exitf(2, "loading tree for snapshot %q failed: %v", snapshotIDString, err)
|
||||
}
|
||||
|
||||
err = printFromTree(ctx, tree, repo, "", splittedPath)
|
||||
err = printFromTree(ctx, tree, repo, "", splittedPath, pathToPrint)
|
||||
if err != nil {
|
||||
Exitf(2, "cannot dump file: %v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func getNodeData(ctx context.Context, output io.Writer, repo restic.Repository, node *restic.Node) error {
|
||||
var buf []byte
|
||||
for _, id := range node.Content {
|
||||
|
||||
size, found := repo.LookupBlobSize(id, restic.DataBlob)
|
||||
if !found {
|
||||
return errors.Errorf("id %v not found in repository", id)
|
||||
}
|
||||
|
||||
buf = buf[:cap(buf)]
|
||||
if len(buf) < restic.CiphertextLength(int(size)) {
|
||||
buf = restic.NewBlobBuffer(int(size))
|
||||
}
|
||||
|
||||
n, err := repo.LoadBlob(ctx, restic.DataBlob, id, buf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
buf = buf[:n]
|
||||
|
||||
_, err = output.Write(buf)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "Write")
|
||||
}
|
||||
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func tarTree(ctx context.Context, repo restic.Repository, rootNode *restic.Node, rootPath string) error {
|
||||
|
||||
if stdoutIsTerminal() {
|
||||
return fmt.Errorf("stdout is the terminal, please redirect output")
|
||||
}
|
||||
|
||||
tw := tar.NewWriter(os.Stdout)
|
||||
defer tw.Close()
|
||||
|
||||
// If we want to dump "/" we'll need to add the name of the first node, too
|
||||
// as it would get lost otherwise.
|
||||
if rootNode.Path == "/" {
|
||||
rootNode.Path = path.Join(rootNode.Path, rootNode.Name)
|
||||
rootPath = rootNode.Path
|
||||
}
|
||||
|
||||
// we know that rootNode is a folder and walker.Walk will already process
|
||||
// the next node, so we have to tar this one first, too
|
||||
if err := tarNode(ctx, tw, rootNode, repo); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err := walker.Walk(ctx, repo, *rootNode.Subtree, 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
|
||||
}
|
||||
|
||||
node.Path = path.Join(rootPath, nodepath)
|
||||
|
||||
if node.Type == "file" || node.Type == "symlink" || node.Type == "dir" {
|
||||
err := tarNode(ctx, tw, node, repo)
|
||||
if err != err {
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
|
||||
return false, nil
|
||||
})
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func tarNode(ctx context.Context, tw *tar.Writer, node *restic.Node, repo restic.Repository) error {
|
||||
|
||||
header := &tar.Header{
|
||||
Name: node.Path,
|
||||
Size: int64(node.Size),
|
||||
Mode: int64(node.Mode),
|
||||
Uid: int(node.UID),
|
||||
Gid: int(node.GID),
|
||||
ModTime: node.ModTime,
|
||||
AccessTime: node.AccessTime,
|
||||
ChangeTime: node.ChangeTime,
|
||||
PAXRecords: parseXattrs(node.ExtendedAttributes),
|
||||
}
|
||||
|
||||
if node.Type == "symlink" {
|
||||
header.Typeflag = tar.TypeSymlink
|
||||
header.Linkname = node.LinkTarget
|
||||
}
|
||||
|
||||
if node.Type == "dir" {
|
||||
header.Typeflag = tar.TypeDir
|
||||
}
|
||||
|
||||
err := tw.WriteHeader(header)
|
||||
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "TarHeader ")
|
||||
}
|
||||
|
||||
return getNodeData(ctx, tw, repo, node)
|
||||
|
||||
}
|
||||
|
||||
func parseXattrs(xattrs []restic.ExtendedAttribute) map[string]string {
|
||||
tmpMap := make(map[string]string)
|
||||
|
||||
for _, attr := range xattrs {
|
||||
attrString := string(attr.Value)
|
||||
|
||||
if strings.HasPrefix(attr.Name, "system.posix_acl_") {
|
||||
na := acl{}
|
||||
na.decode(attr.Value)
|
||||
|
||||
if na.String() != "" {
|
||||
if strings.Contains(attr.Name, "system.posix_acl_access") {
|
||||
tmpMap["SCHILY.acl.access"] = na.String()
|
||||
} else if strings.Contains(attr.Name, "system.posix_acl_default") {
|
||||
tmpMap["SCHILY.acl.default"] = na.String()
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
tmpMap["SCHILY.xattr."+attr.Name] = attrString
|
||||
}
|
||||
}
|
||||
|
||||
return tmpMap
|
||||
}
|
||||
|
@@ -62,7 +62,7 @@ func init() {
|
||||
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.BoolVar(&findOptions.ShowPackID, "show-pack-id", false, "display the pack-ID the blobs belong to (with --blob or --tree)")
|
||||
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")
|
||||
|
||||
@@ -213,7 +213,7 @@ func (s *statefulOutput) PrintObjectNormal(kind, id, nodepath, treeID string, sn
|
||||
} else {
|
||||
Printf(" ... path %s\n", nodepath)
|
||||
}
|
||||
Printf(" ... in snapshot %s (%s)\n", sn.ID().Str(), sn.Time.Format(TimeFormat))
|
||||
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) {
|
||||
@@ -258,9 +258,13 @@ 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(_ restic.ID, nodepath string, node *restic.Node, err error) (bool, error) {
|
||||
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
|
||||
debug.Log("Error loading tree %v: %v", parentTreeID, err)
|
||||
|
||||
Printf("Unable to load tree %s\n ... which belongs to snapshot %s.\n", parentTreeID, sn.ID())
|
||||
|
||||
return false, walker.SkipNode
|
||||
}
|
||||
|
||||
if node == nil {
|
||||
@@ -340,7 +344,11 @@ func (f *Finder) findIDs(ctx context.Context, sn *restic.Snapshot) error {
|
||||
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
|
||||
debug.Log("Error loading tree %v: %v", parentTreeID, err)
|
||||
|
||||
Printf("Unable to load tree %s\n ... which belongs to snapshot %s.\n", parentTreeID, sn.ID())
|
||||
|
||||
return false, walker.SkipNode
|
||||
}
|
||||
|
||||
if node == nil {
|
||||
@@ -442,27 +450,36 @@ func (f *Finder) packsToBlobs(ctx context.Context, packs []string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *Finder) findBlobsPacks(ctx context.Context) {
|
||||
func (f *Finder) findObjectPack(ctx context.Context, id string, t restic.BlobType) {
|
||||
idx := f.repo.Index()
|
||||
|
||||
rid, err := restic.ParseID(id)
|
||||
if err != nil {
|
||||
Printf("Note: cannot find pack for object '%s', unable to parse ID: %v\n", id, err)
|
||||
return
|
||||
}
|
||||
|
||||
blobs, found := idx.Lookup(rid, t)
|
||||
if !found {
|
||||
Printf("Object %s not found in the index\n", rid.Str())
|
||||
return
|
||||
}
|
||||
|
||||
for _, b := range blobs {
|
||||
if b.ID.Equal(rid) {
|
||||
Printf("Object belongs to pack %s\n ... Pack %s: %s\n", b.PackID, b.PackID.Str(), b.String())
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (f *Finder) findObjectsPacks(ctx context.Context) {
|
||||
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
|
||||
}
|
||||
f.findObjectPack(ctx, i, restic.DataBlob)
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
for i := range f.treeIDs {
|
||||
f.findObjectPack(ctx, i, restic.TreeBlob)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -557,8 +574,8 @@ func runFind(opts FindOptions, gopts GlobalOptions, args []string) error {
|
||||
}
|
||||
f.out.Finish()
|
||||
|
||||
if opts.ShowPackID && f.blobIDs != nil {
|
||||
f.findBlobsPacks(ctx)
|
||||
if opts.ShowPackID && (f.blobIDs != nil || f.treeIDs != nil) {
|
||||
f.findObjectsPacks(ctx)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@@ -3,10 +3,8 @@ package main
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"sort"
|
||||
"strings"
|
||||
"io"
|
||||
|
||||
"github.com/restic/restic/internal/errors"
|
||||
"github.com/restic/restic/internal/restic"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
@@ -59,7 +57,7 @@ func init() {
|
||||
f.IntVarP(&forgetOptions.Weekly, "keep-weekly", "w", 0, "keep the last `n` weekly snapshots")
|
||||
f.IntVarP(&forgetOptions.Monthly, "keep-monthly", "m", 0, "keep the last `n` monthly snapshots")
|
||||
f.IntVarP(&forgetOptions.Yearly, "keep-yearly", "y", 0, "keep the last `n` yearly snapshots")
|
||||
f.VarP(&forgetOptions.Within, "keep-within", "", "keep snapshots that are older than `duration` (eg. 1y5m7d) relative to the latest snapshot")
|
||||
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)")
|
||||
f.StringVar(&forgetOptions.Host, "host", "", "only consider snapshots with the given `host`")
|
||||
@@ -90,153 +88,129 @@ func runForget(opts ForgetOptions, gopts GlobalOptions, args []string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// group by hostname and dirs
|
||||
type key struct {
|
||||
Hostname string
|
||||
Paths []string
|
||||
Tags []string
|
||||
}
|
||||
snapshotGroups := make(map[string]restic.Snapshots)
|
||||
|
||||
var GroupByTag bool
|
||||
var GroupByHost bool
|
||||
var GroupByPath bool
|
||||
var GroupOptionList []string
|
||||
|
||||
GroupOptionList = strings.Split(opts.GroupBy, ",")
|
||||
|
||||
for _, option := range GroupOptionList {
|
||||
switch option {
|
||||
case "host":
|
||||
GroupByHost = true
|
||||
case "paths":
|
||||
GroupByPath = true
|
||||
case "tags":
|
||||
GroupByTag = true
|
||||
case "":
|
||||
default:
|
||||
return errors.Fatal("unknown grouping option: '" + option + "'")
|
||||
}
|
||||
}
|
||||
|
||||
removeSnapshots := 0
|
||||
|
||||
ctx, cancel := context.WithCancel(gopts.ctx)
|
||||
defer cancel()
|
||||
|
||||
var snapshots restic.Snapshots
|
||||
|
||||
for sn := range FindFilteredSnapshots(ctx, repo, opts.Host, opts.Tags, opts.Paths, args) {
|
||||
if len(args) > 0 {
|
||||
// When explicit snapshots args are given, remove them immediately.
|
||||
snapshots = append(snapshots, sn)
|
||||
}
|
||||
|
||||
if len(args) > 0 {
|
||||
// When explicit snapshots args are given, remove them immediately.
|
||||
for _, sn := range snapshots {
|
||||
if !opts.DryRun {
|
||||
h := restic.Handle{Type: restic.SnapshotFile, Name: sn.ID().String()}
|
||||
if err = repo.Backend().Remove(gopts.ctx, h); err != nil {
|
||||
return err
|
||||
}
|
||||
Verbosef("removed snapshot %v\n", sn.ID().Str())
|
||||
if !gopts.JSON {
|
||||
Verbosef("removed snapshot %v\n", sn.ID().Str())
|
||||
}
|
||||
removeSnapshots++
|
||||
} else {
|
||||
Verbosef("would have removed snapshot %v\n", sn.ID().Str())
|
||||
if !gopts.JSON {
|
||||
Verbosef("would have removed snapshot %v\n", sn.ID().Str())
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Determining grouping-keys
|
||||
var tags []string
|
||||
var hostname string
|
||||
var paths []string
|
||||
|
||||
if GroupByTag {
|
||||
tags = sn.Tags
|
||||
sort.StringSlice(tags).Sort()
|
||||
}
|
||||
if GroupByHost {
|
||||
hostname = sn.Hostname
|
||||
}
|
||||
if GroupByPath {
|
||||
paths = sn.Paths
|
||||
}
|
||||
|
||||
sort.StringSlice(sn.Paths).Sort()
|
||||
var k []byte
|
||||
var err error
|
||||
|
||||
k, err = json.Marshal(key{Tags: tags, Hostname: hostname, Paths: paths})
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
snapshotGroups[string(k)] = append(snapshotGroups[string(k)], sn)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
snapshotGroups, _, err := restic.GroupSnapshots(snapshots, opts.GroupBy)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
policy := restic.ExpirePolicy{
|
||||
Last: opts.Last,
|
||||
Hourly: opts.Hourly,
|
||||
Daily: opts.Daily,
|
||||
Weekly: opts.Weekly,
|
||||
Monthly: opts.Monthly,
|
||||
Yearly: opts.Yearly,
|
||||
Within: opts.Within,
|
||||
Tags: opts.KeepTags,
|
||||
}
|
||||
policy := restic.ExpirePolicy{
|
||||
Last: opts.Last,
|
||||
Hourly: opts.Hourly,
|
||||
Daily: opts.Daily,
|
||||
Weekly: opts.Weekly,
|
||||
Monthly: opts.Monthly,
|
||||
Yearly: opts.Yearly,
|
||||
Within: opts.Within,
|
||||
Tags: opts.KeepTags,
|
||||
}
|
||||
|
||||
if policy.Empty() && len(args) == 0 {
|
||||
Verbosef("no policy was specified, no snapshots will be removed\n")
|
||||
}
|
||||
if policy.Empty() && len(args) == 0 {
|
||||
if !gopts.JSON {
|
||||
Verbosef("no policy was specified, no snapshots will be removed\n")
|
||||
}
|
||||
}
|
||||
|
||||
if !policy.Empty() {
|
||||
Verbosef("Applying Policy: %v\n", policy)
|
||||
|
||||
for k, snapshotGroup := range snapshotGroups {
|
||||
var key key
|
||||
if json.Unmarshal([]byte(k), &key) != nil {
|
||||
return err
|
||||
if !policy.Empty() {
|
||||
if !gopts.JSON {
|
||||
Verbosef("Applying Policy: %v\n", policy)
|
||||
}
|
||||
|
||||
// Info
|
||||
Verbosef("snapshots")
|
||||
var infoStrings []string
|
||||
if GroupByTag {
|
||||
infoStrings = append(infoStrings, "tags ["+strings.Join(key.Tags, ", ")+"]")
|
||||
}
|
||||
if GroupByHost {
|
||||
infoStrings = append(infoStrings, "host ["+key.Hostname+"]")
|
||||
}
|
||||
if GroupByPath {
|
||||
infoStrings = append(infoStrings, "paths ["+strings.Join(key.Paths, ", ")+"]")
|
||||
}
|
||||
if infoStrings != nil {
|
||||
Verbosef(" for (" + strings.Join(infoStrings, ", ") + ")")
|
||||
}
|
||||
Verbosef(":\n\n")
|
||||
var jsonGroups []*ForgetGroup
|
||||
|
||||
keep, remove, reasons := restic.ApplyPolicy(snapshotGroup, policy)
|
||||
|
||||
if len(keep) != 0 && !gopts.Quiet {
|
||||
Printf("keep %d snapshots:\n", len(keep))
|
||||
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, nil, opts.Compact)
|
||||
Printf("\n")
|
||||
}
|
||||
|
||||
removeSnapshots += len(remove)
|
||||
|
||||
if !opts.DryRun {
|
||||
for _, sn := range remove {
|
||||
h := restic.Handle{Type: restic.SnapshotFile, Name: sn.ID().String()}
|
||||
err = repo.Backend().Remove(gopts.ctx, h)
|
||||
for k, snapshotGroup := range snapshotGroups {
|
||||
if gopts.Verbose >= 1 && !gopts.JSON {
|
||||
err = PrintSnapshotGroupHeader(gopts.stdout, k)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
var key restic.SnapshotGroupKey
|
||||
if json.Unmarshal([]byte(k), &key) != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var fg ForgetGroup
|
||||
fg.Tags = key.Tags
|
||||
fg.Host = key.Hostname
|
||||
fg.Paths = key.Paths
|
||||
|
||||
keep, remove, reasons := restic.ApplyPolicy(snapshotGroup, policy)
|
||||
|
||||
if len(keep) != 0 && !gopts.Quiet && !gopts.JSON {
|
||||
Printf("keep %d snapshots:\n", len(keep))
|
||||
PrintSnapshots(globalOptions.stdout, keep, reasons, opts.Compact)
|
||||
Printf("\n")
|
||||
}
|
||||
addJSONSnapshots(&fg.Keep, keep)
|
||||
|
||||
if len(remove) != 0 && !gopts.Quiet && !gopts.JSON {
|
||||
Printf("remove %d snapshots:\n", len(remove))
|
||||
PrintSnapshots(globalOptions.stdout, remove, nil, opts.Compact)
|
||||
Printf("\n")
|
||||
}
|
||||
addJSONSnapshots(&fg.Remove, remove)
|
||||
|
||||
fg.Reasons = reasons
|
||||
|
||||
jsonGroups = append(jsonGroups, &fg)
|
||||
|
||||
removeSnapshots += len(remove)
|
||||
|
||||
if !opts.DryRun {
|
||||
for _, sn := range remove {
|
||||
h := restic.Handle{Type: restic.SnapshotFile, Name: sn.ID().String()}
|
||||
err = repo.Backend().Remove(gopts.ctx, h)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if gopts.JSON {
|
||||
err = printJSONForget(gopts.stdout, jsonGroups)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if removeSnapshots > 0 && opts.Prune {
|
||||
Verbosef("%d snapshots have been removed, running prune\n", removeSnapshots)
|
||||
if !gopts.JSON {
|
||||
Verbosef("%d snapshots have been removed, running prune\n", removeSnapshots)
|
||||
}
|
||||
if !opts.DryRun {
|
||||
return pruneRepository(gopts, repo)
|
||||
}
|
||||
@@ -244,3 +218,28 @@ func runForget(opts ForgetOptions, gopts GlobalOptions, args []string) error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ForgetGroup helps to print what is forgotten in JSON.
|
||||
type ForgetGroup struct {
|
||||
Tags []string `json:"tags"`
|
||||
Host string `json:"host"`
|
||||
Paths []string `json:"paths"`
|
||||
Keep []Snapshot `json:"keep"`
|
||||
Remove []Snapshot `json:"remove"`
|
||||
Reasons []restic.KeepReason `json:"reasons"`
|
||||
}
|
||||
|
||||
func addJSONSnapshots(js *[]Snapshot, list restic.Snapshots) {
|
||||
for _, sn := range list {
|
||||
k := Snapshot{
|
||||
Snapshot: sn,
|
||||
ID: sn.ID(),
|
||||
ShortID: sn.ID().Str(),
|
||||
}
|
||||
*js = append(*js, k)
|
||||
}
|
||||
}
|
||||
|
||||
func printJSONForget(stdout io.Writer, forgets []*ForgetGroup) error {
|
||||
return json.NewEncoder(stdout).Encode(forgets)
|
||||
}
|
||||
|
@@ -12,7 +12,7 @@ var cmdGenerate = &cobra.Command{
|
||||
Use: "generate [command]",
|
||||
Short: "Generate manual pages and auto-completion files (bash, zsh)",
|
||||
Long: `
|
||||
The "generate" command writes automatically generated files like the man pages
|
||||
The "generate" command writes automatically generated files (like the man pages
|
||||
and the auto-completion files for bash and zsh).
|
||||
`,
|
||||
DisableAutoGenTag: true,
|
||||
|
@@ -59,7 +59,7 @@ 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),
|
||||
}
|
||||
|
||||
keys = append(keys, key)
|
||||
|
@@ -64,10 +64,8 @@ func init() {
|
||||
|
||||
type lsSnapshot struct {
|
||||
*restic.Snapshot
|
||||
|
||||
ID *restic.ID `json:"id"`
|
||||
ShortID string `json:"short_id"`
|
||||
Nodes []lsNode `json:"nodes"`
|
||||
StructType string `json:"struct_type"` // "snapshot"
|
||||
}
|
||||
|
||||
@@ -150,24 +148,22 @@ func runLs(opts LsOptions, gopts GlobalOptions, args []string) error {
|
||||
var (
|
||||
printSnapshot func(sn *restic.Snapshot)
|
||||
printNode func(path string, node *restic.Node)
|
||||
printFinish func() error
|
||||
)
|
||||
|
||||
if gopts.JSON {
|
||||
var lssnapshots []lsSnapshot
|
||||
enc := json.NewEncoder(gopts.stdout)
|
||||
|
||||
printSnapshot = func(sn *restic.Snapshot) {
|
||||
lss := lsSnapshot{
|
||||
enc.Encode(lsSnapshot{
|
||||
Snapshot: sn,
|
||||
ID: sn.ID(),
|
||||
ShortID: sn.ID().Str(),
|
||||
StructType: "snapshot",
|
||||
}
|
||||
lssnapshots = append(lssnapshots, lss)
|
||||
})
|
||||
}
|
||||
|
||||
printNode = func(path string, node *restic.Node) {
|
||||
lsn := lsNode{
|
||||
enc.Encode(lsNode{
|
||||
Name: node.Name,
|
||||
Type: node.Type,
|
||||
Path: path,
|
||||
@@ -179,25 +175,15 @@ func runLs(opts LsOptions, gopts GlobalOptions, args []string) error {
|
||||
AccessTime: node.AccessTime,
|
||||
ChangeTime: node.ChangeTime,
|
||||
StructType: "node",
|
||||
}
|
||||
s := &lssnapshots[len(lssnapshots)-1]
|
||||
s.Nodes = append(s.Nodes, lsn)
|
||||
}
|
||||
|
||||
printFinish = func() error {
|
||||
return json.NewEncoder(gopts.stdout).Encode(lssnapshots)
|
||||
})
|
||||
}
|
||||
} else {
|
||||
// default output methods
|
||||
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))
|
||||
}
|
||||
printFinish = func() error {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
for sn := range FindFilteredSnapshots(ctx, repo, opts.Host, opts.Tags, opts.Paths, args[:1]) {
|
||||
@@ -240,5 +226,6 @@ func runLs(opts LsOptions, gopts GlobalOptions, args []string) error {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return printFinish()
|
||||
|
||||
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...)
|
||||
@@ -142,7 +149,7 @@ func mount(opts MountOptions, gopts GlobalOptions, mountpoint string) error {
|
||||
}
|
||||
|
||||
Printf("Now serving the repository at %s\n", mountpoint)
|
||||
Printf("Don't forget to umount after quitting!\n")
|
||||
Printf("When finished, quit with Ctrl-c or umount the mountpoint.\n")
|
||||
|
||||
debug.Log("serving mount at %v", mountpoint)
|
||||
err = fs.Serve(c, root)
|
||||
|
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
|
||||
}
|
@@ -6,6 +6,7 @@ import (
|
||||
"github.com/restic/restic/internal/filter"
|
||||
"github.com/restic/restic/internal/restic"
|
||||
"github.com/restic/restic/internal/restorer"
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
@@ -28,13 +29,15 @@ repository.
|
||||
|
||||
// RestoreOptions collects all options for the restore command.
|
||||
type RestoreOptions struct {
|
||||
Exclude []string
|
||||
Include []string
|
||||
Target string
|
||||
Host string
|
||||
Paths []string
|
||||
Tags restic.TagLists
|
||||
Verify bool
|
||||
Exclude []string
|
||||
InsensitiveExclude []string
|
||||
Include []string
|
||||
InsensitiveInclude []string
|
||||
Target string
|
||||
Host string
|
||||
Paths []string
|
||||
Tags restic.TagLists
|
||||
Verify bool
|
||||
}
|
||||
|
||||
var restoreOptions RestoreOptions
|
||||
@@ -44,7 +47,9 @@ func init() {
|
||||
|
||||
flags := cmdRestore.Flags()
|
||||
flags.StringArrayVarP(&restoreOptions.Exclude, "exclude", "e", nil, "exclude a `pattern` (can be specified multiple times)")
|
||||
flags.StringArrayVar(&restoreOptions.InsensitiveExclude, "iexclude", nil, "same as `--exclude` but ignores the casing of filenames")
|
||||
flags.StringArrayVarP(&restoreOptions.Include, "include", "i", nil, "include a `pattern`, exclude everything else (can be specified multiple times)")
|
||||
flags.StringArrayVar(&restoreOptions.InsensitiveInclude, "iinclude", nil, "same as `--include` but ignores the casing of filenames")
|
||||
flags.StringVarP(&restoreOptions.Target, "target", "t", "", "directory to extract data to")
|
||||
|
||||
flags.StringVarP(&restoreOptions.Host, "host", "H", "", `only consider snapshots for this host when the snapshot ID is "latest"`)
|
||||
@@ -55,6 +60,16 @@ func init() {
|
||||
|
||||
func runRestore(opts RestoreOptions, gopts GlobalOptions, args []string) error {
|
||||
ctx := gopts.ctx
|
||||
hasExcludes := len(opts.Exclude) > 0 || len(opts.InsensitiveExclude) > 0
|
||||
hasIncludes := len(opts.Include) > 0 || len(opts.InsensitiveInclude) > 0
|
||||
|
||||
for i, str := range opts.InsensitiveExclude {
|
||||
opts.InsensitiveExclude[i] = strings.ToLower(str)
|
||||
}
|
||||
|
||||
for i, str := range opts.InsensitiveInclude {
|
||||
opts.InsensitiveInclude[i] = strings.ToLower(str)
|
||||
}
|
||||
|
||||
switch {
|
||||
case len(args) == 0:
|
||||
@@ -67,7 +82,7 @@ func runRestore(opts RestoreOptions, gopts GlobalOptions, args []string) error {
|
||||
return errors.Fatal("please specify a directory to restore to (--target)")
|
||||
}
|
||||
|
||||
if len(opts.Exclude) > 0 && len(opts.Include) > 0 {
|
||||
if hasExcludes && hasIncludes {
|
||||
return errors.Fatal("exclude and include patterns are mutually exclusive")
|
||||
}
|
||||
|
||||
@@ -113,8 +128,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
|
||||
}
|
||||
@@ -125,11 +140,16 @@ func runRestore(opts RestoreOptions, gopts GlobalOptions, args []string) error {
|
||||
Warnf("error for exclude pattern: %v", err)
|
||||
}
|
||||
|
||||
matchedInsensitive, _, err := filter.List(opts.InsensitiveExclude, strings.ToLower(item))
|
||||
if err != nil {
|
||||
Warnf("error for iexclude pattern: %v", err)
|
||||
}
|
||||
|
||||
// An exclude filter is basically a 'wildcard but foo',
|
||||
// so even if a childMayMatch, other children of a dir may not,
|
||||
// therefore childMayMatch does not matter, but we should not go down
|
||||
// unless the dir is selected for restore
|
||||
selectedForRestore = !matched
|
||||
selectedForRestore = !matched && !matchedInsensitive
|
||||
childMayBeSelected = selectedForRestore && node.Type == "dir"
|
||||
|
||||
return selectedForRestore, childMayBeSelected
|
||||
@@ -141,15 +161,20 @@ func runRestore(opts RestoreOptions, gopts GlobalOptions, args []string) error {
|
||||
Warnf("error for include pattern: %v", err)
|
||||
}
|
||||
|
||||
selectedForRestore = matched
|
||||
childMayBeSelected = childMayMatch && node.Type == "dir"
|
||||
matchedInsensitive, childMayMatchInsensitive, err := filter.List(opts.InsensitiveInclude, strings.ToLower(item))
|
||||
if err != nil {
|
||||
Warnf("error for iexclude pattern: %v", err)
|
||||
}
|
||||
|
||||
selectedForRestore = matched || matchedInsensitive
|
||||
childMayBeSelected = (childMayMatch || childMayMatchInsensitive) && node.Type == "dir"
|
||||
|
||||
return selectedForRestore, childMayBeSelected
|
||||
}
|
||||
|
||||
if len(opts.Exclude) > 0 {
|
||||
if hasExcludes {
|
||||
res.SelectFilter = selectExcludeFilter
|
||||
} else if len(opts.Include) > 0 {
|
||||
} else if hasIncludes {
|
||||
res.SelectFilter = selectIncludeFilter
|
||||
}
|
||||
|
||||
|
@@ -1,4 +1,4 @@
|
||||
// +build selfupdate
|
||||
// xbuild selfupdate
|
||||
|
||||
package main
|
||||
|
||||
@@ -14,7 +14,7 @@ var cmdSelfUpdate = &cobra.Command{
|
||||
Use: "self-update [flags]",
|
||||
Short: "Update the restic binary",
|
||||
Long: `
|
||||
The command "update-restic" downloads the latest stable release of restic from
|
||||
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.
|
||||
@@ -36,16 +36,38 @@ func init() {
|
||||
cmdRoot.AddCommand(cmdSelfUpdate)
|
||||
|
||||
flags := cmdSelfUpdate.Flags()
|
||||
flags.StringVar(&selfUpdateOptions.Output, "output", os.Args[0], "Save the downloaded file as `filename`")
|
||||
flags.StringVar(&selfUpdateOptions.Output, "output", "", "Save the downloaded file as `filename` (default: running binary itself)")
|
||||
}
|
||||
|
||||
func runSelfUpdate(opts SelfUpdateOptions, gopts GlobalOptions, args []string) error {
|
||||
v, err := selfupdate.DownloadLatestStableRelease(gopts.ctx, opts.Output, Verbosef)
|
||||
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)
|
||||
}
|
||||
|
||||
Printf("successfully updated restic to version %v\n", v)
|
||||
if v != version {
|
||||
Printf("successfully updated restic to version %v\n", v)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
@@ -32,6 +32,7 @@ type SnapshotOptions struct {
|
||||
Paths []string
|
||||
Compact bool
|
||||
Last bool
|
||||
GroupBy string
|
||||
}
|
||||
|
||||
var snapshotOptions SnapshotOptions
|
||||
@@ -45,6 +46,7 @@ func init() {
|
||||
f.StringArrayVar(&snapshotOptions.Paths, "path", nil, "only consider snapshots for this `path` (can be specified multiple times)")
|
||||
f.BoolVarP(&snapshotOptions.Compact, "compact", "c", false, "use compact format")
|
||||
f.BoolVar(&snapshotOptions.Last, "last", false, "only show the last snapshot for each host and path")
|
||||
f.StringVarP(&snapshotOptions.GroupBy, "group-by", "g", "", "string for grouping snapshots by host,paths,tags")
|
||||
}
|
||||
|
||||
func runSnapshots(opts SnapshotOptions, gopts GlobalOptions, args []string) error {
|
||||
@@ -64,25 +66,41 @@ func runSnapshots(opts SnapshotOptions, gopts GlobalOptions, args []string) erro
|
||||
ctx, cancel := context.WithCancel(gopts.ctx)
|
||||
defer cancel()
|
||||
|
||||
var list restic.Snapshots
|
||||
var snapshots restic.Snapshots
|
||||
for sn := range FindFilteredSnapshots(ctx, repo, opts.Host, opts.Tags, opts.Paths, args) {
|
||||
list = append(list, sn)
|
||||
snapshots = append(snapshots, sn)
|
||||
}
|
||||
snapshotGroups, grouped, err := restic.GroupSnapshots(snapshots, opts.GroupBy)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if opts.Last {
|
||||
list = FilterLastSnapshots(list)
|
||||
for k, list := range snapshotGroups {
|
||||
if opts.Last {
|
||||
list = FilterLastSnapshots(list)
|
||||
}
|
||||
sort.Sort(sort.Reverse(list))
|
||||
snapshotGroups[k] = list
|
||||
}
|
||||
|
||||
sort.Sort(sort.Reverse(list))
|
||||
|
||||
if gopts.JSON {
|
||||
err := printSnapshotsJSON(gopts.stdout, list)
|
||||
err := printSnapshotGroupJSON(gopts.stdout, snapshotGroups, grouped)
|
||||
if err != nil {
|
||||
Warnf("error printing snapshot: %v\n", err)
|
||||
Warnf("error printing snapshots: %v\n", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
PrintSnapshots(gopts.stdout, list, nil, opts.Compact)
|
||||
|
||||
for k, list := range snapshotGroups {
|
||||
if grouped {
|
||||
err := PrintSnapshotGroupHeader(gopts.stdout, k)
|
||||
if err != nil {
|
||||
Warnf("error printing snapshots: %v\n", err)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
PrintSnapshots(gopts.stdout, list, nil, opts.Compact)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -184,7 +202,7 @@ func PrintSnapshots(stdout io.Writer, list restic.Snapshots, reasons []restic.Ke
|
||||
for _, sn := range list {
|
||||
data := snapshot{
|
||||
ID: sn.ID().Str(),
|
||||
Timestamp: sn.Time.Format(TimeFormat),
|
||||
Timestamp: sn.Time.Local().Format(TimeFormat),
|
||||
Hostname: sn.Hostname,
|
||||
Tags: sn.Tags,
|
||||
Paths: sn.Paths,
|
||||
@@ -195,7 +213,7 @@ func PrintSnapshots(stdout io.Writer, list restic.Snapshots, reasons []restic.Ke
|
||||
data.Reasons = keepReasons[*id].Matches
|
||||
}
|
||||
|
||||
if len(sn.Paths) > 1 {
|
||||
if len(sn.Paths) > 1 && !compact {
|
||||
multiline = true
|
||||
}
|
||||
|
||||
@@ -223,6 +241,42 @@ func PrintSnapshots(stdout io.Writer, list restic.Snapshots, reasons []restic.Ke
|
||||
tab.Write(stdout)
|
||||
}
|
||||
|
||||
// PrintSnapshotGroupHeader prints which group of the group-by option the
|
||||
// following snapshots belong to.
|
||||
// Prints nothing, if we did not group at all.
|
||||
func PrintSnapshotGroupHeader(stdout io.Writer, groupKeyJSON string) error {
|
||||
var key restic.SnapshotGroupKey
|
||||
var err error
|
||||
|
||||
err = json.Unmarshal([]byte(groupKeyJSON), &key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if key.Hostname == "" && key.Tags == nil && key.Paths == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Info
|
||||
fmt.Fprintf(stdout, "snapshots")
|
||||
var infoStrings []string
|
||||
if key.Hostname != "" {
|
||||
infoStrings = append(infoStrings, "host ["+key.Hostname+"]")
|
||||
}
|
||||
if key.Tags != nil {
|
||||
infoStrings = append(infoStrings, "tags ["+strings.Join(key.Tags, ", ")+"]")
|
||||
}
|
||||
if key.Paths != nil {
|
||||
infoStrings = append(infoStrings, "paths ["+strings.Join(key.Paths, ", ")+"]")
|
||||
}
|
||||
if infoStrings != nil {
|
||||
fmt.Fprintf(stdout, " for (%s)", strings.Join(infoStrings, ", "))
|
||||
}
|
||||
fmt.Fprintf(stdout, ":\n")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Snapshot helps to print Snaphots as JSON with their ID included.
|
||||
type Snapshot struct {
|
||||
*restic.Snapshot
|
||||
@@ -231,19 +285,58 @@ type Snapshot struct {
|
||||
ShortID string `json:"short_id"`
|
||||
}
|
||||
|
||||
// printSnapshotsJSON writes the JSON representation of list to stdout.
|
||||
func printSnapshotsJSON(stdout io.Writer, list restic.Snapshots) error {
|
||||
// SnapshotGroup helps to print SnaphotGroups as JSON with their GroupReasons included.
|
||||
type SnapshotGroup struct {
|
||||
GroupKey restic.SnapshotGroupKey `json:"group_key"`
|
||||
Snapshots []Snapshot `json:"snapshots"`
|
||||
}
|
||||
|
||||
// printSnapshotsJSON writes the JSON representation of list to stdout.
|
||||
func printSnapshotGroupJSON(stdout io.Writer, snGroups map[string]restic.Snapshots, grouped bool) error {
|
||||
if grouped {
|
||||
var snapshotGroups []SnapshotGroup
|
||||
|
||||
for k, list := range snGroups {
|
||||
var key restic.SnapshotGroupKey
|
||||
var err error
|
||||
var snapshots []Snapshot
|
||||
|
||||
err = json.Unmarshal([]byte(k), &key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, sn := range list {
|
||||
k := Snapshot{
|
||||
Snapshot: sn,
|
||||
ID: sn.ID(),
|
||||
ShortID: sn.ID().Str(),
|
||||
}
|
||||
snapshots = append(snapshots, k)
|
||||
}
|
||||
|
||||
group := SnapshotGroup{
|
||||
GroupKey: key,
|
||||
Snapshots: snapshots,
|
||||
}
|
||||
snapshotGroups = append(snapshotGroups, group)
|
||||
}
|
||||
|
||||
return json.NewEncoder(stdout).Encode(snapshotGroups)
|
||||
}
|
||||
|
||||
// Old behavior
|
||||
var snapshots []Snapshot
|
||||
|
||||
for _, sn := range list {
|
||||
|
||||
k := Snapshot{
|
||||
Snapshot: sn,
|
||||
ID: sn.ID(),
|
||||
ShortID: sn.ID().Str(),
|
||||
for _, list := range snGroups {
|
||||
for _, sn := range list {
|
||||
k := Snapshot{
|
||||
Snapshot: sn,
|
||||
ID: sn.ID(),
|
||||
ShortID: sn.ID().Str(),
|
||||
}
|
||||
snapshots = append(snapshots, k)
|
||||
}
|
||||
snapshots = append(snapshots, k)
|
||||
}
|
||||
|
||||
return json.NewEncoder(stdout).Encode(snapshots)
|
||||
|
@@ -36,7 +36,8 @@ The modes are:
|
||||
* 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.
|
||||
|
||||
Refer to the online manual for more details about each mode.
|
||||
`,
|
||||
DisableAutoGenTag: true,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
@@ -48,7 +49,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", "H", "filter latest snapshot by this hostname")
|
||||
f.StringVarP(&snapshotByHost, "host", "H", "", "filter latest snapshot by this hostname")
|
||||
}
|
||||
|
||||
func runStats(gopts GlobalOptions, args []string) error {
|
||||
@@ -296,7 +297,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
|
||||
}
|
||||
|
@@ -88,6 +88,18 @@ func rejectByPattern(patterns []string) RejectByNameFunc {
|
||||
}
|
||||
}
|
||||
|
||||
// Same as `rejectByPattern` but case insensitive.
|
||||
func rejectByInsensitivePattern(patterns []string) RejectByNameFunc {
|
||||
for index, path := range patterns {
|
||||
patterns[index] = strings.ToLower(path)
|
||||
}
|
||||
|
||||
rejFunc := rejectByPattern(patterns)
|
||||
return func(item string) bool {
|
||||
return rejFunc(strings.ToLower(item))
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
|
@@ -36,6 +36,33 @@ func TestRejectByPattern(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestRejectByInsensitivePattern(t *testing.T) {
|
||||
var tests = []struct {
|
||||
filename string
|
||||
reject bool
|
||||
}{
|
||||
{filename: "/home/user/foo.GO", reject: true},
|
||||
{filename: "/home/user/foo.c", reject: false},
|
||||
{filename: "/home/user/foobar", reject: false},
|
||||
{filename: "/home/user/FOObar/x", reject: true},
|
||||
{filename: "/home/user/README", reject: false},
|
||||
{filename: "/home/user/readme.md", reject: true},
|
||||
}
|
||||
|
||||
patterns := []string{"*.go", "README.md", "/home/user/foobar/*"}
|
||||
|
||||
for _, tc := range tests {
|
||||
t.Run("", func(t *testing.T) {
|
||||
reject := rejectByInsensitivePattern(patterns)
|
||||
res := reject(tc.filename)
|
||||
if res != tc.reject {
|
||||
t.Fatalf("wrong result for filename %v: want %v, got %v",
|
||||
tc.filename, tc.reject, res)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsExcludedByFile(t *testing.T) {
|
||||
const (
|
||||
tagFilename = "CACHEDIR.TAG"
|
||||
|
@@ -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)
|
||||
}
|
||||
|
@@ -1,6 +1,7 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
@@ -34,26 +35,29 @@ import (
|
||||
"github.com/restic/restic/internal/errors"
|
||||
|
||||
"golang.org/x/crypto/ssh/terminal"
|
||||
"os/exec"
|
||||
)
|
||||
|
||||
var version = "0.9.3"
|
||||
var version = "0.9.5"
|
||||
|
||||
// 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
|
||||
@@ -91,6 +95,8 @@ 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")
|
||||
@@ -179,7 +185,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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -220,7 +225,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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -236,7 +240,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)) {
|
||||
@@ -245,7 +265,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
|
||||
}
|
||||
|
||||
@@ -254,15 +274,10 @@ func resolvePassword(opts GlobalOptions, env string) (string, error) {
|
||||
|
||||
// readPassword reads the password from the given reader directly.
|
||||
func readPassword(in io.Reader) (password string, err error) {
|
||||
buf := make([]byte, 1000)
|
||||
n, err := io.ReadFull(in, buf)
|
||||
buf = buf[:n]
|
||||
sc := bufio.NewScanner(in)
|
||||
sc.Scan()
|
||||
|
||||
if err != nil && errors.Cause(err) != io.ErrUnexpectedEOF {
|
||||
return "", errors.Wrap(err, "ReadFull")
|
||||
}
|
||||
|
||||
return strings.TrimRight(string(buf), "\r\n"), nil
|
||||
return sc.Text(), errors.Wrap(err, "Scan")
|
||||
}
|
||||
|
||||
// readPasswordTerminal reads the password from the given reader which must be a
|
||||
@@ -317,13 +332,15 @@ func ReadPasswordTwice(gopts GlobalOptions, prompt1, prompt2 string) (string, er
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
pw2, err := ReadPassword(gopts, prompt2)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if stdinIsTerminal() {
|
||||
pw2, err := ReadPassword(gopts, prompt2)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if pw1 != pw2 {
|
||||
return "", errors.Fatal("passwords do not match")
|
||||
if pw1 != pw2 {
|
||||
return "", errors.Fatal("passwords do not match")
|
||||
}
|
||||
}
|
||||
|
||||
return pw1, nil
|
||||
@@ -353,17 +370,19 @@ 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
|
||||
}
|
||||
|
||||
if stdoutIsTerminal() {
|
||||
if stdoutIsTerminal() && !opts.JSON {
|
||||
id := s.Config().ID
|
||||
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 {
|
||||
|
@@ -219,6 +219,35 @@ func testRunForget(t testing.TB, gopts GlobalOptions, args ...string) {
|
||||
rtest.OK(t, runForget(opts, gopts, args))
|
||||
}
|
||||
|
||||
func testRunForgetJSON(t testing.TB, gopts GlobalOptions, args ...string) {
|
||||
buf := bytes.NewBuffer(nil)
|
||||
oldJSON := gopts.JSON
|
||||
gopts.stdout = buf
|
||||
gopts.JSON = true
|
||||
defer func() {
|
||||
gopts.stdout = os.Stdout
|
||||
gopts.JSON = oldJSON
|
||||
}()
|
||||
|
||||
opts := ForgetOptions{
|
||||
DryRun: true,
|
||||
Last: 1,
|
||||
}
|
||||
|
||||
rtest.OK(t, runForget(opts, gopts, args))
|
||||
|
||||
var forgets []*ForgetGroup
|
||||
rtest.OK(t, json.Unmarshal(buf.Bytes(), &forgets))
|
||||
|
||||
rtest.Assert(t, len(forgets) == 1,
|
||||
"Expected 1 snapshot group, got %v", len(forgets))
|
||||
rtest.Assert(t, len(forgets[0].Keep) == 1,
|
||||
"Expected 1 snapshot to be kept, got %v", len(forgets[0].Keep))
|
||||
rtest.Assert(t, len(forgets[0].Remove) == 2,
|
||||
"Expected 2 snapshots to be removed, got %v", len(forgets[0].Remove))
|
||||
return
|
||||
}
|
||||
|
||||
func testRunPrune(t testing.TB, gopts GlobalOptions) {
|
||||
rtest.OK(t, runPrune(gopts))
|
||||
}
|
||||
@@ -1051,6 +1080,7 @@ func TestPrune(t *testing.T) {
|
||||
rtest.Assert(t, len(snapshotIDs) == 3,
|
||||
"expected 3 snapshot, got %v", snapshotIDs)
|
||||
|
||||
testRunForgetJSON(t, env.gopts)
|
||||
testRunForget(t, env.gopts, firstSnapshot[0].String())
|
||||
testRunPrune(t, env.gopts)
|
||||
testRunCheck(t, env.gopts)
|
||||
|
@@ -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)
|
||||
|
@@ -30,12 +30,12 @@ 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
|
||||
$ pacman -S restic
|
||||
|
||||
Debian
|
||||
======
|
||||
@@ -72,12 +72,18 @@ macOS
|
||||
=====
|
||||
|
||||
If you are using macOS, you can install restic using the
|
||||
`homebrew <http://brew.sh/>`__ package manager:
|
||||
`homebrew <https://brew.sh/>`__ package manager:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ brew install restic
|
||||
|
||||
You may also install it using `MacPorts <https://www.macports.org/>`__:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ sudo port install restic
|
||||
|
||||
Nix & NixOS
|
||||
===========
|
||||
|
||||
@@ -168,28 +174,28 @@ 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:
|
||||
command (needs restic 0.9.3 or later):
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ restic version
|
||||
restic 0.9.1 compiled with go1.10.3 on linux/amd64
|
||||
restic 0.9.3 compiled with go1.11.2 on linux/amd64
|
||||
|
||||
$ restic self-update
|
||||
find latest release of restic at GitHub
|
||||
latest version is 0.9.2
|
||||
latest version is 0.9.4
|
||||
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
|
||||
download restic_0.9.4_linux_amd64.bz2
|
||||
downloaded restic_0.9.4_linux_amd64.bz2
|
||||
saved 12115904 bytes in ./restic
|
||||
successfully updated restic to version 0.9.2
|
||||
successfully updated restic to version 0.9.4
|
||||
|
||||
$ restic version
|
||||
restic 0.9.2 compiled with go1.10.3 on linux/amd64
|
||||
restic 0.9.4 compiled with go1.12.1 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.
|
||||
|
@@ -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
|
||||
****
|
||||
|
||||
@@ -114,7 +122,17 @@ Last, if you'd like to use an entirely different program to create the
|
||||
SFTP connection, you can specify the command to be run with the option
|
||||
``-o sftp.command="foobar"``.
|
||||
|
||||
.. note:: Please be aware that sftp servers close connections when no data is
|
||||
received by the client. This can happen when restic is processing huge
|
||||
amounts of unchanged data. To avoid this issue add the following lines
|
||||
to the client’s .ssh/config file:
|
||||
|
||||
::
|
||||
|
||||
ServerAliveInterval 60
|
||||
ServerAliveCountMax 240
|
||||
|
||||
|
||||
REST Server
|
||||
***********
|
||||
|
||||
@@ -260,6 +278,18 @@ the naming convention of those variables follows the official Python Swift clien
|
||||
$ export OS_PROJECT_NAME=<MY_PROJECT_NAME>
|
||||
$ export OS_PROJECT_DOMAIN_NAME=<MY_PROJECT_DOMAIN_NAME>
|
||||
|
||||
# For keystone v3 application credential authentication (application credential id)
|
||||
$ export OS_AUTH_URL=<MY_AUTH_URL>
|
||||
$ export OS_APPLICATION_CREDENTIAL_ID=<MY_APPLICATION_CREDENTIAL_ID>
|
||||
$ export OS_APPLICATION_CREDENTIAL_SECRET=<MY_APPLICATION_CREDENTIAL_SECRET>
|
||||
|
||||
# For keystone v3 application credential authentication (application credential name)
|
||||
$ export OS_AUTH_URL=<MY_AUTH_URL>
|
||||
$ export OS_USERNAME=<MY_USERNAME>
|
||||
$ export OS_USER_DOMAIN_NAME=<MY_DOMAIN_NAME>
|
||||
$ export OS_APPLICATION_CREDENTIAL_NAME=<MY_APPLICATION_CREDENTIAL_NAME>
|
||||
$ export OS_APPLICATION_CREDENTIAL_SECRET=<MY_APPLICATION_CREDENTIAL_SECRET>
|
||||
|
||||
# For authentication based on tokens
|
||||
$ export OS_STORAGE_URL=<MY_STORAGE_URL>
|
||||
$ export OS_AUTH_TOKEN=<MY_AUTH_TOKEN>
|
||||
@@ -294,14 +324,14 @@ Backblaze B2
|
||||
|
||||
Restic can backup data to any Backblaze B2 bucket. You need to first setup the
|
||||
following environment variables with the credentials you can find in the
|
||||
dashboard in on the "Buckets" page when signed into your B2 account:
|
||||
dashboard 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
|
||||
@@ -512,7 +542,7 @@ interaction. If you use emulation environments like
|
||||
``Mintty`` or ``rxvt``, you may get a password error.
|
||||
|
||||
You can workaround this by using a special tool called ``winpty`` (look
|
||||
`here <https://sourceforge.net/p/msys2/wiki/Porting/>`__ and
|
||||
`here <https://github.com/msys2/msys2/wiki/Porting>`__ and
|
||||
`here <https://github.com/rprichard/winpty>`__ for detail information).
|
||||
On MSYS2, you can install ``winpty`` as follows:
|
||||
|
||||
|
@@ -139,10 +139,10 @@ You can exclude folders and files by specifying exclude patterns, currently
|
||||
the exclude options are:
|
||||
|
||||
- ``--exclude`` Specified one or more times to exclude one or more items
|
||||
- ``--iexclude`` Same as ``--exclude`` but ignores the case of paths
|
||||
- ``--exclude-caches`` Specified once to exclude folders containing a special file
|
||||
- ``--exclude-file`` Specified one or more times to exclude items listed in a given file
|
||||
- ``--exclude-if-present`` Specified one or more times to exclude a folders content
|
||||
if it contains a given file (optionally having a given header)
|
||||
- ``--exclude-if-present foo`` Specified one or more times to exclude a folder's content if it contains a file called ``foo``` (optionally having a given header, no wildcards for the file name supported)
|
||||
|
||||
Let's say we have a file called ``excludes.txt`` with the following content:
|
||||
|
||||
@@ -204,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
|
||||
@@ -276,6 +279,10 @@ written, and the next backup needs to write new metadata again. If you really
|
||||
want to save the access time for files and directories, you can pass the
|
||||
``--with-atime`` option to the ``backup`` command.
|
||||
|
||||
In filesystems that do not support inode consistency, like FUSE-based ones and pCloud, it is
|
||||
possible to ignore inode on changed files comparison by passing ``--ignore-inode`` to
|
||||
``backup`` command.
|
||||
|
||||
Reading data from stdin
|
||||
***********************
|
||||
|
||||
@@ -286,6 +293,7 @@ this mode of operation, just supply the option ``--stdin`` to the
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ set -o pipefail
|
||||
$ mysqldump [...] | restic -r /srv/restic-repo backup --stdin
|
||||
|
||||
This creates a new snapshot of the output of ``mysqldump``. You can then
|
||||
@@ -299,6 +307,13 @@ specified with ``--stdin-filename``, e.g. like this:
|
||||
|
||||
$ mysqldump [...] | restic -r /srv/restic-repo backup --stdin --stdin-filename production.sql
|
||||
|
||||
The option ``pipefail`` is highly recommended so that a non-zero exit code from
|
||||
one of the programs in the pipe (e.g. ``mysqldump`` here) makes the whole chain
|
||||
return a non-zero exit code. Refer to the `Use the Unofficial Bash Strict Mode
|
||||
<http://redsymbol.net/articles/unofficial-bash-strict-mode/>`__ for more
|
||||
details on this.
|
||||
|
||||
|
||||
Tags for backup
|
||||
***************
|
||||
|
||||
@@ -357,7 +372,11 @@ environment variables. The following list of environment variables:
|
||||
|
||||
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_PROJECT_DOMAIN_NAME Project domain name for keystone authentication
|
||||
|
||||
OS_APPLICATION_CREDENTIAL_ID Application Credential ID (keystone v3)
|
||||
OS_APPLICATION_CREDENTIAL_NAME Application Credential Name (keystone v3)
|
||||
OS_APPLICATION_CREDENTIAL_SECRET Application Credential Secret (keystone v3)
|
||||
|
||||
OS_STORAGE_URL Storage URL for token authentication
|
||||
OS_AUTH_TOKEN Auth token for token authentication
|
||||
|
@@ -56,6 +56,31 @@ Or filter by host:
|
||||
|
||||
Combining filters is also possible.
|
||||
|
||||
Furthermore you can group the output by the same filters (host, paths, tags):
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ restic -r /srv/restic-repo snapshots --group-by host
|
||||
|
||||
enter password for repository:
|
||||
snapshots for (host [kasimir])
|
||||
ID Date Host Tags Directory
|
||||
----------------------------------------------------------------------
|
||||
40dc1520 2015-05-08 21:38:30 kasimir /home/user/work
|
||||
79766175 2015-05-08 21:40:19 kasimir /home/user/work
|
||||
2 snapshots
|
||||
snapshots for (host [luigi])
|
||||
ID Date Host Tags Directory
|
||||
----------------------------------------------------------------------
|
||||
bdbd3439 2015-05-08 21:45:17 luigi /home/art
|
||||
9f0bc19e 2015-05-08 21:46:11 luigi /srv
|
||||
2 snapshots
|
||||
snapshots for (host [kazik])
|
||||
ID Date Host Tags Directory
|
||||
----------------------------------------------------------------------
|
||||
590c8fc8 2015-05-08 21:47:38 kazik /srv
|
||||
1 snapshots
|
||||
|
||||
|
||||
Checking a repo's integrity and consistency
|
||||
===========================================
|
||||
@@ -66,7 +91,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
|
||||
@@ -101,7 +126,7 @@ data files:
|
||||
|
||||
Use ``--read-data-subset=n/t`` parameter to check subset of repository data
|
||||
files. The parameter takes two values, ``n`` and ``t``. All repository data
|
||||
files are logically devided in ``t`` roughly equal groups and only files that
|
||||
files are logically divided in ``t`` roughly equal groups and only files that
|
||||
belong to the group number ``n`` are checked. For example, the following
|
||||
commands check all repository data files over 5 separate invocations:
|
||||
|
||||
|
@@ -52,6 +52,10 @@ You can use the command ``restic ls latest`` or ``restic find foo`` to find the
|
||||
path to the file within the snapshot. This path you can then pass to
|
||||
`--include` in verbatim to only restore the single file or directory.
|
||||
|
||||
There are case insensitive variants of of ``--exclude`` and ``--include`` called
|
||||
``--iexclude`` and ``--iinclude``. These options will behave the same way but
|
||||
ignore the casing of paths.
|
||||
|
||||
Restore using mount
|
||||
===================
|
||||
|
||||
@@ -65,7 +69,7 @@ command to serve the repository with FUSE:
|
||||
$ restic -r /srv/restic-repo mount /mnt/restic
|
||||
enter password for repository:
|
||||
Now serving /srv/restic-repo at /mnt/restic
|
||||
Don't forget to umount after quitting!
|
||||
When finished, quit with Ctrl-c or umount the mountpoint.
|
||||
|
||||
Mounting repositories via FUSE is not possible on OpenBSD, Solaris/illumos
|
||||
and Windows. For Linux, the ``fuse`` kernel module needs to be loaded. For
|
||||
@@ -120,3 +124,13 @@ e.g.:
|
||||
.. code-block:: console
|
||||
|
||||
$ restic -r /srv/restic-repo dump --path /production.sql latest production.sql | mysql
|
||||
|
||||
It is also possible to ``dump`` the contents of a whole folder structure to
|
||||
stdout. To retain the information about the files and folders Restic will
|
||||
output the contents in the tar format:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ restic -r /srv/restic-repo dump /home/other/work latest > restore.tar
|
||||
|
||||
|
||||
|
@@ -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.
|
||||
@@ -209,7 +210,7 @@ all snapshots, use ``--keep-last 1`` and then finally remove the last
|
||||
snapshot ID manually (by passing the ID to ``forget``).
|
||||
|
||||
All snapshots are evaluated against all matching ``--keep-*`` counts. A
|
||||
single snapshot on 2017-09-30 (Sun) will count as a daily, weekly and monthly.
|
||||
single snapshot on 2017-09-30 (Sat) will count as a daily, weekly and monthly.
|
||||
|
||||
Let's explain this with an example: Suppose you have only made a backup
|
||||
on each Sunday for 12 weeks. Then ``forget --keep-daily 4`` will keep
|
||||
|
@@ -85,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
|
||||
@@ -113,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
|
||||
@@ -128,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>`__:
|
||||
|
||||
|
@@ -275,7 +275,12 @@ _restic_backup()
|
||||
flags+=("-h")
|
||||
local_nonpersistent_flags+=("--help")
|
||||
flags+=("--host=")
|
||||
two_word_flags+=("-H")
|
||||
local_nonpersistent_flags+=("--host=")
|
||||
flags+=("--iexclude=")
|
||||
local_nonpersistent_flags+=("--iexclude=")
|
||||
flags+=("--ignore-inode")
|
||||
local_nonpersistent_flags+=("--ignore-inode")
|
||||
flags+=("--one-file-system")
|
||||
flags+=("-x")
|
||||
local_nonpersistent_flags+=("--one-file-system")
|
||||
@@ -295,12 +300,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")
|
||||
@@ -343,12 +350,14 @@ _restic_cache()
|
||||
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")
|
||||
@@ -385,12 +394,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")
|
||||
@@ -435,12 +446,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")
|
||||
@@ -479,12 +492,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")
|
||||
@@ -528,12 +543,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")
|
||||
@@ -600,12 +617,14 @@ _restic_find()
|
||||
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")
|
||||
@@ -681,12 +700,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")
|
||||
@@ -729,12 +750,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")
|
||||
@@ -771,12 +794,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")
|
||||
@@ -815,12 +840,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")
|
||||
@@ -857,12 +884,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")
|
||||
@@ -911,12 +940,14 @@ _restic_ls()
|
||||
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")
|
||||
@@ -956,12 +987,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")
|
||||
@@ -1001,6 +1034,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=")
|
||||
@@ -1013,12 +1048,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")
|
||||
@@ -1055,12 +1092,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")
|
||||
@@ -1097,12 +1136,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")
|
||||
@@ -1141,6 +1226,10 @@ _restic_restore()
|
||||
flags+=("--host=")
|
||||
two_word_flags+=("-H")
|
||||
local_nonpersistent_flags+=("--host=")
|
||||
flags+=("--iexclude=")
|
||||
local_nonpersistent_flags+=("--iexclude=")
|
||||
flags+=("--iinclude=")
|
||||
local_nonpersistent_flags+=("--iinclude=")
|
||||
flags+=("--include=")
|
||||
two_word_flags+=("-i")
|
||||
local_nonpersistent_flags+=("--include=")
|
||||
@@ -1157,12 +1246,14 @@ _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")
|
||||
@@ -1201,12 +1292,14 @@ _restic_self-update()
|
||||
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")
|
||||
@@ -1239,6 +1332,9 @@ _restic_snapshots()
|
||||
flags+=("--compact")
|
||||
flags+=("-c")
|
||||
local_nonpersistent_flags+=("--compact")
|
||||
flags+=("--group-by=")
|
||||
two_word_flags+=("-g")
|
||||
local_nonpersistent_flags+=("--group-by=")
|
||||
flags+=("--help")
|
||||
flags+=("-h")
|
||||
local_nonpersistent_flags+=("--help")
|
||||
@@ -1255,12 +1351,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")
|
||||
@@ -1294,6 +1392,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=")
|
||||
@@ -1301,12 +1400,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")
|
||||
@@ -1356,12 +1457,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")
|
||||
@@ -1400,12 +1503,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")
|
||||
@@ -1442,12 +1547,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")
|
||||
@@ -1487,6 +1594,7 @@ _restic_root_command()
|
||||
commands+=("mount")
|
||||
commands+=("prune")
|
||||
commands+=("rebuild-index")
|
||||
commands+=("recover")
|
||||
commands+=("restore")
|
||||
commands+=("self-update")
|
||||
commands+=("snapshots")
|
||||
@@ -1508,12 +1616,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,7 +9,7 @@ Versions
|
||||
========
|
||||
|
||||
The cache directory is selected according to the `XDG base dir specification
|
||||
<http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html>`__.
|
||||
<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.
|
||||
|
@@ -27,7 +27,7 @@ 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 intented, what do I do now?
|
||||
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,
|
||||
@@ -45,7 +45,7 @@ $ restic backup --exclude "~/documents" ~
|
||||
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 preceeding your restic command.
|
||||
In order to spot an issue like this, you can make use of the following ruby command preceding your restic command.
|
||||
|
||||
::
|
||||
|
||||
@@ -71,7 +71,7 @@ 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``
|
||||
- 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
|
||||
|
||||
|
||||
@@ -172,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.
|
||||
|
@@ -26,7 +26,8 @@ given as the arguments.
|
||||
|
||||
.PP
|
||||
\fB\-\-exclude\-caches\fP[=false]
|
||||
excludes cache directories that are marked with a CACHEDIR.TAG file
|
||||
excludes cache directories that are marked with a CACHEDIR.TAG file. See
|
||||
\[la]http://bford.info/cachedir/spec.html\[ra] for the Cache Directory Tagging Standard
|
||||
|
||||
.PP
|
||||
\fB\-\-exclude\-file\fP=[]
|
||||
@@ -37,8 +38,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,9 +50,17 @@ given as the arguments.
|
||||
help for backup
|
||||
|
||||
.PP
|
||||
\fB\-\-host\fP="H"
|
||||
\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
|
||||
\fB\-\-iexclude\fP=[]
|
||||
same as \fB\fC\-\-exclude\fR but ignores the casing of filenames
|
||||
|
||||
.PP
|
||||
\fB\-\-ignore\-inode\fP[=false]
|
||||
ignore inode number changes when checking for modified files
|
||||
|
||||
.PP
|
||||
\fB\-x\fP, \fB\-\-one\-file\-system\fP[=false]
|
||||
exclude other file systems
|
||||
@@ -98,6 +107,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 +131,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)
|
||||
|
@@ -53,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)
|
||||
@@ -73,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)
|
||||
|
@@ -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)
|
||||
|
@@ -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)
|
||||
|
@@ -61,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)
|
||||
@@ -81,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)
|
||||
|
@@ -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)
|
||||
|
@@ -59,7 +59,7 @@ It can also be used to search for restic blobs or trees for troubleshooting.
|
||||
|
||||
.PP
|
||||
\fB\-\-show\-pack\-id\fP[=false]
|
||||
display the pack\-ID the blobs belong to (with \-\-blob)
|
||||
display the pack\-ID the blobs belong to (with \-\-blob or \-\-tree)
|
||||
|
||||
.PP
|
||||
\fB\-s\fP, \fB\-\-snapshot\fP=[]
|
||||
@@ -91,6 +91,10 @@ It can also be used to search for restic blobs or trees for troubleshooting.
|
||||
\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)
|
||||
@@ -111,6 +115,10 @@ It can also be used to search for restic blobs or trees for troubleshooting.
|
||||
\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 @@ data after 'forget' was run successfully, see the 'prune' command.
|
||||
|
||||
.PP
|
||||
\fB\-\-keep\-within\fP=
|
||||
keep snapshots that are older than \fB\fCduration\fR (eg. 1y5m7d) relative to the latest snapshot
|
||||
keep snapshots that are newer than \fB\fCduration\fR (eg. 1y5m7d2h) relative to the latest snapshot
|
||||
|
||||
.PP
|
||||
\fB\-\-keep\-tag\fP=[]
|
||||
@@ -104,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)
|
||||
@@ -124,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)
|
||||
|
@@ -15,7 +15,7 @@ restic\-generate \- Generate manual pages and auto\-completion files (bash, zsh)
|
||||
|
||||
.SH DESCRIPTION
|
||||
.PP
|
||||
The "generate" command writes automatically generated files like the man pages
|
||||
The "generate" command writes automatically generated files (like the man pages
|
||||
and the auto\-completion files for bash and zsh).
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
@@ -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)
|
||||
|
@@ -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)
|
||||
|
@@ -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)
|
||||
|
@@ -76,6 +76,10 @@ a path separator); paths use the forward slash '/' as separator.
|
||||
\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)
|
||||
@@ -96,6 +100,10 @@ a path separator); paths use the forward slash '/' as separator.
|
||||
\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)
|
||||
|
@@ -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
|
||||
@@ -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)
|
||||
|
@@ -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)
|
||||
|
@@ -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
|
@@ -36,6 +36,14 @@ repository.
|
||||
\fB\-H\fP, \fB\-\-host\fP=""
|
||||
only consider snapshots for this host when the snapshot ID is "latest"
|
||||
|
||||
.PP
|
||||
\fB\-\-iexclude\fP=[]
|
||||
same as \fB\fC\-\-exclude\fR but ignores the casing of filenames
|
||||
|
||||
.PP
|
||||
\fB\-\-iinclude\fP=[]
|
||||
same as \fB\fC\-\-include\fR but ignores the casing of filenames
|
||||
|
||||
.PP
|
||||
\fB\-i\fP, \fB\-\-include\fP=[]
|
||||
include a \fB\fCpattern\fR, exclude everything else (can be specified multiple times)
|
||||
@@ -74,6 +82,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 +106,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)
|
||||
|
@@ -15,7 +15,7 @@ restic\-self\-update \- Update the restic binary
|
||||
|
||||
.SH DESCRIPTION
|
||||
.PP
|
||||
The command "update\-restic" downloads the latest stable release of restic from
|
||||
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.
|
||||
@@ -27,8 +27,8 @@ files.
|
||||
help for self\-update
|
||||
|
||||
.PP
|
||||
\fB\-\-output\fP="./restic\-generate.temp"
|
||||
Save the downloaded file as \fB\fCfilename\fR
|
||||
\fB\-\-output\fP=""
|
||||
Save the downloaded file as \fB\fCfilename\fR (default: running binary itself)
|
||||
|
||||
|
||||
.SH OPTIONS INHERITED FROM PARENT COMMANDS
|
||||
@@ -48,6 +48,10 @@ files.
|
||||
\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)
|
||||
@@ -68,6 +72,10 @@ files.
|
||||
\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)
|
||||
|
@@ -23,6 +23,10 @@ The "snapshots" command lists all snapshots stored in the repository.
|
||||
\fB\-c\fP, \fB\-\-compact\fP[=false]
|
||||
use compact format
|
||||
|
||||
.PP
|
||||
\fB\-g\fP, \fB\-\-group\-by\fP=""
|
||||
string for grouping snapshots by host,paths,tags
|
||||
|
||||
.PP
|
||||
\fB\-h\fP, \fB\-\-help\fP[=false]
|
||||
help for snapshots
|
||||
@@ -61,6 +65,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 +89,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)
|
||||
|
@@ -40,11 +40,12 @@ 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.
|
||||
.IP \(bu 2
|
||||
Refer to the online manual for more details about each mode.
|
||||
|
||||
.RE
|
||||
|
||||
.PP
|
||||
Refer to the online manual for more details about each mode.
|
||||
|
||||
|
||||
.SH OPTIONS
|
||||
.PP
|
||||
@@ -52,7 +53,7 @@ Refer to the online manual for more details about each mode.
|
||||
help for stats
|
||||
|
||||
.PP
|
||||
\fB\-\-host\fP="H"
|
||||
\fB\-H\fP, \fB\-\-host\fP=""
|
||||
filter latest snapshot by this hostname
|
||||
|
||||
.PP
|
||||
@@ -77,6 +78,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)
|
||||
@@ -97,6 +102,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)
|
||||
|
@@ -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)
|
||||
|
@@ -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)
|
||||
|
@@ -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)
|
||||
|
@@ -40,6 +40,10 @@ directories in an encrypted repository stored on different backends.
|
||||
\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)
|
||||
@@ -60,6 +64,10 @@ directories in an encrypted repository stored on different backends.
|
||||
\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)
|
||||
@@ -83,4 +91,4 @@ directories in an encrypted repository stored on different backends.
|
||||
|
||||
.SH SEE ALSO
|
||||
.PP
|
||||
\fBrestic\-backup(1)\fP, \fBrestic\-cache(1)\fP, \fBrestic\-cat(1)\fP, \fBrestic\-check(1)\fP, \fBrestic\-diff(1)\fP, \fBrestic\-dump(1)\fP, \fBrestic\-find(1)\fP, \fBrestic\-forget(1)\fP, \fBrestic\-generate(1)\fP, \fBrestic\-init(1)\fP, \fBrestic\-key(1)\fP, \fBrestic\-list(1)\fP, \fBrestic\-ls(1)\fP, \fBrestic\-migrate(1)\fP, \fBrestic\-mount(1)\fP, \fBrestic\-prune(1)\fP, \fBrestic\-rebuild\-index(1)\fP, \fBrestic\-restore(1)\fP, \fBrestic\-self\-update(1)\fP, \fBrestic\-snapshots(1)\fP, \fBrestic\-stats(1)\fP, \fBrestic\-tag(1)\fP, \fBrestic\-unlock(1)\fP, \fBrestic\-version(1)\fP
|
||||
\fBrestic\-backup(1)\fP, \fBrestic\-cache(1)\fP, \fBrestic\-cat(1)\fP, \fBrestic\-check(1)\fP, \fBrestic\-diff(1)\fP, \fBrestic\-dump(1)\fP, \fBrestic\-find(1)\fP, \fBrestic\-forget(1)\fP, \fBrestic\-generate(1)\fP, \fBrestic\-init(1)\fP, \fBrestic\-key(1)\fP, \fBrestic\-list(1)\fP, \fBrestic\-ls(1)\fP, \fBrestic\-migrate(1)\fP, \fBrestic\-mount(1)\fP, \fBrestic\-prune(1)\fP, \fBrestic\-rebuild\-index(1)\fP, \fBrestic\-recover(1)\fP, \fBrestic\-restore(1)\fP, \fBrestic\-self\-update(1)\fP, \fBrestic\-snapshots(1)\fP, \fBrestic\-stats(1)\fP, \fBrestic\-tag(1)\fP, \fBrestic\-unlock(1)\fP, \fBrestic\-version(1)\fP
|
||||
|
@@ -47,6 +47,7 @@ Usage help is available:
|
||||
--cleanup-cache auto remove old cache directories
|
||||
-h, --help help for restic
|
||||
--json set output mode to JSON for commands that support it
|
||||
--key-hint string key ID of key to try decrypting first (default: $RESTIC_KEY_HINT)
|
||||
--limit-download int limits downloads to a maximum rate in KiB/s. (default: unlimited)
|
||||
--limit-upload int limits uploads to a maximum rate in KiB/s. (default: unlimited)
|
||||
--no-cache do not use a local cache
|
||||
@@ -77,10 +78,10 @@ command:
|
||||
|
||||
Flags:
|
||||
-e, --exclude pattern exclude a pattern (can be specified multiple times)
|
||||
--exclude-caches excludes cache directories that are marked with a CACHEDIR.TAG file
|
||||
--exclude-caches excludes cache directories that are marked with a CACHEDIR.TAG file. See http://bford.info/cachedir/spec.html for the Cache Directory Tagging Standard
|
||||
--exclude-file file read exclude patterns from a file (can be specified multiple times)
|
||||
--exclude-if-present stringArray 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)
|
||||
--files-from string read the files to backup from file (can be combined with file args)
|
||||
--files-from string read the files to backup from file (can be combined with file args/can be specified multiple times)
|
||||
-f, --force force re-reading the target files/directories (overrides the "parent" flag)
|
||||
-h, --help help for backup
|
||||
--hostname hostname set the hostname for the snapshot manually. To prevent an expensive rescan use the "parent" flag
|
||||
@@ -97,6 +98,7 @@ command:
|
||||
--cache-dir string set the cache directory. (default: use system default cache directory)
|
||||
--cleanup-cache auto remove old cache directories
|
||||
--json set output mode to JSON for commands that support it
|
||||
--key-hint string key ID of key to try decrypting first (default: $RESTIC_KEY_HINT)
|
||||
--limit-download int limits downloads to a maximum rate in KiB/s. (default: unlimited)
|
||||
--limit-upload int limits uploads to a maximum rate in KiB/s. (default: unlimited)
|
||||
--no-cache do not use a local cache
|
||||
@@ -179,7 +181,7 @@ locks with the following command:
|
||||
d369ccc7d126594950bf74f0a348d5d98d9e99f3215082eb69bf02dc9b3e464c
|
||||
|
||||
The ``find`` command searches for a given
|
||||
`pattern <http://golang.org/pkg/path/filepath/#Match>`__ in the
|
||||
`pattern <https://golang.org/pkg/path/filepath/#Match>`__ in the
|
||||
repository.
|
||||
|
||||
.. code-block:: console
|
||||
|
@@ -7,7 +7,7 @@ case $state in
|
||||
level1)
|
||||
case $words[1] in
|
||||
restic)
|
||||
_arguments '1: :(backup cache cat check diff dump find forget generate help init key list ls migrate mount options prune rebuild-index restore self-update snapshots stats tag unlock version)'
|
||||
_arguments '1: :(backup cache cat check diff dump find forget generate help init key list ls migrate mount options prune rebuild-index recover restore self-update snapshots stats tag unlock version)'
|
||||
;;
|
||||
*)
|
||||
_arguments '*: :_files'
|
||||
|
@@ -2,6 +2,6 @@ FROM alpine:latest
|
||||
|
||||
COPY restic /usr/bin
|
||||
|
||||
RUN apk add --update --no-cache ca-certificates fuse
|
||||
RUN apk add --update --no-cache ca-certificates fuse openssh-client
|
||||
|
||||
ENTRYPOINT ["/usr/bin/restic"]
|
||||
|
66
go.mod
66
go.mod
@@ -2,53 +2,49 @@ module github.com/restic/restic
|
||||
|
||||
require (
|
||||
bazil.org/fuse v0.0.0-20180421153158-65cc252bf669
|
||||
cloud.google.com/go v0.27.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go v20.1.0+incompatible
|
||||
github.com/Azure/go-autorest v10.15.3+incompatible // indirect
|
||||
github.com/cenkalti/backoff v2.0.0+incompatible
|
||||
cloud.google.com/go v0.36.0 // indirect
|
||||
contrib.go.opencensus.io/exporter/ocagent v0.4.3 // indirect
|
||||
github.com/Azure/azure-sdk-for-go v26.4.0+incompatible
|
||||
github.com/Azure/go-autorest v11.4.0+incompatible // indirect
|
||||
github.com/cenkalti/backoff v2.1.1+incompatible
|
||||
github.com/cpuguy83/go-md2man v1.0.8 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible // indirect
|
||||
github.com/dnaeon/go-vcr v0.0.0-20180814043457-aafff18a5cc2 // indirect
|
||||
github.com/dnaeon/go-vcr v1.0.1 // indirect
|
||||
github.com/elithrar/simple-scrypt v1.3.0
|
||||
github.com/go-ini/ini v1.38.2 // indirect
|
||||
github.com/golang/protobuf v1.2.0 // indirect
|
||||
github.com/go-ini/ini v1.41.0 // indirect
|
||||
github.com/google/go-cmp v0.2.0
|
||||
github.com/gopherjs/gopherjs v0.0.0-20180825215210-0210a2f0f73c // indirect
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e // indirect
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.7.0 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.0.0 // indirect
|
||||
github.com/jtolds/gls v4.2.1+incompatible // indirect
|
||||
github.com/juju/ratelimit v1.0.1
|
||||
github.com/kr/fs v0.1.0 // indirect
|
||||
github.com/kr/pretty v0.1.0 // indirect
|
||||
github.com/kurin/blazer v0.5.1
|
||||
github.com/kurin/blazer v0.5.3
|
||||
github.com/marstr/guid v1.1.0 // indirect
|
||||
github.com/mattn/go-isatty v0.0.4
|
||||
github.com/minio/minio-go v6.0.7+incompatible
|
||||
github.com/mitchellh/go-homedir v1.0.0 // indirect
|
||||
github.com/ncw/swift v1.0.41
|
||||
github.com/pkg/errors v0.8.0
|
||||
github.com/mattn/go-isatty v0.0.7
|
||||
github.com/minio/minio-go v6.0.14+incompatible
|
||||
github.com/mitchellh/go-homedir v1.1.0 // indirect
|
||||
github.com/ncw/swift v1.0.45
|
||||
github.com/pkg/errors v0.8.1
|
||||
github.com/pkg/profile v1.2.1
|
||||
github.com/pkg/sftp v1.8.2
|
||||
github.com/pkg/xattr v0.3.1
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/pkg/sftp v1.10.0
|
||||
github.com/pkg/xattr v0.4.0
|
||||
github.com/restic/chunker v0.2.0
|
||||
github.com/russross/blackfriday v1.5.1 // indirect
|
||||
github.com/satori/go.uuid v1.2.0 // indirect
|
||||
github.com/smartystreets/assertions v0.0.0-20180820201707-7c9eb446e3cf // indirect
|
||||
github.com/smartystreets/goconvey v0.0.0-20180222194500-ef6db91d284a // indirect
|
||||
github.com/smartystreets/assertions v0.0.0-20190116191733-b6c0e53d7304 // indirect
|
||||
github.com/smartystreets/goconvey v0.0.0-20181108003508-044398e4856c // indirect
|
||||
github.com/spf13/cobra v0.0.3
|
||||
github.com/spf13/pflag v1.0.2
|
||||
github.com/stretchr/testify v1.2.2 // indirect
|
||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f
|
||||
golang.org/x/sys v0.0.0-20180907202204-917fdcba135d
|
||||
golang.org/x/text v0.3.0
|
||||
google.golang.org/api v0.0.0-20180907210053-b609d5e6b7ab
|
||||
google.golang.org/appengine v1.1.0 // indirect
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect
|
||||
gopkg.in/ini.v1 v1.38.2 // indirect
|
||||
github.com/spf13/pflag v1.0.3
|
||||
github.com/stretchr/testify v1.3.0 // indirect
|
||||
go.opencensus.io v0.19.0 // indirect
|
||||
golang.org/x/crypto v0.0.0-20190208162236-193df9c0f06f
|
||||
golang.org/x/net v0.0.0-20190206173232-65e2d4e15006
|
||||
golang.org/x/oauth2 v0.0.0-20190130055435-99b60b757ec1
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4
|
||||
golang.org/x/sys v0.0.0-20190316082340-a2f829d7f35f
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2
|
||||
google.golang.org/api v0.1.0
|
||||
google.golang.org/grpc v1.18.0 // indirect
|
||||
gopkg.in/ini.v1 v1.41.0 // indirect
|
||||
gopkg.in/tomb.v2 v2.0.0-20161208151619-d5d1b5820637
|
||||
gopkg.in/yaml.v2 v2.2.1 // indirect
|
||||
)
|
||||
|
253
go.sum
253
go.sum
@@ -1,106 +1,265 @@
|
||||
bazil.org/fuse v0.0.0-20180421153158-65cc252bf669 h1:FNCRpXiquG1aoyqcIWVFmpTSKVcx2bQD38uZZeGtdlw=
|
||||
bazil.org/fuse v0.0.0-20180421153158-65cc252bf669/go.mod h1:Xbm+BRKSBEpa4q4hTSxohYNQpsxXPbPry4JJWOB3LB8=
|
||||
cloud.google.com/go v0.27.0 h1:Xa8ZWro6QYKOwDKtxfKsiE0ea2jD39nx32RxtF5RjYE=
|
||||
cloud.google.com/go v0.27.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
github.com/Azure/azure-sdk-for-go v20.1.0+incompatible h1:b8OWFQuH5MPi2LYyAR2Ga+7KVH9ipwiSSSMga04/Urc=
|
||||
github.com/Azure/azure-sdk-for-go v20.1.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
|
||||
github.com/Azure/go-autorest v10.15.3+incompatible h1:nhKI/bvazIs3C3TFGoSqKY6hZ8f5od5mb5/UcS6HVIY=
|
||||
github.com/Azure/go-autorest v10.15.3+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
|
||||
github.com/cenkalti/backoff v2.0.0+incompatible h1:5IIPUHhlnUZbcHQsQou5k1Tn58nJkeJL9U+ig5CHJbY=
|
||||
github.com/cenkalti/backoff v2.0.0+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
|
||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.31.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.36.0 h1:+aCSj7tOo2LODWVEuZDZeGCckdt6MlSF+X/rB3wUiS8=
|
||||
cloud.google.com/go v0.36.0/go.mod h1:RUoy9p/M4ge0HzT8L+SDZ8jg+Q6fth0CiBuhFJpSV40=
|
||||
contrib.go.opencensus.io/exporter/ocagent v0.4.3 h1:QjNm697iO7CZ09IxxSiCUzOhALENIsLsixdPwjV1yGs=
|
||||
contrib.go.opencensus.io/exporter/ocagent v0.4.3/go.mod h1:YuG83h+XWwqWjvCqn7vK4KSyLKhThY3+gNGQ37iS2V0=
|
||||
dmitri.shuralyov.com/app/changes v0.0.0-20180602232624-0a106ad413e3/go.mod h1:Yl+fi1br7+Rr3LqpNJf1/uxUdtRUV+Tnj0o93V2B9MU=
|
||||
dmitri.shuralyov.com/html/belt v0.0.0-20180602232347-f7d459c86be0/go.mod h1:JLBrvjyP0v+ecvNYvCpyZgu5/xkfAUhi6wJj28eUfSU=
|
||||
dmitri.shuralyov.com/service/change v0.0.0-20181023043359-a85b471d5412/go.mod h1:a1inKt/atXimZ4Mv927x+r7UpyzRUf4emIoiiSC2TN4=
|
||||
dmitri.shuralyov.com/state v0.0.0-20180228185332-28bcc343414c/go.mod h1:0PRwlb0D6DFvNNtx+9ybjezNCa8XF0xaYcETyp6rHWU=
|
||||
git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg=
|
||||
git.apache.org/thrift.git v0.0.0-20181218151757-9b75e4fe745a/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg=
|
||||
github.com/Azure/azure-sdk-for-go v26.4.0+incompatible h1:ISw3xYFYPGBmcwP7CQjzQDoYhkywcIVfYzo4CHgQzOw=
|
||||
github.com/Azure/azure-sdk-for-go v26.4.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
|
||||
github.com/Azure/go-autorest v11.4.0+incompatible h1:z3Yr6KYqs0nhSNwqGXEBpWK977hxVqsLv2n9PVYcixY=
|
||||
github.com/Azure/go-autorest v11.4.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
|
||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||
github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g=
|
||||
github.com/cenkalti/backoff v2.1.1+incompatible h1:tKJnvO2kl0zmb/jA5UKAt4VoEVw1qxKWjE/Bpp46npY=
|
||||
github.com/cenkalti/backoff v2.1.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
|
||||
github.com/census-instrumentation/opencensus-proto v0.1.0-0.20181214143942-ba49f56771b8 h1:gUqsFVdUKoRHNg8fkFd8gB5OOEa/g5EwlAHznb4zjbI=
|
||||
github.com/census-instrumentation/opencensus-proto v0.1.0-0.20181214143942-ba49f56771b8/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||
github.com/cpuguy83/go-md2man v1.0.8 h1:DwoNytLphI8hzS2Af4D0dfaEaiSq2bN05mEm4R6vf8M=
|
||||
github.com/cpuguy83/go-md2man v1.0.8/go.mod h1:N6JayAiVKtlHSnuTCeuLSQVs75hb8q+dYQLjr7cDsKY=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||
github.com/dnaeon/go-vcr v0.0.0-20180814043457-aafff18a5cc2 h1:G9/PqfhOrt8JXnw0DGTfVoOkKHDhOlEZqhE/cu+NvQM=
|
||||
github.com/dnaeon/go-vcr v0.0.0-20180814043457-aafff18a5cc2/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E=
|
||||
github.com/dnaeon/go-vcr v1.0.1 h1:r8L/HqC0Hje5AXMu1ooW8oyQyOFv4GxqpL0nRP7SLLY=
|
||||
github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E=
|
||||
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||
github.com/elithrar/simple-scrypt v1.3.0 h1:KIlOlxdoQf9JWKl5lMAJ28SY2URB0XTRDn2TckyzAZg=
|
||||
github.com/elithrar/simple-scrypt v1.3.0/go.mod h1:U2XQRI95XHY0St410VE3UjT7vuKb1qPwrl/EJwEqnZo=
|
||||
github.com/go-ini/ini v1.38.2 h1:6Hl/z3p3iFkA0dlDfzYxuFuUGD+kaweypF6btsR2/Q4=
|
||||
github.com/go-ini/ini v1.38.2/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
|
||||
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
|
||||
github.com/go-ini/ini v1.41.0 h1:526aoxDtxRHFQKMZfcX2OG9oOI8TJ5yPLM0Mkno/uTY=
|
||||
github.com/go-ini/ini v1.41.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
|
||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20180825215210-0210a2f0f73c h1:16eHWuMGvCjSfgRJKqIzapE78onvvTbdi1rMkU00lZw=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20180825215210-0210a2f0f73c/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ=
|
||||
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
|
||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY=
|
||||
github.com/googleapis/gax-go/v2 v2.0.3/go.mod h1:LLvjysVCY1JZeum8Z6l8qUty8fiNwE08qbEPm1M08qg=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e h1:JKmoR8x90Iww1ks85zJ1lfDGgIiMDuIptTOhJq+zKyg=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.6.2/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.7.0 h1:tPFY/SM+d656aSgLWO2Eckc3ExwpwwybwdN5Ph20h1A=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.7.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw=
|
||||
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
|
||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||
github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU=
|
||||
github.com/jtolds/gls v4.2.1+incompatible h1:fSuqC+Gmlu6l/ZYAoZzx2pyucC8Xza35fpRVWLVmUEE=
|
||||
github.com/jtolds/gls v4.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||
github.com/juju/ratelimit v1.0.1 h1:+7AIFJVQ0EQgq/K9+0Krm7m530Du7tIz0METWzN0RgY=
|
||||
github.com/juju/ratelimit v1.0.1/go.mod h1:qapgC/Gy+xNh9UxzV13HGGl/6UXNN+ct+vwSgWNm/qk=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/kr/fs v0.1.0 h1:Jskdu9ieNAYnjxsi0LbQp1ulIKZV1LAFgK1tWhpZgl8=
|
||||
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
|
||||
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kurin/blazer v0.5.1 h1:mBc4i1uhHJEqU0KvzOgpMHhkwf+EcXvxjWEUS7HG+eY=
|
||||
github.com/kurin/blazer v0.5.1/go.mod h1:4FCXMUWo9DllR2Do4TtBd377ezyAJ51vB5uTBjt0pGU=
|
||||
github.com/kurin/blazer v0.5.3 h1:SAgYv0TKU0kN/ETfO5ExjNAPyMt2FocO2s/UlCHfjAk=
|
||||
github.com/kurin/blazer v0.5.3/go.mod h1:4FCXMUWo9DllR2Do4TtBd377ezyAJ51vB5uTBjt0pGU=
|
||||
github.com/marstr/guid v1.1.0 h1:/M4H/1G4avsieL6BbUwCOBzulmoeKVP5ux/3mQNnbyI=
|
||||
github.com/marstr/guid v1.1.0/go.mod h1:74gB1z2wpxxInTG6yaqA7KrtM0NZ+RbrcqDvYHefzho=
|
||||
github.com/mattn/go-isatty v0.0.4 h1:bnP0vzxcAdeI1zdubAl5PjU6zsERjGZb7raWodagDYs=
|
||||
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
github.com/minio/minio-go v6.0.7+incompatible h1:nWABqotkiT/3aLgFnG30doQiwFkDMM9xnGGQnS+Ao6M=
|
||||
github.com/minio/minio-go v6.0.7+incompatible/go.mod h1:7guKYtitv8dktvNUGrhzmNlA5wrAABTQXCoesZdFQO8=
|
||||
github.com/mitchellh/go-homedir v1.0.0 h1:vKb8ShqSby24Yrqr/yDYkuFz8d0WUjys40rvnGC8aR0=
|
||||
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/ncw/swift v1.0.41 h1:kfoTVQKt1A4n0m1Q3YWku9OoXfpo06biqVfi73yseBs=
|
||||
github.com/ncw/swift v1.0.41/go.mod h1:23YIA4yWVnGwv2dQlN4bB7egfYX6YLn0Yo/S6zZO/ZM=
|
||||
github.com/pkg/errors v0.8.0 h1:WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/mattn/go-isatty v0.0.7 h1:UvyT9uN+3r7yLEYSlJsbQGdsaB/a0DlgWP3pql6iwOc=
|
||||
github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4=
|
||||
github.com/minio/minio-go v6.0.14+incompatible h1:fnV+GD28LeqdN6vT2XdGKW8Qe/IfjJDswNVuni6km9o=
|
||||
github.com/minio/minio-go v6.0.14+incompatible/go.mod h1:7guKYtitv8dktvNUGrhzmNlA5wrAABTQXCoesZdFQO8=
|
||||
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
|
||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/ncw/swift v1.0.45 h1:n6MfkuP599wWdcIOiBv4ESRodkzvudF65hNgNXe6tj0=
|
||||
github.com/ncw/swift v1.0.45/go.mod h1:23YIA4yWVnGwv2dQlN4bB7egfYX6YLn0Yo/S6zZO/ZM=
|
||||
github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo=
|
||||
github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM=
|
||||
github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8=
|
||||
github.com/openzipkin/zipkin-go v0.1.3/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8=
|
||||
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/profile v1.2.1 h1:F++O52m40owAmADcojzM+9gyjmMOY/T4oYJkgFDH8RE=
|
||||
github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA=
|
||||
github.com/pkg/sftp v1.8.2 h1:3upwlsK5/USEeM5gzIe9eWdzU4sV+kG3gKKg3RLBuWE=
|
||||
github.com/pkg/sftp v1.8.2/go.mod h1:NxmoDg/QLVWluQDUYG7XBZTLUpKeFa8e3aMf1BfjyHk=
|
||||
github.com/pkg/xattr v0.3.1 h1:6ceg5jxT3cH4lM5n8S2PmiNeOv61MK08yvvYJwyrPH0=
|
||||
github.com/pkg/xattr v0.3.1/go.mod h1:CBdxFOf0VLbaj6HKuP2ITOVV7NY6ycPKgIgnSx2ZNVs=
|
||||
github.com/pkg/sftp v1.10.0 h1:DGA1KlA9esU6WcicH+P8PxFZOl15O6GYtab1cIJdOlE=
|
||||
github.com/pkg/sftp v1.10.0/go.mod h1:NxmoDg/QLVWluQDUYG7XBZTLUpKeFa8e3aMf1BfjyHk=
|
||||
github.com/pkg/xattr v0.4.0 h1:OacIpDCc4H+4b/bWpYBLOT5gXk7G/jwx5O1D8x8Zewo=
|
||||
github.com/pkg/xattr v0.4.0/go.mod h1:W2cGD0TBEus7MkUgv0tNZ9JutLtVO3cXu+IBRuHqnFs=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||
github.com/prometheus/client_golang v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM=
|
||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||
github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
|
||||
github.com/prometheus/common v0.0.0-20181126121408-4724e9255275/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
|
||||
github.com/prometheus/common v0.0.0-20181218105931-67670fe90761/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
|
||||
github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/restic/chunker v0.2.0 h1:GjvmvFuv2mx0iekZs+iAlrioo2UtgsGSSplvoXaVHDU=
|
||||
github.com/restic/chunker v0.2.0/go.mod h1:VdjruEj+7BU1ZZTW8Qqi1exxRx2Omf2JH0NsUEkQ29s=
|
||||
github.com/russross/blackfriday v1.5.1 h1:B8ZN6pD4PVofmlDCDUdELeYrbsVIDM/bpjW3v3zgcRc=
|
||||
github.com/russross/blackfriday v1.5.1/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
||||
github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNueLj0oo=
|
||||
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
||||
github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww=
|
||||
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
|
||||
github.com/smartystreets/assertions v0.0.0-20180820201707-7c9eb446e3cf h1:6V1qxN6Usn4jy8unvggSJz/NC790tefw8Zdy6OZS5co=
|
||||
github.com/smartystreets/assertions v0.0.0-20180820201707-7c9eb446e3cf/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||
github.com/smartystreets/goconvey v0.0.0-20180222194500-ef6db91d284a h1:JSvGDIbmil4Ui/dDdFBExb7/cmkNjyX5F97oglmvCDo=
|
||||
github.com/smartystreets/goconvey v0.0.0-20180222194500-ef6db91d284a/go.mod h1:XDJAKZRPZ1CvBcN2aX5YOUTYGHki24fSF0Iv48Ibg0s=
|
||||
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
|
||||
github.com/shurcooL/component v0.0.0-20170202220835-f88ec8f54cc4/go.mod h1:XhFIlyj5a1fBNx5aJTbKoIq0mNaPvOagO+HjB3EtxrY=
|
||||
github.com/shurcooL/events v0.0.0-20181021180414-410e4ca65f48/go.mod h1:5u70Mqkb5O5cxEA8nxTsgrgLehJeAw6Oc4Ab1c/P1HM=
|
||||
github.com/shurcooL/github_flavored_markdown v0.0.0-20181002035957-2122de532470/go.mod h1:2dOwnU2uBioM+SGy2aZoq1f/Sd1l9OkAeAUvjSyvgU0=
|
||||
github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk=
|
||||
github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041/go.mod h1:N5mDOmsrJOB+vfqUK+7DmDyjhSLIIBnXo9lvZJj3MWQ=
|
||||
github.com/shurcooL/gofontwoff v0.0.0-20180329035133-29b52fc0a18d/go.mod h1:05UtEgK5zq39gLST6uB0cf3NEHjETfB4Fgr3Gx5R9Vw=
|
||||
github.com/shurcooL/gopherjslib v0.0.0-20160914041154-feb6d3990c2c/go.mod h1:8d3azKNyqcHP1GaQE/c6dDgjkgSx2BZ4IoEi4F1reUI=
|
||||
github.com/shurcooL/highlight_diff v0.0.0-20170515013008-09bb4053de1b/go.mod h1:ZpfEhSmds4ytuByIcDnOLkTHGUI6KNqRNPDLHDk+mUU=
|
||||
github.com/shurcooL/highlight_go v0.0.0-20181028180052-98c3abbbae20/go.mod h1:UDKB5a1T23gOMUJrI+uSuH0VRDStOiUVSjBTRDVBVag=
|
||||
github.com/shurcooL/home v0.0.0-20181020052607-80b7ffcb30f9/go.mod h1:+rgNQw2P9ARFAs37qieuu7ohDNQ3gds9msbT2yn85sg=
|
||||
github.com/shurcooL/htmlg v0.0.0-20170918183704-d01228ac9e50/go.mod h1:zPn1wHpTIePGnXSHpsVPWEktKXHr6+SS6x/IKRb7cpw=
|
||||
github.com/shurcooL/httperror v0.0.0-20170206035902-86b7830d14cc/go.mod h1:aYMfkZ6DWSJPJ6c4Wwz3QtW22G7mf/PEgaB9k/ik5+Y=
|
||||
github.com/shurcooL/httpfs v0.0.0-20171119174359-809beceb2371/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg=
|
||||
github.com/shurcooL/httpgzip v0.0.0-20180522190206-b1c53ac65af9/go.mod h1:919LwcH0M7/W4fcZ0/jy0qGght1GIhqyS/EgWGH2j5Q=
|
||||
github.com/shurcooL/issues v0.0.0-20181008053335-6292fdc1e191/go.mod h1:e2qWDig5bLteJ4fwvDAc2NHzqFEthkqn7aOZAOpj+PQ=
|
||||
github.com/shurcooL/issuesapp v0.0.0-20180602232740-048589ce2241/go.mod h1:NPpHK2TI7iSaM0buivtFUc9offApnI0Alt/K8hcHy0I=
|
||||
github.com/shurcooL/notifications v0.0.0-20181007000457-627ab5aea122/go.mod h1:b5uSkrEVM1jQUspwbixRBhaIjIzL2xazXp6kntxYle0=
|
||||
github.com/shurcooL/octicon v0.0.0-20181028054416-fa4f57f9efb2/go.mod h1:eWdoE5JD4R5UVWDucdOPg1g2fqQRq78IQa9zlOV1vpQ=
|
||||
github.com/shurcooL/reactions v0.0.0-20181006231557-f2e0b4ca5b82/go.mod h1:TCR1lToEk4d2s07G3XGfz2QrgHXg4RJBvjrOozvoWfk=
|
||||
github.com/shurcooL/sanitized_anchor_name v0.0.0-20170918181015-86672fcb3f95/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||
github.com/shurcooL/users v0.0.0-20180125191416-49c67e49c537/go.mod h1:QJTqeLYEDaXHZDBsXlPCDqdhQuJkuw4NOtaxYe3xii4=
|
||||
github.com/shurcooL/webdavfs v0.0.0-20170829043945-18c3829fa133/go.mod h1:hKmq5kWdCj2z2KEozexVbfEZIWiTjhE0+UjmZgPqehw=
|
||||
github.com/smartystreets/assertions v0.0.0-20190116191733-b6c0e53d7304 h1:Jpy1PXuP99tXNrhbq2BaPz9B+jNAvH1JPQQpG/9GCXY=
|
||||
github.com/smartystreets/assertions v0.0.0-20190116191733-b6c0e53d7304/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||
github.com/smartystreets/goconvey v0.0.0-20181108003508-044398e4856c h1:Ho+uVpkel/udgjbwB5Lktg9BtvJSh2DT0Hi6LPSyI2w=
|
||||
github.com/smartystreets/goconvey v0.0.0-20181108003508-044398e4856c/go.mod h1:XDJAKZRPZ1CvBcN2aX5YOUTYGHki24fSF0Iv48Ibg0s=
|
||||
github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:UdhH50NIW0fCiwBSr0co2m7BnFLdv4fQTgdqdJTHFeE=
|
||||
github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA=
|
||||
github.com/spf13/cobra v0.0.3 h1:ZlrZ4XsMRm04Fr5pSFxBgfND2EBVa1nLpiy1stUsX/8=
|
||||
github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
|
||||
github.com/spf13/pflag v1.0.2 h1:Fy0orTDgHdbnzHcsOgfCN4LtHf0ec3wwtiwJqwvf3Gc=
|
||||
github.com/spf13/pflag v1.0.2/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=
|
||||
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793 h1:u+LnwYTOOW7Ukr/fppxEb1Nwz0AtPflrblfvUudpo+I=
|
||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA=
|
||||
go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA=
|
||||
go.opencensus.io v0.18.1-0.20181204023538-aab39bd6a98b/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA=
|
||||
go.opencensus.io v0.19.0 h1:+jrnNy8MR4GZXvwF9PEuSyHxA4NaTf6601oNRwCSXq0=
|
||||
go.opencensus.io v0.19.0/go.mod h1:AYeH0+ZxYyghG8diqaaIq/9P3VgCCt5GF2ldCY4dkFg=
|
||||
go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE=
|
||||
golang.org/x/build v0.0.0-20190111050920-041ab4dc3f9d/go.mod h1:OWs+y06UdEOHN4y+MfF/py+xQ/tYqIWW03b70/CG9Rw=
|
||||
golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190208162236-193df9c0f06f h1:ETU2VEl7TnT5bl7IvuKEzTDpplg5wzGYsOCAPhdoEIg=
|
||||
golang.org/x/crypto v0.0.0-20190208162236-193df9c0f06f/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20181217174547-8f45f776aaf1/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd h1:nTDtHvHSdCn1m6ITfMRqtOd/9+7a3s8RBNOZ3eYZzJA=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181029044818-c44066c5c816/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181106065722-10aee1819953/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181217023233-e147a9138326/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190206173232-65e2d4e15006 h1:bfLnR+k0tq5Lqt6dflRLcZiz6UaXCMt3vhYJ1l4FQ80=
|
||||
golang.org/x/net v0.0.0-20190206173232-65e2d4e15006/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be h1:vEDujvNQGv4jgYKudGeI/+DAX4Jffq6hpD55MmoEvKs=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190130055435-99b60b757ec1 h1:VeAkjQVzKLmu+JnFcK96TPbkuaTIqwGGAzQ9hgwPjVg=
|
||||
golang.org/x/oauth2 v0.0.0-20190130055435-99b60b757ec1/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852/go.mod h1:JLpeXjPJfIyPr5TlbXLkXWLhP8nz10XfvxElABhCtcw=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f h1:wMNYb4v58l5UBM7MYRLPG6ZhfOqbKu7X5eyFl8ZhKvA=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180525142821-c11f84a56e43/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180907202204-917fdcba135d h1:kWn1hlsqeUrk6JsLJO0ZFyz9bMg8u85voZlIuc68ZU4=
|
||||
golang.org/x/sys v0.0.0-20180907202204-917fdcba135d/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 h1:YUO/7uOKsKeq9UokNS62b8FYywz3ker1l1vDZRCRefw=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181021155630-eda9bb28ed51/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181029174526-d69651ed3497/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181218192612-074acd46bca6/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190316082340-a2f829d7f35f h1:yCrMx/EeIue0+Qca57bWZS7VX6ymEoypmhWyPhz0NHM=
|
||||
golang.org/x/sys v0.0.0-20190316082340-a2f829d7f35f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
google.golang.org/api v0.0.0-20180907210053-b609d5e6b7ab h1:qNpJa8m9WofZ7RLj+7o15Ppapwm30+RweyIDSNpw8ps=
|
||||
google.golang.org/api v0.0.0-20180907210053-b609d5e6b7ab/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2 h1:z99zHgr7hKfrUcX/KsoJk5FJfjTceCKIp96+biqP4To=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20181030000716-a0a13e073c7b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20181219222714-6e267b5cc78e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
|
||||
google.golang.org/api v0.0.0-20181030000543-1d582fd0359e/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
|
||||
google.golang.org/api v0.0.0-20181220000619-583d854617af/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
|
||||
google.golang.org/api v0.1.0 h1:K6z2u68e86TPdSdefXdzvXgR1zEMa+459vBSfWYAZkI=
|
||||
google.golang.org/api v0.1.0/go.mod h1:UGEZY7KEX120AnNLIHFMKIo4obdJhkp2tPbaPlQx13Y=
|
||||
google.golang.org/appengine v1.1.0 h1:igQkv0AAhEIvTEpD5LIpAfav2eeVO9HBTjvKHVJPRSs=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.4.0 h1:/wp5JvzpHIxhs/dumFmF7BXTf3Z+dd4uXta4kVyO508=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20181202183823-bd91e49a0898/go.mod h1:7Ep/1NZk928CDR8SjdVbjWNpdIf6nzjE3BTgJDr2Atg=
|
||||
google.golang.org/genproto v0.0.0-20181219182458-5a97ab628bfb/go.mod h1:7Ep/1NZk928CDR8SjdVbjWNpdIf6nzjE3BTgJDr2Atg=
|
||||
google.golang.org/genproto v0.0.0-20190201180003-4b09977fb922 h1:mBVYJnbrXLA/ZCBTCe7PtEgAUP+1bg92qTaFoPHdz+8=
|
||||
google.golang.org/genproto v0.0.0-20190201180003-4b09977fb922/go.mod h1:L3J43x8/uS+qIUoksaLKe6OS3nUKxOKuIFz1sl2/jx4=
|
||||
google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
|
||||
google.golang.org/grpc v1.15.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio=
|
||||
google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio=
|
||||
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
|
||||
google.golang.org/grpc v1.18.0 h1:IZl7mfBGfbhYx2p2rKRtYgDFw6SBz+kclmxYrCksPPA=
|
||||
google.golang.org/grpc v1.18.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/ini.v1 v1.38.2 h1:dGcbywv4RufeGeiMycPT/plKB5FtmLKLnWKwBiLhUA4=
|
||||
gopkg.in/ini.v1 v1.38.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
|
||||
gopkg.in/ini.v1 v1.41.0 h1:Ka3ViY6gNYSKiVy71zXBEqKplnV35ImDLVG+8uoIklE=
|
||||
gopkg.in/ini.v1 v1.41.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/tomb.v2 v2.0.0-20161208151619-d5d1b5820637 h1:yiW+nvdHb9LVqSHQBXfZCieqV4fzYhNBql77zY0ykqs=
|
||||
gopkg.in/tomb.v2 v2.0.0-20161208151619-d5d1b5820637/go.mod h1:BHsqpu/nsuzkT5BpiH1EMZPLyqSMM8JbIavyFACoFNk=
|
||||
gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o=
|
||||
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20180920025451-e3ad64cb4ed3/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
sourcegraph.com/sourcegraph/go-diff v0.5.0/go.mod h1:kuch7UrkMzY0X+p9CRK03kfuPQ2zzQcaEFbx8wA8rck=
|
||||
sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4/go.mod h1:ketZ/q3QxT9HOBeFhu6RdvsftgpsbFHBF5Cas6cDKZ0=
|
||||
|
@@ -398,6 +398,7 @@ func updateDocker(outputDir, version string) {
|
||||
cmd := fmt.Sprintf("bzcat %s/restic_%s_linux_amd64.bz2 > restic", outputDir, version)
|
||||
run("sh", "-c", cmd)
|
||||
run("chmod", "+x", "restic")
|
||||
run("docker", "pull", "alpine:latest")
|
||||
run("docker", "build", "--rm", "--tag", "restic/restic:latest", "-f", "docker/Dockerfile", ".")
|
||||
run("docker", "tag", "restic/restic:latest", "restic/restic:"+version)
|
||||
}
|
||||
|
@@ -78,7 +78,8 @@ type Archiver struct {
|
||||
// WithAtime configures if the access time for files and directories should
|
||||
// be saved. Enabling it may result in much metadata, so it's off by
|
||||
// default.
|
||||
WithAtime bool
|
||||
WithAtime bool
|
||||
IgnoreInode bool
|
||||
}
|
||||
|
||||
// Options is used to configure the archiver.
|
||||
@@ -133,6 +134,7 @@ func New(repo restic.Repository, fs fs.FS, opts Options) *Archiver {
|
||||
CompleteItem: func(string, *restic.Node, *restic.Node, ItemStats, time.Duration) {},
|
||||
StartFile: func(string) {},
|
||||
CompleteBlob: func(string, uint64) {},
|
||||
IgnoreInode: false,
|
||||
}
|
||||
|
||||
return arch
|
||||
@@ -383,7 +385,7 @@ func (arch *Archiver) Save(ctx context.Context, snPath, target string, previous
|
||||
}
|
||||
|
||||
// use previous node if the file hasn't changed
|
||||
if previous != nil && !fileChanged(fi, previous) {
|
||||
if previous != nil && !fileChanged(fi, previous, arch.IgnoreInode) {
|
||||
debug.Log("%v hasn't changed, returning old node", target)
|
||||
arch.CompleteItem(snPath, previous, previous, ItemStats{}, time.Since(start))
|
||||
arch.CompleteBlob(snPath, previous.Size)
|
||||
@@ -436,7 +438,7 @@ func (arch *Archiver) Save(ctx context.Context, snPath, target string, previous
|
||||
|
||||
// fileChanged returns true if the file's content has changed since the node
|
||||
// was created.
|
||||
func fileChanged(fi os.FileInfo, node *restic.Node) bool {
|
||||
func fileChanged(fi os.FileInfo, node *restic.Node, ignoreInode bool) bool {
|
||||
if node == nil {
|
||||
return true
|
||||
}
|
||||
@@ -458,7 +460,7 @@ func fileChanged(fi os.FileInfo, node *restic.Node) bool {
|
||||
}
|
||||
|
||||
// check inode
|
||||
if node.Inode != extFI.Inode {
|
||||
if !ignoreInode && node.Inode != extFI.Inode {
|
||||
return true
|
||||
}
|
||||
|
||||
|
@@ -160,7 +160,6 @@ func TestArchiverSaveFileReaderFS(t *testing.T) {
|
||||
var tests = []struct {
|
||||
Data string
|
||||
}{
|
||||
{Data: ""},
|
||||
{Data: "foo"},
|
||||
{Data: string(restictest.Random(23, 12*1024*1024+1287898))},
|
||||
}
|
||||
@@ -271,7 +270,6 @@ func TestArchiverSaveReaderFS(t *testing.T) {
|
||||
var tests = []struct {
|
||||
Data string
|
||||
}{
|
||||
{Data: ""},
|
||||
{Data: "foo"},
|
||||
{Data: string(restictest.Random(23, 12*1024*1024+1287898))},
|
||||
}
|
||||
@@ -557,9 +555,11 @@ func TestFileChanged(t *testing.T) {
|
||||
}
|
||||
|
||||
var tests = []struct {
|
||||
Name string
|
||||
Content []byte
|
||||
Modify func(t testing.TB, filename string)
|
||||
Name string
|
||||
Content []byte
|
||||
Modify func(t testing.TB, filename string)
|
||||
IgnoreInode bool
|
||||
Check bool
|
||||
}{
|
||||
{
|
||||
Name: "same-content-new-file",
|
||||
@@ -598,6 +598,18 @@ func TestFileChanged(t *testing.T) {
|
||||
save(t, filename, defaultContent)
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "ignore-inode",
|
||||
Modify: func(t testing.TB, filename string) {
|
||||
fi := lstat(t, filename)
|
||||
remove(t, filename)
|
||||
sleep()
|
||||
save(t, filename, defaultContent)
|
||||
setTimestamp(t, filename, fi.ModTime(), fi.ModTime())
|
||||
},
|
||||
IgnoreInode: true,
|
||||
Check: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
@@ -615,15 +627,19 @@ func TestFileChanged(t *testing.T) {
|
||||
fiBefore := lstat(t, filename)
|
||||
node := nodeFromFI(t, filename, fiBefore)
|
||||
|
||||
if fileChanged(fiBefore, node) {
|
||||
if fileChanged(fiBefore, node, false) {
|
||||
t.Fatalf("unchanged file detected as changed")
|
||||
}
|
||||
|
||||
test.Modify(t, filename)
|
||||
|
||||
fiAfter := lstat(t, filename)
|
||||
if !fileChanged(fiAfter, node) {
|
||||
t.Fatalf("modified file detected as unchanged")
|
||||
if test.Check == fileChanged(fiAfter, node, test.IgnoreInode) {
|
||||
if test.Check {
|
||||
t.Fatalf("unmodified file detected as changed")
|
||||
} else {
|
||||
t.Fatalf("modified file detected as unchanged")
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -639,7 +655,7 @@ func TestFilChangedSpecialCases(t *testing.T) {
|
||||
|
||||
t.Run("nil-node", func(t *testing.T) {
|
||||
fi := lstat(t, filename)
|
||||
if !fileChanged(fi, nil) {
|
||||
if !fileChanged(fi, nil, false) {
|
||||
t.Fatal("nil node detected as unchanged")
|
||||
}
|
||||
})
|
||||
@@ -648,7 +664,7 @@ func TestFilChangedSpecialCases(t *testing.T) {
|
||||
fi := lstat(t, filename)
|
||||
node := nodeFromFI(t, filename, fi)
|
||||
node.Type = "symlink"
|
||||
if !fileChanged(fi, node) {
|
||||
if !fileChanged(fi, node, false) {
|
||||
t.Fatal("node with changed type detected as unchanged")
|
||||
}
|
||||
})
|
||||
|
@@ -15,6 +15,7 @@ import (
|
||||
"github.com/restic/restic/internal/debug"
|
||||
"github.com/restic/restic/internal/restic"
|
||||
|
||||
"golang.org/x/oauth2"
|
||||
"golang.org/x/oauth2/google"
|
||||
"google.golang.org/api/googleapi"
|
||||
storage "google.golang.org/api/storage/v1"
|
||||
@@ -40,8 +41,17 @@ type Backend struct {
|
||||
// Ensure that *Backend implements restic.Backend.
|
||||
var _ restic.Backend = &Backend{}
|
||||
|
||||
func getStorageService() (*storage.Service, error) {
|
||||
client, err := google.DefaultClient(context.TODO(), storage.DevstorageReadWriteScope)
|
||||
func getStorageService(rt http.RoundTripper) (*storage.Service, error) {
|
||||
// create a new HTTP client
|
||||
httpClient := &http.Client{
|
||||
Transport: rt,
|
||||
}
|
||||
|
||||
// create a now context with the HTTP client stored at the oauth2.HTTPClient key
|
||||
ctx := context.WithValue(context.Background(), oauth2.HTTPClient, httpClient)
|
||||
|
||||
// use this context
|
||||
client, err := google.DefaultClient(ctx, storage.DevstorageReadWriteScope)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -59,7 +69,7 @@ const defaultListMaxItems = 1000
|
||||
func open(cfg Config, rt http.RoundTripper) (*Backend, error) {
|
||||
debug.Log("open, config %#v", cfg)
|
||||
|
||||
service, err := getStorageService()
|
||||
service, err := getStorageService(rt)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "getStorageService")
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user