tkellogg/dura

Improve "how to recover" experience

tkellogg opened this issue · 4 comments

The readme outlines how to use a dura branch to recover, but it involves a lot of git-fu. It could be a lot easier

Idea: sub-commands

Add dura subcommands to do things like

  • Get the branch corresponding to HEAD, e.g. dura head
  • Get a commit by time frame, e.g. dura head 4h. I imagine this searching across dura branches, since we can indeed go prior to the HEAD commit.
  • Recover to a specific commit hash (or a time frame).

Idea: Add help messages in relevant places

Documentation in the readme is great, but it could be better if we added help directly into messages that dura has to leave anyway. For example, why not set the commit message to something like:

dura auto-backup

To recover to this commit, run this:

<<git commands>>

Using a recent-enough git version, you can use https://git-scm.com/docs/git-restore, and combine this with some git rev-list magic to automatically get the latest snapshot from eg. 1 hour ago, and even restrict to some files only. Stuff it all in a git alias, and here is your magic:

# You need to do this only once and for all
git config --global alias.dura '!restore () { local when="$1"; shift; cd "$GIT_PREFIX"; git restore --worktree --staged --source $(git rev-list -n 1 dura-$(git rev-parse HEAD) --until "$when") ${@:-:/} ; }; restore'
# Then in your repo you can restore things like they were 1 hour ago:
git dura 1hour
# Or longer:
git dura 2month
# You can also give an exact date:
git dura 2021-03-04
# And restrict to some files (any git pathspec will work here):
git dura 15min README 'src/*.c'

Note that by default it will restore all the files in your repository, even if you are in a subdirectory (that's the magic part done by ${@:-:/}).

As it turns out, you don't even need git restore, using --staged --worktree is equivalent to git checkout hence:

git config --global alias.dura '!restore () { local when="$1"; shift; cd "$GIT_PREFIX"; git checkout $(git rev-list -n 1 dura-$(git rev-parse HEAD) --until "$when") -- ${@:-:/} ; }; restore'

The alias will fail if you don't provide a date, which is working as intended (but the error message is confusing, let me know if you want error handling for it).

And if you want to list from all dura branches, and not only the one deriving from HEAD, it's even easier:

git config --global alias.dura '!restore () { local when="$1"; shift; cd "$GIT_PREFIX"; git checkout $(git rev-list -n 1 --branches=dura-* --until "$when") -- ${@:-:/} ; }; restore'

Can you send a pull request to add this to the README?

If you're feeling brave, you could even add this to dura serve, somewhere around here so that it gets set every time dura starts up. Libgit2 docs are here, but I think it would look roughly like this:

fn set_git_alias() -> Result<(), Error> {
    let git_alias = "...";
    let cfg = Config::open(Config::find_global()?)?;
    cfg.set_str("alias.dura-reset", git_alias)?;
    Ok(())
}