larkery / zsh-histdb

A slightly better history for zsh
MIT License
1.25k stars 74 forks source link
history zsh

+TITLE:ZSH History Database

This is a small bit of zsh code that stores your history into a sqlite3 database. It improves on the normal history by storing, for each history command:

It is also possible to merge multiple history databases together without conflict, so long as all your machines have different hostnames.

You will need ~sqlite3~ and the usual coreutils commands installed on your ~PATH~. To load and activate history recording you need to source ~sqlite-history.zsh~ from your shell in your zsh startup files.

Example for installing in ~$HOME/.oh-my-zsh/custom/plugins/zsh-histdb~ (note that ~oh-my-zsh~ is not required):

+BEGIN_SRC zsh

mkdir -p $HOME/.oh-my-zsh/custom/plugins/ git clone https://github.com/larkery/zsh-histdb $HOME/.oh-my-zsh/custom/plugins/zsh-histdb

+END_SRC

Add this to your ~$HOME/.zshrc~:

+BEGIN_SRC zsh

source $HOME/.oh-my-zsh/custom/plugins/zsh-histdb/sqlite-history.zsh autoload -Uz add-zsh-hook

+END_SRC

in your zsh startup files.

** Note for OS X users

Add the following line before you source sqlite-history.zsh. See https://github.com/larkery/zsh-histdb/pull/31 for details.

+BEGIN_SRC zsh

HISTDB_TABULATE_CMD=(sed -e $'s/\x1f/\t/g')

+END_SRC

** Importing your old history

[[https://github.com/drewis/go-histdbimport][go-histdbimport]] and [[https://github.com/phiresky/ts-histdbimport][ts-histdbimport]] are useful tools for doing this! Note that the imported history will not include metadata such as the working directory or the exit status, since that is not stored in the normal history file format, so queries using ~--in DIR~, etc. will not work as expected.

With arguments, it will print history lines matching their concatenation.

For wildcards within a history line, you can use the ~%~ character, which is like the shell glob ~*~, so ~histdb this%that~ will match any history line containing ~this~ followed by ~that~ with zero or more characters in-between.

To search on particular hosts, directories, sessions, or time periods, see the help with ~histdb --help~.

You can also run ~histdb-top~ to see your most frequent commands, and ~histdb-top dir~ to show your favourite directory for running commands in, but these commands are really a bit useless. ** Example:

+BEGIN_SRC text

$ histdb strace time ses dir cmd 17/03 438 ~ strace conkeror 22/03 522 ~ strace apropos cake 22/03 522 ~ strace -e trace=file s 22/03 522 ~ strace -e trace=file ls 22/03 522 ~ strace -e trace=file cat temp/people.vcf 22/03 522 ~ strace -e trace=file cat temp/gammu.log 22/03 522 ~ run-help strace 24/03 547 ~ man strace

+END_SRC

These are all the history entries involving ~strace~ in my history. If there was more than one screenful, I would need to say ~--limit 1000~ or some other large number. The command does not warn you if you haven't seen all the results. The ~ses~ column contains a unique session number, so all the ~522~ rows are from the same shell session.

To see all hosts, add ~--host~ /after/ the query terms. To see a specific host, add ~--host hostname~. To see all of a specific session say e.g. ~-s 522 --limit 10000~. ** Integration with ~zsh-autosuggestions~

If you use [[https://github.com/zsh-users/zsh-autosuggestions][zsh-autosuggestions]] you can configure it to search the history database instead of the zsh history file thus:

+BEGIN_SRC sh

_zsh_autosuggest_strategy_histdb_top_here() { local query="select commands.argv from history left join commands on history.command_id = commands.rowid left join places on history.place_id = places.rowid where places.dir LIKE '$(sql_escape $PWD)%' and commands.argv LIKE '$(sql_escape $1)%' group by commands.argv order by count(*) desc limit 1" suggestion=$(_histdb_query "$query") }

ZSH_AUTOSUGGEST_STRATEGY=histdb_top_here

+END_SRC

This query will find the most frequently issued command that is issued in the current directory or any subdirectory. You can get other behaviours by changing the query, for example

+BEGIN_SRC sh

_zsh_autosuggest_strategy_histdb_top() { local query=" select commands.argv from history left join commands on history.command_id = commands.rowid left join places on history.place_id = places.rowid where commands.argv LIKE '$(sql_escape $1)%' group by commands.argv, places.dir order by places.dir != '$(sql_escape $PWD)', count(*) desc limit 1 " suggestion=$(_histdb_query "$query") }

ZSH_AUTOSUGGEST_STRATEGY=histdb_top

+END_SRC

This will find the most frequently issued command issued exactly in this directory, or if there are no matches it will find the most frequently issued command in any directory. You could use other fields like the hostname to restrict to suggestions on this host, etc. ** Reverse isearch If you want a history-reverse-isearch type feature there is one defined in ~histdb-interactive.zsh~. If you source that file you will get a new widget called _histdb-isearch which you can bind to a key, e.g.

+BEGIN_SRC sh

source histdb-interactive.zsh bindkey '^r' _histdb-isearch

+END_SRC

This is like normal ~history-reverse-isearch~ except:

There are also a few extra keybindings:

For inspiration you can also use ~histdb~ with the ~-d~ argument and it will print the SQL it's running.

The 3-way merge will only work properly if all the computers on which you use the repository have different hostnames.

The ~histdb-sync~ function will initialize git in the histdb directory and configure the merge driver for you first time you run it. Subsequent times it will commit all changes, pull all changes, force a merge, and push all changes back again. The commit message is useless, so if you find that kind of thing upsetting you will need to fix it.

The reason for using ~histdb-sync~ instead of doing it by hand is that if you are running the git steps in your shell the history database will be changed each command, and so you will never be able to do a pull / merge.