From 375b6c9aa0dfec062043921c07d271efcdc0cbc2 Mon Sep 17 00:00:00 2001 From: Bao Trinh Date: Wed, 22 Mar 2023 04:40:18 -0500 Subject: [PATCH] add import function originally posted at github.com/larkery/zsh-histdb/issues/137 Function to parse zsh's histfile and add it to the database. Leverages zsh's history file parsing so it handles newlines and arbitrary characters very well. The other tools suggested in the README require other languages and don't handle multiline commands soundly. Inserts are batched and it's pretty fast. A bit of time is wasted calling `fc -l` to read the timestamps, but it takes ~1s to parse ~20k lines for me so it should be fast enough for most use-cases. Notes: * this sets the `session` to `0` * (sqlite starts autoincrement ids at 1 so it shouldn't coincide with any actual histdb sessions) * `dir` is set to the empty string (`''`) instead of `NULL` because sqlite doesn't let you use `NULL` as part of a key * Added `unique(session, command_id, place_id, start_time) on conflict ignore` constraint to the `history` table so history instances are de-duped. * Useful for importing history file backups that likely contain dupes --- sqlite-history.zsh | 66 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 65 insertions(+), 1 deletion(-) diff --git a/sqlite-history.zsh b/sqlite-history.zsh index fb5338b..a96fbc7 100644 --- a/sqlite-history.zsh +++ b/sqlite-history.zsh @@ -88,7 +88,8 @@ create table history (id integer primary key autoincrement, place_id int references places (id), exit_status int, start_time real, - duration real + duration real, + unique(session, command_id, place_id, start_time) on conflict ignore ) strict; PRAGMA user_version = 3; EOF @@ -136,6 +137,68 @@ where id = (select max(id) from history) and EOF } +_histdb_import() { + emulate -L zsh + # more accurate parsing of history file + setopt HIST_LEX_WORDS + + # set local so later fc -p/-P commands don't modify caller's history settings + local HISTFILE + local HISTSIZE + local SAVEHIST + + local _HISTFILE=${1:-$HISTFILE} + # push current history list onto a stack and initialize a new one + fc -p -a + # read all the lines from the given histfile + HISTFILE=$_HISTFILE + HISTSIZE=999999999 + SAVEHIST=0 + fc -R ${1:-$HISTFILE} + print "loaded ${#history} lines from history file: $HISTFILE" >&2 + # unset HISTFILE after reading so we don't (somehow) accidentally write to it + HISTFILE= + + local -a histories + local -i i=0 + # history is read into `history` associative array, but we don't have + # direct access to timestamps so parse them from `fc -l` + fc -l -t %s -d -D 0 | while { read -r histcmd timestamp duration _cmd } { + # the command output by `fc -l` is escaped so use $history[$histcmd] + # instead to get the raw, unescaped characters + # omit empty commands (not sure how this happens. `print -S` maybe?) + [[ -n "${history[$histcmd]}" ]] || continue + ((++i)) + # duration is formatted as m:ss so parse it back into integer seconds + # escape the history command + histories+=("(${timestamp}, $((${duration%:*}*60 + ${duration#*:})), '${history[$histcmd]//'/''}')") + } + + _histdb_init + + result=$(_histdb_query <