Dura - You shouldn't ever lose your work if you're using Git

Related tags

Command-line git rust


Dura is a background process that watches your Git repositories and commits your uncommitted changes without impacting HEAD, the current branch, or the Git index (staged files). If you ever get into an "oh snap!" situation where you think you just lost days of work, checkout a dura branch and recover.

Without dura, you use Ctrl-Z in your editor to get back to a good state. That's so 2021. Computers crash and Ctrl-Z only works on files independently. Dura snapshots changes across the entire repository as-you-go, so you can revert to "4 hours ago" instead of "hit Ctrl-Z like 40 times or whatever". Finally, some sanity.

How to use

Run it in the background:

$ dura serve &

The serve can happen in any directory. The & is Unix shell syntax to run the process in the background, meaning that you can start dura and then keep using the same terminal window while dura keeps running. You could also run dura serve in a window that you keep open.

Let dura know which repositories to watch:

$ cd some/git/repo
$ dura watch

Right now, you have to cd into each repo that you want to watch, one at a time. If you have thoughts on how to do this better, share them here.

Make some changes. No need to commit or even stage them. Use any Git tool to see the dura branches:

$ git log --all

dura produces a branch for every real commit you make and makes commits to that branch without impacting your working copy. You keep using Git exactly as you did before.

How to recover

The dura branch that's tracking your current uncommitted changes looks like dura-f4a88e5ea0f1f7492845f7021ae82db70f14c725. In $SHELL, you can get the branch name via:

$ echo "dura-$(git rev-parse HEAD)"

Use git log or tig to figure out which commit you want to rollback to. Copy the hash and then run something like

# Or, if you don't trust dura yet, `git stash`
$ git reset HEAD --hard
# get the changes into your working directory
$ git checkout $THE_HASH
# last few commands reset HEAD back to master but with changes uncommitted
$ git checkout -b temp-branch
$ git reset master
$ git checkout master
$ git branch -D temp-branch

If you're interested in improving this experience, collaborate here.


  1. Install Rust (e.g., brew install rustup)
  2. Clone this repository
  3. Run cargo install --path .


Is this stable?

It's still in the prototype phase. Open issues pertaining to stability are marked with the stability tag.

How often does this check for changes?

Every now and then, like 5 seconds or so. Internally there's a control loop that sleeps 5 seconds between iterations, so it runs less frequently than every 5 seconds (potentially a lot less frequently, if there's a lot of work to do).

