How to process some operation in some directory? - emacs

Here is my try :
#!/Usr/bin/emacs --script
(let ((default-directory "/home/vision"))
(shell-command "git pull;")
but it doesn't work properly, I need to git pull in /home/vision directory and make some more actions there. How to do it?

Directory paths should end with a trailing slash.
The docstring in C-hv default-directory RET is explicit about that, in fact.
Fixing that ought to fix your problem.
See also:
cd function.
file-name-as-directory function.

Related

eshell TRAMP find remote file with relative path (or at least less than the full Tramp path)?

I love eshell's TRAMP integration. With it I can do cd /ssh:foo:/etc to
ssh into a remote machine and visit its /etc/ directory. I can also do
find-file motd to open this file in my local emacs. However, what if I need to use sudo to change the file? I know I can give the
full path, like so:
find-file /sudo:foo:/etc/motd
but is there a way to open the file via TRAMPs sudo support, without having to type the full path?
I managed to came up with the following eshell alias that works for me:
alias sff 'find-file "${pwd}/$1"(:s/ssh/sudo/)'
It should be fairly obvious what it does. It prepends the working directory
path, but with the string ssh replaced by sudo. Thus it only works for
remote files accessed over ssh. I rarely edit files using sudo locally, so
that's not a problem for me. However, we can make it work for local files too, at the cost of complexity:
alias sff 'find-file "${pwd}/$1"(:s,^,/sudo::,:s,::/ssh:,:,)'
That is, prepend /sudo:: (which is how to sudo for local files) and
subsequently replace any ocurrence of ::/ssh: with :. (I would have just removed :/ssh:, but eshell's :s/// construct didn't accept an empty
replacement.)
I found an alternative answer that works very well over at EmacsWiki.
Using that you'd still open the file with find-file as usual, but then
invoke M-x sudo-edit-current-file (shown below) to re-open the file as root
using Tramp. I think this is a very elegant solution, because often I
initially just want to look at a file, then later find that I need to edit it.
Here's the function, in case it disappears from the page above:
(set-default 'tramp-default-proxies-alist (quote ((".*" "\\`root\\'" "/ssh:%h:"))))
(require 'tramp)
(defun sudo-edit-current-file ()
(interactive)
(let ((position (point)))
(find-alternate-file
(if (file-remote-p (buffer-file-name))
(let ((vec (tramp-dissect-file-name (buffer-file-name))))
(tramp-make-tramp-file-name
"sudo"
(tramp-file-name-user vec)
(tramp-file-name-host vec)
(tramp-file-name-localname vec)))
(concat "/sudo:root#localhost:" (buffer-file-name))))
(goto-char position)))

How can I use M-x rgrep with the git grep command in Emacs?

I want to be able to use the normal M-x rgrep workflow (entering a path, a pattern and displaying the linked results in a *grep* buffer) but using git grep instead of the normal find command:
find . -type f -exec grep -nH -e {} +
I tried directly setting the grep-find-command variable:
(setq grep-find-command "git grep")
and using grep-apply-setting
(grep-apply-setting 'grep-find-command "git grep")
but neither seems to work. When I run M-x rgrep it just uses the same find command as before.
In fact, I'm pretty sure now that rgrep doesn't even use the grep-find-command variable, but I can't figure out where it's command is stored.
What about M-x vc-git-grep (C-x v f). Doesn't that do what you need?
It prompts you for:
search pattern (default: token at point, or region)
filename pattern (default: current file suffix)
base search directory (default, current dir)
Works nicely for me.
Turns out the relevant variable is actually grep-find-template. This takes a command with a few additional parameters:
<D> for the base directory
<X> for the find options to restrict directory list
<F> for the find options to limit the files matched
<C> for the place to put -i if the search is case-insensitive
<R> for the regular expression to search for
The default template looks like this:
find . <X> -type f <F> -exec grep <C> -nH -e <R> {} +
To make the command work with git grep, I had to pass in a few options to make sure git doesn't use a pager and outputs things in the right format. I also ignored a few of the template options because git grep already restricts the files searched in a natural way. However, it probably makes sense to add them back in somehow.
My new value for grep-find-template is
git --no-pager grep --no-color --line-number <C> <R>
After some cursory testing, it seems to work.
Note that you should set this variable using grep-apply-setting rather than modifying it directly:
(grep-apply-setting 'grep-find-template "git --no-pager grep --no-color --line-number <C> <R>")
Since I don't use two of the inputs to rgrep, I wrote my own git-grep command which temporarily stashes the old grep-find-template and replaces it with mine. This feels a bit hacky, but also seems to work.
(defcustom git-grep-command "git --no-pager grep --no-color --line-number <C> <R>"
"The command to run with M-x git-grep.")
(defun git-grep (regexp)
"Search for the given regexp using `git grep' in the current directory."
(interactive "sRegexp: ")
(unless (boundp 'grep-find-template) (grep-compute-defaults))
(let ((old-command grep-find-template))
(grep-apply-setting 'grep-find-template git-grep-command)
(rgrep regexp "*" "")
(grep-apply-setting 'grep-find-template old-command)))
With Emacs for Windows and Git Bash make sure PATH finds git.exe or vc-git-grep won't work:
(let ((dir "C:/Program Files/Tools/Git/bin"))
(setenv "PATH" (concat (getenv "PATH") ";" dir))
(setq exec-path (append exec-path '(dir))))
exec-path is not enough... resaons are explained here: Using git with emacs
Since vc-git-grep uses the directory of the buffer from which you run the function I also found a wrapper convenient:
(global-set-key [(control f8)]
(lambda() (interactive)
(with-current-buffer ROOT (call-interactively #'vc-git-grep))))
Here ROOT is a buffer (or function that evaluates a buffer) from whose directory the search begins.

PATH and exec-path set, but emacs does not find executable

My .emacs contains
(setenv "PATH" (concat ".:/usr/texbin:/opt/local/bin" (getenv "PATH")))
(setq exec-path (append exec-path '(".:/usr/texbin:/opt/local/bin")))
(add-to-list 'load-path "/usr/local/share/emacs/site-lisp")
(require 'tex-site)
(load "auctex.el" nil t t)
(load "preview-latex.el" nil t t)
/usr/texbin is where latex/pdflatex/.. are located.
/opt/local/bin/ is where gs can be found.
And yet when I run preview-at-point, which apparently needs both latex and gs, I get
Preview-DviPS finished at Thu Dec 22 11:25:46
DviPS sentinel: Searching for program: No such file or directory, gs
which means that latex could be found all right, but not gs.
I am not sure whether setting exec-path is necessary, perhaps PATH is enough, but I've set it as a debugging measure.
Why can emacs not find gs even though the directory it's in is in both PATH and exec-path?
If you're setting $PATH inside your Emacs, you might well be on OS X. GUI applications are not started via your shell, so they see different environment variables.
Here's a trick which I use to ensure the $PATH inside Emacs is the same one I see if I fire up a terminal (but see "update" below):
(defun set-exec-path-from-shell-PATH ()
"Set up Emacs' `exec-path' and PATH environment variable to match that used by the user's shell.
This is particularly useful under Mac OSX, where GUI apps are not started from a shell."
(interactive)
(let ((path-from-shell (replace-regexp-in-string "[ \t\n]*$" "" (shell-command-to-string "$SHELL --login -i -c 'echo $PATH'"))))
(setenv "PATH" path-from-shell)
(setq exec-path (split-string path-from-shell path-separator))))
Then simply call the set-exec-path-from-shell-PATH function, perhaps from your Emacs init file. I keep that code on github, BTW.
Update: this code has now been improved and published as an elisp library called exec-path-from-shell; installable packages are available in MELPA.
Try replacing the second line with this:
(setq exec-path (append exec-path '("/usr/texbin" "/opt/local/bin")))
I hit a similar problem, but with a correct PATH, including trailing ´:´. It turned out the internal emacs shell program was missing, resulting in a ´Searching for program: No such file or directory´ message.
Fixed with
(setq shell-file-name "bash").
It appears you're missing a path separator : at the end of your path string.

Dired copy asynchronously

Is there a way to modify/tell dired to copy files asynchronously? If you mark multiple files in dired and then use 'C' to copy them, emacs locks up until every file is copied. I instead want this copy to be started, and for me to continue editing as it goes on in the background. Is there a way to get this behaviour?
EDIT: Actually, C calls 'dired-do-copy' in dired-aux, not in dired itself. Sorry for any confusion.
I think emacs is mostly limited to a single thread - so this may not be directly possible through standard dired commands such as 'C' copy.
However, there is a dired command "dired-do-shell-command" which calls out to a shell to do the work in the background. If you select the files you want to copy and then use key '!' (this runs dired-do-shell-command) then type 'cp ? [destination]' (possibly can use 'copy' if you are on windows). I haven't tested this - so see help on "dired-do-shell-command" for full details.
See also the Emacs function dired-do-async-shell-command.
For an even more generic solution see https://github.com/jwiegley/emacs-async with which you also can evaluate arbitrary Emacs Lisp code through call to a separate Emacs process (which of course incurs a bit of extra latency). More specifically regard file operations see the file dired-async.el in this repo.
Also note that there is work on threading in Emacs under the working name Concurrent Emacs but it's not there yet. See http://www.emacswiki.org/emacs/ConcurrentEmacs for details.
I found this answer quite helpful: https://emacs.stackexchange.com/a/13802/10761. Reading that answer shows that you can make it so that dired will copy with the scp method instead of the ssh method (the latter initially encodes the file with gzip and that can be quite slow). The scp method will only copy with the scp program when the file is larger than tramp-copy-size-limit (which is 10240 by default). Using this scp method in conjunction with dired-async-mode is very nice, as it will not only copy quickly with scp, but it will also do it asynchronously and out of your way.
Also, I think this is useful: https://oremacs.com/2016/02/24/dired-rsync/. It provides this snippet of code to use rsync to copy files in dired:
;;;###autoload
(defun ora-dired-rsync (dest)
(interactive
(list
(expand-file-name
(read-file-name
"Rsync to:"
(dired-dwim-target-directory)))))
;; store all selected files into "files" list
(let ((files (dired-get-marked-files
nil current-prefix-arg))
;; the rsync command
(tmtxt/rsync-command
"rsync -arvz --progress "))
;; add all selected file names as arguments
;; to the rsync command
(dolist (file files)
(setq tmtxt/rsync-command
(concat tmtxt/rsync-command
(shell-quote-argument file)
" ")))
;; append the destination
(setq tmtxt/rsync-command
(concat tmtxt/rsync-command
(shell-quote-argument dest)))
;; run the async shell command
(async-shell-command tmtxt/rsync-command "*rsync*")
;; finally, switch to that window
(other-window 1)))
(define-key dired-mode-map "Y" 'ora-dired-rsync)

Ignore Emacs auto-generated files in a diff

How do I make diff ignore temporary files like foo.c~? Is there a configuration file that will make ignoring temporaries the default?
More generally: what's the best way to generate a "clean" patch off a tarball? I do this rarely enough (submitting a bug fix to an OSS project by email) that I always struggle with it...
EDIT: OK, the short answer is
diff -ruN -x *~ ...
Is there a better answer? E.g., can this go in a configuration file?
This doesn't strictly answer your question, but you can avoid the problem by configuring Emacs to use a specific directory to keep the backup files in. There are different implementations for Emacs or XEmacs.
In GNU Emacs
(defvar user-temporary-file-directory
(concat temporary-file-directory user-login-name "/"))
(make-directory user-temporary-file-directory t)
(setq backup-by-copying t)
(setq backup-directory-alist
`(("." . ,user-temporary-file-directory)
(,tramp-file-name-regexp nil)))
(setq auto-save-list-file-prefix
(concat user-temporary-file-directory ".auto-saves-"))
(setq auto-save-file-name-transforms
`((".*" ,user-temporary-file-directory t)))
In XEmacs
(require 'auto-save)
(require 'backup-dir)
(defvar user-temporary-file-directory
(concat (temp-directory) "/" (user-login-name)))
(make-directory user-temporary-file-directory t)
(setq backup-by-copying t)
(setq auto-save-directory user-temporary-file-directory)
(setq auto-save-list-file-prefix
(concat user-temporary-file-directory ".auto-saves-"))
(setq bkup-backup-directory-info
`((t ,user-temporary-file-directory full-path)))
You can also remove them all with a simple find command
find . -name “*~” -delete
Note that the asterisk and tilde are in double quotes to stop the shell expanding them.
By the way, these aren't strictly temporary files. They are a backup of the previous version of the file, so you can manually "undo" your last edit at any time in the future.
You can create an ignore file, like this:
core.*
*~
*.o
*.a
*.so
<more file patterns you want to skip>
and then run diff with -X option, like this:
diff -X ignore-file <other diff options you use/need> path1 path2
There used to be a .diffignore file "close" to the Linux kernel (maybe an informal file), but I couldn't find it anymore. Usually you keep using this ignore-file, just adding new patterns you want to ignore.
You can create a small sunction/script to it, like:
#!/bin/bash
olddir="/tmp/old"
newdir="/tmp/new"
pushd $newdir
for files in $(find . -name \*.c)
do
diff $olddir/$file $newdir/$file
done
popd
This is only one way to script this. The simple way. But I think you got the idea.
Other suggestion is configuring in emacs a backup dir, so your backup files go always to the same place, outside your work dir!
The poster has listed this as the 'short answer':
diff -ruN -x *~ ...
but feeding this to shell will cause the * to be globbed before diff is invoked.
This is better:
diff -r -x '*~' dir1 dir2
I omit the -u and -N flags as those are matters of taste and not relevant to the question at hand.