Skip to content

Commit 032090e

Browse files
jjbhyperupcall
andauthored
Add persistent history to git-repl (#1226)
* persistent command history * notes * decision on which commands to save * configurable history location * fix history initialization in various scenarios * documentation * comment * don't add whitespace or consecutive duplicates to history * fix manpage output * Update bin/git-repl Co-authored-by: Edwin Kofler <edwin@kofler.dev> * Update bin/git-repl Co-authored-by: Edwin Kofler <edwin@kofler.dev> * Update git-repl * Update bin/git-repl * Apply suggestions from code review * Add `extglob` and generate docs * Fix regex * update docs rm man/git-repl.{1,html} touch man/git-repl.md make man/git-repl.{1,html} --------- Co-authored-by: Edwin Kofler <edwin@kofler.dev>
1 parent 5367101 commit 032090e

File tree

7 files changed

+68
-188
lines changed

7 files changed

+68
-188
lines changed

Commands.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -392,6 +392,19 @@ Type `exit`, `quit`, or `q` to end the repl session.
392392
Any arguments to git repl will be taken as the first command to execute in
393393
the repl.
394394

395+
### CONFIGURATION
396+
397+
Commands entered in a repl session will be saved to a history file and be available in
398+
future sessions, similar to a shell or programming language repl. By default,
399+
there is one global history file, ~/.git_repl_history. You can specify that your projects
400+
each have their own independent history file. This file will be saved in .git_repl_history
401+
at the top level of the repo, and will need to be added to the repo or global .gitignore.
402+
403+
```bash
404+
# remove the --global flag to configure only an individual project to have its own history file
405+
git config --global git-extras.repl.use-local-history "true"
406+
```
407+
395408
You can specify a default command to run when hitting enter:
396409

397410
```bash

bin/git-repl

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,26 @@
11
#!/usr/bin/env bash
2+
shopt -s extglob
23

34
git version
45
echo "git-extras version ""$(git-extras -v)"
56
echo "Type 'ls' to ls files below current directory; '!command' to execute any command or just 'subcommand' to execute any git subcommand; 'quit', 'exit', 'q', ^D, or ^C to exit the git repl."
67

8+
HISTIGNORE=${HISTIGNORE:-+([[:space:]])}
9+
HISTCONTROL=${HISTCONTROL:-ignoredups}
10+
use_local_history=$(git config --get --default 'false' git-extras.repl.use-local-history)
11+
if [[ "$use_local_history" == "true" ]]; then
12+
HISTFILE="$(git rev-parse --show-toplevel)/.git_extras_repl_history"
13+
else
14+
HISTFILE=${XDG_STATE_HOME:-$HOME/.local/state}/git_extras_repl_history
15+
fi
16+
17+
# file doesn't exist, is empty, or contains only whitespace
18+
if [[ ! -f "$HISTFILE" ]] || [[ ! -s "$HISTFILE" ]] || ! grep -q '[^[:space:]]' "$HISTFILE"; then
19+
# `history -r` .... `history -a` are not happy with an empty initial file in bash 3.2.57, which is what MacOS 26 ships with
20+
echo '!echo welcome to git-repl!' >> "$HISTFILE"
21+
fi
22+
history -r
23+
724
while true; do
825
# Current branch
926
cur=$(git symbolic-ref HEAD 2> /dev/null | cut -d/ -f3-)
@@ -46,8 +63,10 @@ while true; do
4663
test $? -ne 0 && break
4764
fi
4865

49-
# History
50-
history -s "$cmd"
66+
# Add command to history if it is not all whitespace
67+
if [[ ! "$cmd" =~ ^[[:space:]]*$ ]]; then
68+
history -s "$cmd"
69+
fi
5170

5271
# Built-in commands
5372
case $cmd in
@@ -63,6 +82,8 @@ while true; do
6382
quit|exit|q) break;;
6483
esac
6584

85+
history -a
86+
6687
if [[ $cmd == !* ]]; then
6788
# shellcheck disable=SC2086
6889
eval ${cmd:1}

man/git-extras.1

Lines changed: 0 additions & 183 deletions
Original file line numberDiff line numberDiff line change
@@ -1,183 +0,0 @@
1-
.\" generated with Ronn-NG/v0.10.1
2-
.\" http://github.com/apjanke/ronn-ng/tree/0.10.1
3-
.TH "GIT\-EXTRAS" "1" "May 2025" "" "Git Extras"
4-
.SH "NAME"
5-
\fBgit\-extras\fR \- Awesome GIT utilities
6-
.SH "SYNOPSIS"
7-
\fBgit\-extras\fR [\-v,\-\-version] [\-h,\-\-help] [update]
8-
.SH "OPTIONS"
9-
\-v, \-\-version
10-
.P
11-
Show git\-extras version number\.
12-
.P
13-
\-h, \-\-help
14-
.P
15-
Show this help\. This option can also be used for any of the extras commands\.
16-
.P
17-
update
18-
.P
19-
Self update\.
20-
.SH "ENVIRONMENT AND CONFIGURATION VARIABLES"
21-
\fBgit config \-\-add git\-extras\.default\-branch $BRANCH\fR
22-
.P
23-
Change the default branch to \fB$BRANCH\fR\. If \fBgit\-extras\.default\-branch\fR isn't set, \fBinit\.defaultBranch\fR is used instead\. If none of them are set it defaults to \fBmain\fR\.
24-
.SH "COMMANDS"
25-
.IP "\(bu" 4
26-
\fBgit\-abort(1)\fR Abort current git operation
27-
.IP "\(bu" 4
28-
\fBgit\-alias(1)\fR Define, search and show aliases
29-
.IP "\(bu" 4
30-
\fBgit\-archive\-file(1)\fR Export the current HEAD of the git repository to an archive
31-
.IP "\(bu" 4
32-
\fBgit\-authors(1)\fR Generate authors report
33-
.IP "\(bu" 4
34-
\fBgit\-browse\-ci(1)\fR \fIView the web page for the current repository\fR
35-
.IP "\(bu" 4
36-
\fBgit\-browse(1)\fR \fIView the web page for the current repository\fR
37-
.IP "\(bu" 4
38-
\fBgit\-brv(1)\fR List branches sorted by their last commit date
39-
.IP "\(bu" 4
40-
\fBgit\-bulk(1)\fR Run git commands on multiple repositories
41-
.IP "\(bu" 4
42-
\fBgit\-changelog(1)\fR Generate a changelog report
43-
.IP "\(bu" 4
44-
\fBgit\-clear\-soft(1)\fR Soft clean up a repository
45-
.IP "\(bu" 4
46-
\fBgit\-clear(1)\fR Rigorously clean up a repository
47-
.IP "\(bu" 4
48-
\fBgit\-coauthor(1)\fR Add a co\-author to the last commit
49-
.IP "\(bu" 4
50-
\fBgit\-commits\-since(1)\fR Show commit logs since some date
51-
.IP "\(bu" 4
52-
\fBgit\-continue(1)\fR Continue current git operation
53-
.IP "\(bu" 4
54-
\fBgit\-contrib(1)\fR Show user's contributions
55-
.IP "\(bu" 4
56-
\fBgit\-count(1)\fR Show commit count
57-
.IP "\(bu" 4
58-
\fBgit\-cp(1)\fR Copy a file keeping its history
59-
.IP "\(bu" 4
60-
\fBgit\-create\-branch(1)\fR Create branches
61-
.IP "\(bu" 4
62-
\fBgit\-delete\-branch(1)\fR Delete branches
63-
.IP "\(bu" 4
64-
\fBgit\-delete\-merged\-branches(1)\fR Delete merged branches
65-
.IP "\(bu" 4
66-
\fBgit\-delete\-squashed\-branches(1)\fR Delete branches that were squashed
67-
.IP "\(bu" 4
68-
\fBgit\-delete\-submodule(1)\fR Delete submodules
69-
.IP "\(bu" 4
70-
\fBgit\-delete\-tag(1)\fR Delete tags
71-
.IP "\(bu" 4
72-
\fBgit\-delta(1)\fR Lists changed files
73-
.IP "\(bu" 4
74-
\fBgit\-effort(1)\fR Show effort statistics on file(s)
75-
.IP "\(bu" 4
76-
\fBgit\-feature(1)\fR Create/Merge feature branch
77-
.IP "\(bu" 4
78-
\fBgit\-force\-clone(1)\fR overwrite local repositories with clone
79-
.IP "\(bu" 4
80-
\fBgit\-fork(1)\fR Fork a repo on github
81-
.IP "\(bu" 4
82-
\fBgit\-fresh\-branch(1)\fR Create fresh branches
83-
.IP "\(bu" 4
84-
\fBgit\-get(1)\fR Clone a Git repository under a configured directory
85-
.IP "\(bu" 4
86-
\fBgit\-gh\-pages(1)\fR Create the GitHub Pages branch
87-
.IP "\(bu" 4
88-
\fBgit\-graft(1)\fR Merge and destroy a given branch
89-
.IP "\(bu" 4
90-
\fBgit\-guilt(1)\fR calculate change between two revisions
91-
.IP "\(bu" 4
92-
\fBgit\-ignore\-io(1)\fR Get sample gitignore file
93-
.IP "\(bu" 4
94-
\fBgit\-ignore(1)\fR Add \.gitignore patterns
95-
.IP "\(bu" 4
96-
\fBgit\-info(1)\fR Returns information on current repository
97-
.IP "\(bu" 4
98-
\fBgit\-local\-commits(1)\fR List local commits
99-
.IP "\(bu" 4
100-
\fBgit\-lock(1)\fR Lock a file excluded from version control
101-
.IP "\(bu" 4
102-
\fBgit\-locked(1)\fR ls files that have been locked
103-
.IP "\(bu" 4
104-
\fBgit\-magic(1)\fR Automate add/commit/push routines
105-
.IP "\(bu" 4
106-
\fBgit\-merge\-into(1)\fR Merge one branch into another
107-
.IP "\(bu" 4
108-
\fBgit\-merge\-repo(1)\fR Merge two repo histories
109-
.IP "\(bu" 4
110-
\fBgit\-missing(1)\fR Show commits missing from another branch
111-
.IP "\(bu" 4
112-
\fBgit\-mr(1)\fR Checks out a merge request locally
113-
.IP "\(bu" 4
114-
\fBgit\-obliterate(1)\fR rewrite past commits to remove some files
115-
.IP "\(bu" 4
116-
\fBgit\-paste(1)\fR Send patches to pastebin for chat conversations
117-
.IP "\(bu" 4
118-
\fBgit\-pr(1)\fR Checks out a pull request locally
119-
.IP "\(bu" 4
120-
\fBgit\-psykorebase(1)\fR Rebase a branch with a merge commit
121-
.IP "\(bu" 4
122-
\fBgit\-pull\-request(1)\fR Create pull request for GitHub project
123-
.IP "\(bu" 4
124-
\fBgit\-reauthor(1)\fR Rewrite history to change author's identity
125-
.IP "\(bu" 4
126-
\fBgit\-rebase\-patch(1)\fR Rebases a patch
127-
.IP "\(bu" 4
128-
\fBgit\-release(1)\fR Commit, tag and push changes to the repository
129-
.IP "\(bu" 4
130-
\fBgit\-rename\-branch(1)\fR rename local branch and push to remote
131-
.IP "\(bu" 4
132-
\fBgit\-rename\-file(1)\fR Rename a file or directory and ensure Git recognizes the change, regardless of filesystem case\-sensitivity\.
133-
.IP "\(bu" 4
134-
\fBgit\-rename\-remote(1)\fR Rename a remote
135-
.IP "\(bu" 4
136-
\fBgit\-rename\-tag(1)\fR Rename a tag
137-
.IP "\(bu" 4
138-
\fBgit\-repl(1)\fR git read\-eval\-print\-loop
139-
.IP "\(bu" 4
140-
\fBgit\-reset\-file(1)\fR Reset one file
141-
.IP "\(bu" 4
142-
\fBgit\-root(1)\fR show path of root
143-
.IP "\(bu" 4
144-
\fBgit\-scp(1)\fR Copy files to SSH compatible \fBgit\-remote\fR
145-
.IP "\(bu" 4
146-
\fBgit\-sed(1)\fR replace patterns in git\-controlled files
147-
.IP "\(bu" 4
148-
\fBgit\-setup(1)\fR Set up a git repository
149-
.IP "\(bu" 4
150-
\fBgit\-show\-merged\-branches(1)\fR Show merged branches
151-
.IP "\(bu" 4
152-
\fBgit\-show\-tree(1)\fR show branch tree of commit history
153-
.IP "\(bu" 4
154-
\fBgit\-show\-unmerged\-branches(1)\fR Show unmerged branches
155-
.IP "\(bu" 4
156-
\fBgit\-squash(1)\fR squash N last changes up to a ref'ed commit
157-
.IP "\(bu" 4
158-
\fBgit\-stamp(1)\fR Stamp the last commit message
159-
.IP "\(bu" 4
160-
\fBgit\-standup(1)\fR Recall the commit history
161-
.IP "\(bu" 4
162-
\fBgit\-summary(1)\fR Show repository summary
163-
.IP "\(bu" 4
164-
\fBgit\-sync(1)\fR Sync local branch with remote branch
165-
.IP "\(bu" 4
166-
\fBgit\-touch(1)\fR Touch and add file to the index
167-
.IP "\(bu" 4
168-
\fBgit\-undo(1)\fR Remove latest commits
169-
.IP "\(bu" 4
170-
\fBgit\-unlock(1)\fR Unlock a file excluded from version control
171-
.IP "\(bu" 4
172-
\fBgit\-unwip(1)\fR Undo a Work In Progress commit
173-
.IP "\(bu" 4
174-
\fBgit\-utimes(1)\fR Change files modification time to their last commit date
175-
.IP "\(bu" 4
176-
\fBgit\-wip(1)\fR Create a Work In Progress commit
177-
.IP "" 0
178-
.SH "AUTHOR"
179-
Written by Tj Holowaychuk <\fItj@vision\-media\.ca\fR>
180-
.SH "REPORTING BUGS"
181-
<\fIhttps://github\.com/tj/git\-extras/issues\fR>
182-
.SH "SEE ALSO"
183-
<\fIhttps://github\.com/tj/git\-extras\fR>

man/git-extras.html

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

man/git-repl.1

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
.\" generated with Ronn-NG/v0.10.1
22
.\" http://github.com/apjanke/ronn-ng/tree/0.10.1
3-
.TH "GIT\-REPL" "1" "January 2026" "" "Git Extras"
3+
.TH "GIT\-REPL" "1" "February 2026" "" "Git Extras"
44
.SH "NAME"
55
\fBgit\-repl\fR \- git read\-eval\-print\-loop
66
.SH "SYNOPSIS"
@@ -32,6 +32,14 @@ exit|quit|q
3232
.P
3333
Ends the repl session\.
3434
.SH "CONFIGURATION"
35+
Commands entered in a repl session will be saved to a history file and be available in future sessions, similar to a shell or programming language repl\. By default, the global history file is at \fB~/\.local/state/git_extras_repl_history\fR\. You can specify that your projects each have their own independent history file\. This file will be saved in \.git_repl_history at the top level of the repo, and will need to be added to the repo or global \.gitignore\.
36+
.IP "" 4
37+
.nf
38+
# remove the \-\-global flag to configure only an individual project to have its own history file
39+
git config \-\-global git\-extras\.repl\.use\-local\-history "true"
40+
.fi
41+
.IP "" 0
42+
.P
3543
You can specify a default command to run when hitting enter:
3644
.P
3745
\fBgit config \-\-global git\-extras\.repl\.on\-enter\-command "git status \-sb"\fR

man/git-repl.html

Lines changed: 11 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

man/git-repl.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,17 @@ git-repl(1) -- git read-eval-print-loop
4040

4141
## CONFIGURATION
4242

43+
Commands entered in a repl session will be saved to a history file and be available in
44+
future sessions, similar to a shell or programming language repl. By default,
45+
the global history file is at `~/.local/state/git_extras_repl_history`. You can specify that your projects
46+
each have their own independent history file. This file will be saved in .git_repl_history
47+
at the top level of the repo, and will need to be added to the repo or global .gitignore.
48+
49+
```bash
50+
# remove the --global flag to configure only an individual project to have its own history file
51+
git config --global git-extras.repl.use-local-history "true"
52+
```
53+
4354
You can specify a default command to run when hitting enter:
4455

4556
`git config --global git-extras.repl.on-enter-command "git status -sb"`

0 commit comments

Comments
 (0)