├── .github └── workflows │ ├── gh-pages.yml │ └── release.yml ├── .gitignore ├── .gitmodules ├── LICENSE ├── README.org ├── cache.go ├── default.nix ├── diff.go ├── doc └── site │ ├── archetypes │ └── default.md │ ├── config.toml │ ├── content │ ├── _index.md │ └── docs │ │ ├── changelog.md │ │ ├── contact-support.md │ │ ├── install.md │ │ └── usage.md │ ├── fontawesome │ ├── camera.svg │ ├── get-icons.sh │ ├── keybase.svg │ └── twitter.svg │ ├── layouts │ ├── partials │ │ └── docs │ │ │ └── inject │ │ │ ├── body.html │ │ │ └── head.html │ └── shortcodes │ │ ├── fab.html │ │ └── fas.html │ ├── resources │ └── _gen │ │ └── assets │ │ └── scss │ │ ├── book.scss_50fc8c04e12a2f59027287995557ceff.content │ │ └── book.scss_50fc8c04e12a2f59027287995557ceff.json │ ├── static │ └── fontawesome-all.min.js │ └── themes │ └── book │ ├── .github │ └── workflows │ │ └── main.yml │ ├── .gitignore │ ├── LICENSE │ ├── README.md │ ├── archetypes │ ├── docs.md │ └── posts.md │ ├── assets │ ├── _custom.scss │ ├── _defaults.scss │ ├── _fonts.scss │ ├── _main.scss │ ├── _markdown.scss │ ├── _print.scss │ ├── _shortcodes.scss │ ├── _utils.scss │ ├── _variables.scss │ ├── book.scss │ ├── menu-reset.js │ ├── normalize.css │ ├── plugins │ │ ├── _dark.scss │ │ └── _numbered.scss │ ├── search-data.js │ └── search.js │ ├── exampleSite │ ├── assets │ │ ├── _custom.scss │ │ └── _variables.scss │ ├── config.toml │ ├── config.yaml │ ├── content.cn │ │ └── _index.md │ ├── content.ru │ │ └── _index.md │ ├── content │ │ ├── _index.md │ │ ├── docs │ │ │ ├── example │ │ │ │ ├── 2nd │ │ │ │ │ ├── 3rd │ │ │ │ │ │ ├── 4th.md │ │ │ │ │ │ └── _index.md │ │ │ │ │ └── _index.md │ │ │ │ ├── _index.md │ │ │ │ ├── hidden.md │ │ │ │ └── table-of-contents │ │ │ │ │ ├── _index.md │ │ │ │ │ ├── with-toc.md │ │ │ │ │ └── without-toc.md │ │ │ └── shortcodes │ │ │ │ ├── _index.md │ │ │ │ ├── buttons.md │ │ │ │ ├── columns.md │ │ │ │ ├── expand.md │ │ │ │ ├── hints.md │ │ │ │ ├── katex.md │ │ │ │ ├── mermaid.md │ │ │ │ └── tabs.md │ │ ├── menu │ │ │ └── index.md │ │ └── posts │ │ │ ├── creating-a-new-theme.md │ │ │ ├── goisforlovers.md │ │ │ ├── hugoisforlovers.md │ │ │ └── migrate-from-jekyll.md │ └── resources │ │ └── _gen │ │ └── assets │ │ └── scss │ │ ├── book.scss_50fc8c04e12a2f59027287995557ceff.content │ │ ├── book.scss_50fc8c04e12a2f59027287995557ceff.json │ │ ├── book │ │ ├── book.scss_50fc8c04e12a2f59027287995557ceff.content │ │ └── book.scss_50fc8c04e12a2f59027287995557ceff.json │ │ └── docs │ │ ├── book.scss_50fc8c04e12a2f59027287995557ceff.content │ │ └── book.scss_50fc8c04e12a2f59027287995557ceff.json │ ├── i18n │ ├── cn.yaml │ ├── en.yaml │ ├── es.yaml │ ├── jp.yaml │ ├── ko.yaml │ └── ru.yaml │ ├── images │ ├── screenshot.png │ └── tn.png │ ├── layouts │ ├── 404.html │ ├── _default │ │ └── baseof.html │ ├── docs │ │ ├── list.html │ │ └── single.html │ ├── home.html │ ├── partials │ │ └── docs │ │ │ ├── brand.html │ │ │ ├── comments.html │ │ │ ├── footer.html │ │ │ ├── header.html │ │ │ ├── html-head.html │ │ │ ├── inject │ │ │ ├── body.html │ │ │ ├── footer.html │ │ │ ├── head.html │ │ │ ├── menu-after.html │ │ │ └── menu-before.html │ │ │ ├── languages.html │ │ │ ├── menu-bundle.html │ │ │ ├── menu-filetree.html │ │ │ ├── menu.html │ │ │ ├── post-meta.html │ │ │ ├── search.html │ │ │ ├── taxonomy.html │ │ │ ├── title.html │ │ │ └── toc.html │ ├── posts │ │ ├── list.html │ │ └── single.html │ ├── shortcodes │ │ ├── button.html │ │ ├── columns.html │ │ ├── expand.html │ │ ├── hint.html │ │ ├── katex.html │ │ ├── mermaid.html │ │ ├── tab.html │ │ └── tabs.html │ └── taxonomy │ │ ├── list.html │ │ └── taxonomy.html │ ├── static │ ├── favicon.png │ ├── flexsearch.min.js │ ├── fonts │ │ ├── roboto-mono-v6-latin-regular.woff │ │ ├── roboto-mono-v6-latin-regular.woff2 │ │ ├── roboto-v19-latin-300italic.woff │ │ ├── roboto-v19-latin-300italic.woff2 │ │ ├── roboto-v19-latin-700.woff │ │ ├── roboto-v19-latin-700.woff2 │ │ ├── roboto-v19-latin-regular.woff │ │ └── roboto-v19-latin-regular.woff2 │ ├── mermaid.min.js │ └── svg │ │ ├── calendar.svg │ │ ├── edit.svg │ │ ├── menu.svg │ │ ├── toc.svg │ │ └── translate.svg │ └── theme.toml ├── go.mod ├── go.sum ├── main.go └── site.org /.github/workflows/gh-pages.yml: -------------------------------------------------------------------------------- 1 | name: GitHub Pages 2 | on: 3 | push: 4 | branches: [master] 5 | 6 | jobs: 7 | gh-pages: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/checkout@v2 11 | - run: git fetch --tags || true 12 | - uses: cachix/install-nix-action@v6 13 | 14 | - name: build 15 | run: nix-build -A site 16 | 17 | - name: deploy 18 | uses: peaceiris/actions-gh-pages@v3 19 | with: 20 | deploy_key: ${{ secrets.ACTIONS_DEPLOY_KEY }} 21 | publish_dir: ./result 22 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: release 2 | 3 | on: 4 | push: 5 | tags: 6 | - v[0-9]+.[0-9]+.[0-9]+ 7 | 8 | 9 | jobs: 10 | build: 11 | name: build / publish 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v2 15 | - uses: cachix/install-nix-action@v9 16 | - uses: cachix/cachix-action@v6 17 | with: 18 | name: zsd 19 | signingKey: '${{ secrets.CACHIX_SIGNING_KEY }}' 20 | 21 | - name: build for all platforms 22 | run: | 23 | set -x 24 | git fetch --tags -f 25 | VERSION=$(git describe --always) 26 | for GOOS in linux freebsd darwin solaris; do 27 | echo "BUILD: $GOOS" 28 | BUILD=$(nix-build --no-out-link --no-build-output -A zsd --argstr goos $GOOS) 29 | cp -fv $BUILD/bin/zsd . 30 | cp -fv $BUILD/share/LICENSE . 31 | 32 | ARCHIVE=zsd-$GOOS-$VERSION.tgz 33 | tar cvfz $ARCHIVE zsd LICENSE 34 | done 35 | 36 | - name: upload assets 37 | uses: softprops/action-gh-release@v1 38 | with: 39 | files: zsd*.tgz 40 | env: 41 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 42 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /result* 2 | /zsd 3 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "doc/site/themes/book"] 2 | path = doc/site/themes/book 3 | url = https://github.com/alex-shpak/hugo-book 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014-2020 j-keck [jhyphenkeck@gmail.com] 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.org: -------------------------------------------------------------------------------- 1 | * ~zsd~ 2 | 3 | cli tool to find older versions of a given file in your zfs snapshots. 4 | 5 | /For a browser based version see [[https://j-keck.github.io/zfs-snap-diff][zfs-snap-diff]]./ 6 | 7 | ** Documentation 8 | 9 | See the [[https://j-keck.github.io/zsd][website]] for more details. 10 | 11 | 12 | ** Get it 13 | 14 | You can download a prebuild version from [[https://j-keck.github.io/zsd/docs/install][here]]. 15 | 16 | 17 | ** Run it 18 | 19 | #+BEGIN_SRC sh 20 | ./zsd 21 | #+END_SRC 22 | -------------------------------------------------------------------------------- /cache.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | "errors" 6 | "fmt" 7 | "github.com/j-keck/zfs-snap-diff/pkg/fs" 8 | "github.com/j-keck/zfs-snap-diff/pkg/scanner" 9 | "os" 10 | ) 11 | 12 | func cacheFileVersions(versions []scanner.FileVersion) error { 13 | j, err := json.Marshal(versions) 14 | if err != nil { 15 | return err 16 | } 17 | 18 | cacheDir, err := fs.CacheDir() 19 | if err != nil { 20 | return err 21 | } 22 | 23 | _, err = cacheDir.WriteFile("zsd.cache", j, 0644) 24 | return err 25 | } 26 | 27 | func loadCachedFileVersions() ([]scanner.FileVersion, error) { 28 | 29 | cacheDir, err := fs.CacheDir() 30 | if err != nil { 31 | return nil, err 32 | } 33 | 34 | b, err := cacheDir.ReadFile("zsd.cache") 35 | if os.IsNotExist(err) { 36 | return nil, errors.New("cached file-versions not found - try the 'list' action at first") 37 | } else if err != nil { 38 | return nil, fmt.Errorf("unable to load cached file-version - %v", err) 39 | } 40 | 41 | versions := make([]scanner.FileVersion, 0) 42 | err = json.Unmarshal(b, &versions) 43 | return versions, err 44 | } 45 | -------------------------------------------------------------------------------- /default.nix: -------------------------------------------------------------------------------- 1 | { goos ? "linux", with-dev-tools ? false }: 2 | let 3 | 4 | fetchNixpkgs = {rev, sha256}: builtins.fetchTarball { 5 | url = "https://github.com/NixOS/nixpkgs-channels/archive/${rev}.tar.gz"; 6 | inherit sha256; 7 | }; 8 | 9 | pkgs = import (fetchNixpkgs { 10 | rev = "8a9807f1941d046f120552b879cf54a94fca4b38"; 11 | sha256 = "0s8gj8b7y1w53ak138f3hw1fvmk40hkpzgww96qrsgf490msk236"; 12 | }) {}; 13 | 14 | version = 15 | let lookup-version = pkgs.stdenv.mkDerivation { 16 | src = builtins.path { name = "git"; path = ./.git; }; 17 | name = "zsd-lookup-version"; 18 | phases = "buildPhase"; 19 | buildPhase = '' 20 | mkdir -p $out 21 | ${pkgs.git}/bin/git --git-dir=$src describe --always --tags > $out/version 22 | ''; 23 | }; 24 | in pkgs.lib.removeSuffix "\n" (builtins.readFile "${lookup-version}/version"); 25 | 26 | 27 | zsd = pkgs.buildGo112Module rec { 28 | pname = "zsd"; 29 | inherit version; 30 | src = pkgs.nix-gitignore.gitignoreSource [ ".gitignore" ] ./.; 31 | modSha256 = "1i8bd6cli45zcl40xc5cw0ywj8nz1xnz5jyxq5rd7adw89m0fd40"; 32 | 33 | preBuild = '' 34 | export GOOS=${goos} 35 | ''; 36 | 37 | CGO_ENABLED = 0; 38 | 39 | buildFlagsArray = '' 40 | -ldflags= 41 | -X main.version=${version} 42 | ''; 43 | 44 | installPhase = '' 45 | mkdir -p $out 46 | 47 | BIN_PATH=${if goos == pkgs.stdenv.buildPlatform.parsed.kernel.name 48 | then "$GOPATH/bin" 49 | else "$GOPATH/bin/${goos}_$GOARCH"} 50 | 51 | mkdir -p $out/bin 52 | cp $BIN_PATH/zsd $out/bin 53 | 54 | mkdir -p $out/share 55 | cp LICENSE $out/share 56 | ''; 57 | }; 58 | 59 | 60 | 61 | site = 62 | let theme = pkgs.fetchFromGitHub { 63 | owner = "alex-shpak"; 64 | repo = "hugo-book"; 65 | rev = "dae803fa442973561821a44b08e3a964614d07df"; 66 | sha256 = "0dpb860kddclsqnr4ls356jn4d1l8ymw5rs9wfz2xq4kkrgls4dl"; 67 | }; 68 | in pkgs.stdenv.mkDerivation rec { 69 | name = "zsd-site"; 70 | inherit version; 71 | src = ./doc/site; 72 | buildPhase = '' 73 | cp -a ${theme}/. themes/book 74 | ${pkgs.hugo}/bin/hugo --minify 75 | ''; 76 | installPhase = '' 77 | cp -r public $out 78 | ''; 79 | }; 80 | 81 | in 82 | 83 | if pkgs.lib.inNixShell then pkgs.mkShell { 84 | buildInputs = with pkgs; 85 | [ go_1_12 ] ++ 86 | (if with-dev-tools 87 | then [ hugo ] 88 | else []); 89 | 90 | shellHooks = '' 91 | unset GOPATH 92 | ''; 93 | } 94 | else { 95 | inherit zsd site; 96 | } 97 | -------------------------------------------------------------------------------- /diff.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | diffPkg "github.com/j-keck/zfs-snap-diff/pkg/diff" 7 | "strings" 8 | ) 9 | 10 | func diffsPrettyText(diff diffPkg.Diff, colored bool) string { 11 | 12 | var buff bytes.Buffer 13 | for n, deltas := range diff.Deltas { 14 | header := fmt.Sprintf("Chunk %d - starting at line %d", n, deltas[0].LineNrFrom) 15 | buff.WriteString(strings.Repeat("=", len(header)) + "\n") 16 | buff.WriteString(header + "\n") 17 | buff.WriteString(strings.Repeat("-", len(header)) + "\n") 18 | buff.WriteString(diffPrettyText(deltas, colored)) 19 | buff.WriteString("\n") 20 | } 21 | 22 | return buff.String() 23 | } 24 | 25 | func diffPrettyText(deltas diffPkg.Deltas, colored bool) string { 26 | var buff bytes.Buffer 27 | 28 | for _, delta := range deltas { 29 | switch delta.Type { 30 | case diffPkg.Ins: 31 | if colored { 32 | buff.WriteString("\x1b[32m") 33 | buff.WriteString(delta.Text) 34 | buff.WriteString("\x1b[0m") 35 | } else { 36 | for n, line := range strings.Split(delta.Text, "\n") { 37 | if n > 0 { 38 | buff.WriteString("\n") 39 | } 40 | if len(line) > 0 { 41 | buff.WriteString("+ " + line) 42 | } 43 | } 44 | } 45 | case diffPkg.Del: 46 | if colored { 47 | buff.WriteString("\x1b[31m") 48 | buff.WriteString(delta.Text) 49 | buff.WriteString("\x1b[0m") 50 | } else { 51 | for n, line := range strings.Split(delta.Text, "\n") { 52 | if n > 0 { 53 | buff.WriteString("\n") 54 | } 55 | if len(line) > 0 { 56 | buff.WriteString("- " + line) 57 | } 58 | } 59 | } 60 | case diffPkg.Eq: 61 | if colored { 62 | buff.WriteString(delta.Text) 63 | } else { 64 | buff.WriteString(" " + delta.Text) 65 | } 66 | } 67 | } 68 | return buff.String() 69 | } 70 | -------------------------------------------------------------------------------- /doc/site/archetypes/default.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "{{ replace .Name "-" " " | title }}" 3 | date: {{ .Date }} 4 | draft: true 5 | --- 6 | 7 | -------------------------------------------------------------------------------- /doc/site/config.toml: -------------------------------------------------------------------------------- 1 | baseURL = "https://j-keck.github.io/zsd" 2 | title = "zsd" 3 | theme = "book" 4 | canonifyURLs = true 5 | 6 | [markup.goldmark.renderer] 7 | unsafe = true 8 | 9 | [params] 10 | BookSearch = false 11 | -------------------------------------------------------------------------------- /doc/site/content/_index.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = "zsd" 3 | type = "docs" 4 | draft = false 5 | creator = "Emacs 26.3 (Org mode 9.1.9 + ox-hugo)" 6 | weight = 10 7 | +++ 8 | 9 | `zsd` - cli tool to find older versions of a given file in your zfs snapshots. 10 | 11 | With `zsd` you can 12 | 13 | - find older file versions in your zfs snapshots for a given file 14 | 15 | - view the file content from a given snapshot 16 | 17 | - inspect a diff from the older version to the actual version 18 | 19 | - revert a single change 20 | 21 | - restore a version from a zfs snapshot 22 | 23 | - grep changes 24 | 25 | _For a browser based version see [zfs-snap-diff](https://j-keck.github.io/zfs-snap-diff)._ 26 | 27 | 28 | ## License {#license} 29 | 30 | `zsd` is released under the ****MIT**** License. 31 | See the [license file](https://github.com/j-keck/zsd/blob/master/LICENSE) for more information. 32 | -------------------------------------------------------------------------------- /doc/site/content/docs/changelog.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = "Changelog" 3 | draft = false 4 | creator = "Emacs 26.3 (Org mode 9.1.9 + ox-hugo)" 5 | weight = 50 6 | +++ 7 | 8 | ## 1.3.0 {#1-dot-3-dot-0} 9 | 10 | - new action: `grep` to search changes ([Usage](https://j-keck.github.io/zsd/docs/usage/#grep-changes)) 11 | 12 | [all commits from v1.2.0 to v1.3.0](https://github.com/j-keck/zsd/compare/v1.2.0...v1.3.0) 13 | 14 | 15 | ## 1.2.0 {#1-dot-2-dot-0} 16 | 17 | - new action: revert to revert a single change ([Usage](https://j-keck.github.io/zsd/docs/usage/#revert-a-change)) 18 | - show age in hours if it's younger than 48 hours 19 | - diff context size configurable (flag: -diff-context-size 3) 20 | - show diff in chunks 21 | - include "File changed" age in list action 22 | 23 | [all commits from v1.1.2 to v1.2.0](https://github.com/j-keck/zsd/compare/v1.1.2...v1.2.0) 24 | 25 | 26 | ## 1.1.2 {#1-dot-1-dot-2} 27 | 28 | - update `-snapshot-timemachine` format 29 | - use the file timestamp instead of the snapshot timestamp 30 | - change the timestamp format 31 | 32 | [all commits from v1.1.1 to v1.1.2](https://github.com/j-keck/zsd/compare/v1.1.1...v1.1.2) 33 | 34 | 35 | ## 1.1.1 {#1-dot-1-dot-1} 36 | 37 | Import from [zfs-snap-diff](https://j-keck.github.io/zfs-snap-diff). 38 | -------------------------------------------------------------------------------- /doc/site/content/docs/contact-support.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = "Contact / Support" 3 | draft = false 4 | creator = "Emacs 26.3 (Org mode 9.1.9 + ox-hugo)" 5 | weight = 60 6 | +++ 7 | 8 | ## Contact {#contact} 9 | 10 | {{< columns >}} 11 | [{{< fas envelope lg >}} Check my GitHub Profile for my mail address.](https://github.com/j-keck) 12 | <---> 13 | [{{< fab twitter lg >}} Send me an direct message on twitter.](https://twitter.com/jhyphenkeck) 14 | <---> 15 | [{{< fab keybase lg >}} Use keybase to contact me.](https://keybase.io/jkeck) 16 | {{< /columns >}} 17 | 18 | 19 | ## Support {#support} 20 | 21 | If you have any questions, trouble or other input, feel free to contact 22 | me directly (see [Contact](/docs/contact-support#contact)) or open a [issue@github](https://github.com/j-keck/zsd/issues/new). 23 | -------------------------------------------------------------------------------- /doc/site/content/docs/install.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = "Installation" 3 | draft = false 4 | creator = "Emacs 26.3 (Org mode 9.1.9 + ox-hugo)" 5 | weight = 20 6 | +++ 7 | 8 | You can download the latest binary from here or from the [GitHub release page](https://github.com/j-keck/zsd/releases). 9 | 10 | {{}} 11 | {{< tab "Linux (x64)" >}} 12 | 1.) ****Download**** the latest version: [zsd-linux-v1.3.0.tgz](https://github.com/j-keck/zsd/releases/download/v1.3.0/zsd-linux-v1.3.0.tgz) 13 | 14 | 2.) Run it: `./zsd ` 15 | {{< /tab >}} 16 | 17 | {{< tab "FreeBSD (x64)" >}} 18 | 1.) ****Download**** the latest version: [zsd-freebsd-v1.3.0.tgz](https://github.com/j-keck/zsd/releases/download/v1.3.0/zsd-freebsd-v1.3.0.tgz) 19 | 20 | 2.) Run it: `./zsd ` 21 | {{< /tab >}} 22 | 23 | {{< tab "Solaris (x64)" >}} 24 | 1.) ****Download**** the latest version: [zsd-solaris-v1.3.0.tgz](https://github.com/j-keck/zsd/releases/download/v1.3.0/zsd-solaris-v1.3.0.tgz) 25 | 26 | 2.) Run it: `./zsd ` 27 | {{< /tab >}} 28 | 29 | {{< tab "Mac OS (x64)" >}} 30 | 1.) ****Download**** the latest version: [zsd-darwin-v1.3.0.tgz](https://github.com/j-keck/zsd/releases/download/v1.3.0/zsd-darwin-v1.3.0.tgz) 31 | 32 | 2.) Run it: `./zsd ` 33 | {{< /tab >}} 34 | 35 | {{< /tabs >}} 36 | 37 | Checkout [Usage](/docs/usage) how to use it. 38 | -------------------------------------------------------------------------------- /doc/site/content/docs/usage.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = "Usage" 3 | draft = false 4 | creator = "Emacs 26.3 (Org mode 9.1.9 + ox-hugo)" 5 | weight = 30 6 | +++ 7 | 8 | ## List snapshots {#list-snapshots} 9 | 10 | Use the `list` action to list all snapshots where the 11 | given file was modified. 12 | 13 | `zsd` lists only snapshots where the given file was modified. 14 | You can adjust the number of days to scan with the `-d` flag. 15 | 16 | ```text 17 | main⟩ zsd main.go list 18 | scan the last 2 days for other file versions 19 | # | File changed | Snapshot | Snapshot age 20 | ---------------------------------------------------------------------------- 21 | 0 | 20 minutes | zfs-auto-snap_frequent-2020-07-12-14h00U | 18 minutes 22 | 1 | 34 minutes | zfs-auto-snap_frequent-2020-07-12-13h45U | 33 minutes 23 | 2 | 49 minutes | zfs-auto-snap_frequent-2020-07-12-13h30U | 48 minutes 24 | ``` 25 | 26 | 27 | ## Show file content {#show-file-content} 28 | 29 | Use the `cat` action to show the file content from 30 | the given snapshot. 31 | 32 | {{< hint info >}} 33 | You can use the snapshot number from the `list` output 34 | or the snapshot name to select a snapshot. 35 | {{< /hint >}} 36 | 37 | ```text 38 | # i use `head` here to keep the example short 39 | main⟩ zsd main.go cat 2 | head -5 40 | package main 41 | 42 | import ( 43 | "errors" 44 | "flag" 45 | ``` 46 | 47 | 48 | ## Show diff {#show-diff} 49 | 50 | To show a diff from the selected snapshot to the actual version 51 | use the `diff` action. 52 | 53 | {{< hint info >}} 54 | You can use the snapshot number from the `list` output 55 | or the snapshot name to select a snapshot. 56 | {{< /hint >}} 57 | 58 | ```text 59 | # i use `head` here to keep the example short 60 | main⟩ zsd -no-color -diff-context-size 2 main.go diff 1 | head -30 61 | Diff from the actual version to the version from: 2020-07-12 15:44:43.566374368 +0200 CEST 62 | ============================= 63 | Chunk 0 - starting at line 43 64 | ----------------------------- 65 | fmt.Fprintf(os.Stderr, " revert <#|SNAPSHOT> : revert the given chunk\n") 66 | fmt.Fprintf(os.Stderr, " restore <#|SNAPSHOT> : restore the file from the given snapshot\n") 67 | - fmt.Fprintf(os.Stderr, " grep : grep changed lines\n") 68 | + fmt.Fprintf(os.Stderr, " grep : grep changes\n") 69 | fmt.Fprintf(os.Stderr, "\nYou can use the snapshot number from the `list` output or the snapshot name to select a snapshot.\n") 70 | fmt.Fprintf(os.Stderr, "\nProject home page: https://j-keck.github.io/zsd\n") 71 | 72 | ============================== 73 | Chunk 1 - starting at line 264 74 | ------------------------------ 75 | pattern := strings.ToLower(flag.Arg(2)) 76 | 77 | + if !cliCfg.scriptingOutput { 78 | + fmt.Printf("scan the last %d days for other file versions\n", config.Get.DaysToScan) 79 | + } 80 | 81 | dr := scanner.NDaysBack(config.Get.DaysToScan, time.Now()) 82 | sc := scanner.NewScanner(dr, "auto", ds, zfs) 83 | 84 | ============================== 85 | Chunk 2 - starting at line 272 86 | ------------------------------ 87 | } 88 | ``` 89 | 90 | 91 | ## Revert a change {#revert-a-change} 92 | 93 | You can revert a single change with the `revert` action. 94 | 95 | 96 | #### View a diff to the backup version {#view-a-diff-to-the-backup-version} 97 | 98 | ```text 99 | main⟩ zsd -diff-context-size 1 -no-color cache.go diff 0 100 | Diff from the actual version to the version from: 2020-07-04 14:29:37.807643286 +0200 CEST 101 | ============================= 102 | Chunk 0 - starting at line 13 103 | ----------------------------- 104 | j, err := json.Marshal(versions) 105 | + println(err) 106 | if err != nil { 107 | 108 | ============================= 109 | Chunk 1 - starting at line 18 110 | ----------------------------- 111 | cacheDir, err := fs.CacheDir() 112 | + println(err) 113 | if err != nil { 114 | ``` 115 | 116 | 117 | #### Revert a single change {#revert-a-single-change} 118 | 119 | Use the chunk-nr from the `diff` output to select the change to revert. 120 | See the ****Chunk <NR>**** line to get it. 121 | 122 | ```text 123 | main⟩ ./zsd -diff-context-size 1 -no-color cache.go revert 0 1 124 | backup from the actual version created at: /home/j/.cache/zfs-snap-diff/backups/home/j/prj/priv/zfs-snap-diff/zsd/cache.go_20200704_144023 125 | reverted: 126 | cacheDir, err := fs.CacheDir() 127 | + println(err) 128 | if err != nil { 129 | ``` 130 | 131 | 132 | #### Check the result {#check-the-result} 133 | 134 | ```text 135 | main⟩ zsd -diff-context-size 1 -no-color cache.go diff 0 136 | Diff from the actual version to the version from: 2020-07-04 14:29:37.807643286 +0200 CEST 137 | ============================= 138 | Chunk 0 - starting at line 13 139 | ----------------------------- 140 | j, err := json.Marshal(versions) 141 | + println(err) 142 | if err != nil { 143 | ``` 144 | 145 | {{< hint warning >}} 146 | A backup of the current version will be created. 147 | {{< /hint >}} 148 | 149 | 150 | ## Restore file {#restore-file} 151 | 152 | To restore a given file with an older version use `restore`. 153 | 154 | {{< hint info >}} 155 | You can use the snapshot number from the `list` output 156 | or the snapshot name to select a snapshot. 157 | {{< /hint >}} 158 | 159 | ```text 160 | main⟩ zsd go.mod restore 0 161 | backup from the actual version created at: /home/j/.cache/zfs-snap-diff/backups/home/j/prj/priv/zfs-snap-diff/go.mod_20200212_182709% 162 | version restored from snapshot: zfs-auto-snap_hourly-2020-02-12-12h00U 163 | ``` 164 | 165 | {{< hint warning >}} 166 | A backup of the current version will be created. 167 | {{< /hint >}} 168 | 169 | 170 | ## Grep changes {#grep-changes} 171 | 172 | You can search changes with `grep `. This search only changed lines. 173 | 174 | To view the whole diff use the snapshot number or snapshot name from the output. 175 | 176 | ```text 177 | main⟩ zsd main.go grep trim 178 | scan the last 2 days for other file versions 179 | # | File changed | Snapshot | Snapshot age | Line | Change 180 | ------------------------------------------------------------------------------------------------------------------------ 181 | 3 | 1 hours | zfs-auto-snap_hourly-2020-07-12-13h00U | 2 hours | 285 | + line := strings.TrimSpace(line) 182 | ``` 183 | 184 | 185 | ## Flags {#flags} 186 | 187 | Use the `-h` flag to see the supported flags. 188 | 189 | ```text 190 | main⟩ zsd -h 191 | zsd - cli tool to find older versions of a given file in your zfs snapshots. 192 | 193 | USAGE: 194 | ./zsd [OPTIONS] 195 | 196 | OPTIONS: 197 | -H Scripting mode. Do not print headers, print absolute dates and separate fields by a single tab 198 | -V print version and exit 199 | -d int 200 | days to scan (default 2) 201 | -mount-snapshots 202 | mount snapshot (only necessary if it's not mounted by zfs automatically) 203 | -snapshot-timemachine 204 | Special output for Snapshot-timemachine (https://github.com/mrBliss/snapshot-timemachine) 205 | -use-sudo 206 | use sudo when executing 'zfs' commands 207 | -v debug output 208 | -vv 209 | trace output with caller location 210 | 211 | ACTIONS: 212 | list : list zfs snapshots where the given file was modified 213 | cat <#|SNAPSHOT> : show the file content from the given snapshot 214 | diff <#|SNAPSHOT> : show a diff from the selected snapshot to the current version 215 | revert <#|SNAPSHOT> : revert the given chunk 216 | restore <#|SNAPSHOT> : restore the file from the given snapshot 217 | grep : grep changes 218 | 219 | You can use the snapshot number from the `list` output or the snapshot name to select a snapshot. 220 | 221 | Project home page: https://j-keck.github.io/zsd 222 | ``` 223 | -------------------------------------------------------------------------------- /doc/site/fontawesome/camera.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /doc/site/fontawesome/get-icons.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | ICONS="solid/camera brands/twitter brands/keybase" 5 | 6 | for icon in $ICONS; do 7 | wget -O "$(basename $icon).svg" \ 8 | "https://raw.githubusercontent.com/FortAwesome/Font-Awesome/master/svgs/$icon.svg" 9 | done 10 | -------------------------------------------------------------------------------- /doc/site/fontawesome/keybase.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /doc/site/fontawesome/twitter.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /doc/site/layouts/partials/docs/inject/body.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /doc/site/layouts/partials/docs/inject/head.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /doc/site/layouts/shortcodes/fab.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /doc/site/layouts/shortcodes/fas.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /doc/site/resources/_gen/assets/scss/book.scss_50fc8c04e12a2f59027287995557ceff.content: -------------------------------------------------------------------------------- 1 | /*!normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css*/html{line-height:1.15;-webkit-text-size-adjust:100%}body{margin:0}main{display:block}h1{font-size:2em;margin:.67em 0}hr{box-sizing:content-box;height:0;overflow:visible}pre{font-family:monospace,monospace;font-size:1em}a{background-color:transparent}abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}b,strong{font-weight:bolder}code,kbd,samp{font-family:monospace,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}img{border-style:none}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;line-height:1.15;margin:0}button,input{overflow:visible}button,select{text-transform:none}button,[type=button],[type=reset],[type=submit]{-webkit-appearance:button}button::-moz-focus-inner,[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner{border-style:none;padding:0}button:-moz-focusring,[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring{outline:1px dotted ButtonText}fieldset{padding:.35em .75em .625em}legend{box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal}progress{vertical-align:baseline}textarea{overflow:auto}[type=checkbox],[type=radio]{box-sizing:border-box;padding:0}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}details{display:block}summary{display:list-item}template{display:none}[hidden]{display:none}.flex{display:flex}.flex-auto{flex:1 1 auto}.flex-even{flex:1 1}.flex-wrap{flex-wrap:wrap}.justify-start{justify-content:flex-start}.justify-end{justify-content:flex-end}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.align-center{align-items:center}.mx-auto{margin:0 auto}.text-center{text-align:center}.hidden{display:none}.clearfix::after{content:"";display:table;clear:both}html{font-size:16px;letter-spacing:.33px;scroll-behavior:smooth}html,body{min-width:20rem;overflow-x:hidden}body{color:#343a40;background:#fff;font-weight:400;text-rendering:optimizeLegibility;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;box-sizing:border-box}body *{box-sizing:inherit}h1,h2,h3,h4,h5{font-weight:400}a{text-decoration:none;color:#05b}a:visited{color:#8440f1}img{vertical-align:baseline}aside nav ul{padding:0;margin:0;list-style:none}aside nav ul li{margin:1em 0}aside nav ul a{display:block}aside nav ul a:hover{opacity:.5}aside nav ul ul{padding-left:1rem}.levels-1 ul ul,.levels-2 ul ul ul,.levels-3 ul ul ul ul,.levels-4 ul ul ul ul ul,.levels-5 ul ul ul ul ul ul,.levels-6 ul ul ul ul ul ul ul{display:none}ul.pagination{display:flex;justify-content:center;list-style-type:none}ul.pagination .page-item a{padding:1rem}.container{max-width:80rem;margin:0 auto}.book-icon{filter:none}.book-brand{margin-top:0}.book-brand img{height:1.5em;width:auto;vertical-align:middle;margin-right:.5rem}.book-menu{flex:0 0 16rem;font-size:.875rem}.book-menu nav{width:16rem;padding:1rem;position:fixed;top:0;bottom:0;overflow-x:hidden;overflow-y:auto}.book-menu a{color:inherit}.book-menu a.active{color:#05b}.book-section-flat{margin-bottom:2rem}.book-section-flat:not(:first-child){margin-top:2rem}.book-section-flat>a,.book-section-flat>span{font-weight:bolder}.book-section-flat>ul{padding-left:0}.book-page{min-width:20rem;flex-grow:1;padding:1rem}.book-post{margin-bottom:3rem}.book-header{display:none;margin-bottom:1rem}.book-header label{line-height:0}.book-search{position:relative;margin:1rem 0;border-bottom:1px solid transparent}.book-search::after{display:block;content:"";clear:both}.book-search input{width:100%;padding:.5rem;border:0;border-radius:.25rem;background:#f8f9fa;color:#343a40}.book-search input:required+.book-search-spinner{display:block}.book-search .book-search-spinner{position:absolute;margin:.5rem;right:0;top:0;width:1rem;height:1rem;border:1px solid transparent;border-top-color:#343a40;border-radius:50%;animation:spin 1s ease infinite}@keyframes spin{100%{transform:rotate(360deg)}}.book-toc{flex:0 0 16rem;font-size:.75rem}.book-toc nav{width:16rem;padding:1rem;position:fixed;top:0;bottom:0;overflow-x:hidden;overflow-y:auto}.book-toc img{height:1em}.book-toc nav>ul>li:first-child{margin-top:0}.book-footer{padding-top:1rem;font-size:.875rem}.book-footer img{height:1em;margin-right:.5rem}.book-comments{margin-top:1rem}.book-languages{position:relative;overflow:visible;padding:1rem;margin:-1rem}.book-languages:hover .book-languages-list{display:block}.book-languages .book-languages-list{display:none;position:absolute;bottom:0;left:0;padding:.5rem 0;background:#fff;box-shadow:0 0 .25rem rgba(0,0,0,.1)}.book-languages .book-languages-list li{padding:.5rem 1rem;white-space:nowrap}.book-languages .book-languages-list img{opacity:.1}.book-languages .book-languages-list li.active img{opacity:1}.book-languages .book-languages-list a{color:inherit}.book-languages ul{margin:0;padding:0;list-style:none}.book-home{padding:1rem}aside nav,.book-page,.book-header aside,.markdown{transition:.2s ease-in-out;transition-property:transform,margin,opacity;will-change:transform,margin}@media screen and (max-width:56rem){.book-menu{margin-left:-16rem;font-size:16px}.book-toc{display:none}.book-header{display:block}#menu-control:checked+main .book-menu nav,#menu-control:checked+main .book-page{transform:translateX(16rem)}#menu-control:checked+main .book-header aside,#menu-control:checked+main .markdown{opacity:.25}#toc-control:checked+aside{display:block}}@media screen and (min-width:80rem){.book-page,.book-menu nav,.book-toc nav{padding:2rem 1rem}}@font-face{font-family:roboto;font-style:italic;font-weight:300;font-display:swap;src:local("Roboto Light Italic"),local("Roboto-LightItalic"),url(fonts/roboto-v19-latin-300italic.woff2)format("woff2"),url(fonts/roboto-v19-latin-300italic.woff)format("woff")}@font-face{font-family:roboto;font-style:normal;font-weight:400;font-display:swap;src:local("Roboto"),local("Roboto-Regular"),url(fonts/roboto-v19-latin-regular.woff2)format("woff2"),url(fonts/roboto-v19-latin-regular.woff)format("woff")}@font-face{font-family:roboto;font-style:normal;font-weight:700;font-display:swap;src:local("Roboto Bold"),local("Roboto-Bold"),url(fonts/roboto-v19-latin-700.woff2)format("woff2"),url(fonts/roboto-v19-latin-700.woff)format("woff")}@font-face{font-family:roboto mono;font-style:normal;font-weight:400;font-display:swap;src:local("Roboto Mono"),local("RobotoMono-Regular"),url(fonts/roboto-mono-v6-latin-regular.woff2)format("woff2"),url(fonts/roboto-mono-v6-latin-regular.woff)format("woff")}body{font-family:roboto,sans-serif}code{font-family:roboto mono,monospace}@media print{.book-menu,.book-footer,.book-toc{display:none}.book-header,.book-header aside{display:block}main{display:block!important}}.markdown{line-height:1.6em}.markdown>:first-child{margin-top:0}.markdown h1,.markdown h2,.markdown h3,.markdown h4,.markdown h5,.markdown h6{font-weight:400;line-height:1em;margin-top:1.5em;margin-bottom:1rem}.markdown h4,.markdown h5,.markdown h6{font-weight:bolder}.markdown h5{font-size:.875em}.markdown h6{font-size:.75em}.markdown b,.markdown optgroup,.markdown strong{font-weight:bolder}.markdown a{text-decoration:none}.markdown a:hover{text-decoration:underline}.markdown img{max-width:100%}.markdown code{padding:0 .25rem;background:#e9ecef;border-radius:.15rem;font-size:.875em}.markdown pre{padding:1rem;background:#f8f9fa;border-radius:.15rem;overflow-x:auto}.markdown pre code{padding:0;background:0 0}.markdown blockquote{margin:1rem 0;padding:.5rem 1rem .5rem .75rem;border-left:.25rem solid #e9ecef;border-radius:.15rem}.markdown blockquote :first-child{margin-top:0}.markdown blockquote :last-child{margin-bottom:0}.markdown table{overflow:auto;display:block;border-spacing:0;border-collapse:collapse;margin-top:1rem;margin-bottom:1rem}.markdown table tr th,.markdown table tr td{padding:.5rem 1rem;border:1px solid #e9ecef}.markdown table tr:nth-child(2n){background:#f8f9fa}.markdown hr{height:1px;border:none;background:#e9ecef}.markdown ul,.markdown ol{padding-left:2rem}.markdown dl dt{font-weight:bolder;margin-top:1rem}.markdown dl dd{margin-left:2rem}.markdown .highlight table tr td:nth-child(1) pre{margin:0;padding-right:0}.markdown .highlight table tr td:nth-child(2) pre{margin:0;padding-left:0}.markdown details{padding:1rem;border:1px solid #e9ecef;border-radius:.15rem}.markdown details summary{line-height:1;cursor:pointer}.markdown-inner>:first-child{margin-top:0}.markdown-inner>:last-child{margin-bottom:0}.book-expand{margin-top:1rem;margin-bottom:1rem;border:1px solid #e9ecef;border-radius:.15rem;overflow:hidden}.book-expand .book-expand-head{background:#f8f9fa;padding:.5rem 1rem;cursor:pointer}.book-expand .book-expand-content{display:none;padding:1rem}.book-expand input[type=checkbox]:checked+.book-expand-content{display:block}.book-tabs{margin-top:1rem;margin-bottom:1rem;border:1px solid #e9ecef;border-radius:.15rem;overflow:hidden;display:flex;flex-wrap:wrap}.book-tabs label{display:inline-block;padding:.5rem 1rem;border-bottom:1px transparent;cursor:pointer}.book-tabs .book-tabs-content{order:999;width:100%;border-top:1px solid #f8f9fa;padding:1rem;display:none}.book-tabs input[type=radio]:checked+label{border-bottom:1px solid #05b}.book-tabs input[type=radio]:checked+label+.book-tabs-content{display:block}.book-columns{margin-left:-1rem;margin-right:-1rem}.book-columns>div{margin:1rem 0;min-width:10rem;padding:0 1rem}a.book-btn{display:inline-block;color:#05b!important;text-decoration:none!important;border:1px solid #05b;border-radius:.15rem;padding:.25rem 1rem;margin-top:.5rem;margin-bottom:.5rem;cursor:pointer}.book-hint.info{border-left-color:#6bf;background-color:rgba(102,187,255,.1)}.book-hint.warning{border-left-color:#fd6;background-color:rgba(255,221,102,.1)}.book-hint.danger{border-left-color:#f66;background-color:rgba(255,102,102,.1)} -------------------------------------------------------------------------------- /doc/site/resources/_gen/assets/scss/book.scss_50fc8c04e12a2f59027287995557ceff.json: -------------------------------------------------------------------------------- 1 | {"Target":"book.min.66d2f6926159dd697150bdcd901eead56a7adc9408513ccb9ab553939262dd4c.css","MediaType":"text/css","Data":{"Integrity":"sha256-ZtL2kmFZ3WlxUL3NkB7q1Wp63JQIUTzLmrVTk5Ji3Uw="}} -------------------------------------------------------------------------------- /doc/site/themes/book/.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: Build with Hugo 2 | 3 | on: [push] 4 | 5 | jobs: 6 | hugo: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@master 10 | 11 | - name: Install Hugo 12 | run: | 13 | wget https://github.com/gohugoio/hugo/releases/download/v0.60.1/hugo_extended_0.60.1_Linux-64bit.deb -O /tmp/hugo.deb 14 | sudo dpkg -i /tmp/hugo.deb 15 | 16 | - name: Run Hugo 17 | working-directory: exampleSite 18 | run: hugo --themesDir ../.. 19 | -------------------------------------------------------------------------------- /doc/site/themes/book/.gitignore: -------------------------------------------------------------------------------- 1 | public/ 2 | exampleSite/public/ 3 | .DS_Store 4 | -------------------------------------------------------------------------------- /doc/site/themes/book/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2018 Alex Shpak 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /doc/site/themes/book/README.md: -------------------------------------------------------------------------------- 1 | # Hugo Book Theme 2 | 3 | [![Hugo](https://img.shields.io/badge/hugo-0.60-blue.svg)](https://gohugo.io) 4 | [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE) 5 | 6 | ### [Hugo](https://gohugo.io) documentation theme as simple as plain book 7 | 8 | ![Screenshot](https://github.com/alex-shpak/hugo-book/blob/master/images/screenshot.png) 9 | 10 | - [Features](#features) 11 | - [Requirements](#requirements) 12 | - [Installation](#installation) 13 | - [Menu](#menu) 14 | - [Blog](#blog) 15 | - [Configuration](#configuration) 16 | - [Shortcodes](#shortcodes) 17 | - [Contributing](#contributing) 18 | 19 | ## Features 20 | 21 | - Clean simple design 22 | - Light and Mobile-Friendly 23 | - Multi-language support 24 | - Customisable 25 | - Zero initial configuration 26 | - Handy shortcodes 27 | - Optional support for Disqus 28 | 29 | ## Requirements 30 | 31 | - Hugo 0.60 or higher 32 | - Hugo extended version, read more [here](https://gohugo.io/news/0.48-relnotes/) 33 | 34 | ## Installation 35 | 36 | Navigate to your hugo project root and run: 37 | 38 | ``` 39 | git submodule add https://github.com/alex-shpak/hugo-book themes/book 40 | ``` 41 | 42 | Then run hugo (or set `theme = "book"`/`theme: book` in configuration file) 43 | 44 | ``` 45 | hugo server --minify --theme book 46 | ``` 47 | 48 | ### Creating site from scratch 49 | 50 | Below is example how to create new site from scratch 51 | 52 | ```sh 53 | hugo new site mydocs; cd mydocs 54 | git init 55 | git submodule add https://github.com/alex-shpak/hugo-book themes/book 56 | cp -R themes/book/exampleSite/content . 57 | ``` 58 | 59 | ```sh 60 | hugo server --minify --theme book 61 | ``` 62 | 63 | ## Menu 64 | 65 | ### File tree menu (default) 66 | 67 | By default theme will render pages from `content/docs` section as menu in a tree structure. 68 | You can set `title` and `weight` in front matter of pages to adjust order and titles in menu. 69 | 70 | ### Leaf bundle menu 71 | 72 | You can also use leaf bundle and content of it's `index.md` as menu. 73 | Given you have this file structure 74 | 75 | ``` 76 | ├── content 77 | │ ├── docs 78 | │ │ ├── page-one.md 79 | │ │ └── page-two.md 80 | │ └── posts 81 | │ ├── post-one.md 82 | │ └── post-two.md 83 | ``` 84 | 85 | Create file `content/docs/menu/index.md` with content 86 | 87 | ```md 88 | +++ 89 | headless = true 90 | +++ 91 | 92 | - [Book Example]({{< relref "/docs/" >}}) 93 | - [Page One]({{< relref "/docs/page-one" >}}) 94 | - [Page Two]({{< relref "/docs/page-two" >}}) 95 | - [Blog]({{< relref "/posts" >}}) 96 | ``` 97 | 98 | And Enable it by settings `BookMenuBundle: /menu` in Site configuration 99 | 100 | - [Example menu](https://github.com/alex-shpak/hugo-book/blob/master/exampleSite/content/menu/index.md) 101 | - [Example config file](https://github.com/alex-shpak/hugo-book/blob/master/exampleSite/config.yaml) 102 | - [Leaf bundles](https://gohugo.io/content-management/page-bundles/) 103 | 104 | ## Blog 105 | 106 | Simple blog supported for section `posts`. 107 | Blog is not primary use case so book theme so it has only minimal features 108 | 109 | ## Configuration 110 | 111 | ### Site Configuration 112 | 113 | There are few configuration options you can add to your `config.toml` file. 114 | You can also see `yaml` example [here](https://github.com/alex-shpak/hugo-book/blob/master/exampleSite/config.yaml). 115 | 116 | ```toml 117 | # (Optional) Set Google Analytics if you use it to track your website. 118 | # Always put it on the top of the configuration file, otherwise it won't work 119 | googleAnalytics = "UA-XXXXXXXXX-X" 120 | 121 | # (Optional) If you provide a Disqus shortname, comments will be enabled on 122 | # all pages. 123 | disqusShortname = "my-site" 124 | 125 | # (Optional) Set this to true if you use capital letters in file names 126 | disablePathToLower = true 127 | 128 | # (Optional) Set this to true to enable 'Last Modified by' date and git author 129 | # information on 'doc' type pages. 130 | enableGitInfo = true 131 | 132 | # (Optional) Theme is intended for documentation use, therefore it doesn't render taxonomy. 133 | # You can remove related files with config below 134 | disableKinds = ['taxonomy', 'taxonomyTerm'] 135 | 136 | [params] 137 | # (Optional, default true) Controls table of contents visibility on right side of pages. 138 | # Start and end levels can be controlled with markup.tableOfContents setting. 139 | # You can also specify this parameter per page in front matter. 140 | BookToC = true 141 | 142 | # (Optional, default none) Set the path to a logo for the book. If the logo is 143 | # /static/logo.png then the path would be 'logo.png' 144 | BookLogo = 'logo.png' 145 | 146 | # (Optional, default none) Set leaf bundle to render as side menu 147 | # When not specified file structure and weights will be used 148 | BookMenuBundle = '/menu' 149 | 150 | # (Optional, default docs) Specify section of content to render as menu 151 | # You can also set value to "*" to render all sections to menu 152 | BookSection = 'docs' 153 | 154 | # Set source repository location. 155 | # Used for 'Last Modified' and 'Edit this page' links. 156 | BookRepo = 'https://github.com/alex-shpak/hugo-book' 157 | 158 | # Enable 'Edit this page' links for 'doc' page type. 159 | # Disabled by default. Uncomment to enable. Requires 'BookRepo' param. 160 | # Path must point to 'content' directory of repo. 161 | BookEditPath = 'edit/master/exampleSite/content' 162 | 163 | # (Optional, default January 2, 2006) Configure the date format used on the pages 164 | # - In git information 165 | # - In blog posts 166 | BookDateFormat = 'Jan 2, 2006' 167 | 168 | # (Optional, default true) Enables search function with flexsearch, 169 | # Index is built on fly, therefore it might slowdown your website. 170 | # Configuration for indexing can be adjusted in i18n folder per language. 171 | BookSearch = true 172 | 173 | # (Optional, default true) Enables comments template on pages 174 | # By default partals/docs/comments.html includes Disqus template 175 | # See https://gohugo.io/content-management/comments/#configure-disqus 176 | # Can be overwritten by same param in page frontmatter 177 | BookComments = true 178 | ``` 179 | 180 | ### Multi-Language Support 181 | Theme supports Hugo's [multilingual mode](https://gohugo.io/content-management/multilingual/), just follow configuration guide there. You can also tweak search indexing configuration per language in `i18n` folder. 182 | 183 | ### Page Configuration 184 | 185 | You can specify additional params per page in front matter 186 | 187 | ```toml 188 | # Set type to 'docs' if you want to render page outside of configured section or if you render section other than 'docs' 189 | type = 'docs' 190 | 191 | # Set page weight to re-arrange items in file-tree menu (if BookMenuBundle not set) 192 | weight = 10 193 | 194 | # (Optional) Set to mark page as flat section in file-tree menu (if BookMenuBundle not set) 195 | bookFlatSection = true 196 | 197 | # (Optional, Experimental) Set to hide nested sections or pages at that level. Works only with file-tree menu mode 198 | bookCollapseSection = true 199 | 200 | # (Optional) Set true to hide page or section from side menu (if BookMenuBundle not set) 201 | bookHidden = true 202 | 203 | # (Optional) Set 'false' to hide ToC from page 204 | bookToC = true 205 | 206 | # (Optional) If you have enabled BookComments for the site, you can disable it for specific pages. 207 | bookComments = true 208 | ``` 209 | 210 | ### Partials 211 | 212 | There are few empty partials you can override in `layouts/partials/` 213 | 214 | | Partial | Placement | 215 | | ----------------------------------------------- | -------------------------------------- | 216 | | `layouts/partials/docs/inject/head.html` | Before closing `` tag | 217 | | `layouts/partials/docs/inject/body.html` | Before closing `` tag | 218 | | `layouts/partials/docs/inject/footer.html` | After page content | 219 | | `layouts/partials/docs/inject/menu-before.html` | At the beginning of `