Does this work on my OS?

  • Mac: yes
  • Linux: probably
  • Windows: possibly
  • Config file

    Config file

    I think a config file to change certain settings, like default branch name, check interval, and setting default username and email like #21 suggested, would be very helpful. Could be in your home directory called .dura in a format similar to a .gitignore.

    opened by chand1012 10
  • Shutting down because other poller took lock: None

    Shutting down because other poller took lock: None

    I was running dura yesterday and it was working great. I think I accidentally closed the terminal it was running in and re-opened it, and now when I run dura serve I get this error:

    $ dura serve
    pid: 2285
    Wrote /Users/dan/.config/dura/config.json
    Shutting down because other poller took lock: None

    My first thought was maybe I had another dura running but I don't think I do:

    ps aux | grep dura
    dan               2168   0.0  0.0 407963440    416 s001  R+   10:02AM   0:00.00 grep dura

    Running dura kill didn't help either, I still get the same error when running dura serve.

    opened by jazzdan 7
  • Fix tests: write runtime db

    Fix tests: write runtime db

    Tests broke in #66 but we didn't catch it because our CI/CD pipeline doesn't run cargo test. This also enables tests on all platforms.

    Ping @JakeStanger

    closes #72

    opened by tkellogg 5
  • Build Fail: Can't find GLIBC

    Build Fail: Can't find GLIBC

    @drupol This definitely seems like a NixOS environment issue. Can you take a look? On Ubuntu it's usually solved with installing some C/C++ tools, like gcc. Not really sure about NixOS.


    opened by tkellogg 5
  • Better CLI implementation

    Better CLI implementation

    The current CLI is fine, but the need to be in the current directory for some operations is a little annoying. The current implementation also makes it difficult to expand for more features (such as the WatchConfig fields added in #49 ).

    I would suggest initially migrating the current code to use Clap or similar.

    Commands could then be updated to the something like the below format:

    dura serve # can be left as-is
    # these default to cwd
    dura watch [dir] [--include <includes>] [--exclude <excludes>] [--max-depth <depth>]
    dura unwatch [dir]
    dura capture [dir]
    opened by JakeStanger 5
  • feat: add support for recursively scanning directories

    feat: add support for recursively scanning directories

    This works towards implementing features discussed in #3. It is still using the config.json format at the moment. I'll pick up discussion in #28 for migrating to a human-friendly format.

    You can now eg navigate to ~/Programming and run dura watch to watch the whole directory recursively. The program will scan each child dir until either a repo is found or the max depth is hit.

    Includes, excludes and the max depth can be manually tweaked in the config.json file.

    If include is non-empty, then exclude is ignored.

    Example config which includes two repos, one at Programming/dura and the other at Programming/unity/my-game:

    {"pid":27814,"repos":{"/home/jake/Programming":{"include":["dura", "unity/my-game"],"exclude":[],"max_depth":255}}}

    Example config which excludes all repos inside Programming/third-party:

    opened by JakeStanger 5
  • How about a single branch mode?

    How about a single branch mode?

    I use git branch often, so I don't want to have hundreds of dura-HASH branches in the output, but I don't mind another static branch next to the master and staging. Is it possible to have just one dura branch, but keep snapshotting everything? I know that filtering out dura-HASH branches would be a solution to my problem, but still, I would like the single branch mode better.

    P.S.: I'm very executed about this project for I've developed a habit of making a temporary commit every few minutes over the years, but your solution is better because it's automated ❤️

    opened by nskazki 5
  • Log to stdout

    Log to stdout

    Last night I made a change to log JSON to ~/.config/dura/logs/. However, writing to stdout has a lot of advantages when running with other tools, like systemd.

    • Print to stdout by default
    • Print to file if --log-file is specified
    good first issue 
    opened by tkellogg 5
  • Panicked while starting the daemon server

    Panicked while starting the daemon server

    • MacOS Monterey 12.1 (21C52) (Intel)
    • rustc 1.57.0 (f1edd0429 2021-11-29)

    On commit f4d552da1f92f3d5ad85de483f2faa0a3d2efa65.

    Steps executed:

    • brew install rustup
    • rustup-init
    • cargo install --path .
    • RUST_BACKTRACE=1 dura serve

    And got:

    pid: 62355
    thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: Os { code: 2, kind: NotFound, message: "No such file or directory" }', src/config.rs:63:14
    stack backtrace:
       0: rust_begin_unwind
                 at /rustc/f1edd0429582dd29cccacaf50fd134b05593bd9c/library/std/src/panicking.rs:517:5
       1: core::panicking::panic_fmt
                 at /rustc/f1edd0429582dd29cccacaf50fd134b05593bd9c/library/core/src/panicking.rs:100:14
       2: core::result::unwrap_failed
                 at /rustc/f1edd0429582dd29cccacaf50fd134b05593bd9c/library/core/src/result.rs:1616:5
       3: dura::config::Config::save
       4: <core::future::from_generator::GenFuture<T> as core::future::future::Future>::poll
       5: tokio::park::thread::CachedParkThread::block_on
       6: tokio::runtime::thread_pool::ThreadPool::block_on
       7: tokio::runtime::Runtime::block_on
       8: dura::main
    note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.
    opened by eliseumds 5
  • brew install dura

    brew install dura

    Hi, just wanted to share that Dura is now available on Homebrew, so Mac users can install it with:

    brew install dura

    Homebrew precompiles binaries for both x86-64 and arm64, so installation is pretty much instant 🎉

    Ref: https://github.com/Homebrew/homebrew-core/pull/106554

    opened by ankane 4
  • Add commit sha to build artifacts

    Add commit sha to build artifacts

    Hi, This adds the commit sha256 sum to the end of the artifact names to differentiate between versions.

    BTW, do you have any plans for a release schedule? I can create a workflow for automatic releases. I also added a pre-requisite for that in this commit: (https://github.com/tkellogg/dura/commit/1a09368649347e7ebbcfb40d6ed96a3f1f158fff)


    opened by DestroyerXyz 4
  • Duralite: cache directory listing

    Duralite: cache directory listing

    Reduce disk I/O by moving most directory listing to a probabalistic cache approach. For one, I don't want to wear out disks by constantly accessing them. Spinning disks are especially problematic because latency can be quite high.

    This introduces CachedDirIter which replaces usages of fs::ReadDir. It's an enum that can either wrap a fs::ReadDir or represent a cache hit by iterating a vector af paths. The cache itself is a Trie, so it shouldn't take that much memory to hold in memory all paths under $HOME, for example. The cache lives at program scope and is passed down into where it's needed.

    Cache invalidation is a problem, though. I don't want to refresh the entire Trie all at once, but I also want guarantees that a new directory will be recognized within a certain time limit, e.g. within 10 minutes. Here I take a probabalistic approach. Each individual directory entry is invalidated independently. On any given pass, there's an X% chance that a single directory will be invalidated. X is calculated such that caches will be invalidated within some maximum time bound (10 minutes) 95% of the time. In the remaining 10% of cases, they're force-invalidated at the 10 minute mark.

    The duralite sub-project has been about reducing dura's presence on host machines by utilizing as few resources as possible. I don't want people to not use dura because "my computer runs slow with it". Something I've been observing is that, as I reduce these I/O-intensive bottlenecks, dura uses even more CPU. In a follow-up PR I want to add some strategically placed thread::sleep's to spread out the CPU usage evenly throughout the entire 5 second polling interval.

    opened by tkellogg 1
  • Error: UnbornBranch

    Error: UnbornBranch


    • Run dura serve
    • Create a new repo, e.g. with cargo new, and make changes without committing
    • Check the stdout log, there should be errors that look like:
    {"target":"dura::poller","file":"src/poller.rs","name":"event src/poller.rs:41","level":"Level(Info)","fields":{"message":"info_operation","operation":{"Snapshot":{"error":"reference 'refs/heads/master' not found; class=Reference (4); code=UnbornBranch (-9)","latency":0.000267355,"op":null,"repo":"/Users/timkellogg/code/oss/syndr"}}},"time":"2022-11-28T14:27:18.611085+00:00"}

    Expected behavior

    This happens when there's no first commit. Normally the branch name is dura_{base_commit_hash}, in this case we could make it a constant dura_unborn or dura_init.

    This also means that the first commit in sequence might not have a parent, which may cause other problems.

    bug good first issue rust 
    opened by tkellogg 6
  • Couldnt you just git commit more often?

    Couldnt you just git commit more often?

    It takes seconds to run the command. Not sure the necessity of this at all, and it may actually cause bad habits and behaviors that could lead to other sloppiness and waste in other areas of your life.

    opened by james-see 4
  • Dura doesn't create any branches on github codespaces

    Dura doesn't create any branches on github codespaces

    I'm new to using dura, so I might be doing something wrong. I've had dura running in the background for the last 30min or so (e.g. via dura serve &). See terminal output below;

    @silvergasp ➜ /workspaces/embedded_controller (main ✗) $ ps
      PID TTY          TIME CMD
     3596 pts/0    00:00:00 bash
     5004 pts/0    00:00:00 dura
     7420 pts/0    00:00:00 ps
    @silvergasp ➜ /workspaces/embedded_controller (main ✗) $ git branch
    * main
    @silvergasp ➜ /workspaces/embedded_controller (main ✗) $ 

    Note that the only branch is the 'main' branch.

    Note that this is running in Github codespaces on the latest version of ubuntu;

    No LSB modules are available.
    Distributor ID: Ubuntu
    Description:    Ubuntu 20.04.4 LTS
    Release:        20.04
    Codename:       focal
    opened by silvergasp 2
  • flake.lock: Update

    flake.lock: Update

    Automated changes by the update-flake-lock GitHub Action.

    Flake lock file updates:
    • Updated input 'flake-utils':
        'github:numtide/flake-utils/1ed9fb1935d260de5fe1c2f7ee0ebaae17ed2fa1' (2022-05-30)
      → 'github:numtide/flake-utils/5aed5285a952e0b949eb3ba02c12fa4fcfef535f' (2022-11-02)
    • Updated input 'nixpkgs':
        'github:nixos/nixpkgs/90cd5459a1fd707819b9a3fb9c852beaaac3b79a' (2022-06-11)
      → 'github:nixos/nixpkgs/652e92b8064949a11bc193b90b74cb727f2a1405' (2022-12-22)
    • Updated input 'rust-overlay':
        'github:oxalica/rust-overlay/e04a88d7f859ae9ec42267866bb68c1a741e6859' (2022-06-12)
      → 'github:oxalica/rust-overlay/fd2740316bacb3e0106381c325e0bb90d6790aeb' (2022-12-25)
    • Updated input 'rust-overlay/flake-utils':
        'github:numtide/flake-utils/bba5dcc8e0b20ab664967ad83d24d64cb64ec4f4' (2021-11-15)
      → 'github:numtide/flake-utils/c0e246b9b83f637f4681389ecabcb2681b4f3af0' (2022-08-07)
    • Updated input 'rust-overlay/nixpkgs':
        'github:NixOS/nixpkgs/8afc4e543663ca0a6a4f496262cd05233737e732' (2021-11-21)
      → 'github:NixOS/nixpkgs/14ccaaedd95a488dd7ae142757884d8e125b3363' (2022-10-09)

    Running GitHub Actions on this PR

    GitHub Actions will not run workflows on pull requests which are opened by a GitHub Action.

    To run GitHub Actions workflows on this PR, run:

    git branch -D update_flake_lock_action
    git fetch origin
    git checkout update_flake_lock_action
    git commit --amend --no-edit
    git push origin update_flake_lock_action --force
    opened by github-actions[bot] 0
Tim Kellogg
Tim Kellogg
A git sub-command to view your git repository in the web browser

git-view A git sub-command to view your git repository in the web browser! About Are you also frustrated from moving your hands away from the keyboard

Hamothy 5 Sep 26, 2022
This is the weirdest thing I've ever seen

strange-config-format I got nerdsniped on Twitter. Here's the fastest way to get a solution I could manage. It took about 90 minutes. Usage $ cargo ru

Charlotte Som 2 Jul 25, 2022
A library for loading and executing PE (Portable Executable) from memory without ever touching the disk

memexec A library for loading and executing PE (Portable Executable) from memory without ever touching the disk This is my own version for specific pr

FssAy 5 Aug 27, 2022
git-cliff can generate changelog files from the Git history by utilizing conventional commits as well as regex-powered custom parsers.⛰️

git-cliff can generate changelog files from the Git history by utilizing conventional commits as well as regex-powered custom parsers. The changelog template can be customized with a configuration file to match the desired format.

Orhun Parmaksız 5k Jan 9, 2023
Git Explorer: cross-platform git workflow improvement tool inspired by Magit

Gex Git workflow improvement CLI tool inspired by Magit. This project is still under initial development, but I am actively dogfooding it and features

Peter Hebden 204 Jan 6, 2023
Print your git contributions in your terminal, blazingly fast

Takoyaki Blazingly fast git contribution graph in your terminal Features ✔️ Customizable ✔️ Plugins to support a bunch of cloud based git repositories

kyeboard 13 Feb 6, 2023
🌌⭐cosmo is a wrapper for Git essentially, allowing you to compress multiple commands into one

❯ Cosmo Git tooling of the future New feature: Cosmo hooks! Click here for more info! ❯ ?? Features Config files (with defaults!) Fast Easy to use Fri

Jack 1 Oct 31, 2021
gfold is a CLI-driven application that helps you keep track of multiple Git repositories.

gfold is a CLI-driven application that helps you keep track of multiple Git repositories.

Nick Gerace 215 Jan 4, 2023
zigfi is an open-source stocks, commodities and cryptocurrencies price monitoring CLI app, written fully in Rust, where you can organize assets you're watching easily into watchlists for easy access on your terminal.

zigfi zigfi is an open-source stocks, commodities and cryptocurrencies price monitoring CLI app, written fully in Rust, where you can organize assets

Aldrin Zigmund Cortez Velasco 18 Oct 24, 2022
A git command to quickly save your local changes in case of earthquake !

git-eq (aka git earthquake) Earthquakes are part of the daily life in many countries like in Taiwan. git-eq is a simple git command to quickly save yo

Jérôme MEVEL 6 Dec 16, 2022
FastSSH is a TUI that allows you to quickly connect to your services by navigating through your SSH config.

Connect quickly to your services ?? FastSSH is a TUI that allows you to quickly connect to your services by navigating through your SSH config. Instal

Julien 85 Dec 14, 2022
Deadliner helps you keep track of the time left for your deadline by dynamically updating the wallpaper of your desktop with the time left.

Deadliner Watch the YouTube video What's Deadliner? Deadliner is a cross-platform desktop application for setting deadline for a project and keeping t

Deadliner 34 Dec 16, 2022
A Yocto setup and management tool that helps you keep your environment up-to-date and in-sync with your team

yb (Yocto Buddy) yb is designed to make it easy to setup and (perhaps more importantly) keep Yocto environments up-to-date and in-sync with your team.

null 13 Oct 31, 2022
Answering the question nobody asked: what if you wanted to text your friends using only ARP?

arpchat so... you know arp? the protocol your computer uses to find the mac addresses of other computers on your network? yeah. that. i thought it wou

Kognise 1.3k Jan 1, 2023
An experimental, work-in-progress PAM module for Tailscale

Experimental Tailscale PAM Module This is a very very experimental Tailscale PAM module that allows you to SSH using your Tailscale credentials. This

Tailscale 129 Nov 20, 2022
Work-in-progress Rust application that converts C++ header-only libraries to single self-contained headers.

unosolo Work-in-progress Rust application that converts C++ header-only libraries to single self-contained headers. Disclaimer This is my first Rust p

Vittorio Romeo 26 Jul 9, 2021
Work-in-progress software for managing the Azeron keypad on any operating system.

azeron-cli A small, unfinished CLI application intended to manage the Azeron Cyborg. The code is still in a very messy state and doesn't look very rus

cozyGalvinism 5 Nov 24, 2022
Work in progress NCBI's Common Tree alternative in the terminal

Lomanai Usage lomanai --species 'Mus musculus' --species 'Homo sapiens' #> Mammalia #> ++Rodentia #> | \-Mus musculus #> \+Primates #> \-Homo sapien

Jean Manguy 3 Dec 20, 2022
A work-in-progress static analyser.

Statan Statan is an early-stage static analyser for PHP and PXP projects. It is being developed in public and the journey is documented on my blog. Th

PXP 12 Jan 30, 2023