Grep in a git repository using ivy19 Apr 2015
Just got this request a few minutes ago, and now this feature is in
repository (available as
counsel from MELPA).
The motivation was to write an ivy equivalent of helm-git-grep. I didn't use this feature before, but the only thing that I needed to get me started was this shell command:
git --no-pager grep --full-name -n --no-color -i -e foobar
The rest of the code (just 20 lines) followed all by itself:
(defun counsel-git-grep-function (string &optional _pred &rest _u) "Grep in the current git repository for STRING." (split-string (shell-command-to-string (format "git --no-pager grep --full-name -n --no-color -i -e \"%s\"" string)) "\n" t)) (defun counsel-git-grep () "Grep for a string in the current git repository." (interactive) (let ((default-directory (locate-dominating-file default-directory ".git")) (val (ivy-read "pattern: " 'counsel-git-grep-function)) lst) (when val (setq lst (split-string val ":")) (find-file (car lst)) (goto-char (point-min)) (forward-line (1- (string-to-number (cadr lst)))))))
Thanks to the push from Stefan Monnier,
ivy-read also supports a
function to be passed instead of a static collection of strings. In
this case, it's
counsel-git-grep-function that basically takes one
argument: the thing that we're looking for.
shell-command-to-string is my go-to function to quickly
bring some shell output into Elisp. As you can see, it's enough to
pass it a shell command in a string form to get a string response. I
transform the response into a list of line strings with
split-string, making sure to pass the
t argument to avoid empty
One final trick that you can learn for your own Elisp programming is
(locate-dominating-file default-directory ".git") combo.
It's quite useful for dealing with git shell commands.
And that's it, it only remains to open a file and jump to the selected line.
I think that
counsel-git-grep might complement and slightly displace
ag in my setup.
So I've given it a nice binding:
(global-set-key (kbd "C-c j") 'counsel-git-grep)
I hope that I've made a good case of how easy it is to quickly write something in Elisp, especially if it's just a shell command wrapper. So if you're on the fence of whether to learn Elisp or not, do yourself a favor and learn it: it pays off quickly and is a lot of fun.
Side note: I've mentioned
\bfun\b in this blog 182 times, mostly as a variable representing a function (courtesy of