mirror of
https://github.com/restic/restic.git
synced 2026-02-22 16:56:24 +00:00
Compare commits
363 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
546d6f36b2 | ||
|
|
6ecd14d780 | ||
|
|
f6ed7dc013 | ||
|
|
e290f2591e | ||
|
|
75f90ca303 | ||
|
|
ca1430184f | ||
|
|
a297ab9d7c | ||
|
|
f078525d98 | ||
|
|
e03cc81a9a | ||
|
|
af27f1dde5 | ||
|
|
32505c3916 | ||
|
|
9a8d5a1bff | ||
|
|
740ee787c1 | ||
|
|
2eba0bfeec | ||
|
|
d780ec4bce | ||
|
|
6b564d21b3 | ||
|
|
6c2b2a58ad | ||
|
|
b80b68dcb3 | ||
|
|
29c92ca415 | ||
|
|
bc04ce8e6b | ||
|
|
6b6b75fa4a | ||
|
|
84e493beba | ||
|
|
323376efa2 | ||
|
|
e353b00501 | ||
|
|
2510d770ab | ||
|
|
7d8765a937 | ||
|
|
81a04656c5 | ||
|
|
2f26fb8834 | ||
|
|
d3ebe1311f | ||
|
|
42a8c19aae | ||
|
|
27ccea6371 | ||
|
|
4f46b4f393 | ||
|
|
221e741537 | ||
|
|
8b3b7bc5ef | ||
|
|
934ae1b559 | ||
|
|
0e7e3cb714 | ||
|
|
95b6e4e9e9 | ||
|
|
3a5e040b7e | ||
|
|
28c826868b | ||
|
|
1695c8ed55 | ||
|
|
366622f09a | ||
|
|
0dc31c03e1 | ||
|
|
0405e67f8b | ||
|
|
df350e1f6e | ||
|
|
06cb3f7058 | ||
|
|
56b884be17 | ||
|
|
a25d280f3e | ||
|
|
2253a73837 | ||
|
|
946c8399e2 | ||
|
|
9d0f13c4c0 | ||
|
|
eb9e2bc79a | ||
|
|
0722c44ba1 | ||
|
|
2424012d75 | ||
|
|
82ded35706 | ||
|
|
69fcb604c8 | ||
|
|
88607fc625 | ||
|
|
7092af6329 | ||
|
|
23d7d91597 | ||
|
|
ad82781743 | ||
|
|
20d78ab0d9 | ||
|
|
be24237063 | ||
|
|
d886cb5c27 | ||
|
|
63bb1933e5 | ||
|
|
81e6a9d0d0 | ||
|
|
5d4110d2a7 | ||
|
|
0cedb3ac9f | ||
|
|
0b44c629f2 | ||
|
|
2579fe6b7b | ||
|
|
812ce4bfc4 | ||
|
|
410efe0694 | ||
|
|
b2d944d5cb | ||
|
|
b846c3915c | ||
|
|
ffbc68aa2e | ||
|
|
eddb8549ef | ||
|
|
bb44855078 | ||
|
|
2567026ccb | ||
|
|
0cc8fc6f18 | ||
|
|
cc81b916a6 | ||
|
|
27fadd2c6e | ||
|
|
dc38265b54 | ||
|
|
1ea518d5ef | ||
|
|
901cd5edef | ||
|
|
e1fd47765b | ||
|
|
c02923fbfc | ||
|
|
7c5ce83044 | ||
|
|
37e2e9a844 | ||
|
|
26e5db1849 | ||
|
|
a2766ffe0c | ||
|
|
0f5e38609f | ||
|
|
f178cbf93d | ||
|
|
c8096ca8d2 | ||
|
|
27d29b9853 | ||
|
|
8a171731ba | ||
|
|
abde9e2fba | ||
|
|
6a4a328bbc | ||
|
|
8253fadc96 | ||
|
|
134abbd82b | ||
|
|
fe557b022a | ||
|
|
cd8226130a | ||
|
|
1ebf0e8de8 | ||
|
|
37ea764000 | ||
|
|
0fdb9a6129 | ||
|
|
47b326b7b5 | ||
|
|
e2cf6eb434 | ||
|
|
f79698dcdd | ||
|
|
35a5307db3 | ||
|
|
6341c7d72c | ||
|
|
f4bab789b8 | ||
|
|
fa893ee477 | ||
|
|
014cec06f1 | ||
|
|
431ab5aa6a | ||
|
|
262b0cd9d4 | ||
|
|
e83ec17e95 | ||
|
|
ea593fca1b | ||
|
|
fe1f151ae1 | ||
|
|
b12bba4e2a | ||
|
|
e2005e02bb | ||
|
|
41c8c946ba | ||
|
|
fe08686558 | ||
|
|
0ed2401711 | ||
|
|
06bd606d85 | ||
|
|
c347431907 | ||
|
|
f63d7048f9 | ||
|
|
f39f7c76dd | ||
|
|
0268d0e7d6 | ||
|
|
8515d093e0 | ||
|
|
fe3f326d8d | ||
|
|
8170db40c7 | ||
|
|
99ac0da4bc | ||
|
|
7e2c93420f | ||
|
|
6d46824fb0 | ||
|
|
bb435b39d9 | ||
|
|
2a67d7a6c2 | ||
|
|
ba43c8bab5 | ||
|
|
931e6ed2ac | ||
|
|
a5f0e9ab65 | ||
|
|
6fc133ad6a | ||
|
|
e1b80859f2 | ||
|
|
d069ee31b2 | ||
|
|
981752ade0 | ||
|
|
d01d07fc0a | ||
|
|
526aaca6f5 | ||
|
|
2f8147af59 | ||
|
|
f3016a9096 | ||
|
|
f854a41ba9 | ||
|
|
ca3cadef5e | ||
|
|
3304b0fcf0 | ||
|
|
d8938e259a | ||
|
|
53a554c89d | ||
|
|
e71db01230 | ||
|
|
178e946fc7 | ||
|
|
f3bff12939 | ||
|
|
7a99418dc5 | ||
|
|
c71ba466ea | ||
|
|
8ce5d35543 | ||
|
|
134f834c60 | ||
|
|
8a37c07295 | ||
|
|
bd0ada7842 | ||
|
|
eea96f652d | ||
|
|
38c3061df7 | ||
|
|
f5fa602482 | ||
|
|
e44ac55f63 | ||
|
|
f1cfb73a8b | ||
|
|
5b96885c6d | ||
|
|
c5da90a5b7 | ||
|
|
bcdebfb84e | ||
|
|
359b273649 | ||
|
|
2e2c8dc620 | ||
|
|
8d37b723ca | ||
|
|
315b7f282f | ||
|
|
a3f8e9dfa7 | ||
|
|
982810f7cc | ||
|
|
90b96d19cd | ||
|
|
6a52bb6f54 | ||
|
|
cacaa4393f | ||
|
|
d63ab4e9a4 | ||
|
|
ca6daec8dd | ||
|
|
c87f2420a6 | ||
|
|
f5bbbc52f4 | ||
|
|
9e3dde8ec7 | ||
|
|
9dba182e51 | ||
|
|
944fc857eb | ||
|
|
7507a658ac | ||
|
|
9fa4f5eb6b | ||
|
|
ce4d71d626 | ||
|
|
8e2ef3f38b | ||
|
|
8dc952775e | ||
|
|
99b6163e27 | ||
|
|
beaf55f1fc | ||
|
|
980bb9059f | ||
|
|
0e7281eb71 | ||
|
|
0b6133d7b5 | ||
|
|
b57ca64275 | ||
|
|
faadbd734b | ||
|
|
88b0a93409 | ||
|
|
4a995105a9 | ||
|
|
7fe496f983 | ||
|
|
e56370eb5b | ||
|
|
b8af7f63a0 | ||
|
|
3eea555155 | ||
|
|
897c923cc9 | ||
|
|
67193e3deb | ||
|
|
0e722efb09 | ||
|
|
3736f33ebf | ||
|
|
d1d9c3f9d7 | ||
|
|
cd5cbe0910 | ||
|
|
814e992c0b | ||
|
|
660fe78735 | ||
|
|
87d084e18c | ||
|
|
9ce2a73fc5 | ||
|
|
f2314b26ba | ||
|
|
74dcf41f25 | ||
|
|
b6ba30186f | ||
|
|
32637a0328 | ||
|
|
0addd90e14 | ||
|
|
1b5ee5b10a | ||
|
|
042adeb5d0 | ||
|
|
7e4ce0dacc | ||
|
|
8ceb22fe8a | ||
|
|
c5553ec855 | ||
|
|
bb3ed54291 | ||
|
|
513ba3b6f7 | ||
|
|
17d688afef | ||
|
|
d81eee26b3 | ||
|
|
cc5ada63a4 | ||
|
|
88fb60e0b5 | ||
|
|
02200acad0 | ||
|
|
1a2d190bdb | ||
|
|
2db4ff168a | ||
|
|
a77c8cc5d2 | ||
|
|
b8866c1fe4 | ||
|
|
f0f17db847 | ||
|
|
a5c003acb0 | ||
|
|
7b44fd0f9d | ||
|
|
cebee0b8fa | ||
|
|
d886bc6c48 | ||
|
|
d81adcfaa5 | ||
|
|
6da9bfbbce | ||
|
|
69a6e622d0 | ||
|
|
1dcfd64028 | ||
|
|
5d1c1f721e | ||
|
|
fbc8bbf305 | ||
|
|
cdef55bb88 | ||
|
|
26df48b2aa | ||
|
|
a7baea0522 | ||
|
|
55e6003749 | ||
|
|
846acd5d4c | ||
|
|
43f8145858 | ||
|
|
79759928f6 | ||
|
|
eb59d28154 | ||
|
|
79f63a2e74 | ||
|
|
6a62254048 | ||
|
|
657a1d75af | ||
|
|
2694def56a | ||
|
|
7843341da3 | ||
|
|
d46314648e | ||
|
|
e45011af57 | ||
|
|
fb09884893 | ||
|
|
d1eecafa63 | ||
|
|
b47d991f56 | ||
|
|
553ea812a7 | ||
|
|
216e374310 | ||
|
|
034b0b8040 | ||
|
|
5ab9e12b46 | ||
|
|
abe6e0d22d | ||
|
|
f5b550191c | ||
|
|
3dcacb3730 | ||
|
|
033589a66b | ||
|
|
e46a647c45 | ||
|
|
f26492fc2d | ||
|
|
3473c3f7b6 | ||
|
|
1b5242b4f9 | ||
|
|
6d897def1b | ||
|
|
2f10e25738 | ||
|
|
555bd257bd | ||
|
|
3afd974dea | ||
|
|
f4120c9d45 | ||
|
|
61cb1cc6f8 | ||
|
|
49bc1d0b3b | ||
|
|
ba23d24dd1 | ||
|
|
556a63de19 | ||
|
|
fae3c4d437 | ||
|
|
89c2ed2a1c | ||
|
|
23f1cb06d6 | ||
|
|
ac92e2dd2d | ||
|
|
bf58425351 | ||
|
|
a3dc0ab398 | ||
|
|
224ebdb8b9 | ||
|
|
cf80d295f3 | ||
|
|
2133869127 | ||
|
|
97330ac621 | ||
|
|
1ee1559506 | ||
|
|
eccc336319 | ||
|
|
7fe657ec71 | ||
|
|
77c07bfd19 | ||
|
|
4de938d97a | ||
|
|
dad1c87afe | ||
|
|
801dbb6d03 | ||
|
|
fa0be82da8 | ||
|
|
7e8bc8d362 | ||
|
|
0bb2a8e0d0 | ||
|
|
2e72b57f2f | ||
|
|
bff1039e3a | ||
|
|
5a999cb77f | ||
|
|
3a2539e0ac | ||
|
|
e262f35d0a | ||
|
|
176bfa6529 | ||
|
|
240c4cf2fd | ||
|
|
db5ec5d876 | ||
|
|
e1dfaf5d87 | ||
|
|
5436154f0d | ||
|
|
809e218d20 | ||
|
|
1eaad6cebb | ||
|
|
56fccecd06 | ||
|
|
3890a947ca | ||
|
|
e299272378 | ||
|
|
70248bd05a | ||
|
|
7a5fde8f5a | ||
|
|
62ba9f1950 | ||
|
|
610b676444 | ||
|
|
58699e3c90 | ||
|
|
9be24a1c9f | ||
|
|
5ace41471e | ||
|
|
3b2106ed30 | ||
|
|
5f4f997126 | ||
|
|
49d397a419 | ||
|
|
ea1ab96749 | ||
|
|
24c62e719a | ||
|
|
9c6b7f688e | ||
|
|
d41dce5ecb | ||
|
|
52a3eafede | ||
|
|
55dfc85159 | ||
|
|
a7a478a19e | ||
|
|
2080afd9de | ||
|
|
9aa136b982 | ||
|
|
3a191f37cb | ||
|
|
429106340f | ||
|
|
530c73b457 | ||
|
|
fb9729fdb9 | ||
|
|
45a09c76ff | ||
|
|
efd65a1b65 | ||
|
|
ae60188eb9 | ||
|
|
3b904525d9 | ||
|
|
1e31f5202f | ||
|
|
f12d41138a | ||
|
|
98369f6a5d | ||
|
|
8f9bf1995b | ||
|
|
e7de3b5f9d | ||
|
|
3541d06d07 | ||
|
|
db0e3cd772 | ||
|
|
d3fee08f9a | ||
|
|
727fb6eabe | ||
|
|
d610c60991 | ||
|
|
3f6e11d26e | ||
|
|
a4577769ae | ||
|
|
7f927d4774 | ||
|
|
e091673f8f | ||
|
|
9842eff887 | ||
|
|
c40b3d3983 | ||
|
|
ac5eefdee4 | ||
|
|
bf508643a5 | ||
|
|
02fc16e97d | ||
|
|
1a83a739dc |
36
.github/ISSUE_TEMPLATE.md
vendored
36
.github/ISSUE_TEMPLATE.md
vendored
@@ -4,8 +4,9 @@ take a lot longer to find the problem! Please take the time to help us
|
||||
debugging the problem by collecting information, even if it seems irrelevant to
|
||||
you. Thanks!
|
||||
|
||||
If you have a question, maybe the forum at https://discourse.restic.net is a
|
||||
better place.
|
||||
If you have a question, the forum at https://discourse.restic.net is a better place.
|
||||
Please do not create issues for usage or documentation questions! We're using
|
||||
the GitHub issue tracker mainly for tracking bugs and feature requests.
|
||||
-->
|
||||
|
||||
## Output of `restic version`
|
||||
@@ -14,24 +15,49 @@ better place.
|
||||
## How did you run restic exactly?
|
||||
|
||||
<!--
|
||||
Include the complete command line and any environment variables you used to
|
||||
configure restic's backend access. Make sure to replace sensitive values!
|
||||
This section should include at least:
|
||||
|
||||
* The complete command line and any environment variables you used to
|
||||
configure restic's backend access. Make sure to replace sensitive values!
|
||||
|
||||
* The output of the commands, what restic prints gives may give us much
|
||||
information to diagnose the problem!
|
||||
-->
|
||||
|
||||
|
||||
## What backend/server/service did you use?
|
||||
## What backend/server/service did you use to store the repository?
|
||||
|
||||
|
||||
## Expected behavior
|
||||
|
||||
<!--
|
||||
Describe what you'd like restic to do differently.
|
||||
-->
|
||||
|
||||
## Actual behavior
|
||||
|
||||
<!--
|
||||
In this section, please try to concentrate on observations, so only describe
|
||||
what you observed directly.
|
||||
-->
|
||||
|
||||
## Steps to reproduce the behavior
|
||||
|
||||
<!--
|
||||
The more time you spend describing an easy way to reproduce the behavior (if
|
||||
this is possible), the easier it is for the project developers to fix it!
|
||||
-->
|
||||
|
||||
|
||||
## Do you have any idea what may have caused this?
|
||||
|
||||
|
||||
## Do you have an idea how to solve the issue?
|
||||
|
||||
## Did restic help you or made you happy in any way?
|
||||
|
||||
<!--
|
||||
Answering this question is not required, but if you have anything positive to share, please do so here!
|
||||
Sometimes we get tired of reading bug reports all day and a little positive end note does wonders.
|
||||
Idea by Joey Hess, https://joeyh.name/blog/entry/two_holiday_stories/
|
||||
-->
|
||||
|
||||
31
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
31
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
<!--
|
||||
Thank you very much for contributing code or documentation to restic! Please
|
||||
fill out the following questions to make it easier for us to review your
|
||||
changes.
|
||||
|
||||
You do not need to check all the boxes below all at once, feel free to take
|
||||
your time and add more commits. If you're done and ready for review, please
|
||||
check the last box.
|
||||
-->
|
||||
|
||||
### What is the purpose of this change? What does it change?
|
||||
|
||||
<!--
|
||||
Describe the changes here, as detailed as needed.
|
||||
-->
|
||||
|
||||
### Was the change discussed in an issue or in the forum before?
|
||||
|
||||
<!--
|
||||
Link issues and relevant forum posts here.
|
||||
-->
|
||||
|
||||
### Checklist
|
||||
|
||||
- [ ] I have read the [Contribution Guidelines](https://github.com/restic/restic/blob/master/CONTRIBUTING.md#providing-patches)
|
||||
- [ ] I have added tests for all changes in this PR
|
||||
- [ ] I have added documentation for the changes (in the manual)
|
||||
- [ ] There's an entry in the `CHANGELOG.md` file that describe the changes for our users
|
||||
- [ ] I have run `gofmt` on the code in all commits
|
||||
- [ ] All commit messages are formatted in the same style as [the other commits in the repo](https://github.com/restic/restic/blob/master/CONTRIBUTING.md#git-commits)
|
||||
- [ ] I'm done, this Pull Request is ready for review
|
||||
824
CHANGELOG.md
824
CHANGELOG.md
@@ -1,284 +1,700 @@
|
||||
This file describes changes relevant to all users that are made in each
|
||||
released version of restic from the perspective of the user.
|
||||
Changelog for restic 0.8.1 (UNRELEASED)
|
||||
=======================================
|
||||
|
||||
Important Changes in 0.7.3
|
||||
==========================
|
||||
The following sections list the changes in restic 0.8.1 relevant to
|
||||
restic users. The changes are ordered by importance.
|
||||
|
||||
Summary
|
||||
-------
|
||||
|
||||
* Fix #1457: Improve s3 backend with DigitalOcean Spaces
|
||||
* Fix #1454: Correct cache dir location for Windows and Darwin
|
||||
* Fix #1457: Disable handling SIGPIPE
|
||||
* Chg #1452: Do not save atime by default
|
||||
* Enh #1436: Add code to detect old cache directories
|
||||
* Enh #1439: Improve cancellation logic
|
||||
* Enh #11: Add the `diff` command
|
||||
|
||||
Details
|
||||
-------
|
||||
|
||||
* Bugfix #1457: Improve s3 backend with DigitalOcean Spaces
|
||||
|
||||
https://github.com/restic/restic/issues/1457
|
||||
https://github.com/restic/restic/pull/1459
|
||||
|
||||
* Bugfix #1454: Correct cache dir location for Windows and Darwin
|
||||
|
||||
The cache directory on Windows and Darwin was not correct, instead the directory `.cache` was
|
||||
used.
|
||||
|
||||
https://github.com/restic/restic/pull/1454
|
||||
|
||||
* Bugfix #1457: Disable handling SIGPIPE
|
||||
|
||||
We've disabled handling SIGPIPE again. Turns out, writing to broken TCP connections also
|
||||
raised SIGPIPE, so restic exits on the first write to a broken connection. Instead, restic
|
||||
should retry the request.
|
||||
|
||||
https://github.com/restic/restic/issues/1457
|
||||
https://github.com/restic/restic/issues/1466
|
||||
https://github.com/restic/restic/pull/1459
|
||||
|
||||
* Change #1452: Do not save atime by default
|
||||
|
||||
By default, the access time for files and dirs is not saved any more. It is not possible to
|
||||
reliably disable updating the access time during a backup, so for the next backup the access
|
||||
time is different again. This means a lot of metadata is saved. If you want to save the access time
|
||||
anyway, pass `--with-atime` to the `backup` command.
|
||||
|
||||
https://github.com/restic/restic/pull/1452
|
||||
|
||||
* Enhancement #1436: Add code to detect old cache directories
|
||||
|
||||
We've added code to detect old cache directories of repositories that haven't been used in a
|
||||
long time, restic now prints a note when it detects that such dirs exist. Also, the option
|
||||
`--cleanup-cache` was added to automatically remove such directories. That's not a problem
|
||||
because the cache will be rebuild once a repo is accessed again.
|
||||
|
||||
https://github.com/restic/restic/pull/1436
|
||||
|
||||
* Enhancement #1439: Improve cancellation logic
|
||||
|
||||
The cancellation logic was improved, restic can now shut down cleanly when requested to do so
|
||||
(e.g. via ctrl+c).
|
||||
|
||||
https://github.com/restic/restic/pull/1439
|
||||
|
||||
* Enhancement #11: Add the `diff` command
|
||||
|
||||
The command `diff` was added, it allows comparing two snapshots and listing all differences.
|
||||
|
||||
https://github.com/restic/restic/issues/11
|
||||
https://github.com/restic/restic/issues/1460
|
||||
https://github.com/restic/restic/pull/1462
|
||||
|
||||
|
||||
Changelog for restic 0.8.0 (2017-11-26)
|
||||
=======================================
|
||||
|
||||
The following sections list the changes in restic 0.8.0 relevant to
|
||||
restic users. The changes are ordered by importance.
|
||||
|
||||
Summary
|
||||
-------
|
||||
|
||||
* Sec #1445: Prevent writing outside the target directory during restore
|
||||
* Fix #1256: Re-enable workaround for S3 backend
|
||||
* Fix #1291: Reuse backend TCP connections to BackBlaze B2
|
||||
* Fix #1317: Run prune when `forget --prune` is called with just snapshot IDs
|
||||
* Fix #1292: Remove implicit path `/restic` for the s3 backend
|
||||
* Enh #1102: Add subdirectory `ids` to fuse mount
|
||||
* Enh #1114: Add `--cacert` to specify TLS certificates to check against
|
||||
* Enh #1216: Add upload/download limiting
|
||||
* Enh #1271: Cache results for excludes for `backup`
|
||||
* Enh #1274: Add `generate` command, replaces `manpage` and `autocomplete`
|
||||
* Enh #1367: Allow comments in files read from via `--file-from`
|
||||
* Enh #448: Sftp backend prompts for password
|
||||
* Enh #510: Add `dump` command
|
||||
* Enh #29: Add local metadata cache
|
||||
* Enh #1249: Add `latest` symlink in fuse mount
|
||||
* Enh #1269: Add `--compact` to `forget` command
|
||||
* Enh #1281: Google Cloud Storage backend needs less permissions
|
||||
* Enh #1303: Make `check` print `no errors found` explicitly
|
||||
* Enh #1353: Retry failed backend requests
|
||||
|
||||
Details
|
||||
-------
|
||||
|
||||
* Security #1445: Prevent writing outside the target directory during restore
|
||||
|
||||
A vulnerability was found in the restic restorer, which allowed attackers in special
|
||||
circumstances to restore files to a location outside of the target directory. Due to the
|
||||
circumstances we estimate this to be a low-risk vulnerability, but urge all users to upgrade to
|
||||
the latest version of restic.
|
||||
|
||||
Exploiting the vulnerability requires a Linux/Unix system which saves backups via restic and
|
||||
a Windows systems which restores files from the repo. In addition, the attackers need to be able
|
||||
to create create files with arbitrary names which are then saved to the restic repo. For
|
||||
example, by creating a file named "..\test.txt" (which is a perfectly legal filename on Linux)
|
||||
and restoring a snapshot containing this file on Windows, it would be written to the parent of
|
||||
the target directory.
|
||||
|
||||
We'd like to thank Tyler Spivey for reporting this responsibly!
|
||||
|
||||
https://github.com/restic/restic/pull/1445
|
||||
|
||||
* Bugfix #1256: Re-enable workaround for S3 backend
|
||||
|
||||
We've re-enabled a workaround for `minio-go` (the library we're using to access s3 backends),
|
||||
this reduces memory usage.
|
||||
|
||||
https://github.com/restic/restic/issues/1256
|
||||
https://github.com/restic/restic/pull/1267
|
||||
|
||||
* Bugfix #1291: Reuse backend TCP connections to BackBlaze B2
|
||||
|
||||
A bug was discovered in the library we're using to access Backblaze, it now reuses already
|
||||
established TCP connections which should be a lot faster and not cause network failures any
|
||||
more.
|
||||
|
||||
https://github.com/restic/restic/issues/1291
|
||||
https://github.com/restic/restic/pull/1301
|
||||
|
||||
* Bugfix #1317: Run prune when `forget --prune` is called with just snapshot IDs
|
||||
|
||||
A bug in the `forget` command caused `prune` not to be run when `--prune` was specified without a
|
||||
policy, e.g. when only snapshot IDs that should be forgotten are listed manually.
|
||||
|
||||
https://github.com/restic/restic/pull/1317
|
||||
|
||||
* Bugfix #1292: Remove implicit path `/restic` for the s3 backend
|
||||
|
||||
The s3 backend used the subdir `restic` within a bucket if no explicit path after the bucket name
|
||||
was specified. Since this version, restic does not use this default path any more. If you
|
||||
created a repo on s3 in a bucket without specifying a path within the bucket, you need to add
|
||||
`/restic` at the end of the repository specification to access your repo:
|
||||
`s3:s3.amazonaws.com/bucket/restic`
|
||||
|
||||
https://github.com/restic/restic/issues/1292
|
||||
https://github.com/restic/restic/pull/1437
|
||||
|
||||
* Enhancement #1102: Add subdirectory `ids` to fuse mount
|
||||
|
||||
The fuse mount now has an `ids` subdirectory which contains the snapshots below their (short)
|
||||
IDs.
|
||||
|
||||
https://github.com/restic/restic/issues/1102
|
||||
https://github.com/restic/restic/pull/1299
|
||||
https://github.com/restic/restic/pull/1320
|
||||
|
||||
* Enhancement #1114: Add `--cacert` to specify TLS certificates to check against
|
||||
|
||||
We've added the `--cacert` option which can be used to pass one (or more) CA certificates to
|
||||
restic. These are used in addition to the system CA certificates to verify HTTPS certificates
|
||||
(e.g. for the REST backend).
|
||||
|
||||
https://github.com/restic/restic/issues/1114
|
||||
https://github.com/restic/restic/pull/1276
|
||||
|
||||
* Enhancement #1216: Add upload/download limiting
|
||||
|
||||
We've added support for rate limiting through `--limit-upload` and `--limit-download`
|
||||
flags.
|
||||
|
||||
https://github.com/restic/restic/issues/1216
|
||||
https://github.com/restic/restic/pull/1336
|
||||
https://github.com/restic/restic/pull/1358
|
||||
|
||||
* Enhancement #1271: Cache results for excludes for `backup`
|
||||
|
||||
The `backup` command now caches the result of excludes for a directory.
|
||||
|
||||
https://github.com/restic/restic/issues/1271
|
||||
https://github.com/restic/restic/pull/1326
|
||||
|
||||
* Enhancement #1274: Add `generate` command, replaces `manpage` and `autocomplete`
|
||||
|
||||
The `generate` command has been added, which replaces the now removed commands `manpage` and
|
||||
`autocomplete`. This release of restic contains the most recent manpages in `doc/man` and the
|
||||
auto-completion files for bash and zsh in `doc/bash-completion.sh` and
|
||||
`doc/zsh-completion.zsh`
|
||||
|
||||
https://github.com/restic/restic/issues/1274
|
||||
https://github.com/restic/restic/pull/1282
|
||||
|
||||
* Enhancement #1367: Allow comments in files read from via `--file-from`
|
||||
|
||||
When the list of files/dirs to be saved is read from a file with `--files-from`, comment lines
|
||||
(starting with `#`) are now ignored.
|
||||
|
||||
https://github.com/restic/restic/issues/1367
|
||||
https://github.com/restic/restic/pull/1368
|
||||
|
||||
* Enhancement #448: Sftp backend prompts for password
|
||||
|
||||
The sftp backend now prompts for the password if a password is necessary for login.
|
||||
|
||||
https://github.com/restic/restic/issues/448
|
||||
https://github.com/restic/restic/pull/1270
|
||||
|
||||
* Enhancement #510: Add `dump` command
|
||||
|
||||
We've added the `dump` command which prints a file from a snapshot to stdout. This can e.g. be
|
||||
used to restore files read with `backup --stdin`.
|
||||
|
||||
https://github.com/restic/restic/issues/510
|
||||
https://github.com/restic/restic/pull/1346
|
||||
|
||||
* Enhancement #29: Add local metadata cache
|
||||
|
||||
We've added a local cache for metadata so that restic doesn't need to load all metadata
|
||||
(snapshots, indexes, ...) from the repo each time it starts. By default the cache is active, but
|
||||
there's a new global option `--no-cache` that can be used to disable the cache. By deafult, the
|
||||
cache a standard cache folder for the OS, which can be overridden with `--cache-dir`. The cache
|
||||
will automatically populate, indexes and snapshots are saved as they are loaded. Cache
|
||||
directories for repos that haven't been used recently can automatically be removed by restic
|
||||
with the `--cleanup-cache` option.
|
||||
|
||||
A related change was to by default create pack files in the repo that contain either data or
|
||||
metadata, not both mixed together. This allows easy caching of only the metadata files. The
|
||||
next run of `restic prune` will untangle mixed files automatically.
|
||||
|
||||
https://github.com/restic/restic/issues/29
|
||||
https://github.com/restic/restic/issues/738
|
||||
https://github.com/restic/restic/issues/282
|
||||
https://github.com/restic/restic/pull/1040
|
||||
https://github.com/restic/restic/pull/1287
|
||||
https://github.com/restic/restic/pull/1436
|
||||
https://github.com/restic/restic/pull/1265
|
||||
|
||||
* Enhancement #1249: Add `latest` symlink in fuse mount
|
||||
|
||||
The directory structure in the fuse mount now exposes a symlink `latest` which points to the
|
||||
latest snapshot in that particular directory.
|
||||
|
||||
https://github.com/restic/restic/pull/1249
|
||||
|
||||
* Enhancement #1269: Add `--compact` to `forget` command
|
||||
|
||||
The option `--compact` was added to the `forget` command to provide the same compact view as the
|
||||
`snapshots` command.
|
||||
|
||||
https://github.com/restic/restic/pull/1269
|
||||
|
||||
* Enhancement #1281: Google Cloud Storage backend needs less permissions
|
||||
|
||||
The Google Cloud Storage backend no longer requires the service account to have the
|
||||
`storage.buckets.get` permission ("Storage Admin" role) in `restic init` if the bucket
|
||||
already exists.
|
||||
|
||||
https://github.com/restic/restic/pull/1281
|
||||
|
||||
* Enhancement #1303: Make `check` print `no errors found` explicitly
|
||||
|
||||
The `check` command now explicetly prints `No errors were found` when no errors could be found.
|
||||
|
||||
https://github.com/restic/restic/issues/1303
|
||||
https://github.com/restic/restic/pull/1319
|
||||
|
||||
* Enhancement #1353: Retry failed backend requests
|
||||
|
||||
https://github.com/restic/restic/pull/1353
|
||||
|
||||
|
||||
Changelog for restic 0.7.3 (2017-09-20)
|
||||
=======================================
|
||||
|
||||
The following sections list the changes in restic 0.7.3 relevant to
|
||||
restic users. The changes are ordered by importance.
|
||||
|
||||
Summary
|
||||
-------
|
||||
|
||||
* Fix #1246: List all files stored in Google Cloud Storage
|
||||
|
||||
Details
|
||||
-------
|
||||
|
||||
* Bugfix #1246: List all files stored in Google Cloud Storage
|
||||
|
||||
For large backups stored in Google Cloud Storage, the `prune` command fails because listing
|
||||
only returns the first 1000 files. This has been corrected, no data is lost in the process. In
|
||||
addition, a plausibility check was added to `prune`.
|
||||
|
||||
* For large backups stored in Google Cloud Storage, the `prune` command fails
|
||||
because listing only returns the first 1000 files. This has been corrected,
|
||||
no data is lost in the process. In addition, a plausibility check was added
|
||||
to `prune`.
|
||||
https://github.com/restic/restic/issues/1246
|
||||
https://github.com/restic/restic/pull/1247
|
||||
|
||||
|
||||
Important Changes in 0.7.2
|
||||
==========================
|
||||
Changelog for restic 0.7.2 (2017-09-13)
|
||||
=======================================
|
||||
|
||||
* We've added an official docker image and a Dockerfile to build this image in
|
||||
`docker/`.
|
||||
https://github.com/restic/restic/pull/1061
|
||||
The following sections list the changes in restic 0.7.2 relevant to
|
||||
restic users. The changes are ordered by importance.
|
||||
|
||||
* The git repository layout was changed to resemble the layout typically used
|
||||
in Go projects, we're not using `gb` for building restic any more and
|
||||
vendoring the dependencies is now taken care of by `dep`.
|
||||
https://github.com/restic/restic/pull/1126
|
||||
Summary
|
||||
-------
|
||||
|
||||
* We now support saving backups on Google Cloud Storage.
|
||||
https://github.com/restic/restic/pull/1134
|
||||
https://github.com/restic/restic/pull/1052
|
||||
https://github.com/restic/restic/issues/211
|
||||
* Fix #1167: Do not create a local repo unless `init` is used
|
||||
* Fix #1164: Make the `key remove` command behave as documented
|
||||
* Fix #1191: Make sure to write profiling files on interrupt
|
||||
* Enh #1132: Make `key` command always prompt for a password
|
||||
* Enh #1179: Resolve name conflicts, append a counter
|
||||
* Enh #1218: Add `--compact` to `snapshots` command
|
||||
* Enh #317: Add `--exclude-caches` and `--exclude-if-present`
|
||||
* Enh #697: Automatically generate man pages for all restic commands
|
||||
* Enh #1044: Improve `restore`, do not traverse/load excluded directories
|
||||
* Enh #1061: Add Dockerfile and official Docker image
|
||||
* Enh #1126: Use the standard Go git repository layout, use `dep` for vendoring
|
||||
* Enh #211: Add support for storing backups on Google Cloud Storage
|
||||
* Enh #1144: Properly report errors when reading files with exclude patterns.
|
||||
* Enh #609: Add support for storing backups on Microsoft Azure Blob Storage
|
||||
* Enh #1196: Add `--group-by` to `forget` command for flexible grouping
|
||||
* Enh #1203: Print stats on all BSD systems when SIGINFO (ctrl+t) is received
|
||||
* Enh #1205: Allow specifying time/date for a backup with `--time`
|
||||
|
||||
* We've added support for Microsoft Azure Blob Storage as a restic backend.
|
||||
https://github.com/restic/restic/pull/1149
|
||||
https://github.com/restic/restic/pull/1059
|
||||
https://github.com/restic/restic/issues/609
|
||||
Details
|
||||
-------
|
||||
|
||||
* In the course of supporting Microsoft Azure Blobe Storage Go 1.8 is now a
|
||||
requirement to build restic.
|
||||
* Bugfix #1167: Do not create a local repo unless `init` is used
|
||||
|
||||
* The `restore` command has been improved: When dirs are excluded (or not
|
||||
included) in a restore, they are not loaded from the repo any more.
|
||||
https://github.com/restic/restic/pull/1044
|
||||
When a restic command other than `init` is used with a local repository and the repository
|
||||
directory does not exist, restic creates the directory structure. That's an error, only the
|
||||
`init` command should create the dir.
|
||||
|
||||
* Name collisions are now resolved by appending a counter.
|
||||
https://github.com/restic/restic/issues/1179
|
||||
https://github.com/restic/restic/pull/1209
|
||||
|
||||
|
||||
Small changes
|
||||
-------------
|
||||
|
||||
* The `key` command now prompts for a password even if the original password
|
||||
to access a repo has been specified via the `RESTIC_PASSWORD` environment
|
||||
variable or a password file.
|
||||
https://github.com/restic/restic/issues/1132
|
||||
https://github.com/restic/restic/pull/1133
|
||||
|
||||
* Properly report errors when reading files with exclude patterns.
|
||||
https://github.com/restic/restic/pull/1144
|
||||
|
||||
* We now automatically generate man pages for all restic commands, see the
|
||||
subdir `doc/man`.
|
||||
https://github.com/restic/restic/issues/697
|
||||
https://github.com/restic/restic/pull/1147
|
||||
|
||||
* The `key remove` command was corrected and now works as documented.
|
||||
https://github.com/restic/restic/pull/1164
|
||||
|
||||
* When a restic command other than `init` is used with a local repository and
|
||||
the repository directory does not exist, restic creates the directory
|
||||
structure. That's an error, only the `init` command should create the dir.
|
||||
https://github.com/restic/restic/issues/1167
|
||||
https://github.com/restic/restic/pull/1182
|
||||
|
||||
* Restic now prints stats on all BSD systems (not only on darwin) when SIGINFO
|
||||
is received (usually when ctrl+t is pressed).
|
||||
https://github.com/restic/restic/pull/1203
|
||||
https://github.com/restic/restic/pull/1082#issuecomment-326279920
|
||||
* Bugfix #1164: Make the `key remove` command behave as documented
|
||||
|
||||
https://github.com/restic/restic/pull/1164
|
||||
|
||||
* Bugfix #1191: Make sure to write profiling files on interrupt
|
||||
|
||||
Since a few releases restic had the ability to write profiling files for memory and CPU usage
|
||||
when `debug` is enabled. It was discovered that when restic is interrupted (ctrl+c is
|
||||
pressed), the proper shutdown hook is not run. This is now corrected.
|
||||
|
||||
* Since a few releases restic had the ability to write profiling files for
|
||||
memory and CPU usage when `debug` is enabled. It was discovered that when
|
||||
restic is interrupted (ctrl+c is pressed), the proper shutdown hook is not
|
||||
run. This is now corrected.
|
||||
https://github.com/restic/restic/pull/1191
|
||||
|
||||
* A new option `--exclude-caches` was added that allows excluding cache
|
||||
directories (that are tagged as such). This is a special case of a more
|
||||
generic option `--exclude-if-present` which excludes a directory if a file
|
||||
with a specific name (and contents) is present.
|
||||
* Enhancement #1132: Make `key` command always prompt for a password
|
||||
|
||||
The `key` command now prompts for a password even if the original password to access a repo has
|
||||
been specified via the `RESTIC_PASSWORD` environment variable or a password file.
|
||||
|
||||
https://github.com/restic/restic/issues/1132
|
||||
https://github.com/restic/restic/pull/1133
|
||||
|
||||
* Enhancement #1179: Resolve name conflicts, append a counter
|
||||
|
||||
https://github.com/restic/restic/issues/1179
|
||||
https://github.com/restic/restic/pull/1209
|
||||
|
||||
* Enhancement #1218: Add `--compact` to `snapshots` command
|
||||
|
||||
The option `--compact` was added to the `snapshots` command to get a better overview of the
|
||||
snapshots in a repo. It limits each snapshot to a single line.
|
||||
|
||||
https://github.com/restic/restic/issues/1218
|
||||
https://github.com/restic/restic/pull/1223
|
||||
|
||||
* Enhancement #317: Add `--exclude-caches` and `--exclude-if-present`
|
||||
|
||||
A new option `--exclude-caches` was added that allows excluding cache directories (that are
|
||||
tagged as such). This is a special case of a more generic option `--exclude-if-present` which
|
||||
excludes a directory if a file with a specific name (and contents) is present.
|
||||
|
||||
https://github.com/restic/restic/issues/317
|
||||
https://github.com/restic/restic/pull/1170
|
||||
https://github.com/restic/restic/pull/1224
|
||||
|
||||
* The `forget` command now has an option `--group-by` that allows flexible
|
||||
grouping policies.
|
||||
* Enhancement #697: Automatically generate man pages for all restic commands
|
||||
|
||||
https://github.com/restic/restic/issues/697
|
||||
https://github.com/restic/restic/pull/1147
|
||||
|
||||
* Enhancement #1044: Improve `restore`, do not traverse/load excluded directories
|
||||
|
||||
https://github.com/restic/restic/pull/1044
|
||||
|
||||
* Enhancement #1061: Add Dockerfile and official Docker image
|
||||
|
||||
https://github.com/restic/restic/pull/1061
|
||||
|
||||
* Enhancement #1126: Use the standard Go git repository layout, use `dep` for vendoring
|
||||
|
||||
The git repository layout was changed to resemble the layout typically used in Go projects,
|
||||
we're not using `gb` for building restic any more and vendoring the dependencies is now taken
|
||||
care of by `dep`.
|
||||
|
||||
https://github.com/restic/restic/pull/1126
|
||||
|
||||
* Enhancement #211: Add support for storing backups on Google Cloud Storage
|
||||
|
||||
https://github.com/restic/restic/issues/211
|
||||
https://github.com/restic/restic/pull/1134
|
||||
https://github.com/restic/restic/pull/1052
|
||||
|
||||
* Enhancement #1144: Properly report errors when reading files with exclude patterns.
|
||||
|
||||
https://github.com/restic/restic/pull/1144
|
||||
|
||||
* Enhancement #609: Add support for storing backups on Microsoft Azure Blob Storage
|
||||
|
||||
The library we're using to access the service requires Go 1.8, so restic now needs at least Go
|
||||
1.8.
|
||||
|
||||
https://github.com/restic/restic/issues/609
|
||||
https://github.com/restic/restic/pull/1149
|
||||
https://github.com/restic/restic/pull/1059
|
||||
|
||||
* Enhancement #1196: Add `--group-by` to `forget` command for flexible grouping
|
||||
|
||||
https://github.com/restic/restic/pull/1196
|
||||
|
||||
* The date and time restic records for a new backup can now be specified
|
||||
externally by passing `--time` to the `backup` command.
|
||||
* Enhancement #1203: Print stats on all BSD systems when SIGINFO (ctrl+t) is received
|
||||
|
||||
https://github.com/restic/restic/pull/1203
|
||||
https://github.com/restic/restic/pull/1082
|
||||
|
||||
* Enhancement #1205: Allow specifying time/date for a backup with `--time`
|
||||
|
||||
https://github.com/restic/restic/pull/1205
|
||||
|
||||
* The option `--compact` was added to the `snapshots` command to get a better
|
||||
overview of the snapshots in a repo. It limits each snapshot to a single
|
||||
line.
|
||||
https://github.com/restic/restic/issues/1218
|
||||
https://github.com/restic/restic/pull/1223
|
||||
|
||||
Changelog for restic 0.7.1 (2017-07-22)
|
||||
=======================================
|
||||
|
||||
Important Changes in 0.7.1
|
||||
==========================
|
||||
The following sections list the changes in restic 0.7.1 relevant to
|
||||
restic users. The changes are ordered by importance.
|
||||
|
||||
* The `migrate` command for chaning the `s3legacy` layout to the `default`
|
||||
layout for s3 backends has been improved: It can now be restarted with
|
||||
`restic migrate --force s3_layout` and automatically retries operations on
|
||||
error.
|
||||
https://github.com/restic/restic/issues/1073
|
||||
https://github.com/restic/restic/pull/1075
|
||||
Summary
|
||||
-------
|
||||
|
||||
Small changes
|
||||
-------------
|
||||
* Fix #1115: Fix `prune`, only include existing files in indexes
|
||||
* Enh #1055: Create subdirs below `data/` for local/sftp backends
|
||||
* Enh #1067: Allow loading credentials for s3 from IAM
|
||||
* Enh #1073: Add `migrate` cmd to migrate from `s3legacy` to `default` layout
|
||||
* Enh #1081: Clarify semantic for `--tasg` for the `forget` command
|
||||
* Enh #1080: Ignore chmod() errors on filesystems which do not support it
|
||||
* Enh #1082: Print stats on SIGINFO on Darwin and FreeBSD (ctrl+t)
|
||||
|
||||
Details
|
||||
-------
|
||||
|
||||
* Bugfix #1115: Fix `prune`, only include existing files in indexes
|
||||
|
||||
A bug was found (and corrected) in the index rebuilding after prune, which led to indexes which
|
||||
include blobs that were not present in the repo any more. There were already checks in place
|
||||
which detected this situation and aborted with an error message. A new run of either `prune` or
|
||||
`rebuild-index` corrected the index files. This is now fixed and a test has been added to detect
|
||||
this.
|
||||
|
||||
https://github.com/restic/restic/pull/1115
|
||||
|
||||
* Enhancement #1055: Create subdirs below `data/` for local/sftp backends
|
||||
|
||||
The local and sftp backends now create the subdirs below `data/` on open/init. This way, restic
|
||||
makes sure that they always exist. This is connected to an issue for the sftp server:
|
||||
|
||||
* The local and sftp backends now create the subdirs below `data/` on
|
||||
open/init. This way, restic makes sure that they always exist. This is
|
||||
connected to an issue for the sftp server:
|
||||
https://github.com/restic/rest-server/pull/11#issuecomment-309879710
|
||||
https://github.com/restic/restic/issues/1055
|
||||
https://github.com/restic/restic/pull/1077
|
||||
https://github.com/restic/restic/pull/1105
|
||||
https://github.com/restic/rest-server/pull/11#issuecomment-309879710
|
||||
|
||||
* Enhancement #1067: Allow loading credentials for s3 from IAM
|
||||
|
||||
When no S3 credentials are specified in the environment variables, restic now tries to load
|
||||
credentials from an IAM instance profile when the s3 backend is used.
|
||||
|
||||
* When no S3 credentials are specified in the environment variables, restic
|
||||
now tries to load credentials from an IAM instance profile when the s3
|
||||
backend is used.
|
||||
https://github.com/restic/restic/issues/1067
|
||||
https://github.com/restic/restic/pull/1086
|
||||
|
||||
* On Darwin and FreeBSD, restic now prints stats when SIGINFO is received
|
||||
(usually when ctrl+t is pressed).
|
||||
https://github.com/restic/restic/pull/1082
|
||||
* Enhancement #1073: Add `migrate` cmd to migrate from `s3legacy` to `default` layout
|
||||
|
||||
* The dependencies have been updated.
|
||||
https://github.com/restic/restic/pull/1108
|
||||
https://github.com/restic/restic/pull/1124
|
||||
The `migrate` command for chaning the `s3legacy` layout to the `default` layout for s3
|
||||
backends has been improved: It can now be restarted with `restic migrate --force s3_layout`
|
||||
and automatically retries operations on error.
|
||||
|
||||
* A bug was found (and corrected) in the index rebuilding after prune, which
|
||||
led to indexes which include blobs that were not present in the repo any
|
||||
more. There were already checks in place which detected this situation and
|
||||
aborted with an error message. A new run of either `prune` or
|
||||
`rebuild-index` corrected the index files. This is now fixed and a test has
|
||||
been added to detect this.
|
||||
https://github.com/restic/restic/pull/1115
|
||||
https://github.com/restic/restic/issues/1073
|
||||
https://github.com/restic/restic/pull/1075
|
||||
|
||||
* Errors for chmod() on Unix for filesystems which do not support it (e.g. smb
|
||||
mounted via gvfs) are now ignored.
|
||||
https://github.com/restic/restic/pull/1080
|
||||
https://github.com/restic/restic/pull/1112
|
||||
* Enhancement #1081: Clarify semantic for `--tasg` for the `forget` command
|
||||
|
||||
* The semantic for the `--tags` option to `forget` and `snapshots` was
|
||||
clarified:
|
||||
https://github.com/restic/restic/issues/1081
|
||||
https://github.com/restic/restic/pull/1090
|
||||
|
||||
Important Changes in 0.7.0
|
||||
==========================
|
||||
* Enhancement #1080: Ignore chmod() errors on filesystems which do not support it
|
||||
|
||||
* New "swift" backend: A new backend for the OpenStack Swift cloud storage
|
||||
protocol has been added, https://wiki.openstack.org/wiki/Swift
|
||||
https://github.com/restic/restic/pull/975
|
||||
https://github.com/restic/restic/pull/648
|
||||
https://github.com/restic/restic/pull/1080
|
||||
https://github.com/restic/restic/pull/1112
|
||||
|
||||
* New "b2" backend: A new backend for Backblaze B2 cloud storage
|
||||
service has been added, https://www.backblaze.com
|
||||
https://github.com/restic/restic/issues/512
|
||||
https://github.com/restic/restic/pull/978
|
||||
* Enhancement #1082: Print stats on SIGINFO on Darwin and FreeBSD (ctrl+t)
|
||||
|
||||
* Improved performance for the `find` command: Restic recognizes paths it has
|
||||
already checked for the files in question, so the number of backend requests
|
||||
is reduced a lot.
|
||||
https://github.com/restic/restic/issues/989
|
||||
https://github.com/restic/restic/pull/993
|
||||
https://github.com/restic/restic/pull/1082
|
||||
|
||||
* Improved performance for the fuse mount: Listing directories which contain
|
||||
large files now is significantly faster.
|
||||
https://github.com/restic/restic/pull/998
|
||||
|
||||
* The default layout for the s3 backend is now `default` (instead of
|
||||
`s3legacy`). Also, there's a new `migrate` command to convert an existing
|
||||
repo, it can be run like this: `restic migrate s3_layout`
|
||||
https://github.com/restic/restic/issues/965
|
||||
https://github.com/restic/restic/pull/1004
|
||||
Changelog for restic 0.7.0 (2017-07-01)
|
||||
=======================================
|
||||
|
||||
* The fuse mount now has two more directories: `tags` contains a subdir for
|
||||
each tag, which in turn contains only the snapshots that have this tag. The
|
||||
subdir `hosts` contains a subdir for each host that has a snapshot, and the
|
||||
subdir contains the snapshots for that host.
|
||||
https://github.com/restic/restic/issues/636
|
||||
https://github.com/restic/restic/pull/1050
|
||||
The following sections list the changes in restic 0.7.0 relevant to
|
||||
restic users. The changes are ordered by importance.
|
||||
|
||||
Small changes
|
||||
-------------
|
||||
Summary
|
||||
-------
|
||||
|
||||
* Fix #1013: Switch back to using the high-level minio-go API for s3
|
||||
* Fix #965: Switch to `default` repo layout for the s3 backend
|
||||
* Enh #1021: Detect invalid backend name and print error
|
||||
* Enh #1029: Remove invalid pack files when `prune` is run
|
||||
* Enh #512: Add Backblaze B2 backend
|
||||
* Enh #636: Add dirs `tags` and `hosts` to fuse mount
|
||||
* Enh #989: Improve performance of the `find` command
|
||||
* Enh #975: Add new backend for OpenStack Swift
|
||||
* Enh #998: Improve performance of the fuse mount
|
||||
|
||||
Details
|
||||
-------
|
||||
|
||||
* Bugfix #1013: Switch back to using the high-level minio-go API for s3
|
||||
|
||||
For the s3 backend we're back to using the high-level API the s3 client library for uploading
|
||||
data, a few users reported dropped connections (which the library will automatically retry
|
||||
now).
|
||||
|
||||
* For the s3 backend we're back to using the high-level API the s3 client
|
||||
library for uploading data, a few users reported dropped connections (which
|
||||
the library will automatically retry now).
|
||||
https://github.com/restic/restic/issues/1013
|
||||
https://github.com/restic/restic/issues/1023
|
||||
https://github.com/restic/restic/pull/1025
|
||||
|
||||
* The `prune` command has been improved and will now remove invalid pack
|
||||
files, for example files that have not been uploaded completely because a
|
||||
backup was interrupted.
|
||||
https://github.com/restic/restic/issues/1029
|
||||
https://github.com/restic/restic/pull/1036
|
||||
* Bugfix #965: Switch to `default` repo layout for the s3 backend
|
||||
|
||||
The default layout for the s3 backend is now `default` (instead of `s3legacy`). Also, there's a
|
||||
new `migrate` command to convert an existing repo, it can be run like this: `restic migrate
|
||||
s3_layout`
|
||||
|
||||
https://github.com/restic/restic/issues/965
|
||||
https://github.com/restic/restic/pull/1004
|
||||
|
||||
* Enhancement #1021: Detect invalid backend name and print error
|
||||
|
||||
Restic now tries to detect when an invalid/unknown backend is used and returns an error
|
||||
message.
|
||||
|
||||
* restic now tries to detect when an invalid/unknown backend is used and
|
||||
returns an error message.
|
||||
https://github.com/restic/restic/issues/1021
|
||||
https://github.com/restic/restic/pull/1070
|
||||
|
||||
Important Changes in 0.6.1
|
||||
==========================
|
||||
* Enhancement #1029: Remove invalid pack files when `prune` is run
|
||||
|
||||
This is mostly a bugfix release and only contains small changes:
|
||||
The `prune` command has been improved and will now remove invalid pack files, for example files
|
||||
that have not been uploaded completely because a backup was interrupted.
|
||||
|
||||
* We've fixed a bug where `rebuild-index` would corrupt the index when used
|
||||
with the s3 backend together with the `default` layout. This is not the
|
||||
default setting.
|
||||
https://github.com/restic/restic/issues/1029
|
||||
https://github.com/restic/restic/pull/1036
|
||||
|
||||
* Enhancement #512: Add Backblaze B2 backend
|
||||
|
||||
https://github.com/restic/restic/issues/512
|
||||
https://github.com/restic/restic/pull/978
|
||||
|
||||
* Enhancement #636: Add dirs `tags` and `hosts` to fuse mount
|
||||
|
||||
The fuse mount now has two more directories: `tags` contains a subdir for each tag, which in turn
|
||||
contains only the snapshots that have this tag. The subdir `hosts` contains a subdir for each
|
||||
host that has a snapshot, and the subdir contains the snapshots for that host.
|
||||
|
||||
https://github.com/restic/restic/issues/636
|
||||
https://github.com/restic/restic/pull/1050
|
||||
|
||||
* Enhancement #989: Improve performance of the `find` command
|
||||
|
||||
Improved performance for the `find` command: Restic recognizes paths it has already checked
|
||||
for the files in question, so the number of backend requests is reduced a lot.
|
||||
|
||||
https://github.com/restic/restic/issues/989
|
||||
https://github.com/restic/restic/pull/993
|
||||
|
||||
* Enhancement #975: Add new backend for OpenStack Swift
|
||||
|
||||
https://github.com/restic/restic/pull/975
|
||||
https://github.com/restic/restic/pull/648
|
||||
|
||||
* Enhancement #998: Improve performance of the fuse mount
|
||||
|
||||
Listing directories which contain large files now is significantly faster.
|
||||
|
||||
https://github.com/restic/restic/pull/998
|
||||
|
||||
|
||||
Changelog for restic 0.6.1 (2017-06-01)
|
||||
=======================================
|
||||
|
||||
The following sections list the changes in restic 0.6.1 relevant to
|
||||
restic users. The changes are ordered by importance.
|
||||
|
||||
Summary
|
||||
-------
|
||||
|
||||
* Enh #985: Allow multiple parallel idle HTTP connections
|
||||
* Enh #981: Remove temporary path from binary in `build.go`
|
||||
* Enh #974: Remove regular status reports
|
||||
|
||||
Details
|
||||
-------
|
||||
|
||||
* Enhancement #985: Allow multiple parallel idle HTTP connections
|
||||
|
||||
Backends based on HTTP now allow several idle connections in parallel. This is especially
|
||||
important for the REST backend, which (when used with a local server) may create a lot
|
||||
connections and exhaust available ports quickly.
|
||||
|
||||
* Backends based on HTTP now allow several idle connections in parallel. This
|
||||
is especially important for the REST backend, which (when used with a local
|
||||
server) may create a lot connections and exhaust available ports quickly.
|
||||
https://github.com/restic/restic/issues/985
|
||||
https://github.com/restic/restic/pull/986
|
||||
|
||||
* Regular status report: We've removed the status report that was printed
|
||||
every 10 seconds when restic is run non-interactively. You can still force
|
||||
reporting the current status by sending a `USR1` signal to the process.
|
||||
https://github.com/restic/restic/pull/974
|
||||
* Enhancement #981: Remove temporary path from binary in `build.go`
|
||||
|
||||
The `build.go` now strips the temporary directory used for compilation from the binary. This
|
||||
is the first step in enabling reproducible builds.
|
||||
|
||||
* The `build.go` now strips the temporary directory used for compilation from
|
||||
the binary. This is the first step in enabling reproducible builds.
|
||||
https://github.com/restic/restic/pull/981
|
||||
|
||||
Important Changes in 0.6.0
|
||||
==========================
|
||||
* Enhancement #974: Remove regular status reports
|
||||
|
||||
Consistent forget policy
|
||||
------------------------
|
||||
Regular status report: We've removed the status report that was printed every 10 seconds when
|
||||
restic is run non-interactively. You can still force reporting the current status by sending a
|
||||
`USR1` signal to the process.
|
||||
|
||||
The `forget` command was corrected to be more consistent in which snapshots are
|
||||
to be forgotten. It is possible that the new code removes more snapshots than
|
||||
before, so please review what would be deleted by using the `--dry-run` option.
|
||||
https://github.com/restic/restic/pull/974
|
||||
|
||||
https://github.com/restic/restic/pull/957
|
||||
https://github.com/restic/restic/issues/953
|
||||
|
||||
Unified repository layout
|
||||
-------------------------
|
||||
Changelog for restic 0.6.0 (2017-05-29)
|
||||
=======================================
|
||||
|
||||
Up to now the s3 backend used a special repository layout. We've decided to
|
||||
unify the repository layout and implemented the default layout also for the s3
|
||||
backend. For creating a new repository on s3 with the default layout, use
|
||||
`restic -o s3.layout=default init`. For further commands the option is not
|
||||
necessary any more, restic will automatically detect the correct layout to use.
|
||||
A future version will switch to the default layout for new repositories.
|
||||
The following sections list the changes in restic 0.6.0 relevant to
|
||||
restic users. The changes are ordered by importance.
|
||||
|
||||
https://github.com/restic/restic/pull/966
|
||||
https://github.com/restic/restic/issues/965
|
||||
Summary
|
||||
-------
|
||||
|
||||
Memory and time improvements for the s3 backend
|
||||
-----------------------------------------------
|
||||
* Enh #953: Make `forget` consistent
|
||||
* Enh #965: Unify repository layout for all backends
|
||||
* Enh #962: Improve memory and runtime for the s3 backend
|
||||
|
||||
Details
|
||||
-------
|
||||
|
||||
* Enhancement #953: Make `forget` consistent
|
||||
|
||||
The `forget` command was corrected to be more consistent in which snapshots are to be
|
||||
forgotten. It is possible that the new code removes more snapshots than before, so please
|
||||
review what would be deleted by using the `--dry-run` option.
|
||||
|
||||
https://github.com/restic/restic/issues/953
|
||||
https://github.com/restic/restic/pull/957
|
||||
|
||||
* Enhancement #965: Unify repository layout for all backends
|
||||
|
||||
Up to now the s3 backend used a special repository layout. We've decided to unify the repository
|
||||
layout and implemented the default layout also for the s3 backend. For creating a new
|
||||
repository on s3 with the default layout, use `restic -o s3.layout=default init`. For further
|
||||
commands the option is not necessary any more, restic will automatically detect the correct
|
||||
layout to use. A future version will switch to the default layout for new repositories.
|
||||
|
||||
https://github.com/restic/restic/issues/965
|
||||
https://github.com/restic/restic/pull/966
|
||||
|
||||
* Enhancement #962: Improve memory and runtime for the s3 backend
|
||||
|
||||
We've updated the library used for accessing s3, switched to using a lower level API and added
|
||||
caching for some requests. This lead to a decrease in memory usage and a great speedup. In
|
||||
addition, we added benchmark functions for all backends, so we can track improvements over
|
||||
time. The Continuous Integration test service we're using (Travis) now runs the s3 backend
|
||||
tests not only against a Minio server, but also against the Amazon s3 live service, so we should
|
||||
be notified of any regressions much sooner.
|
||||
|
||||
https://github.com/restic/restic/pull/962
|
||||
https://github.com/restic/restic/pull/960
|
||||
https://github.com/restic/restic/pull/946
|
||||
https://github.com/restic/restic/pull/938
|
||||
https://github.com/restic/restic/pull/883
|
||||
|
||||
We've updated the library used for accessing s3, switched to using a lower
|
||||
level API and added caching for some requests. This lead to a decrease in
|
||||
memory usage and a great speedup. In addition, we added benchmark functions for
|
||||
all backends, so we can track improvements over time. The Continuous
|
||||
Integration test service we're using (Travis) now runs the s3 backend tests not
|
||||
only against a Minio server, but also against the Amazon s3 live service, so we
|
||||
should be notified of any regressions much sooner.
|
||||
|
||||
https://github.com/restic/restic/pull/962
|
||||
https://github.com/restic/restic/pull/960
|
||||
https://github.com/restic/restic/pull/946
|
||||
https://github.com/restic/restic/pull/938
|
||||
https://github.com/restic/restic/pull/883
|
||||
|
||||
@@ -129,13 +129,7 @@ down to the following steps:
|
||||
next stable release. While writing, ask yourself: If I were the user, what
|
||||
would I need to be aware of with this change.
|
||||
|
||||
8. When your contribution adds and/or changes command-line parameters or help
|
||||
texts, the manual pages need to be regenerated and commited to the
|
||||
repository. In order to do this, compile restic and save the generated
|
||||
updated man pages in the subdir `doc/man` with the following command:
|
||||
`./restic manpage --output-dir doc/man`
|
||||
|
||||
9. Once your code looks good and passes all the tests, we'll merge it. Thanks
|
||||
8. Once your code looks good and passes all the tests, we'll merge it. Thanks
|
||||
a lot for your contribution!
|
||||
|
||||
Please provide the patches for each bug or feature in a separate branch and
|
||||
|
||||
68
Gopkg.lock
generated
68
Gopkg.lock
generated
@@ -10,20 +10,26 @@
|
||||
[[projects]]
|
||||
name = "cloud.google.com/go"
|
||||
packages = ["compute/metadata"]
|
||||
revision = "5a9e19d4e1e41a734154e44a2132b358afb49a03"
|
||||
version = "v0.13.0"
|
||||
revision = "2d3a6656c17a60b0815b7e06ab0be04eacb6e613"
|
||||
version = "v0.16.0"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/Azure/azure-sdk-for-go"
|
||||
packages = ["storage"]
|
||||
revision = "df4dd90d076ebbf6e87d08d3f00bfac8ff4bde1a"
|
||||
version = "v10.3.1-beta"
|
||||
revision = "7692b0cef22674113fcf71cc17ac3ccc1a7fef48"
|
||||
version = "v11.2.2-beta"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/Azure/go-autorest"
|
||||
packages = ["autorest","autorest/adal","autorest/azure","autorest/date"]
|
||||
revision = "5432abe734f8d95c78340cd56712f912906e6514"
|
||||
version = "v8.3.1"
|
||||
revision = "c67b24a8e30d876542a85022ebbdecf0e5a935e8"
|
||||
version = "v9.4.1"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/cenkalti/backoff"
|
||||
packages = ["."]
|
||||
revision = "61153c768f31ee5f130071d08fc82b85208528de"
|
||||
version = "v1.1.0"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/cpuguy83/go-md2man"
|
||||
@@ -34,14 +40,14 @@
|
||||
[[projects]]
|
||||
name = "github.com/dgrijalva/jwt-go"
|
||||
packages = ["."]
|
||||
revision = "d2709f9f1f31ebcda9651b03077758c1f3a0018c"
|
||||
version = "v3.0.0"
|
||||
revision = "dbeaa9332f19a944acb5736b4456cfcc02140e29"
|
||||
version = "v3.1.0"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/dustin/go-humanize"
|
||||
packages = ["."]
|
||||
revision = "79e699ccd02f240a1f1fbbdcee7e64c1c12e41aa"
|
||||
revision = "bb3d318650d48840a39aa21a027c6630e198e626"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/elithrar/simple-scrypt"
|
||||
@@ -52,14 +58,14 @@
|
||||
[[projects]]
|
||||
name = "github.com/go-ini/ini"
|
||||
packages = ["."]
|
||||
revision = "20b96f641a5ea98f2f8619ff4f3e061cff4833bd"
|
||||
version = "v1.28.2"
|
||||
revision = "32e4c1e6bc4e7d0d8451aa6b75200d19e37a536a"
|
||||
version = "v1.32.0"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/golang/protobuf"
|
||||
packages = ["proto"]
|
||||
revision = "17ce1425424ab154092bbb43af630bd647f3bb0d"
|
||||
revision = "1e59b77b52bf8e4b449a57e6f79f21226d571845"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/inconshreveable/mousetrap"
|
||||
@@ -67,6 +73,12 @@
|
||||
revision = "76626ae9c91c4f2a10f34cad8ce83ea42c93bb75"
|
||||
version = "v1.0"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/juju/ratelimit"
|
||||
packages = ["."]
|
||||
revision = "59fac5042749a5afb9af70e813da1dd5474f0167"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/kr/fs"
|
||||
@@ -76,8 +88,8 @@
|
||||
[[projects]]
|
||||
name = "github.com/kurin/blazer"
|
||||
packages = ["b2","base","internal/b2types","internal/blog"]
|
||||
revision = "1a870c3ee8b83e17d762307c6eae8f390ac3f4a0"
|
||||
version = "v0.1.1"
|
||||
revision = "e269a1a17bb6aec278c06a57cb7e8f8d0d333e04"
|
||||
version = "v0.2.1"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
@@ -88,14 +100,14 @@
|
||||
[[projects]]
|
||||
name = "github.com/minio/minio-go"
|
||||
packages = [".","pkg/credentials","pkg/encrypt","pkg/policy","pkg/s3signer","pkg/s3utils","pkg/set"]
|
||||
revision = "4e0f567303d4cc90ceb055a451959fb9fc391fb9"
|
||||
version = "3.0.3"
|
||||
revision = "57a8ae886b49af6eb0d2c27c2d007ed2f71e1da5"
|
||||
version = "4.0.3"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/ncw/swift"
|
||||
packages = ["."]
|
||||
revision = "9d3f812e23d270d1c66a9a01e20af1005061cdc4"
|
||||
revision = "c95c6e5c2d1a3d37fc44c8c6dc9e231c7500667d"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/pkg/errors"
|
||||
@@ -124,8 +136,8 @@
|
||||
[[projects]]
|
||||
name = "github.com/restic/chunker"
|
||||
packages = ["."]
|
||||
revision = "bb2ecf9a98e35a0b336ffc23fc515fb6e7961577"
|
||||
version = "v0.1.0"
|
||||
revision = "db83917be3b88cc307464b7d8a221c173e34a0db"
|
||||
version = "v0.2.0"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/russross/blackfriday"
|
||||
@@ -146,10 +158,10 @@
|
||||
version = "v1.0.3"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/spf13/cobra"
|
||||
packages = [".","doc"]
|
||||
revision = "b78744579491c1ceeaaa3b40205e56b0591b93a3"
|
||||
revision = "7b2c5ac9fc04fc5efafb60700713d4fa609b777b"
|
||||
version = "v0.0.1"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/spf13/pflag"
|
||||
@@ -161,31 +173,31 @@
|
||||
branch = "master"
|
||||
name = "golang.org/x/crypto"
|
||||
packages = ["curve25519","ed25519","ed25519/internal/edwards25519","pbkdf2","poly1305","scrypt","ssh","ssh/terminal"]
|
||||
revision = "faadfbdc035307d901e69eea569f5dda451a3ee3"
|
||||
revision = "94eea52f7b742c7cbe0b03b22f0c4c8631ece122"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "golang.org/x/net"
|
||||
packages = ["context","context/ctxhttp"]
|
||||
revision = "b129b8e0fbeb39c8358e51a07ab6c50ad415e72e"
|
||||
revision = "a8b9294777976932365dabb6640cf1468d95c70f"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "golang.org/x/oauth2"
|
||||
packages = [".","google","internal","jws","jwt"]
|
||||
revision = "13449ad91cb26cb47661c1b080790392170385fd"
|
||||
revision = "f95fa95eaa936d9d87489b15d1d18b97c1ba9c28"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "golang.org/x/sys"
|
||||
packages = ["unix","windows"]
|
||||
revision = "062cd7e4e68206d8bab9b18396626e855c992658"
|
||||
revision = "8b4580aae2a0dd0c231a45d3ccb8434ff533b840"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "google.golang.org/api"
|
||||
packages = ["gensupport","googleapi","googleapi/internal/uritemplates","storage/v1"]
|
||||
revision = "2fe03ca2dc379c00d654a4459d1a50812cac2848"
|
||||
revision = "3a1d936b7575b82197a1fea0632218dd07b1e65c"
|
||||
|
||||
[[projects]]
|
||||
name = "google.golang.org/appengine"
|
||||
@@ -197,11 +209,11 @@
|
||||
branch = "v2"
|
||||
name = "gopkg.in/yaml.v2"
|
||||
packages = ["."]
|
||||
revision = "eb3733d160e74a9c7e442f435eb3bea458e1d19f"
|
||||
revision = "287cf08546ab5e7e37d55a84f7ed3fd1db036de5"
|
||||
|
||||
[solve-meta]
|
||||
analyzer-name = "dep"
|
||||
analyzer-version = 1
|
||||
inputs-digest = "53e4779dc4c7de2cd8b195f13c215c24da5efc5e33acf584615b5c43bfefd2db"
|
||||
inputs-digest = "f0a207197cb502238ac87ca8e07b2640c02ec380a50b036e09ef87e40e31ca2d"
|
||||
solver-name = "gps-cdcl"
|
||||
solver-version = 1
|
||||
|
||||
57
Gopkg.toml
57
Gopkg.toml
@@ -19,60 +19,3 @@
|
||||
# [[override]]
|
||||
# name = "github.com/x/y"
|
||||
# version = "2.4.0"
|
||||
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "bazil.org/fuse"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/elithrar/simple-scrypt"
|
||||
branch = "master"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/kurin/blazer"
|
||||
version = "0.1.0"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/minio/minio-go"
|
||||
version = "3.0.0"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "github.com/ncw/swift"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/pkg/errors"
|
||||
version = "0.8.0"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/pkg/profile"
|
||||
version = "1.2.1"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "github.com/pkg/sftp"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/pkg/xattr"
|
||||
version = "0.2.1"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/restic/chunker"
|
||||
version = "0.1.0"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "github.com/spf13/cobra"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "golang.org/x/crypto"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "golang.org/x/net"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "golang.org/x/sys"
|
||||
|
||||
18
LICENSE
18
LICENSE
@@ -1,19 +1,21 @@
|
||||
BSD 2-Clause License
|
||||
|
||||
Copyright (c) 2014, Alexander Neumann <alexander@bumpern.de>
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
|
||||
26
README.rst
26
README.rst
@@ -1,4 +1,4 @@
|
||||
|Documentation| |Build Status| |Build status| |Report Card| |Say Thanks|
|
||||
|Documentation| |Build Status| |Build status| |Report Card| |Say Thanks| |TestCoverage|
|
||||
|
||||
Introduction
|
||||
------------
|
||||
@@ -13,7 +13,7 @@ Quick start
|
||||
-----------
|
||||
|
||||
Once you've `installed
|
||||
<https://restic.readthedocs.io/en/latest/installation.html>`__ restic, start
|
||||
<https://restic.readthedocs.io/en/latest/020_installation.html>`__ restic, start
|
||||
off with creating a repository for your backups:
|
||||
|
||||
.. code-block:: console
|
||||
@@ -41,7 +41,7 @@ Next you can either use ``restic restore`` to restore files or use ``restic
|
||||
mount`` to mount the repository via fuse and browse the files from previous
|
||||
snapshots.
|
||||
|
||||
For more options check out the `manual guide <https://restic.readthedocs.io/en/latest/manual.html>`__.
|
||||
For more options check out the `online documentation <https://restic.readthedocs.io/en/latest/>`__.
|
||||
|
||||
Backends
|
||||
--------
|
||||
@@ -49,14 +49,14 @@ Backends
|
||||
Saving a backup on the same machine is nice but not a real backup strategy.
|
||||
Therefore, restic supports the following backends for storing backups natively:
|
||||
|
||||
- `Local directory <https://restic.readthedocs.io/en/latest/manual.html#local>`__
|
||||
- `sftp server (via SSH) <https://restic.readthedocs.io/en/latest/manual.html#sftp>`__
|
||||
- `HTTP REST server <https://restic.readthedocs.io/en/latest/manual.html#rest-server>`__ (`protocol <doc/rest_backend.rst>`__ `rest-server <https://github.com/restic/rest-server>`__)
|
||||
- `AWS S3 <https://restic.readthedocs.io/en/latest/manual.html#amazon-s3>`__ (either from Amazon or using the `Minio <https://minio.io>`__ server)
|
||||
- `OpenStack Swift <https://restic.readthedocs.io/en/latest/manual.html#openstack-swift>`__
|
||||
- `BackBlaze B2 <https://restic.readthedocs.io/en/latest/manual.html#backblaze-b2>`__
|
||||
- `Microsoft Azure Blob Storage <https://restic.readthedocs.io/en/latest/manual.html#microsoft-azure-blob-storage>`__
|
||||
- `Google Cloud Storage <https://restic.readthedocs.io/en/latest/manual.html#google-cloud-storage>`__
|
||||
- `Local directory <https://restic.readthedocs.io/en/latest/030_preparing_a_new_repo.html#local>`__
|
||||
- `sftp server (via SSH) <https://restic.readthedocs.io/en/latest/030_preparing_a_new_repo.html#sftp>`__
|
||||
- `HTTP REST server <https://restic.readthedocs.io/en/latest/030_preparing_a_new_repo.html#rest-server>`__ (`protocol <doc/100_references.rst#rest-backend>`__ `rest-server <https://github.com/restic/rest-server>`__)
|
||||
- `AWS S3 <https://restic.readthedocs.io/en/latest/030_preparing_a_new_repo.html#amazon-s3>`__ (either from Amazon or using the `Minio <https://minio.io>`__ server)
|
||||
- `OpenStack Swift <https://restic.readthedocs.io/en/latest/030_preparing_a_new_repo.html#openstack-swift>`__
|
||||
- `BackBlaze B2 <https://restic.readthedocs.io/en/latest/030_preparing_a_new_repo.html#backblaze-b2>`__
|
||||
- `Microsoft Azure Blob Storage <https://restic.readthedocs.io/en/latest/030_preparing_a_new_repo.html#microsoft-azure-blob-storage>`__
|
||||
- `Google Cloud Storage <https://restic.readthedocs.io/en/latest/030_preparing_a_new_repo.html#google-cloud-storage>`__
|
||||
|
||||
Design Principles
|
||||
-----------------
|
||||
@@ -107,7 +107,7 @@ the `development blog <https://restic.github.io/blog/>`__.
|
||||
License
|
||||
-------
|
||||
|
||||
Restic is licensed under "BSD 2-Clause License". You can find the
|
||||
Restic is licensed under `BSD 2-Clause License <https://opensource.org/licenses/BSD-2-Clause>`__. You can find the
|
||||
complete text in ``LICENSE``.
|
||||
|
||||
.. |Documentation| image:: https://readthedocs.org/projects/restic/badge/?version=latest
|
||||
@@ -120,3 +120,5 @@ complete text in ``LICENSE``.
|
||||
:target: https://goreportcard.com/report/github.com/restic/restic
|
||||
.. |Say Thanks| image:: https://img.shields.io/badge/Say%20Thanks-!-1EAEDB.svg
|
||||
:target: https://saythanks.io/to/restic
|
||||
.. |TestCoverage| image:: https://codecov.io/gh/restic/restic/branch/master/graph/badge.svg
|
||||
:target: https://codecov.io/gh/restic/restic
|
||||
|
||||
8
changelog/0.6.0/issue-953
Normal file
8
changelog/0.6.0/issue-953
Normal file
@@ -0,0 +1,8 @@
|
||||
Enhancement: Make `forget` consistent
|
||||
|
||||
The `forget` command was corrected to be more consistent in which snapshots are
|
||||
to be forgotten. It is possible that the new code removes more snapshots than
|
||||
before, so please review what would be deleted by using the `--dry-run` option.
|
||||
|
||||
https://github.com/restic/restic/pull/957
|
||||
https://github.com/restic/restic/issues/953
|
||||
11
changelog/0.6.0/issue-965
Normal file
11
changelog/0.6.0/issue-965
Normal file
@@ -0,0 +1,11 @@
|
||||
Enhancement: Unify repository layout for all backends
|
||||
|
||||
Up to now the s3 backend used a special repository layout. We've decided to
|
||||
unify the repository layout and implemented the default layout also for the s3
|
||||
backend. For creating a new repository on s3 with the default layout, use
|
||||
`restic -o s3.layout=default init`. For further commands the option is not
|
||||
necessary any more, restic will automatically detect the correct layout to use.
|
||||
A future version will switch to the default layout for new repositories.
|
||||
|
||||
https://github.com/restic/restic/pull/966
|
||||
https://github.com/restic/restic/issues/965
|
||||
15
changelog/0.6.0/pull-962
Normal file
15
changelog/0.6.0/pull-962
Normal file
@@ -0,0 +1,15 @@
|
||||
Enhancement: Improve memory and runtime for the s3 backend
|
||||
|
||||
We've updated the library used for accessing s3, switched to using a lower
|
||||
level API and added caching for some requests. This lead to a decrease in
|
||||
memory usage and a great speedup. In addition, we added benchmark functions for
|
||||
all backends, so we can track improvements over time. The Continuous
|
||||
Integration test service we're using (Travis) now runs the s3 backend tests not
|
||||
only against a Minio server, but also against the Amazon s3 live service, so we
|
||||
should be notified of any regressions much sooner.
|
||||
|
||||
https://github.com/restic/restic/pull/962
|
||||
https://github.com/restic/restic/pull/960
|
||||
https://github.com/restic/restic/pull/946
|
||||
https://github.com/restic/restic/pull/938
|
||||
https://github.com/restic/restic/pull/883
|
||||
8
changelog/0.6.1/issue-985
Normal file
8
changelog/0.6.1/issue-985
Normal file
@@ -0,0 +1,8 @@
|
||||
Enhancement: Allow multiple parallel idle HTTP connections
|
||||
|
||||
Backends based on HTTP now allow several idle connections in parallel. This
|
||||
is especially important for the REST backend, which (when used with a local
|
||||
server) may create a lot connections and exhaust available ports quickly.
|
||||
|
||||
https://github.com/restic/restic/issues/985
|
||||
https://github.com/restic/restic/pull/986
|
||||
6
changelog/0.6.1/pull-891
Normal file
6
changelog/0.6.1/pull-891
Normal file
@@ -0,0 +1,6 @@
|
||||
Enhancement: Remove temporary path from binary in `build.go`
|
||||
|
||||
The `build.go` now strips the temporary directory used for compilation from
|
||||
the binary. This is the first step in enabling reproducible builds.
|
||||
|
||||
https://github.com/restic/restic/pull/981
|
||||
7
changelog/0.6.1/pull-974
Normal file
7
changelog/0.6.1/pull-974
Normal file
@@ -0,0 +1,7 @@
|
||||
Enhancement: Remove regular status reports
|
||||
|
||||
Regular status report: We've removed the status report that was printed
|
||||
every 10 seconds when restic is run non-interactively. You can still force
|
||||
reporting the current status by sending a `USR1` signal to the process.
|
||||
|
||||
https://github.com/restic/restic/pull/974
|
||||
9
changelog/0.7.0/issue-1013
Normal file
9
changelog/0.7.0/issue-1013
Normal file
@@ -0,0 +1,9 @@
|
||||
Bugfix: Switch back to using the high-level minio-go API for s3
|
||||
|
||||
For the s3 backend we're back to using the high-level API the s3 client library
|
||||
for uploading data, a few users reported dropped connections (which the library
|
||||
will automatically retry now).
|
||||
|
||||
https://github.com/restic/restic/issues/1013
|
||||
https://github.com/restic/restic/issues/1023
|
||||
https://github.com/restic/restic/pull/1025
|
||||
7
changelog/0.7.0/issue-1021
Normal file
7
changelog/0.7.0/issue-1021
Normal file
@@ -0,0 +1,7 @@
|
||||
Enhancement: Detect invalid backend name and print error
|
||||
|
||||
restic now tries to detect when an invalid/unknown backend is used and
|
||||
returns an error message.
|
||||
|
||||
https://github.com/restic/restic/issues/1021
|
||||
https://github.com/restic/restic/pull/1070
|
||||
8
changelog/0.7.0/issue-1029
Normal file
8
changelog/0.7.0/issue-1029
Normal file
@@ -0,0 +1,8 @@
|
||||
Enhancement: Remove invalid pack files when `prune` is run
|
||||
|
||||
The `prune` command has been improved and will now remove invalid pack files,
|
||||
for example files that have not been uploaded completely because a backup was
|
||||
interrupted.
|
||||
|
||||
https://github.com/restic/restic/issues/1029
|
||||
https://github.com/restic/restic/pull/1036
|
||||
4
changelog/0.7.0/issue-512
Normal file
4
changelog/0.7.0/issue-512
Normal file
@@ -0,0 +1,4 @@
|
||||
Enhancement: Add Backblaze B2 backend
|
||||
|
||||
https://github.com/restic/restic/issues/512
|
||||
https://github.com/restic/restic/pull/978
|
||||
9
changelog/0.7.0/issue-636
Normal file
9
changelog/0.7.0/issue-636
Normal file
@@ -0,0 +1,9 @@
|
||||
Enhancement: Add dirs `tags` and `hosts` to fuse mount
|
||||
|
||||
The fuse mount now has two more directories: `tags` contains a subdir for
|
||||
each tag, which in turn contains only the snapshots that have this tag. The
|
||||
subdir `hosts` contains a subdir for each host that has a snapshot, and the
|
||||
subdir contains the snapshots for that host.
|
||||
|
||||
https://github.com/restic/restic/issues/636
|
||||
https://github.com/restic/restic/pull/1050
|
||||
8
changelog/0.7.0/issue-965
Normal file
8
changelog/0.7.0/issue-965
Normal file
@@ -0,0 +1,8 @@
|
||||
Bugfix: Switch to `default` repo layout for the s3 backend
|
||||
|
||||
The default layout for the s3 backend is now `default` (instead of `s3legacy`).
|
||||
Also, there's a new `migrate` command to convert an existing repo, it can be
|
||||
run like this: `restic migrate s3_layout`
|
||||
|
||||
https://github.com/restic/restic/issues/965
|
||||
https://github.com/restic/restic/pull/1004
|
||||
8
changelog/0.7.0/issue-989
Normal file
8
changelog/0.7.0/issue-989
Normal file
@@ -0,0 +1,8 @@
|
||||
Enhancement: Improve performance of the `find` command
|
||||
|
||||
Improved performance for the `find` command: Restic recognizes paths it has
|
||||
already checked for the files in question, so the number of backend requests
|
||||
is reduced a lot.
|
||||
|
||||
https://github.com/restic/restic/issues/989
|
||||
https://github.com/restic/restic/pull/993
|
||||
4
changelog/0.7.0/pull-975
Normal file
4
changelog/0.7.0/pull-975
Normal file
@@ -0,0 +1,4 @@
|
||||
Enhancement: Add new backend for OpenStack Swift
|
||||
|
||||
https://github.com/restic/restic/pull/975
|
||||
https://github.com/restic/restic/pull/648
|
||||
5
changelog/0.7.0/pull-998
Normal file
5
changelog/0.7.0/pull-998
Normal file
@@ -0,0 +1,5 @@
|
||||
Enhancement: Improve performance of the fuse mount
|
||||
|
||||
Listing directories which contain large files now is significantly faster.
|
||||
|
||||
https://github.com/restic/restic/pull/998
|
||||
10
changelog/0.7.1/issue-1055
Normal file
10
changelog/0.7.1/issue-1055
Normal file
@@ -0,0 +1,10 @@
|
||||
Enhancement: Create subdirs below `data/` for local/sftp backends
|
||||
|
||||
The local and sftp backends now create the subdirs below `data/` on
|
||||
open/init. This way, restic makes sure that they always exist. This is
|
||||
connected to an issue for the sftp server:
|
||||
|
||||
https://github.com/restic/rest-server/pull/11#issuecomment-309879710
|
||||
https://github.com/restic/restic/issues/1055
|
||||
https://github.com/restic/restic/pull/1077
|
||||
https://github.com/restic/restic/pull/1105
|
||||
8
changelog/0.7.1/issue-1067
Normal file
8
changelog/0.7.1/issue-1067
Normal file
@@ -0,0 +1,8 @@
|
||||
Enhancement: Allow loading credentials for s3 from IAM
|
||||
|
||||
When no S3 credentials are specified in the environment variables, restic
|
||||
now tries to load credentials from an IAM instance profile when the s3
|
||||
backend is used.
|
||||
|
||||
https://github.com/restic/restic/issues/1067
|
||||
https://github.com/restic/restic/pull/1086
|
||||
8
changelog/0.7.1/issue-1073
Normal file
8
changelog/0.7.1/issue-1073
Normal file
@@ -0,0 +1,8 @@
|
||||
Enhancement: Add `migrate` cmd to migrate from `s3legacy` to `default` layout
|
||||
|
||||
The `migrate` command for chaning the `s3legacy` layout to the `default` layout
|
||||
for s3 backends has been improved: It can now be restarted with `restic migrate
|
||||
--force s3_layout` and automatically retries operations on error.
|
||||
|
||||
https://github.com/restic/restic/issues/1073
|
||||
https://github.com/restic/restic/pull/1075
|
||||
4
changelog/0.7.1/issue-1081
Normal file
4
changelog/0.7.1/issue-1081
Normal file
@@ -0,0 +1,4 @@
|
||||
Enhancement: Clarify semantic for `--tasg` for the `forget` command
|
||||
|
||||
https://github.com/restic/restic/issues/1081
|
||||
https://github.com/restic/restic/pull/1090
|
||||
5
changelog/0.7.1/plul-1080
Normal file
5
changelog/0.7.1/plul-1080
Normal file
@@ -0,0 +1,5 @@
|
||||
Enhancement: Ignore chmod() errors on filesystems which do not support it
|
||||
|
||||
https://github.com/restic/restic/pull/1080
|
||||
https://github.com/restic/restic/pull/1112
|
||||
|
||||
3
changelog/0.7.1/pull-1082
Normal file
3
changelog/0.7.1/pull-1082
Normal file
@@ -0,0 +1,3 @@
|
||||
Enhancement: Print stats on SIGINFO on Darwin and FreeBSD (ctrl+t)
|
||||
|
||||
https://github.com/restic/restic/pull/1082
|
||||
9
changelog/0.7.1/pull-1115
Normal file
9
changelog/0.7.1/pull-1115
Normal file
@@ -0,0 +1,9 @@
|
||||
Bugfix: Fix `prune`, only include existing files in indexes
|
||||
|
||||
A bug was found (and corrected) in the index rebuilding after prune, which led
|
||||
to indexes which include blobs that were not present in the repo any more.
|
||||
There were already checks in place which detected this situation and aborted
|
||||
with an error message. A new run of either `prune` or `rebuild-index` corrected
|
||||
the index files. This is now fixed and a test has been added to detect this.
|
||||
|
||||
https://github.com/restic/restic/pull/1115
|
||||
8
changelog/0.7.2/issue-1132
Normal file
8
changelog/0.7.2/issue-1132
Normal file
@@ -0,0 +1,8 @@
|
||||
Enhancement: Make `key` command always prompt for a password
|
||||
|
||||
The `key` command now prompts for a password even if the original password
|
||||
to access a repo has been specified via the `RESTIC_PASSWORD` environment
|
||||
variable or a password file.
|
||||
|
||||
https://github.com/restic/restic/issues/1132
|
||||
https://github.com/restic/restic/pull/1133
|
||||
8
changelog/0.7.2/issue-1167
Normal file
8
changelog/0.7.2/issue-1167
Normal file
@@ -0,0 +1,8 @@
|
||||
Bugfix: Do not create a local repo unless `init` is used
|
||||
|
||||
When a restic command other than `init` is used with a local repository and the
|
||||
repository directory does not exist, restic creates the directory structure.
|
||||
That's an error, only the `init` command should create the dir.
|
||||
|
||||
https://github.com/restic/restic/issues/1167
|
||||
https://github.com/restic/restic/pull/1182
|
||||
4
changelog/0.7.2/issue-1179
Normal file
4
changelog/0.7.2/issue-1179
Normal file
@@ -0,0 +1,4 @@
|
||||
Enhancement: Resolve name conflicts, append a counter
|
||||
|
||||
https://github.com/restic/restic/issues/1179
|
||||
https://github.com/restic/restic/pull/1209
|
||||
7
changelog/0.7.2/issue-1208
Normal file
7
changelog/0.7.2/issue-1208
Normal file
@@ -0,0 +1,7 @@
|
||||
Enhancement: Add `--compact` to `snapshots` command
|
||||
|
||||
The option `--compact` was added to the `snapshots` command to get a better
|
||||
overview of the snapshots in a repo. It limits each snapshot to a single line.
|
||||
|
||||
https://github.com/restic/restic/issues/1218
|
||||
https://github.com/restic/restic/pull/1223
|
||||
10
changelog/0.7.2/issue-317
Normal file
10
changelog/0.7.2/issue-317
Normal file
@@ -0,0 +1,10 @@
|
||||
Enhancement: Add `--exclude-caches` and `--exclude-if-present`
|
||||
|
||||
A new option `--exclude-caches` was added that allows excluding cache
|
||||
directories (that are tagged as such). This is a special case of a more generic
|
||||
option `--exclude-if-present` which excludes a directory if a file with a
|
||||
specific name (and contents) is present.
|
||||
|
||||
https://github.com/restic/restic/issues/317
|
||||
https://github.com/restic/restic/pull/1170
|
||||
https://github.com/restic/restic/pull/1224
|
||||
4
changelog/0.7.2/issues-697
Normal file
4
changelog/0.7.2/issues-697
Normal file
@@ -0,0 +1,4 @@
|
||||
Enhancement: Automatically generate man pages for all restic commands
|
||||
|
||||
https://github.com/restic/restic/issues/697
|
||||
https://github.com/restic/restic/pull/1147
|
||||
3
changelog/0.7.2/pull-1044
Normal file
3
changelog/0.7.2/pull-1044
Normal file
@@ -0,0 +1,3 @@
|
||||
Enhancement: Improve `restore`, do not traverse/load excluded directories
|
||||
|
||||
https://github.com/restic/restic/pull/1044
|
||||
3
changelog/0.7.2/pull-1061
Normal file
3
changelog/0.7.2/pull-1061
Normal file
@@ -0,0 +1,3 @@
|
||||
Enhancement: Add Dockerfile and official Docker image
|
||||
|
||||
https://github.com/restic/restic/pull/1061
|
||||
7
changelog/0.7.2/pull-1126
Normal file
7
changelog/0.7.2/pull-1126
Normal file
@@ -0,0 +1,7 @@
|
||||
Enhancement: Use the standard Go git repository layout, use `dep` for vendoring
|
||||
|
||||
The git repository layout was changed to resemble the layout typically used in
|
||||
Go projects, we're not using `gb` for building restic any more and vendoring
|
||||
the dependencies is now taken care of by `dep`.
|
||||
|
||||
https://github.com/restic/restic/pull/1126
|
||||
5
changelog/0.7.2/pull-1134
Normal file
5
changelog/0.7.2/pull-1134
Normal file
@@ -0,0 +1,5 @@
|
||||
Enhancement: Add support for storing backups on Google Cloud Storage
|
||||
|
||||
https://github.com/restic/restic/pull/1134
|
||||
https://github.com/restic/restic/pull/1052
|
||||
https://github.com/restic/restic/issues/211
|
||||
3
changelog/0.7.2/pull-1144
Normal file
3
changelog/0.7.2/pull-1144
Normal file
@@ -0,0 +1,3 @@
|
||||
Enhancement: Properly report errors when reading files with exclude patterns.
|
||||
|
||||
https://github.com/restic/restic/pull/1144
|
||||
8
changelog/0.7.2/pull-1149
Normal file
8
changelog/0.7.2/pull-1149
Normal file
@@ -0,0 +1,8 @@
|
||||
Enhancement: Add support for storing backups on Microsoft Azure Blob Storage
|
||||
|
||||
The library we're using to access the service requires Go 1.8, so restic now
|
||||
needs at least Go 1.8.
|
||||
|
||||
https://github.com/restic/restic/pull/1149
|
||||
https://github.com/restic/restic/pull/1059
|
||||
https://github.com/restic/restic/issues/609
|
||||
3
changelog/0.7.2/pull-1164
Normal file
3
changelog/0.7.2/pull-1164
Normal file
@@ -0,0 +1,3 @@
|
||||
Bugfix: Make the `key remove` command behave as documented
|
||||
|
||||
https://github.com/restic/restic/pull/1164
|
||||
8
changelog/0.7.2/pull-1191
Normal file
8
changelog/0.7.2/pull-1191
Normal file
@@ -0,0 +1,8 @@
|
||||
Bugfix: Make sure to write profiling files on interrupt
|
||||
|
||||
Since a few releases restic had the ability to write profiling files for memory
|
||||
and CPU usage when `debug` is enabled. It was discovered that when restic is
|
||||
interrupted (ctrl+c is pressed), the proper shutdown hook is not run. This is
|
||||
now corrected.
|
||||
|
||||
https://github.com/restic/restic/pull/1191
|
||||
3
changelog/0.7.2/pull-1196
Normal file
3
changelog/0.7.2/pull-1196
Normal file
@@ -0,0 +1,3 @@
|
||||
Enhancement: Add `--group-by` to `forget` command for flexible grouping
|
||||
|
||||
https://github.com/restic/restic/pull/1196
|
||||
5
changelog/0.7.2/pull-1203
Normal file
5
changelog/0.7.2/pull-1203
Normal file
@@ -0,0 +1,5 @@
|
||||
Enhancement: Print stats on all BSD systems when SIGINFO (ctrl+t) is received
|
||||
|
||||
https://github.com/restic/restic/pull/1203
|
||||
https://github.com/restic/restic/pull/1082#issuecomment-326279920
|
||||
|
||||
3
changelog/0.7.2/pull-1205
Normal file
3
changelog/0.7.2/pull-1205
Normal file
@@ -0,0 +1,3 @@
|
||||
Enhancement: Allow specifying time/date for a backup with `--time`
|
||||
|
||||
https://github.com/restic/restic/pull/1205
|
||||
9
changelog/0.7.3/issue-1246
Normal file
9
changelog/0.7.3/issue-1246
Normal file
@@ -0,0 +1,9 @@
|
||||
Bugfix: List all files stored in Google Cloud Storage
|
||||
|
||||
For large backups stored in Google Cloud Storage, the `prune` command fails
|
||||
because listing only returns the first 1000 files. This has been corrected, no
|
||||
data is lost in the process. In addition, a plausibility check was added to
|
||||
`prune`.
|
||||
|
||||
https://github.com/restic/restic/issues/1246
|
||||
https://github.com/restic/restic/pull/1247
|
||||
9
changelog/0.8.0/issue-1102
Normal file
9
changelog/0.8.0/issue-1102
Normal file
@@ -0,0 +1,9 @@
|
||||
Enhancement: Add subdirectory `ids` to fuse mount
|
||||
|
||||
The fuse mount now has an `ids` subdirectory which contains the snapshots below
|
||||
their (short) IDs.
|
||||
|
||||
https://github.com/restic/restic/issues/1102
|
||||
https://github.com/restic/restic/pull/1299
|
||||
https://github.com/restic/restic/pull/1320
|
||||
|
||||
10
changelog/0.8.0/issue-1114
Normal file
10
changelog/0.8.0/issue-1114
Normal file
@@ -0,0 +1,10 @@
|
||||
Enhancement: Add `--cacert` to specify TLS certificates to check against
|
||||
|
||||
We've added the `--cacert` option which can be used to pass one (or more) CA
|
||||
certificates to restic. These are used in addition to the system CA
|
||||
certificates to verify HTTPS certificates (e.g. for the REST backend).
|
||||
|
||||
https://github.com/restic/restic/issues/1114
|
||||
https://github.com/restic/restic/pull/1276
|
||||
|
||||
|
||||
9
changelog/0.8.0/issue-1216
Normal file
9
changelog/0.8.0/issue-1216
Normal file
@@ -0,0 +1,9 @@
|
||||
Enhancement: Add upload/download limiting
|
||||
|
||||
We've added support for rate limiting through `--limit-upload` and
|
||||
`--limit-download` flags.
|
||||
|
||||
https://github.com/restic/restic/issues/1216
|
||||
https://github.com/restic/restic/pull/1336
|
||||
https://github.com/restic/restic/pull/1358
|
||||
|
||||
7
changelog/0.8.0/issue-1256
Normal file
7
changelog/0.8.0/issue-1256
Normal file
@@ -0,0 +1,7 @@
|
||||
Bugfix: Re-enable workaround for S3 backend
|
||||
|
||||
We've re-enabled a workaround for `minio-go` (the library we're using to
|
||||
access s3 backends), this reduces memory usage.
|
||||
|
||||
https://github.com/restic/restic/issues/1256
|
||||
https://github.com/restic/restic/pull/1267
|
||||
6
changelog/0.8.0/issue-1271
Normal file
6
changelog/0.8.0/issue-1271
Normal file
@@ -0,0 +1,6 @@
|
||||
Enhancement: Cache results for excludes for `backup`
|
||||
|
||||
The `backup` command now caches the result of excludes for a directory.
|
||||
|
||||
https://github.com/restic/restic/issues/1271
|
||||
https://github.com/restic/restic/pull/1326
|
||||
9
changelog/0.8.0/issue-1274
Normal file
9
changelog/0.8.0/issue-1274
Normal file
@@ -0,0 +1,9 @@
|
||||
Enhancement: Add `generate` command, replaces `manpage` and `autocomplete`
|
||||
|
||||
The `generate` command has been added, which replaces the now removed
|
||||
commands `manpage` and `autocomplete`. This release of restic contains the
|
||||
most recent manpages in `doc/man` and the auto-completion files for bash and
|
||||
zsh in `doc/bash-completion.sh` and `doc/zsh-completion.zsh`
|
||||
|
||||
https://github.com/restic/restic/issues/1274
|
||||
https://github.com/restic/restic/pull/1282
|
||||
8
changelog/0.8.0/issue-1291
Normal file
8
changelog/0.8.0/issue-1291
Normal file
@@ -0,0 +1,8 @@
|
||||
Bugfix: Reuse backend TCP connections to BackBlaze B2
|
||||
|
||||
A bug was discovered in the library we're using to access Backblaze, it now
|
||||
reuses already established TCP connections which should be a lot faster and
|
||||
not cause network failures any more.
|
||||
|
||||
https://github.com/restic/restic/issues/1291
|
||||
https://github.com/restic/restic/pull/1301
|
||||
7
changelog/0.8.0/issue-1367
Normal file
7
changelog/0.8.0/issue-1367
Normal file
@@ -0,0 +1,7 @@
|
||||
Enhancement: Allow comments in files read from via `--file-from`
|
||||
|
||||
When the list of files/dirs to be saved is read from a file with
|
||||
`--files-from`, comment lines (starting with `#`) are now ignored.
|
||||
|
||||
https://github.com/restic/restic/issues/1367
|
||||
https://github.com/restic/restic/pull/1368
|
||||
18
changelog/0.8.0/issue-1445
Normal file
18
changelog/0.8.0/issue-1445
Normal file
@@ -0,0 +1,18 @@
|
||||
Security: Prevent writing outside the target directory during restore
|
||||
|
||||
A vulnerability was found in the restic restorer, which allowed attackers in
|
||||
special circumstances to restore files to a location outside of the target
|
||||
directory. Due to the circumstances we estimate this to be a low-risk
|
||||
vulnerability, but urge all users to upgrade to the latest version of restic.
|
||||
|
||||
Exploiting the vulnerability requires a Linux/Unix system which saves backups
|
||||
via restic and a Windows systems which restores files from the repo. In
|
||||
addition, the attackers need to be able to create create files with arbitrary
|
||||
names which are then saved to the restic repo. For example, by creating a file
|
||||
named "..\test.txt" (which is a perfectly legal filename on Linux) and
|
||||
restoring a snapshot containing this file on Windows, it would be written to
|
||||
the parent of the target directory.
|
||||
|
||||
We'd like to thank Tyler Spivey for reporting this responsibly!
|
||||
|
||||
https://github.com/restic/restic/pull/1445
|
||||
9
changelog/0.8.0/issue-448
Normal file
9
changelog/0.8.0/issue-448
Normal file
@@ -0,0 +1,9 @@
|
||||
Enhancement: sftp backend prompts for password
|
||||
|
||||
The sftp backend now prompts for the password if a password is necessary for
|
||||
login.
|
||||
|
||||
https://github.com/restic/restic/issues/448
|
||||
https://github.com/restic/restic/pull/1270
|
||||
|
||||
|
||||
7
changelog/0.8.0/issue-510
Normal file
7
changelog/0.8.0/issue-510
Normal file
@@ -0,0 +1,7 @@
|
||||
Enhancement: Add `dump` command
|
||||
|
||||
We've added the `dump` command which prints a file from a snapshot to
|
||||
stdout. This can e.g. be used to restore files read with `backup --stdin`.
|
||||
|
||||
https://github.com/restic/restic/issues/510
|
||||
https://github.com/restic/restic/pull/1346
|
||||
23
changelog/0.8.0/pull-1040
Normal file
23
changelog/0.8.0/pull-1040
Normal file
@@ -0,0 +1,23 @@
|
||||
Enhancement: Add local metadata cache
|
||||
|
||||
We've added a local cache for metadata so that restic doesn't need to load
|
||||
all metadata (snapshots, indexes, ...) from the repo each time it starts. By
|
||||
default the cache is active, but there's a new global option `--no-cache`
|
||||
that can be used to disable the cache. By deafult, the cache a standard
|
||||
cache folder for the OS, which can be overridden with `--cache-dir`. The
|
||||
cache will automatically populate, indexes and snapshots are saved as they
|
||||
are loaded. Cache directories for repos that haven't been used recently can
|
||||
automatically be removed by restic with the `--cleanup-cache` option.
|
||||
|
||||
A related change was to by default create pack files in the repo that contain
|
||||
either data or metadata, not both mixed together. This allows easy caching of
|
||||
only the metadata files. The next run of `restic prune` will untangle mixed
|
||||
files automatically.
|
||||
|
||||
https://github.com/restic/restic/pull/1040
|
||||
https://github.com/restic/restic/issues/29
|
||||
https://github.com/restic/restic/issues/738
|
||||
https://github.com/restic/restic/issues/282
|
||||
https://github.com/restic/restic/pull/1287
|
||||
https://github.com/restic/restic/pull/1436
|
||||
https://github.com/restic/restic/pull/1265
|
||||
6
changelog/0.8.0/pull-1249
Normal file
6
changelog/0.8.0/pull-1249
Normal file
@@ -0,0 +1,6 @@
|
||||
Enhancement: Add `latest` symlink in fuse mount
|
||||
|
||||
The directory structure in the fuse mount now exposes a symlink `latest`
|
||||
which points to the latest snapshot in that particular directory.
|
||||
|
||||
https://github.com/restic/restic/pull/1249
|
||||
6
changelog/0.8.0/pull-1269
Normal file
6
changelog/0.8.0/pull-1269
Normal file
@@ -0,0 +1,6 @@
|
||||
Enhancement: Add `--compact` to `forget` command
|
||||
|
||||
The option `--compact` was added to the `forget` command to provide the same
|
||||
compact view as the `snapshots` command.
|
||||
|
||||
https://github.com/restic/restic/pull/1269
|
||||
7
changelog/0.8.0/pull-1281
Normal file
7
changelog/0.8.0/pull-1281
Normal file
@@ -0,0 +1,7 @@
|
||||
Enhancement: Google Cloud Storage backend needs less permissions
|
||||
|
||||
The Google Cloud Storage backend no longer requires the service account to
|
||||
have the `storage.buckets.get` permission ("Storage Admin" role) in `restic
|
||||
init` if the bucket already exists.
|
||||
|
||||
https://github.com/restic/restic/pull/1281
|
||||
7
changelog/0.8.0/pull-1317
Normal file
7
changelog/0.8.0/pull-1317
Normal file
@@ -0,0 +1,7 @@
|
||||
Bugfix: Run prune when `forget --prune` is called with just snapshot IDs
|
||||
|
||||
A bug in the `forget` command caused `prune` not to be run when `--prune` was
|
||||
specified without a policy, e.g. when only snapshot IDs that should be
|
||||
forgotten are listed manually.
|
||||
|
||||
https://github.com/restic/restic/pull/1317
|
||||
8
changelog/0.8.0/pull-1319
Normal file
8
changelog/0.8.0/pull-1319
Normal file
@@ -0,0 +1,8 @@
|
||||
Enhancement: Make `check` print `no errors found` explicitly
|
||||
|
||||
The `check` command now explicetly prints `No errors were found` when no errors
|
||||
could be found.
|
||||
|
||||
https://github.com/restic/restic/pull/1319
|
||||
https://github.com/restic/restic/issues/1303
|
||||
|
||||
3
changelog/0.8.0/pull-1353
Normal file
3
changelog/0.8.0/pull-1353
Normal file
@@ -0,0 +1,3 @@
|
||||
Enhancement: Retry failed backend requests
|
||||
|
||||
https://github.com/restic/restic/pull/1353
|
||||
10
changelog/0.8.0/pull-1437
Normal file
10
changelog/0.8.0/pull-1437
Normal file
@@ -0,0 +1,10 @@
|
||||
Bugfix: Remove implicit path `/restic` for the s3 backend
|
||||
|
||||
The s3 backend used the subdir `restic` within a bucket if no explicit path
|
||||
after the bucket name was specified. Since this version, restic does not use
|
||||
this default path any more. If you created a repo on s3 in a bucket without
|
||||
specifying a path within the bucket, you need to add `/restic` at the end of
|
||||
the repository specification to access your repo: `s3:s3.amazonaws.com/bucket/restic`
|
||||
|
||||
https://github.com/restic/restic/pull/1437
|
||||
https://github.com/restic/restic/issues/1292
|
||||
4
changelog/0.8.1/issue-1457
Normal file
4
changelog/0.8.1/issue-1457
Normal file
@@ -0,0 +1,4 @@
|
||||
Bugfix: Improve s3 backend with DigitalOcean Spaces
|
||||
|
||||
https://github.com/restic/restic/pull/1459
|
||||
https://github.com/restic/restic/issues/1457
|
||||
9
changelog/0.8.1/pull-1436
Normal file
9
changelog/0.8.1/pull-1436
Normal file
@@ -0,0 +1,9 @@
|
||||
Enhancement: Add code to detect old cache directories
|
||||
|
||||
We've added code to detect old cache directories of repositories that
|
||||
haven't been used in a long time, restic now prints a note when it detects
|
||||
that such dirs exist. Also, the option `--cleanup-cache` was added to
|
||||
automatically remove such directories. That's not a problem because the
|
||||
cache will be rebuild once a repo is accessed again.
|
||||
|
||||
https://github.com/restic/restic/pull/1436
|
||||
6
changelog/0.8.1/pull-1439
Normal file
6
changelog/0.8.1/pull-1439
Normal file
@@ -0,0 +1,6 @@
|
||||
Enhancement: Improve cancellation logic
|
||||
|
||||
The cancellation logic was improved, restic can now shut down cleanly when
|
||||
requested to do so (e.g. via ctrl+c).
|
||||
|
||||
https://github.com/restic/restic/pull/1439
|
||||
9
changelog/0.8.1/pull-1452
Normal file
9
changelog/0.8.1/pull-1452
Normal file
@@ -0,0 +1,9 @@
|
||||
Change: Do not save atime by default
|
||||
|
||||
By default, the access time for files and dirs is not saved any more. It is
|
||||
not possible to reliably disable updating the access time during a backup,
|
||||
so for the next backup the access time is different again. This means a lot
|
||||
of metadata is saved. If you want to save the access time anyway, pass
|
||||
`--with-atime` to the `backup` command.
|
||||
|
||||
https://github.com/restic/restic/pull/1452
|
||||
6
changelog/0.8.1/pull-1454
Normal file
6
changelog/0.8.1/pull-1454
Normal file
@@ -0,0 +1,6 @@
|
||||
Bugfix: Correct cache dir location for Windows and Darwin
|
||||
|
||||
The cache directory on Windows and Darwin was not correct, instead the
|
||||
directory `.cache` was used.
|
||||
|
||||
https://github.com/restic/restic/pull/1454
|
||||
9
changelog/0.8.1/pull-1459
Normal file
9
changelog/0.8.1/pull-1459
Normal file
@@ -0,0 +1,9 @@
|
||||
Bugfix: Disable handling SIGPIPE
|
||||
|
||||
We've disabled handling SIGPIPE again. Turns out, writing to broken TCP
|
||||
connections also raised SIGPIPE, so restic exits on the first write to a
|
||||
broken connection. Instead, restic should retry the request.
|
||||
|
||||
https://github.com/restic/restic/pull/1459
|
||||
https://github.com/restic/restic/issues/1457
|
||||
https://github.com/restic/restic/issues/1466
|
||||
8
changelog/0.8.1/pull-1462
Normal file
8
changelog/0.8.1/pull-1462
Normal file
@@ -0,0 +1,8 @@
|
||||
Enhancement: Add the `diff` command
|
||||
|
||||
The command `diff` was added, it allows comparing two snapshots and listing
|
||||
all differences.
|
||||
|
||||
https://github.com/restic/restic/issues/11
|
||||
https://github.com/restic/restic/issues/1460
|
||||
https://github.com/restic/restic/pull/1462
|
||||
32
changelog/CHANGELOG.tmpl
Normal file
32
changelog/CHANGELOG.tmpl
Normal file
@@ -0,0 +1,32 @@
|
||||
{{- range $changes := . }}{{ with $changes -}}
|
||||
Changelog for restic {{ .Version }} ({{ .Date }})
|
||||
=======================================
|
||||
|
||||
The following sections list the changes in restic {{ .Version }} relevant to
|
||||
restic users. The changes are ordered by importance.
|
||||
|
||||
Summary
|
||||
-------
|
||||
{{ range $entry := .Entries }}{{ with $entry }}
|
||||
* {{ .TypeShort }} #{{ .PrimaryID }}: {{ .Title }}
|
||||
{{- end }}{{ end }}
|
||||
|
||||
Details
|
||||
-------
|
||||
{{ range $entry := .Entries }}{{ with $entry }}
|
||||
* {{ .Type }} #{{ .PrimaryID }}: {{ .Title }}
|
||||
{{ range $par := .Paragraphs }}
|
||||
{{ wrap $par 80 3 }}
|
||||
{{ end -}}
|
||||
{{ range $id := .Issues }}
|
||||
https://github.com/restic/restic/issues/{{ $id -}}
|
||||
{{ end -}}
|
||||
{{ range $id := .PRs }}
|
||||
https://github.com/restic/restic/pull/{{ $id -}}
|
||||
{{ end -}}
|
||||
{{ range $url := .OtherURLs }}
|
||||
{{ $url -}}
|
||||
{{ end }}
|
||||
{{ end }}{{ end }}
|
||||
|
||||
{{ end }}{{ end -}}
|
||||
12
changelog/changelog-entry.tmpl
Normal file
12
changelog/changelog-entry.tmpl
Normal file
@@ -0,0 +1,12 @@
|
||||
Bugfix: Fix behavior for foobar (in present tense)
|
||||
|
||||
We've fixed the behavior for foobar, a long-standing annoyance for restic
|
||||
users.
|
||||
|
||||
The text in the paragraphs is written in past tense. The last section is a list
|
||||
of issue URLs, PR URLs and other URLs. The first issue ID (or the first PR ID,
|
||||
in case there aren't any issue links) is used as the primary ID.
|
||||
|
||||
https://github.com/restic/restic/issues/1234
|
||||
https://github.com/restic/restic/pull/55555
|
||||
https://forum.restic/.net/foo/bar/baz
|
||||
32
changelog/changelog-github.tmpl
Normal file
32
changelog/changelog-github.tmpl
Normal file
@@ -0,0 +1,32 @@
|
||||
{{- range $changes := . }}{{ with $changes -}}
|
||||
Changelog for restic {{ .Version }} ({{ .Date }})
|
||||
=======================================
|
||||
|
||||
The following sections list the changes in restic {{ .Version }} relevant to
|
||||
restic users. The changes are ordered by importance.
|
||||
|
||||
Summary
|
||||
-------
|
||||
{{ range $entry := .Entries }}{{ with $entry }}
|
||||
* {{ .TypeShort }} [#{{ .PrimaryID }}]({{ .PrimaryURL }}): {{ .Title }}
|
||||
{{- end }}{{ end }}
|
||||
|
||||
Details
|
||||
-------
|
||||
{{ range $entry := .Entries }}{{ with $entry }}
|
||||
* {{ .Type }} #{{ .PrimaryID }}: {{ .Title }}
|
||||
{{ range $par := .Paragraphs }}
|
||||
{{ $par }}
|
||||
{{ end }}
|
||||
{{ range $id := .Issues -}}
|
||||
[{{ $id }}](https://github.com/restic/restic/issues/{{ $id -}})
|
||||
{{- end -}}
|
||||
{{ range $id := .PRs -}}
|
||||
{{ ` ` }}[#{{ $id }}](https://github.com/restic/restic/pull/{{ $id -}})
|
||||
{{- end -}}
|
||||
{{ ` ` }}{{ range $url := .OtherURLs -}}
|
||||
{{ $url -}}
|
||||
{{- end }}
|
||||
{{ end }}{{ end }}
|
||||
|
||||
{{ end }}{{ end -}}
|
||||
13
changelog/releases
Normal file
13
changelog/releases
Normal file
@@ -0,0 +1,13 @@
|
||||
# This file lists all versions for the changelog. Each line consists of the
|
||||
# version string, followed by an optional release date.
|
||||
#
|
||||
# The resulting changelog generated by `calens` will list all versions in
|
||||
# exactly this order.
|
||||
0.8.1
|
||||
0.8.0 2017-11-26
|
||||
0.7.3 2017-09-20
|
||||
0.7.2 2017-09-13
|
||||
0.7.1 2017-07-22
|
||||
0.7.0 2017-07-01
|
||||
0.6.1 2017-06-01
|
||||
0.6.0 2017-05-29
|
||||
@@ -14,15 +14,25 @@ var cleanupHandlers struct {
|
||||
sync.Mutex
|
||||
list []func() error
|
||||
done bool
|
||||
ch chan os.Signal
|
||||
}
|
||||
|
||||
var stderr = os.Stderr
|
||||
|
||||
func init() {
|
||||
c := make(chan os.Signal)
|
||||
signal.Notify(c, syscall.SIGINT)
|
||||
cleanupHandlers.ch = make(chan os.Signal)
|
||||
go CleanupHandler(cleanupHandlers.ch)
|
||||
InstallSignalHandler()
|
||||
}
|
||||
|
||||
go CleanupHandler(c)
|
||||
// InstallSignalHandler listens for SIGINT, and triggers the cleanup handlers.
|
||||
func InstallSignalHandler() {
|
||||
signal.Notify(cleanupHandlers.ch, syscall.SIGINT)
|
||||
}
|
||||
|
||||
// SuspendSignalHandler removes the signal handler for SIGINT.
|
||||
func SuspendSignalHandler() {
|
||||
signal.Reset(syscall.SIGINT)
|
||||
}
|
||||
|
||||
// AddCleanupHandler adds the function f to the list of cleanup handlers so
|
||||
@@ -57,12 +67,18 @@ func RunCleanupHandlers() {
|
||||
cleanupHandlers.list = nil
|
||||
}
|
||||
|
||||
// CleanupHandler handles the SIGINT signal.
|
||||
// CleanupHandler handles the SIGINT signals.
|
||||
func CleanupHandler(c <-chan os.Signal) {
|
||||
for s := range c {
|
||||
debug.Log("signal %v received, cleaning up", s)
|
||||
fmt.Printf("%sInterrupt received, cleaning up\n", ClearLine())
|
||||
Exit(0)
|
||||
fmt.Fprintf(stderr, "%ssignal %v received, cleaning up\n", ClearLine(), s)
|
||||
|
||||
code := 0
|
||||
if s != syscall.SIGINT {
|
||||
code = 1
|
||||
}
|
||||
|
||||
Exit(code)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var cmdAutocomplete = &cobra.Command{
|
||||
Use: "autocomplete",
|
||||
Short: "Generate shell autocompletion script",
|
||||
Long: `The "autocomplete" command generates a shell autocompletion script.
|
||||
|
||||
NOTE: The current version supports Bash only.
|
||||
This should work for *nix systems with Bash installed.
|
||||
|
||||
By default, the file is written directly to /etc/bash_completion.d
|
||||
for convenience, and the command may need superuser rights, e.g.:
|
||||
|
||||
$ sudo restic autocomplete`,
|
||||
|
||||
DisableAutoGenTag: true,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
if err := cmdRoot.GenBashCompletionFile(autocompleteTarget); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
var autocompleteTarget string
|
||||
|
||||
func init() {
|
||||
cmdRoot.AddCommand(cmdAutocomplete)
|
||||
|
||||
cmdAutocomplete.Flags().StringVarP(&autocompleteTarget, "completionfile", "", "/usr/share/bash-completion/completions/restic", "autocompletion file")
|
||||
// For bash-completion
|
||||
cmdAutocomplete.Flags().SetAnnotation("completionfile", cobra.BashCompFilenameExt, []string{})
|
||||
}
|
||||
@@ -2,10 +2,10 @@ package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -65,6 +65,7 @@ type BackupOptions struct {
|
||||
Hostname string
|
||||
FilesFrom string
|
||||
TimeStamp string
|
||||
WithAtime bool
|
||||
}
|
||||
|
||||
var backupOptions BackupOptions
|
||||
@@ -83,9 +84,10 @@ func init() {
|
||||
f.BoolVar(&backupOptions.Stdin, "stdin", false, "read backup from stdin")
|
||||
f.StringVar(&backupOptions.StdinFilename, "stdin-filename", "stdin", "file name to use when reading from stdin")
|
||||
f.StringArrayVar(&backupOptions.Tags, "tag", nil, "add a `tag` for the new snapshot (can be specified multiple times)")
|
||||
f.StringVar(&backupOptions.Hostname, "hostname", "", "set the `hostname` for the snapshot manually")
|
||||
f.StringVar(&backupOptions.Hostname, "hostname", "", "set the `hostname` for the snapshot manually. To prevent an expensive rescan use the \"parent\" flag")
|
||||
f.StringVar(&backupOptions.FilesFrom, "files-from", "", "read the files to backup from file (can be combined with file args)")
|
||||
f.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")
|
||||
}
|
||||
|
||||
func newScanProgress(gopts GlobalOptions) *restic.Progress {
|
||||
@@ -235,10 +237,16 @@ func readBackupFromStdin(opts BackupOptions, gopts GlobalOptions, args []string)
|
||||
return errors.Fatal("when reading from stdin, no additional files can be specified")
|
||||
}
|
||||
|
||||
if opts.StdinFilename == "" {
|
||||
fn := opts.StdinFilename
|
||||
|
||||
if fn == "" {
|
||||
return errors.Fatal("filename for backup from stdin must not be empty")
|
||||
}
|
||||
|
||||
if filepath.Base(fn) != fn || path.Base(fn) != fn {
|
||||
return errors.Fatal("filename is invalid (may not contain a directory, slash or backslash)")
|
||||
}
|
||||
|
||||
if gopts.password == "" {
|
||||
return errors.Fatal("unable to read password from stdin when data is to be read from stdin, use --password-file or $RESTIC_PASSWORD")
|
||||
}
|
||||
@@ -254,7 +262,7 @@ func readBackupFromStdin(opts BackupOptions, gopts GlobalOptions, args []string)
|
||||
return err
|
||||
}
|
||||
|
||||
err = repo.LoadIndex(context.TODO())
|
||||
err = repo.LoadIndex(gopts.ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -265,7 +273,7 @@ func readBackupFromStdin(opts BackupOptions, gopts GlobalOptions, args []string)
|
||||
Hostname: opts.Hostname,
|
||||
}
|
||||
|
||||
_, id, err := r.Archive(context.TODO(), opts.StdinFilename, os.Stdin, newArchiveStdinProgress(gopts))
|
||||
_, id, err := r.Archive(gopts.ctx, fn, os.Stdin, newArchiveStdinProgress(gopts))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -298,9 +306,14 @@ func readLinesFromFile(filename string) ([]string, error) {
|
||||
scanner := bufio.NewScanner(r)
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
// ignore empty lines
|
||||
if line == "" {
|
||||
continue
|
||||
}
|
||||
// strip comments
|
||||
if strings.HasPrefix(line, "#") {
|
||||
continue
|
||||
}
|
||||
lines = append(lines, line)
|
||||
}
|
||||
|
||||
@@ -387,7 +400,17 @@ func runBackup(opts BackupOptions, gopts GlobalOptions, args []string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
err = repo.LoadIndex(context.TODO())
|
||||
// exclude restic cache
|
||||
if repo.Cache != nil {
|
||||
f, err := rejectResticCache(repo)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
rejectFuncs = append(rejectFuncs, f)
|
||||
}
|
||||
|
||||
err = repo.LoadIndex(gopts.ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -406,7 +429,7 @@ func runBackup(opts BackupOptions, gopts GlobalOptions, args []string) error {
|
||||
|
||||
// Find last snapshot to set it as parent, if not already set
|
||||
if !opts.Force && parentSnapshotID == nil {
|
||||
id, err := restic.FindLatestSnapshot(context.TODO(), repo, target, []restic.TagList{opts.Tags}, opts.Hostname)
|
||||
id, err := restic.FindLatestSnapshot(gopts.ctx, repo, target, []restic.TagList{}, opts.Hostname)
|
||||
if err == nil {
|
||||
parentSnapshotID = &id
|
||||
} else if err != restic.ErrNoSnapshotFound {
|
||||
@@ -437,6 +460,7 @@ func runBackup(opts BackupOptions, gopts GlobalOptions, args []string) error {
|
||||
arch := archiver.New(repo)
|
||||
arch.Excludes = opts.Excludes
|
||||
arch.SelectFilter = selectFilter
|
||||
arch.WithAccessTime = opts.WithAtime
|
||||
|
||||
arch.Warn = func(dir string, fi os.FileInfo, err error) {
|
||||
// TODO: make ignoring errors configurable
|
||||
@@ -451,7 +475,7 @@ func runBackup(opts BackupOptions, gopts GlobalOptions, args []string) error {
|
||||
}
|
||||
}
|
||||
|
||||
_, id, err := arch.Snapshot(context.TODO(), newArchiveProgress(gopts, stat), target, opts.Tags, opts.Hostname, parentSnapshotID, timeStamp)
|
||||
_, id, err := arch.Snapshot(gopts.ctx, newArchiveProgress(gopts, stat), target, opts.Tags, opts.Hostname, parentSnapshotID, timeStamp)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
@@ -75,7 +74,7 @@ func runCat(gopts GlobalOptions, args []string) error {
|
||||
fmt.Println(string(buf))
|
||||
return nil
|
||||
case "index":
|
||||
buf, err := repo.LoadAndDecrypt(context.TODO(), restic.IndexFile, id)
|
||||
buf, err := repo.LoadAndDecrypt(gopts.ctx, restic.IndexFile, id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -85,7 +84,7 @@ func runCat(gopts GlobalOptions, args []string) error {
|
||||
|
||||
case "snapshot":
|
||||
sn := &restic.Snapshot{}
|
||||
err = repo.LoadJSONUnpacked(context.TODO(), restic.SnapshotFile, id, sn)
|
||||
err = repo.LoadJSONUnpacked(gopts.ctx, restic.SnapshotFile, id, sn)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -100,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(context.TODO(), repo.Backend(), h)
|
||||
buf, err := backend.LoadAll(gopts.ctx, repo.Backend(), h)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -127,7 +126,7 @@ func runCat(gopts GlobalOptions, args []string) error {
|
||||
fmt.Println(string(buf))
|
||||
return nil
|
||||
case "lock":
|
||||
lock, err := restic.LoadLock(context.TODO(), repo, id)
|
||||
lock, err := restic.LoadLock(gopts.ctx, repo, id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -143,7 +142,7 @@ func runCat(gopts GlobalOptions, args []string) error {
|
||||
}
|
||||
|
||||
// load index, handle all the other types
|
||||
err = repo.LoadIndex(context.TODO())
|
||||
err = repo.LoadIndex(gopts.ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -151,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(context.TODO(), repo.Backend(), h)
|
||||
buf, err := backend.LoadAll(gopts.ctx, repo.Backend(), h)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -173,7 +172,7 @@ func runCat(gopts GlobalOptions, args []string) error {
|
||||
blob := list[0]
|
||||
|
||||
buf := make([]byte, blob.Length)
|
||||
n, err := repo.LoadBlob(context.TODO(), t, id, buf)
|
||||
n, err := repo.LoadBlob(gopts.ctx, t, id, buf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"time"
|
||||
@@ -19,6 +18,9 @@ var cmdCheck = &cobra.Command{
|
||||
Long: `
|
||||
The "check" command tests the repository for errors and reports any errors it
|
||||
finds. It can also be used to read all data and therefore simulate a restore.
|
||||
|
||||
By default, the "check" command will always load all data directly from the
|
||||
repository and not use a local cache.
|
||||
`,
|
||||
DisableAutoGenTag: true,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
@@ -30,6 +32,7 @@ finds. It can also be used to read all data and therefore simulate a restore.
|
||||
type CheckOptions struct {
|
||||
ReadData bool
|
||||
CheckUnused bool
|
||||
WithCache bool
|
||||
}
|
||||
|
||||
var checkOptions CheckOptions
|
||||
@@ -40,6 +43,7 @@ func init() {
|
||||
f := cmdCheck.Flags()
|
||||
f.BoolVar(&checkOptions.ReadData, "read-data", false, "read all data blobs")
|
||||
f.BoolVar(&checkOptions.CheckUnused, "check-unused", false, "find unused blobs")
|
||||
f.BoolVar(&checkOptions.WithCache, "with-cache", false, "use the cache")
|
||||
}
|
||||
|
||||
func newReadProgress(gopts GlobalOptions, todo restic.Stat) *restic.Progress {
|
||||
@@ -77,13 +81,18 @@ func runCheck(opts CheckOptions, gopts GlobalOptions, args []string) error {
|
||||
return errors.Fatal("check has no arguments")
|
||||
}
|
||||
|
||||
if !opts.WithCache {
|
||||
// do not use a cache for the checker
|
||||
gopts.NoCache = true
|
||||
}
|
||||
|
||||
repo, err := OpenRepository(gopts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !gopts.NoLock {
|
||||
Verbosef("Create exclusive lock for repository\n")
|
||||
Verbosef("create exclusive lock for repository\n")
|
||||
lock, err := lockRepoExclusive(repo)
|
||||
defer unlockRepo(lock)
|
||||
if err != nil {
|
||||
@@ -93,8 +102,8 @@ func runCheck(opts CheckOptions, gopts GlobalOptions, args []string) error {
|
||||
|
||||
chkr := checker.New(repo)
|
||||
|
||||
Verbosef("Load indexes\n")
|
||||
hints, errs := chkr.LoadIndex(context.TODO())
|
||||
Verbosef("load indexes\n")
|
||||
hints, errs := chkr.LoadIndex(gopts.ctx)
|
||||
|
||||
dupFound := false
|
||||
for _, hint := range hints {
|
||||
@@ -118,17 +127,17 @@ func runCheck(opts CheckOptions, gopts GlobalOptions, args []string) error {
|
||||
errorsFound := false
|
||||
errChan := make(chan error)
|
||||
|
||||
Verbosef("Check all packs\n")
|
||||
go chkr.Packs(context.TODO(), errChan)
|
||||
Verbosef("check all packs\n")
|
||||
go chkr.Packs(gopts.ctx, errChan)
|
||||
|
||||
for err := range errChan {
|
||||
errorsFound = true
|
||||
fmt.Fprintf(os.Stderr, "%v\n", err)
|
||||
}
|
||||
|
||||
Verbosef("Check snapshots, trees and blobs\n")
|
||||
Verbosef("check snapshots, trees and blobs\n")
|
||||
errChan = make(chan error)
|
||||
go chkr.Structure(context.TODO(), errChan)
|
||||
go chkr.Structure(gopts.ctx, errChan)
|
||||
|
||||
for err := range errChan {
|
||||
errorsFound = true
|
||||
@@ -150,12 +159,12 @@ func runCheck(opts CheckOptions, gopts GlobalOptions, args []string) error {
|
||||
}
|
||||
|
||||
if opts.ReadData {
|
||||
Verbosef("Read all data\n")
|
||||
Verbosef("read all data\n")
|
||||
|
||||
p := newReadProgress(gopts, restic.Stat{Blobs: chkr.CountPacks()})
|
||||
errChan := make(chan error)
|
||||
|
||||
go chkr.ReadData(context.TODO(), p, errChan)
|
||||
go chkr.ReadData(gopts.ctx, p, errChan)
|
||||
|
||||
for err := range errChan {
|
||||
errorsFound = true
|
||||
@@ -166,5 +175,8 @@ func runCheck(opts CheckOptions, gopts GlobalOptions, args []string) error {
|
||||
if errorsFound {
|
||||
return errors.Fatal("repository contains errors")
|
||||
}
|
||||
|
||||
Verbosef("no errors were found\n")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
217
cmd/restic/cmd_debug.go
Normal file
217
cmd/restic/cmd_debug.go
Normal file
@@ -0,0 +1,217 @@
|
||||
// +build debug
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/restic/restic/internal/errors"
|
||||
"github.com/restic/restic/internal/pack"
|
||||
"github.com/restic/restic/internal/repository"
|
||||
"github.com/restic/restic/internal/restic"
|
||||
|
||||
"github.com/restic/restic/internal/worker"
|
||||
)
|
||||
|
||||
var cmdDebug = &cobra.Command{
|
||||
Use: "debug",
|
||||
Short: "Debug commands",
|
||||
}
|
||||
|
||||
var cmdDebugDump = &cobra.Command{
|
||||
Use: "dump [indexes|snapshots|all|packs]",
|
||||
Short: "Dump data structures",
|
||||
Long: `
|
||||
The "dump" command dumps data structures from the repository as JSON objects. It
|
||||
is used for debugging purposes only.`,
|
||||
DisableAutoGenTag: true,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return runDebugDump(globalOptions, args)
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
cmdRoot.AddCommand(cmdDebug)
|
||||
cmdDebug.AddCommand(cmdDebugDump)
|
||||
}
|
||||
|
||||
func prettyPrintJSON(wr io.Writer, item interface{}) error {
|
||||
buf, err := json.MarshalIndent(item, "", " ")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = wr.Write(append(buf, '\n'))
|
||||
return err
|
||||
}
|
||||
|
||||
func debugPrintSnapshots(repo *repository.Repository, wr io.Writer) error {
|
||||
for id := range repo.List(context.TODO(), restic.SnapshotFile) {
|
||||
snapshot, err := restic.LoadSnapshot(context.TODO(), repo, id)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "LoadSnapshot(%v): %v", id.Str(), err)
|
||||
continue
|
||||
}
|
||||
|
||||
fmt.Fprintf(wr, "snapshot_id: %v\n", id)
|
||||
|
||||
err = prettyPrintJSON(wr, snapshot)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
const dumpPackWorkers = 10
|
||||
|
||||
// Pack is the struct used in printPacks.
|
||||
type Pack struct {
|
||||
Name string `json:"name"`
|
||||
|
||||
Blobs []Blob `json:"blobs"`
|
||||
}
|
||||
|
||||
// Blob is the struct used in printPacks.
|
||||
type Blob struct {
|
||||
Type restic.BlobType `json:"type"`
|
||||
Length uint `json:"length"`
|
||||
ID restic.ID `json:"id"`
|
||||
Offset uint `json:"offset"`
|
||||
}
|
||||
|
||||
func printPacks(repo *repository.Repository, wr io.Writer) error {
|
||||
f := func(ctx context.Context, job worker.Job) (interface{}, error) {
|
||||
name := job.Data.(string)
|
||||
|
||||
h := restic.Handle{Type: restic.DataFile, Name: name}
|
||||
|
||||
blobInfo, err := repo.Backend().Stat(ctx, h)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
blobs, err := pack.List(repo.Key(), restic.ReaderAt(repo.Backend(), h), blobInfo.Size)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return blobs, nil
|
||||
}
|
||||
|
||||
jobCh := make(chan worker.Job)
|
||||
resCh := make(chan worker.Job)
|
||||
wp := worker.New(context.TODO(), dumpPackWorkers, f, jobCh, resCh)
|
||||
|
||||
go func() {
|
||||
for name := range repo.Backend().List(context.TODO(), restic.DataFile) {
|
||||
jobCh <- worker.Job{Data: name}
|
||||
}
|
||||
close(jobCh)
|
||||
}()
|
||||
|
||||
for job := range resCh {
|
||||
name := job.Data.(string)
|
||||
|
||||
if job.Error != nil {
|
||||
fmt.Fprintf(os.Stderr, "error for pack %v: %v\n", name, job.Error)
|
||||
continue
|
||||
}
|
||||
|
||||
entries := job.Result.([]restic.Blob)
|
||||
p := Pack{
|
||||
Name: name,
|
||||
Blobs: make([]Blob, len(entries)),
|
||||
}
|
||||
for i, blob := range entries {
|
||||
p.Blobs[i] = Blob{
|
||||
Type: blob.Type,
|
||||
Length: blob.Length,
|
||||
ID: blob.ID,
|
||||
Offset: blob.Offset,
|
||||
}
|
||||
}
|
||||
|
||||
prettyPrintJSON(os.Stdout, p)
|
||||
}
|
||||
|
||||
wp.Wait()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func dumpIndexes(repo restic.Repository) error {
|
||||
for id := range repo.List(context.TODO(), restic.IndexFile) {
|
||||
fmt.Printf("index_id: %v\n", id)
|
||||
|
||||
idx, err := repository.LoadIndex(context.TODO(), repo, id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = idx.Dump(os.Stdout)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func runDebugDump(gopts GlobalOptions, args []string) error {
|
||||
if len(args) != 1 {
|
||||
return errors.Fatal("type not specified")
|
||||
}
|
||||
|
||||
repo, err := OpenRepository(gopts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !gopts.NoLock {
|
||||
lock, err := lockRepo(repo)
|
||||
defer unlockRepo(lock)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
err = repo.LoadIndex(gopts.ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tpe := args[0]
|
||||
|
||||
switch tpe {
|
||||
case "indexes":
|
||||
return dumpIndexes(repo)
|
||||
case "snapshots":
|
||||
return debugPrintSnapshots(repo, os.Stdout)
|
||||
case "packs":
|
||||
return printPacks(repo, os.Stdout)
|
||||
case "all":
|
||||
fmt.Printf("snapshots:\n")
|
||||
err := debugPrintSnapshots(repo, os.Stdout)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Printf("\nindexes:\n")
|
||||
err = dumpIndexes(repo)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
default:
|
||||
return errors.Fatalf("no such type %q", tpe)
|
||||
}
|
||||
}
|
||||
356
cmd/restic/cmd_diff.go
Normal file
356
cmd/restic/cmd_diff.go
Normal file
@@ -0,0 +1,356 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"path"
|
||||
"reflect"
|
||||
"sort"
|
||||
|
||||
"github.com/restic/restic/internal/debug"
|
||||
"github.com/restic/restic/internal/errors"
|
||||
"github.com/restic/restic/internal/repository"
|
||||
"github.com/restic/restic/internal/restic"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var cmdDiff = &cobra.Command{
|
||||
Use: "diff snapshot-ID snapshot-ID",
|
||||
Short: "Show differences between two snapshots",
|
||||
Long: `
|
||||
The "diff" command shows differences from the first to the second snapshot. The
|
||||
first characters in each line display what has happened to a particular file or
|
||||
directory:
|
||||
|
||||
+ The item was added
|
||||
- The item was removed
|
||||
U The metadata (access mode, timestamps, ...) for the item was updated
|
||||
M The file's content was modified
|
||||
T The type was changed, e.g. a file was made a symlink
|
||||
`,
|
||||
DisableAutoGenTag: true,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return runDiff(diffOptions, globalOptions, args)
|
||||
},
|
||||
}
|
||||
|
||||
// DiffOptions collects all options for the diff command.
|
||||
type DiffOptions struct {
|
||||
ShowMetadata bool
|
||||
}
|
||||
|
||||
var diffOptions DiffOptions
|
||||
|
||||
func init() {
|
||||
cmdRoot.AddCommand(cmdDiff)
|
||||
|
||||
f := cmdDiff.Flags()
|
||||
f.BoolVar(&diffOptions.ShowMetadata, "metadata", false, "print changes in metadata")
|
||||
}
|
||||
|
||||
func loadSnapshot(ctx context.Context, repo *repository.Repository, desc string) (*restic.Snapshot, error) {
|
||||
id, err := restic.FindSnapshot(repo, desc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return restic.LoadSnapshot(ctx, repo, id)
|
||||
}
|
||||
|
||||
// Comparer collects all things needed to compare two snapshots.
|
||||
type Comparer struct {
|
||||
repo restic.Repository
|
||||
opts DiffOptions
|
||||
}
|
||||
|
||||
// DiffStat collects stats for all types of items.
|
||||
type DiffStat struct {
|
||||
Files, Dirs, Others int
|
||||
DataBlobs, TreeBlobs int
|
||||
Bytes int
|
||||
}
|
||||
|
||||
// Add adds stats information for node to s.
|
||||
func (s *DiffStat) Add(node *restic.Node) {
|
||||
if node == nil {
|
||||
return
|
||||
}
|
||||
|
||||
switch node.Type {
|
||||
case "file":
|
||||
s.Files++
|
||||
case "dir":
|
||||
s.Dirs++
|
||||
default:
|
||||
s.Others++
|
||||
}
|
||||
}
|
||||
|
||||
// addBlobs adds the blobs of node to s.
|
||||
func addBlobs(bs restic.BlobSet, node *restic.Node) {
|
||||
if node == nil {
|
||||
return
|
||||
}
|
||||
|
||||
switch node.Type {
|
||||
case "file":
|
||||
for _, blob := range node.Content {
|
||||
h := restic.BlobHandle{
|
||||
ID: blob,
|
||||
Type: restic.DataBlob,
|
||||
}
|
||||
bs.Insert(h)
|
||||
}
|
||||
case "dir":
|
||||
h := restic.BlobHandle{
|
||||
ID: *node.Subtree,
|
||||
Type: restic.TreeBlob,
|
||||
}
|
||||
bs.Insert(h)
|
||||
}
|
||||
}
|
||||
|
||||
// DiffStats collects the differences between two snapshots.
|
||||
type DiffStats struct {
|
||||
ChangedFiles int
|
||||
Added DiffStat
|
||||
Removed DiffStat
|
||||
BlobsBefore, BlobsAfter restic.BlobSet
|
||||
}
|
||||
|
||||
// NewDiffStats creates new stats for a diff run.
|
||||
func NewDiffStats() *DiffStats {
|
||||
return &DiffStats{
|
||||
BlobsBefore: restic.NewBlobSet(),
|
||||
BlobsAfter: restic.NewBlobSet(),
|
||||
}
|
||||
}
|
||||
|
||||
// updateBlobs updates the blob counters in the stats struct.
|
||||
func updateBlobs(repo restic.Repository, blobs restic.BlobSet, stats *DiffStat) {
|
||||
for h := range blobs {
|
||||
switch h.Type {
|
||||
case restic.DataBlob:
|
||||
stats.DataBlobs++
|
||||
case restic.TreeBlob:
|
||||
stats.TreeBlobs++
|
||||
}
|
||||
|
||||
size, err := repo.LookupBlobSize(h.ID, h.Type)
|
||||
if err != nil {
|
||||
Warnf("unable to find blob size for %v: %v\n", h, err)
|
||||
continue
|
||||
}
|
||||
|
||||
stats.Bytes += int(size)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Comparer) printDir(ctx context.Context, mode string, stats *DiffStat, blobs restic.BlobSet, prefix string, id restic.ID) error {
|
||||
debug.Log("print %v tree %v", mode, id)
|
||||
tree, err := c.repo.LoadTree(ctx, id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, node := range tree.Nodes {
|
||||
name := path.Join(prefix, node.Name)
|
||||
if node.Type == "dir" {
|
||||
name += "/"
|
||||
}
|
||||
Printf("%-5s%v\n", mode, name)
|
||||
stats.Add(node)
|
||||
addBlobs(blobs, node)
|
||||
|
||||
if node.Type == "dir" {
|
||||
err := c.printDir(ctx, mode, stats, blobs, name, *node.Subtree)
|
||||
if err != nil {
|
||||
Warnf("error: %v\n", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func uniqueNodeNames(tree1, tree2 *restic.Tree) (tree1Nodes, tree2Nodes map[string]*restic.Node, uniqueNames []string) {
|
||||
names := make(map[string]struct{})
|
||||
tree1Nodes = make(map[string]*restic.Node)
|
||||
for _, node := range tree1.Nodes {
|
||||
tree1Nodes[node.Name] = node
|
||||
names[node.Name] = struct{}{}
|
||||
}
|
||||
|
||||
tree2Nodes = make(map[string]*restic.Node)
|
||||
for _, node := range tree2.Nodes {
|
||||
tree2Nodes[node.Name] = node
|
||||
names[node.Name] = struct{}{}
|
||||
}
|
||||
|
||||
uniqueNames = make([]string, 0, len(names))
|
||||
for name := range names {
|
||||
uniqueNames = append(uniqueNames, name)
|
||||
}
|
||||
|
||||
sort.Sort(sort.StringSlice(uniqueNames))
|
||||
return tree1Nodes, tree2Nodes, uniqueNames
|
||||
}
|
||||
|
||||
func (c *Comparer) diffTree(ctx context.Context, stats *DiffStats, prefix string, id1, id2 restic.ID) error {
|
||||
debug.Log("diffing %v to %v", id1, id2)
|
||||
tree1, err := c.repo.LoadTree(ctx, id1)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tree2, err := c.repo.LoadTree(ctx, id2)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tree1Nodes, tree2Nodes, names := uniqueNodeNames(tree1, tree2)
|
||||
|
||||
for _, name := range names {
|
||||
node1, t1 := tree1Nodes[name]
|
||||
node2, t2 := tree2Nodes[name]
|
||||
|
||||
addBlobs(stats.BlobsBefore, node1)
|
||||
addBlobs(stats.BlobsAfter, node2)
|
||||
|
||||
switch {
|
||||
case t1 && t2:
|
||||
name := path.Join(prefix, name)
|
||||
mod := ""
|
||||
|
||||
if node1.Type != node2.Type {
|
||||
mod += "T"
|
||||
}
|
||||
|
||||
if node2.Type == "dir" {
|
||||
name += "/"
|
||||
}
|
||||
|
||||
if node1.Type == "file" &&
|
||||
node2.Type == "file" &&
|
||||
!reflect.DeepEqual(node1.Content, node2.Content) {
|
||||
mod += "M"
|
||||
stats.ChangedFiles++
|
||||
} else if c.opts.ShowMetadata && !node1.Equals(*node2) {
|
||||
mod += "U"
|
||||
}
|
||||
|
||||
if mod != "" {
|
||||
Printf("%-5s%v\n", mod, name)
|
||||
}
|
||||
|
||||
if node1.Type == "dir" && node2.Type == "dir" {
|
||||
err := c.diffTree(ctx, stats, name, *node1.Subtree, *node2.Subtree)
|
||||
if err != nil {
|
||||
Warnf("error: %v\n", err)
|
||||
}
|
||||
}
|
||||
case t1 && !t2:
|
||||
prefix := path.Join(prefix, name)
|
||||
if node1.Type == "dir" {
|
||||
prefix += "/"
|
||||
}
|
||||
Printf("%-5s%v\n", "-", prefix)
|
||||
stats.Removed.Add(node1)
|
||||
|
||||
if node1.Type == "dir" {
|
||||
err := c.printDir(ctx, "-", &stats.Removed, stats.BlobsBefore, prefix, *node1.Subtree)
|
||||
if err != nil {
|
||||
Warnf("error: %v\n", err)
|
||||
}
|
||||
}
|
||||
case !t1 && t2:
|
||||
prefix := path.Join(prefix, name)
|
||||
if node2.Type == "dir" {
|
||||
prefix += "/"
|
||||
}
|
||||
Printf("%-5s%v\n", "+", prefix)
|
||||
stats.Added.Add(node2)
|
||||
|
||||
if node2.Type == "dir" {
|
||||
err := c.printDir(ctx, "+", &stats.Added, stats.BlobsAfter, prefix, *node2.Subtree)
|
||||
if err != nil {
|
||||
Warnf("error: %v\n", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func runDiff(opts DiffOptions, gopts GlobalOptions, args []string) error {
|
||||
if len(args) != 2 {
|
||||
return errors.Fatalf("specify two snapshot IDs")
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithCancel(gopts.ctx)
|
||||
defer cancel()
|
||||
|
||||
repo, err := OpenRepository(gopts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = repo.LoadIndex(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !gopts.NoLock {
|
||||
lock, err := lockRepo(repo)
|
||||
defer unlockRepo(lock)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
sn1, err := loadSnapshot(ctx, repo, args[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sn2, err := loadSnapshot(ctx, repo, args[1])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
Verbosef("comparing snapshot %v to %v:\n\n", sn1.ID().Str(), sn2.ID().Str())
|
||||
|
||||
if sn1.Tree == nil {
|
||||
return errors.Errorf("snapshot %v has nil tree", sn1.ID().Str())
|
||||
}
|
||||
|
||||
if sn2.Tree == nil {
|
||||
return errors.Errorf("snapshot %v has nil tree", sn2.ID().Str())
|
||||
}
|
||||
|
||||
c := &Comparer{
|
||||
repo: repo,
|
||||
opts: diffOptions,
|
||||
}
|
||||
|
||||
stats := NewDiffStats()
|
||||
|
||||
err = c.diffTree(ctx, stats, "/", *sn1.Tree, *sn2.Tree)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
both := stats.BlobsBefore.Intersect(stats.BlobsAfter)
|
||||
updateBlobs(repo, stats.BlobsBefore.Sub(both), &stats.Removed)
|
||||
updateBlobs(repo, stats.BlobsAfter.Sub(both), &stats.Added)
|
||||
|
||||
Printf("\n")
|
||||
Printf("Files: %5d new, %5d removed, %5d changed\n", stats.Added.Files, stats.Removed.Files, stats.ChangedFiles)
|
||||
Printf("Dirs: %5d new, %5d removed\n", stats.Added.Dirs, stats.Removed.Dirs)
|
||||
Printf("Others: %5d new, %5d removed\n", stats.Added.Others, stats.Removed.Others)
|
||||
Printf("Data Blobs: %5d new, %5d removed\n", stats.Added.DataBlobs, stats.Removed.DataBlobs)
|
||||
Printf("Tree Blobs: %5d new, %5d removed\n", stats.Added.TreeBlobs, stats.Removed.TreeBlobs)
|
||||
Printf(" Added: %-5s\n", formatBytes(uint64(stats.Added.Bytes)))
|
||||
Printf(" Removed: %-5s\n", formatBytes(uint64(stats.Removed.Bytes)))
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -1,168 +1,134 @@
|
||||
// xbuild debug
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/restic/restic/internal/debug"
|
||||
"github.com/restic/restic/internal/errors"
|
||||
"github.com/restic/restic/internal/pack"
|
||||
"github.com/restic/restic/internal/repository"
|
||||
"github.com/restic/restic/internal/restic"
|
||||
|
||||
"github.com/restic/restic/internal/worker"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var cmdDump = &cobra.Command{
|
||||
Use: "dump [indexes|snapshots|trees|all|packs]",
|
||||
Short: "Dump data structures",
|
||||
Use: "dump [flags] snapshotID file",
|
||||
Short: "Print a backed-up file to stdout",
|
||||
Long: `
|
||||
The "dump" command dumps data structures from the repository as JSON objects. It
|
||||
is used for debugging purposes only.`,
|
||||
The "dump" command extracts a single file from a snapshot from the repository and
|
||||
prints its contents to stdout.
|
||||
|
||||
The special snapshot "latest" can be used to use the latest snapshot in the
|
||||
repository.
|
||||
`,
|
||||
DisableAutoGenTag: true,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return runDump(globalOptions, args)
|
||||
return runDump(dumpOptions, globalOptions, args)
|
||||
},
|
||||
}
|
||||
|
||||
// DumpOptions collects all options for the dump command.
|
||||
type DumpOptions struct {
|
||||
Host string
|
||||
Paths []string
|
||||
Tags restic.TagLists
|
||||
}
|
||||
|
||||
var dumpOptions DumpOptions
|
||||
|
||||
func init() {
|
||||
cmdRoot.AddCommand(cmdDump)
|
||||
|
||||
flags := cmdDump.Flags()
|
||||
flags.StringVarP(&dumpOptions.Host, "host", "H", "", `only consider snapshots for this host when the snapshot ID is "latest"`)
|
||||
flags.Var(&dumpOptions.Tags, "tag", "only consider snapshots which include this `taglist` for snapshot ID \"latest\"")
|
||||
flags.StringArrayVar(&dumpOptions.Paths, "path", nil, "only consider snapshots which include this (absolute) `path` for snapshot ID \"latest\"")
|
||||
}
|
||||
|
||||
func prettyPrintJSON(wr io.Writer, item interface{}) error {
|
||||
buf, err := json.MarshalIndent(item, "", " ")
|
||||
if err != nil {
|
||||
return err
|
||||
func splitPath(path string) []string {
|
||||
d, f := filepath.Split(path)
|
||||
if d == "" || d == "/" {
|
||||
return []string{f}
|
||||
}
|
||||
|
||||
_, err = wr.Write(append(buf, '\n'))
|
||||
return err
|
||||
s := splitPath(filepath.Clean(d))
|
||||
return append(s, f)
|
||||
}
|
||||
|
||||
func debugPrintSnapshots(repo *repository.Repository, wr io.Writer) error {
|
||||
for id := range repo.List(context.TODO(), restic.SnapshotFile) {
|
||||
snapshot, err := restic.LoadSnapshot(context.TODO(), repo, id)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "LoadSnapshot(%v): %v", id.Str(), err)
|
||||
continue
|
||||
}
|
||||
|
||||
fmt.Fprintf(wr, "snapshot_id: %v\n", id)
|
||||
|
||||
err = prettyPrintJSON(wr, snapshot)
|
||||
func dumpNode(ctx context.Context, repo restic.Repository, node *restic.Node) error {
|
||||
var buf []byte
|
||||
for _, id := range node.Content {
|
||||
size, err := repo.LookupBlobSize(id, restic.DataBlob)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
const dumpPackWorkers = 10
|
||||
|
||||
// Pack is the struct used in printPacks.
|
||||
type Pack struct {
|
||||
Name string `json:"name"`
|
||||
|
||||
Blobs []Blob `json:"blobs"`
|
||||
}
|
||||
|
||||
// Blob is the struct used in printPacks.
|
||||
type Blob struct {
|
||||
Type restic.BlobType `json:"type"`
|
||||
Length uint `json:"length"`
|
||||
ID restic.ID `json:"id"`
|
||||
Offset uint `json:"offset"`
|
||||
}
|
||||
|
||||
func printPacks(repo *repository.Repository, wr io.Writer) error {
|
||||
f := func(ctx context.Context, job worker.Job) (interface{}, error) {
|
||||
name := job.Data.(string)
|
||||
|
||||
h := restic.Handle{Type: restic.DataFile, Name: name}
|
||||
|
||||
blobInfo, err := repo.Backend().Stat(ctx, h)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
blobs, err := pack.List(repo.Key(), restic.ReaderAt(repo.Backend(), h), blobInfo.Size)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return blobs, 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")
|
||||
}
|
||||
|
||||
jobCh := make(chan worker.Job)
|
||||
resCh := make(chan worker.Job)
|
||||
wp := worker.New(context.TODO(), dumpPackWorkers, f, jobCh, resCh)
|
||||
|
||||
go func() {
|
||||
for name := range repo.Backend().List(context.TODO(), restic.DataFile) {
|
||||
jobCh <- worker.Job{Data: name}
|
||||
}
|
||||
close(jobCh)
|
||||
}()
|
||||
|
||||
for job := range resCh {
|
||||
name := job.Data.(string)
|
||||
|
||||
if job.Error != nil {
|
||||
fmt.Fprintf(os.Stderr, "error for pack %v: %v\n", name, job.Error)
|
||||
continue
|
||||
}
|
||||
|
||||
entries := job.Result.([]restic.Blob)
|
||||
p := Pack{
|
||||
Name: name,
|
||||
Blobs: make([]Blob, len(entries)),
|
||||
}
|
||||
for i, blob := range entries {
|
||||
p.Blobs[i] = Blob{
|
||||
Type: blob.Type,
|
||||
Length: blob.Length,
|
||||
ID: blob.ID,
|
||||
Offset: blob.Offset,
|
||||
if repo == nil {
|
||||
return fmt.Errorf("called with a nil repository")
|
||||
}
|
||||
l := len(pathComponents)
|
||||
if l == 0 {
|
||||
return fmt.Errorf("empty path components")
|
||||
}
|
||||
item := filepath.Join(prefix, pathComponents[0])
|
||||
for _, node := range tree.Nodes {
|
||||
if node.Name == pathComponents[0] {
|
||||
switch {
|
||||
case l == 1 && node.Type == "file":
|
||||
return dumpNode(ctx, 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:])
|
||||
case l > 1:
|
||||
return fmt.Errorf("%q should be a dir, but s a %q", item, node.Type)
|
||||
case node.Type != "file":
|
||||
return fmt.Errorf("%q should be a file, but is a %q", item, node.Type)
|
||||
}
|
||||
}
|
||||
|
||||
prettyPrintJSON(os.Stdout, p)
|
||||
}
|
||||
|
||||
wp.Wait()
|
||||
|
||||
return nil
|
||||
return fmt.Errorf("path %q not found in snapshot", item)
|
||||
}
|
||||
|
||||
func dumpIndexes(repo restic.Repository) error {
|
||||
for id := range repo.List(context.TODO(), restic.IndexFile) {
|
||||
fmt.Printf("index_id: %v\n", id)
|
||||
func runDump(opts DumpOptions, gopts GlobalOptions, args []string) error {
|
||||
ctx := gopts.ctx
|
||||
|
||||
idx, err := repository.LoadIndex(context.TODO(), repo, id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = idx.Dump(os.Stdout)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(args) != 2 {
|
||||
return errors.Fatal("no file and no snapshot ID specified")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
snapshotIDString := args[0]
|
||||
pathToPrint := args[1]
|
||||
|
||||
func runDump(gopts GlobalOptions, args []string) error {
|
||||
if len(args) != 1 {
|
||||
return errors.Fatal("type not specified")
|
||||
}
|
||||
debug.Log("dump file %q from %q", pathToPrint, snapshotIDString)
|
||||
|
||||
splittedPath := splitPath(pathToPrint)
|
||||
|
||||
repo, err := OpenRepository(gopts)
|
||||
if err != nil {
|
||||
@@ -177,35 +143,39 @@ func runDump(gopts GlobalOptions, args []string) error {
|
||||
}
|
||||
}
|
||||
|
||||
err = repo.LoadIndex(context.TODO())
|
||||
err = repo.LoadIndex(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tpe := args[0]
|
||||
var id restic.ID
|
||||
|
||||
switch tpe {
|
||||
case "indexes":
|
||||
return dumpIndexes(repo)
|
||||
case "snapshots":
|
||||
return debugPrintSnapshots(repo, os.Stdout)
|
||||
case "packs":
|
||||
return printPacks(repo, os.Stdout)
|
||||
case "all":
|
||||
fmt.Printf("snapshots:\n")
|
||||
err := debugPrintSnapshots(repo, os.Stdout)
|
||||
if snapshotIDString == "latest" {
|
||||
id, err = restic.FindLatestSnapshot(ctx, repo, opts.Paths, opts.Tags, opts.Host)
|
||||
if err != nil {
|
||||
return err
|
||||
Exitf(1, "latest snapshot for criteria not found: %v Paths:%v Host:%v", err, opts.Paths, opts.Host)
|
||||
}
|
||||
|
||||
fmt.Printf("\nindexes:\n")
|
||||
err = dumpIndexes(repo)
|
||||
} else {
|
||||
id, err = restic.FindSnapshot(repo, snapshotIDString)
|
||||
if err != nil {
|
||||
return err
|
||||
Exitf(1, "invalid id %q: %v", snapshotIDString, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
default:
|
||||
return errors.Fatalf("no such type %q", tpe)
|
||||
}
|
||||
|
||||
sn, err := restic.LoadSnapshot(gopts.ctx, repo, id)
|
||||
if err != nil {
|
||||
Exitf(2, "loading snapshot %q failed: %v", snapshotIDString, err)
|
||||
}
|
||||
|
||||
tree, err := repo.LoadTree(ctx, *sn.Tree)
|
||||
if err != nil {
|
||||
Exitf(2, "loading tree for snapshot %q failed: %v", snapshotIDString, err)
|
||||
}
|
||||
|
||||
err = printFromTree(ctx, tree, repo, "", splittedPath)
|
||||
if err != nil {
|
||||
Exitf(2, "cannot dump file: %v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -180,7 +180,7 @@ type Finder struct {
|
||||
notfound restic.IDSet
|
||||
}
|
||||
|
||||
func (f *Finder) findInTree(treeID restic.ID, prefix string) error {
|
||||
func (f *Finder) findInTree(ctx context.Context, treeID restic.ID, prefix string) error {
|
||||
if f.notfound.Has(treeID) {
|
||||
debug.Log("%v skipping tree %v, has already been checked", prefix, treeID.Str())
|
||||
return nil
|
||||
@@ -188,7 +188,7 @@ func (f *Finder) findInTree(treeID restic.ID, prefix string) error {
|
||||
|
||||
debug.Log("%v checking tree %v\n", prefix, treeID.Str())
|
||||
|
||||
tree, err := f.repo.LoadTree(context.TODO(), treeID)
|
||||
tree, err := f.repo.LoadTree(ctx, treeID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -224,7 +224,7 @@ func (f *Finder) findInTree(treeID restic.ID, prefix string) error {
|
||||
}
|
||||
|
||||
if node.Type == "dir" {
|
||||
if err := f.findInTree(*node.Subtree, filepath.Join(prefix, node.Name)); err != nil {
|
||||
if err := f.findInTree(ctx, *node.Subtree, filepath.Join(prefix, node.Name)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -237,14 +237,11 @@ func (f *Finder) findInTree(treeID restic.ID, prefix string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *Finder) findInSnapshot(sn *restic.Snapshot) error {
|
||||
func (f *Finder) findInSnapshot(ctx context.Context, sn *restic.Snapshot) error {
|
||||
debug.Log("searching in snapshot %s\n for entries within [%s %s]", sn.ID(), f.pat.oldest, f.pat.newest)
|
||||
|
||||
f.out.newsn = sn
|
||||
if err := f.findInTree(*sn.Tree, string(filepath.Separator)); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
return f.findInTree(ctx, *sn.Tree, string(filepath.Separator))
|
||||
}
|
||||
|
||||
func runFind(opts FindOptions, gopts GlobalOptions, args []string) error {
|
||||
@@ -284,7 +281,7 @@ func runFind(opts FindOptions, gopts GlobalOptions, args []string) error {
|
||||
}
|
||||
}
|
||||
|
||||
if err = repo.LoadIndex(context.TODO()); err != nil {
|
||||
if err = repo.LoadIndex(gopts.ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -298,7 +295,7 @@ func runFind(opts FindOptions, gopts GlobalOptions, args []string) error {
|
||||
notfound: restic.NewIDSet(),
|
||||
}
|
||||
for sn := range FindFilteredSnapshots(ctx, repo, opts.Host, opts.Tags, opts.Paths, opts.Snapshots) {
|
||||
if err = f.findInSnapshot(sn); err != nil {
|
||||
if err = f.findInSnapshot(ctx, sn); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,9 +35,10 @@ type ForgetOptions struct {
|
||||
Yearly int
|
||||
KeepTags restic.TagLists
|
||||
|
||||
Host string
|
||||
Tags restic.TagLists
|
||||
Paths []string
|
||||
Host string
|
||||
Tags restic.TagLists
|
||||
Paths []string
|
||||
Compact bool
|
||||
|
||||
// Grouping
|
||||
GroupBy string
|
||||
@@ -65,6 +66,7 @@ func init() {
|
||||
f.StringVar(&forgetOptions.Host, "hostname", "", "only consider snapshots with the given `hostname` (deprecated)")
|
||||
f.Var(&forgetOptions.Tags, "tag", "only consider snapshots which include this `taglist` in the format `tag[,tag,...]` (can be specified multiple times)")
|
||||
f.StringArrayVar(&forgetOptions.Paths, "path", nil, "only consider snapshots which include this (absolute) `path` (can be specified multiple times)")
|
||||
f.BoolVarP(&forgetOptions.Compact, "compact", "c", false, "use compact format")
|
||||
|
||||
f.StringVarP(&forgetOptions.GroupBy, "group-by", "g", "host,paths", "string for grouping snapshots by host,paths,tags")
|
||||
f.BoolVarP(&forgetOptions.DryRun, "dry-run", "n", false, "do not delete anything, just print what would be done")
|
||||
@@ -114,6 +116,8 @@ func runForget(opts ForgetOptions, gopts GlobalOptions, args []string) error {
|
||||
}
|
||||
}
|
||||
|
||||
removeSnapshots := 0
|
||||
|
||||
ctx, cancel := context.WithCancel(gopts.ctx)
|
||||
defer cancel()
|
||||
for sn := range FindFilteredSnapshots(ctx, repo, opts.Host, opts.Tags, opts.Paths, args) {
|
||||
@@ -121,15 +125,16 @@ func runForget(opts ForgetOptions, gopts GlobalOptions, args []string) error {
|
||||
// When explicit snapshots args are given, remove them immediately.
|
||||
if !opts.DryRun {
|
||||
h := restic.Handle{Type: restic.SnapshotFile, Name: sn.ID().String()}
|
||||
if err = repo.Backend().Remove(context.TODO(), h); err != nil {
|
||||
if err = repo.Backend().Remove(gopts.ctx, h); err != nil {
|
||||
return err
|
||||
}
|
||||
Verbosef("removed snapshot %v\n", sn.ID().Str())
|
||||
removeSnapshots++
|
||||
} else {
|
||||
Verbosef("would have removed snapshot %v\n", sn.ID().Str())
|
||||
}
|
||||
} else {
|
||||
// Determing grouping-keys
|
||||
// Determining grouping-keys
|
||||
var tags []string
|
||||
var hostname string
|
||||
var paths []string
|
||||
@@ -176,7 +181,6 @@ func runForget(opts ForgetOptions, gopts GlobalOptions, args []string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
removeSnapshots := 0
|
||||
for k, snapshotGroup := range snapshotGroups {
|
||||
var key key
|
||||
if json.Unmarshal([]byte(k), &key) != nil {
|
||||
@@ -204,13 +208,13 @@ func runForget(opts ForgetOptions, gopts GlobalOptions, args []string) error {
|
||||
|
||||
if len(keep) != 0 && !gopts.Quiet {
|
||||
Printf("keep %d snapshots:\n", len(keep))
|
||||
PrintSnapshots(globalOptions.stdout, keep, false)
|
||||
PrintSnapshots(globalOptions.stdout, keep, opts.Compact)
|
||||
Printf("\n")
|
||||
}
|
||||
|
||||
if len(remove) != 0 && !gopts.Quiet {
|
||||
Printf("remove %d snapshots:\n", len(remove))
|
||||
PrintSnapshots(globalOptions.stdout, remove, false)
|
||||
PrintSnapshots(globalOptions.stdout, remove, opts.Compact)
|
||||
Printf("\n")
|
||||
}
|
||||
|
||||
@@ -219,7 +223,7 @@ func runForget(opts ForgetOptions, gopts GlobalOptions, args []string) error {
|
||||
if !opts.DryRun {
|
||||
for _, sn := range remove {
|
||||
h := restic.Handle{Type: restic.SnapshotFile, Name: sn.ID().String()}
|
||||
err = repo.Backend().Remove(context.TODO(), h)
|
||||
err = repo.Backend().Remove(gopts.ctx, h)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
94
cmd/restic/cmd_generate.go
Normal file
94
cmd/restic/cmd_generate.go
Normal file
@@ -0,0 +1,94 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/restic/restic/internal/errors"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/cobra/doc"
|
||||
)
|
||||
|
||||
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
|
||||
and the auto-completion files for bash and zsh).
|
||||
`,
|
||||
DisableAutoGenTag: true,
|
||||
RunE: runGenerate,
|
||||
}
|
||||
|
||||
type generateOptions struct {
|
||||
ManDir string
|
||||
BashCompletionFile string
|
||||
ZSHCompletionFile string
|
||||
}
|
||||
|
||||
var genOpts generateOptions
|
||||
|
||||
func init() {
|
||||
cmdRoot.AddCommand(cmdGenerate)
|
||||
fs := cmdGenerate.Flags()
|
||||
fs.StringVar(&genOpts.ManDir, "man", "", "write man pages to `directory`")
|
||||
fs.StringVar(&genOpts.BashCompletionFile, "bash-completion", "", "write bash completion `file`")
|
||||
fs.StringVar(&genOpts.ZSHCompletionFile, "zsh-completion", "", "write zsh completion `file`")
|
||||
}
|
||||
|
||||
func writeManpages(dir string) error {
|
||||
// use a fixed date for the man pages so that generating them is deterministic
|
||||
date, err := time.Parse("Jan 2006", "Jan 2017")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
header := &doc.GenManHeader{
|
||||
Title: "restic backup",
|
||||
Section: "1",
|
||||
Source: "generated by `restic generate`",
|
||||
Date: &date,
|
||||
}
|
||||
|
||||
Verbosef("writing man pages to directory %v\n", dir)
|
||||
return doc.GenManTree(cmdRoot, header, dir)
|
||||
}
|
||||
|
||||
func writeBashCompletion(file string) error {
|
||||
Verbosef("writing bash completion file to %v\n", file)
|
||||
return cmdRoot.GenBashCompletionFile(file)
|
||||
}
|
||||
|
||||
func writeZSHCompletion(file string) error {
|
||||
Verbosef("writing zsh completion file to %v\n", file)
|
||||
return cmdRoot.GenZshCompletionFile(file)
|
||||
}
|
||||
|
||||
func runGenerate(cmd *cobra.Command, args []string) error {
|
||||
if genOpts.ManDir != "" {
|
||||
err := writeManpages(genOpts.ManDir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if genOpts.BashCompletionFile != "" {
|
||||
err := writeBashCompletion(genOpts.BashCompletionFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if genOpts.ZSHCompletionFile != "" {
|
||||
err := writeZSHCompletion(genOpts.ZSHCompletionFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
var empty generateOptions
|
||||
if genOpts == empty {
|
||||
return errors.Fatal("nothing to do, please specify at least one output file/dir")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -1,8 +1,6 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/restic/restic/internal/errors"
|
||||
"github.com/restic/restic/internal/repository"
|
||||
|
||||
@@ -44,7 +42,7 @@ func runInit(gopts GlobalOptions, args []string) error {
|
||||
|
||||
s := repository.New(be)
|
||||
|
||||
err = s.Init(context.TODO(), gopts.password)
|
||||
err = s.Init(gopts.ctx, gopts.password)
|
||||
if err != nil {
|
||||
return errors.Fatalf("create key in backend at %s failed: %v\n", gopts.Repo, err)
|
||||
}
|
||||
|
||||
@@ -76,7 +76,7 @@ func addKey(gopts GlobalOptions, repo *repository.Repository) error {
|
||||
return err
|
||||
}
|
||||
|
||||
id, err := repository.AddKey(context.TODO(), repo, pw, repo.Key())
|
||||
id, err := repository.AddKey(gopts.ctx, repo, pw, repo.Key())
|
||||
if err != nil {
|
||||
return errors.Fatalf("creating new key failed: %v\n", err)
|
||||
}
|
||||
@@ -86,13 +86,13 @@ func addKey(gopts GlobalOptions, repo *repository.Repository) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func deleteKey(repo *repository.Repository, name string) error {
|
||||
func deleteKey(ctx context.Context, repo *repository.Repository, name string) error {
|
||||
if name == repo.KeyName() {
|
||||
return errors.Fatal("refusing to remove key currently used to access repository")
|
||||
}
|
||||
|
||||
h := restic.Handle{Type: restic.KeyFile, Name: name}
|
||||
err := repo.Backend().Remove(context.TODO(), h)
|
||||
err := repo.Backend().Remove(ctx, h)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -107,13 +107,13 @@ func changePassword(gopts GlobalOptions, repo *repository.Repository) error {
|
||||
return err
|
||||
}
|
||||
|
||||
id, err := repository.AddKey(context.TODO(), repo, pw, repo.Key())
|
||||
id, err := repository.AddKey(gopts.ctx, repo, pw, repo.Key())
|
||||
if err != nil {
|
||||
return errors.Fatalf("creating new key failed: %v\n", err)
|
||||
}
|
||||
|
||||
h := restic.Handle{Type: restic.KeyFile, Name: repo.KeyName()}
|
||||
err = repo.Backend().Remove(context.TODO(), h)
|
||||
err = repo.Backend().Remove(gopts.ctx, h)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -165,7 +165,7 @@ func runKey(gopts GlobalOptions, args []string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
return deleteKey(repo, id)
|
||||
return deleteKey(gopts.ctx, repo, id)
|
||||
case "passwd":
|
||||
lock, err := lockRepoExclusive(repo)
|
||||
defer unlockRepo(lock)
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/restic/restic/internal/errors"
|
||||
@@ -58,7 +57,7 @@ func runList(opts GlobalOptions, args []string) error {
|
||||
case "locks":
|
||||
t = restic.LockFile
|
||||
case "blobs":
|
||||
idx, err := index.Load(context.TODO(), repo, nil)
|
||||
idx, err := index.Load(opts.ctx, repo, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -74,7 +73,7 @@ func runList(opts GlobalOptions, args []string) error {
|
||||
return errors.Fatal("invalid type")
|
||||
}
|
||||
|
||||
for id := range repo.List(context.TODO(), t) {
|
||||
for id := range repo.List(opts.ctx, t) {
|
||||
Printf("%s\n", id)
|
||||
}
|
||||
|
||||
|
||||
@@ -46,8 +46,8 @@ func init() {
|
||||
flags.StringArrayVar(&lsOptions.Paths, "path", nil, "only consider snapshots which include this (absolute) `path`, when no snapshot ID is given")
|
||||
}
|
||||
|
||||
func printTree(repo *repository.Repository, id *restic.ID, prefix string) error {
|
||||
tree, err := repo.LoadTree(context.TODO(), *id)
|
||||
func printTree(ctx context.Context, repo *repository.Repository, id *restic.ID, prefix string) error {
|
||||
tree, err := repo.LoadTree(ctx, *id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -56,7 +56,7 @@ func printTree(repo *repository.Repository, id *restic.ID, prefix string) error
|
||||
Printf("%s\n", formatNode(prefix, entry, lsOptions.ListLong))
|
||||
|
||||
if entry.Type == "dir" && entry.Subtree != nil {
|
||||
if err = printTree(repo, entry.Subtree, filepath.Join(prefix, entry.Name)); err != nil {
|
||||
if err = printTree(ctx, repo, entry.Subtree, filepath.Join(prefix, entry.Name)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -75,7 +75,7 @@ func runLs(opts LsOptions, gopts GlobalOptions, args []string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = repo.LoadIndex(context.TODO()); err != nil {
|
||||
if err = repo.LoadIndex(gopts.ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -84,7 +84,7 @@ func runLs(opts LsOptions, gopts GlobalOptions, args []string) error {
|
||||
for sn := range FindFilteredSnapshots(ctx, repo, opts.Host, opts.Tags, opts.Paths, args) {
|
||||
Verbosef("snapshot %s of %v at %s):\n", sn.ID().Str(), sn.Paths, sn.Time)
|
||||
|
||||
if err = printTree(repo, sn.Tree, string(filepath.Separator)); err != nil {
|
||||
if err = printTree(gopts.ctx, repo, sn.Tree, string(filepath.Separator)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,70 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/restic/restic/internal/errors"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/cobra/doc"
|
||||
)
|
||||
|
||||
var cmdManpage = &cobra.Command{
|
||||
Use: "manpage [command]",
|
||||
Short: "Generate manual pages",
|
||||
Long: `
|
||||
The "manpage" command generates a manual page for a single command. It can also
|
||||
be used to write all manual pages to a directory. If the output directory is
|
||||
set and no command is specified, all manpages are written to the directory.
|
||||
`,
|
||||
DisableAutoGenTag: true,
|
||||
RunE: runManpage,
|
||||
}
|
||||
|
||||
var manpageOpts = struct {
|
||||
OutputDir string
|
||||
}{}
|
||||
|
||||
func init() {
|
||||
cmdRoot.AddCommand(cmdManpage)
|
||||
fs := cmdManpage.Flags()
|
||||
fs.StringVar(&manpageOpts.OutputDir, "output-dir", "", "write man pages to this `directory`")
|
||||
}
|
||||
|
||||
func runManpage(cmd *cobra.Command, args []string) error {
|
||||
// use a fixed date for the man pages so that generating them is deterministic
|
||||
date, err := time.Parse("Jan 2006", "Jan 2017")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
header := &doc.GenManHeader{
|
||||
Title: "restic backup",
|
||||
Section: "1",
|
||||
Source: "generated by `restic manpage`",
|
||||
Date: &date,
|
||||
}
|
||||
|
||||
dir := manpageOpts.OutputDir
|
||||
if dir != "" {
|
||||
Verbosef("writing man pages to directory %v\n", dir)
|
||||
return doc.GenManTree(cmdRoot, header, dir)
|
||||
}
|
||||
|
||||
switch {
|
||||
case len(args) == 0:
|
||||
return errors.Fatalf("no command given")
|
||||
case len(args) > 1:
|
||||
return errors.Fatalf("more than one command given: %v", args)
|
||||
}
|
||||
|
||||
name := args[0]
|
||||
|
||||
for _, cmd := range cmdRoot.Commands() {
|
||||
if cmd.Name() == name {
|
||||
return doc.GenMan(cmd, header, os.Stdout)
|
||||
}
|
||||
}
|
||||
|
||||
return errors.Fatalf("command %q is not known", args)
|
||||
}
|
||||
@@ -4,7 +4,6 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
@@ -67,7 +66,13 @@ func mount(opts MountOptions, gopts GlobalOptions, mountpoint string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
err = repo.LoadIndex(context.TODO())
|
||||
lock, err := lockRepo(repo)
|
||||
defer unlockRepo(lock)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = repo.LoadIndex(gopts.ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -108,7 +113,7 @@ func mount(opts MountOptions, gopts GlobalOptions, mountpoint string) error {
|
||||
Tags: opts.Tags,
|
||||
Paths: opts.Paths,
|
||||
}
|
||||
root, err := fuse.NewRoot(context.TODO(), repo, cfg)
|
||||
root, err := fuse.NewRoot(gopts.ctx, repo, cfg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -85,6 +85,25 @@ func runPrune(gopts GlobalOptions) error {
|
||||
return pruneRepository(gopts, repo)
|
||||
}
|
||||
|
||||
func mixedBlobs(list []restic.Blob) bool {
|
||||
var tree, data bool
|
||||
|
||||
for _, pb := range list {
|
||||
switch pb.Type {
|
||||
case restic.TreeBlob:
|
||||
tree = true
|
||||
case restic.DataBlob:
|
||||
data = true
|
||||
}
|
||||
|
||||
if tree && data {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func pruneRepository(gopts GlobalOptions, repo restic.Repository) error {
|
||||
ctx := gopts.ctx
|
||||
|
||||
@@ -122,7 +141,7 @@ func pruneRepository(gopts GlobalOptions, repo restic.Repository) error {
|
||||
stats.bytes += pack.Size
|
||||
blobs += len(pack.Entries)
|
||||
}
|
||||
Verbosef("repository contains %v packs (%v blobs) with %v bytes\n",
|
||||
Verbosef("repository contains %v packs (%v blobs) with %v\n",
|
||||
len(idx.Packs), blobs, formatBytes(uint64(stats.bytes)))
|
||||
|
||||
blobCount := make(map[restic.BlobHandle]int)
|
||||
@@ -191,6 +210,11 @@ func pruneRepository(gopts GlobalOptions, repo restic.Repository) error {
|
||||
// find packs that need a rewrite
|
||||
rewritePacks := restic.NewIDSet()
|
||||
for _, pack := range idx.Packs {
|
||||
if mixedBlobs(pack.Entries) {
|
||||
rewritePacks.Insert(pack.ID)
|
||||
continue
|
||||
}
|
||||
|
||||
for _, blob := range pack.Entries {
|
||||
h := restic.BlobHandle{ID: blob.ID, Type: blob.Type}
|
||||
if !usedBlobs.Has(h) {
|
||||
|
||||
@@ -3,6 +3,7 @@ package main
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/restic/restic/internal/errors"
|
||||
"github.com/restic/restic/internal/index"
|
||||
"github.com/restic/restic/internal/restic"
|
||||
|
||||
@@ -66,7 +67,7 @@ func rebuildIndex(ctx context.Context, repo restic.Repository, ignorePacks resti
|
||||
|
||||
id, err := idx.Save(ctx, repo, supersedes)
|
||||
if err != nil {
|
||||
return err
|
||||
return errors.Fatalf("unable to save index, last error was: %v", err)
|
||||
}
|
||||
|
||||
Verbosef("saved new index as %v\n", id.Str())
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/restic/restic/internal/restic"
|
||||
"github.com/spf13/cobra"
|
||||
@@ -29,6 +30,7 @@ type SnapshotOptions struct {
|
||||
Tags restic.TagLists
|
||||
Paths []string
|
||||
Compact bool
|
||||
Last bool
|
||||
}
|
||||
|
||||
var snapshotOptions SnapshotOptions
|
||||
@@ -41,6 +43,7 @@ func init() {
|
||||
f.Var(&snapshotOptions.Tags, "tag", "only consider snapshots which include this `taglist` (can be specified multiple times)")
|
||||
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")
|
||||
}
|
||||
|
||||
func runSnapshots(opts SnapshotOptions, gopts GlobalOptions, args []string) error {
|
||||
@@ -64,6 +67,11 @@ func runSnapshots(opts SnapshotOptions, gopts GlobalOptions, args []string) erro
|
||||
for sn := range FindFilteredSnapshots(ctx, repo, opts.Host, opts.Tags, opts.Paths, args) {
|
||||
list = append(list, sn)
|
||||
}
|
||||
|
||||
if opts.Last {
|
||||
list = FilterLastSnapshots(list)
|
||||
}
|
||||
|
||||
sort.Sort(sort.Reverse(list))
|
||||
|
||||
if gopts.JSON {
|
||||
@@ -78,9 +86,50 @@ func runSnapshots(opts SnapshotOptions, gopts GlobalOptions, args []string) erro
|
||||
return nil
|
||||
}
|
||||
|
||||
// filterLastSnapshotsKey is used by FilterLastSnapshots.
|
||||
type filterLastSnapshotsKey struct {
|
||||
Hostname string
|
||||
JoinedPaths string
|
||||
}
|
||||
|
||||
// newFilterLastSnapshotsKey initializes a filterLastSnapshotsKey from a Snapshot
|
||||
func newFilterLastSnapshotsKey(sn *restic.Snapshot) filterLastSnapshotsKey {
|
||||
// Shallow slice copy
|
||||
var paths = make([]string, len(sn.Paths))
|
||||
copy(paths, sn.Paths)
|
||||
sort.Strings(paths)
|
||||
return filterLastSnapshotsKey{sn.Hostname, strings.Join(paths, "|")}
|
||||
}
|
||||
|
||||
// FilterLastSnapshots filters a list of snapshots to only return the last
|
||||
// entry for each hostname and path. If the snapshot contains multiple paths,
|
||||
// they will be joined and treated as one item.
|
||||
func FilterLastSnapshots(list restic.Snapshots) restic.Snapshots {
|
||||
// Sort the snapshots so that the newer ones are listed first
|
||||
sort.SliceStable(list, func(i, j int) bool {
|
||||
return list[i].Time.After(list[j].Time)
|
||||
})
|
||||
|
||||
var results restic.Snapshots
|
||||
seen := make(map[filterLastSnapshotsKey]bool)
|
||||
for _, sn := range list {
|
||||
key := newFilterLastSnapshotsKey(sn)
|
||||
if !seen[key] {
|
||||
seen[key] = true
|
||||
results = append(results, sn)
|
||||
}
|
||||
}
|
||||
return results
|
||||
}
|
||||
|
||||
// PrintSnapshots prints a text table of the snapshots in list to stdout.
|
||||
func PrintSnapshots(stdout io.Writer, list restic.Snapshots, compact bool) {
|
||||
|
||||
// always sort the snapshots so that the newer ones are listed last
|
||||
sort.SliceStable(list, func(i, j int) bool {
|
||||
return list[i].Time.Before(list[j].Time)
|
||||
})
|
||||
|
||||
// Determine the max widths for host and tag.
|
||||
maxHost, maxTag := 10, 6
|
||||
for _, sn := range list {
|
||||
@@ -158,6 +207,8 @@ func PrintSnapshots(stdout io.Writer, list restic.Snapshots, compact bool) {
|
||||
}
|
||||
}
|
||||
|
||||
tab.Footer = fmt.Sprintf("%d snapshots", len(list))
|
||||
|
||||
tab.Write(stdout)
|
||||
}
|
||||
|
||||
@@ -165,7 +216,8 @@ func PrintSnapshots(stdout io.Writer, list restic.Snapshots, compact bool) {
|
||||
type Snapshot struct {
|
||||
*restic.Snapshot
|
||||
|
||||
ID *restic.ID `json:"id"`
|
||||
ID *restic.ID `json:"id"`
|
||||
ShortID string `json:"short_id"`
|
||||
}
|
||||
|
||||
// printSnapshotsJSON writes the JSON representation of list to stdout.
|
||||
@@ -178,6 +230,7 @@ func printSnapshotsJSON(stdout io.Writer, list restic.Snapshots) error {
|
||||
k := Snapshot{
|
||||
Snapshot: sn,
|
||||
ID: sn.ID(),
|
||||
ShortID: sn.ID().Str(),
|
||||
}
|
||||
snapshots = append(snapshots, k)
|
||||
}
|
||||
|
||||
@@ -53,7 +53,7 @@ func init() {
|
||||
tagFlags.StringArrayVar(&tagOptions.Paths, "path", nil, "only consider snapshots which include this (absolute) `path`, when no snapshot-ID is given")
|
||||
}
|
||||
|
||||
func changeTags(repo *repository.Repository, sn *restic.Snapshot, setTags, addTags, removeTags []string) (bool, error) {
|
||||
func changeTags(ctx context.Context, repo *repository.Repository, sn *restic.Snapshot, setTags, addTags, removeTags []string) (bool, error) {
|
||||
var changed bool
|
||||
|
||||
if len(setTags) != 0 {
|
||||
@@ -77,20 +77,20 @@ func changeTags(repo *repository.Repository, sn *restic.Snapshot, setTags, addTa
|
||||
}
|
||||
|
||||
// Save the new snapshot.
|
||||
id, err := repo.SaveJSONUnpacked(context.TODO(), restic.SnapshotFile, sn)
|
||||
id, err := repo.SaveJSONUnpacked(ctx, restic.SnapshotFile, sn)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
debug.Log("new snapshot saved as %v", id.Str())
|
||||
|
||||
if err = repo.Flush(); err != nil {
|
||||
if err = repo.Flush(ctx); err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
// Remove the old snapshot.
|
||||
h := restic.Handle{Type: restic.SnapshotFile, Name: sn.ID().String()}
|
||||
if err = repo.Backend().Remove(context.TODO(), h); err != nil {
|
||||
if err = repo.Backend().Remove(ctx, h); err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
@@ -113,7 +113,7 @@ func runTag(opts TagOptions, gopts GlobalOptions, args []string) error {
|
||||
}
|
||||
|
||||
if !gopts.NoLock {
|
||||
Verbosef("Create exclusive lock for repository\n")
|
||||
Verbosef("create exclusive lock for repository\n")
|
||||
lock, err := lockRepoExclusive(repo)
|
||||
defer unlockRepo(lock)
|
||||
if err != nil {
|
||||
@@ -125,7 +125,7 @@ func runTag(opts TagOptions, gopts GlobalOptions, args []string) error {
|
||||
ctx, cancel := context.WithCancel(gopts.ctx)
|
||||
defer cancel()
|
||||
for sn := range FindFilteredSnapshots(ctx, repo, opts.Host, opts.Tags, opts.Paths, args) {
|
||||
changed, err := changeTags(repo, sn, opts.SetTags, opts.AddTags, opts.RemoveTags)
|
||||
changed, err := changeTags(ctx, repo, sn, opts.SetTags, opts.AddTags, opts.RemoveTags)
|
||||
if err != nil {
|
||||
Warnf("unable to modify the tags for snapshot ID %q, ignoring: %v\n", sn.ID(), err)
|
||||
continue
|
||||
@@ -135,9 +135,9 @@ func runTag(opts TagOptions, gopts GlobalOptions, args []string) error {
|
||||
}
|
||||
}
|
||||
if changeCnt == 0 {
|
||||
Verbosef("No snapshots were modified\n")
|
||||
Verbosef("no snapshots were modified\n")
|
||||
} else {
|
||||
Verbosef("Modified tags on %v snapshots\n", changeCnt)
|
||||
Verbosef("modified tags on %v snapshots\n", changeCnt)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user