Emacs의 자동 저장 및 복구 기능을 좀 더 잘 활용하자
이맥스는 기본적으로 버퍼의 변경점을 자동으로 저장하는 기능이 있다. 물론 강제로 M-x do-auto-save RET 명령어를 사용해서 자동 저장을 시키는 방법도 있지만 보통은 주기적으로 현재 파일의 경로에 # 접두사와 접미사를 붙여서, 즉 test.txt 파일이라면 #test.txt#와 같은 파일명으로 파일을 저장한다.
그러나 사실 로컬에서야 그다지 문제가 되지 않지만, tramp를 커넥션이 느리거나 네트워크 상황이 좋지 않은 곳에서 사용할 때 가끔 자동 저장 파일을 저장하겠다고 이맥스가 미쳐 날 뛰는 상황을 경험하게 된다. 그래서 자동 저장 파일을 파일의 원래 위치에 만드는 것 보다는 특정 디렉토리 한 곳에서 생성하도록 하는 것이 유리한 측면이 많다.
;; store backup files in .emacs.d/backups (defconst emacs-backup-directory "~/.emacs.d/backups/") (setq backup-directory-alist `((".*" . ,emacs-backup-directory)) auto-save-file-name-transforms `((".*" ,emacs-backup-directory t)))
이렇게 설정하게 되면, 모든 자동 저장 파일은 $HOME/.emacs.d/backups 아래에 저장되게 된다. 물론 추가적으로 이렇게 자동 저장 파일을 한 곳으로 몰아 두게 되면 파일 시스템 이곳 저곳에 남아 있는 자동 저장 파일의 흔적들을 굳이 찾아서 지울 필요 없이, 그냥 $HOME/.emacs.d/backups 아래에 있는 파일들만 알아서 지워 주면 되니 그런 측면에서도 꽤 유용하다.
/home/ysjang/.emacs.d/backups: total used in directory 28 available 403893348 drwx------ 2 ysjang ysjang 4096 4월 4 11:48 . drwxrwxr-x 15 ysjang ysjang 4096 4월 4 11:28 .. -rw-rw-r-- 1 ysjang ysjang 3073 4월 4 12:52 #!home!ysjang!blog!review-recover-file.html# -rw-rw-r-- 1 ysjang ysjang 2009 4월 1 14:57 #!home!ysjang!Downloads!shBrushLisp.js# -rw-rw-r-- 1 ysjang ysjang 12 3월 31 15:22 #!home!ysjang!.emacs.d!test.html# -rw-rw-r-- 1 ysjang ysjang 30 4월 4 09:32 #!home!ysjang!.emacs.d!test.org#
자동 저장을 했으면 이 내용을 바탕으로 복구를 할 수 있어야 할것이다. 자동 저장 기능을 켜 둔 채로 이맥스를 쓰다 보면, 파일을 새로 열 때 shBrushLisp.js has auto save data; consider M-x recover-this-file와 같이 복구 가능하다는 메시지가 표시 되는 것을 본 적이 있을 것이다. 이 것은 자동 저장된 파일이 존재할 경우 나타나는 메시지로써 해당 버퍼에서 M-x recover-this-file RET이나 M-x recover-file RET 파일명 RET을 입력함으로써 auto save된 파일의 내용으로 현재 버퍼를 복구할 수 있다.
다만 위의 그림처럼 보듯이, 파일의 저장시간을 표시하는 것만으로는 그다지 유용하다는 느낌을 받을 수 없다. 이전에 kill-buffer나 save-some-buffers에서 변경점을 보여 주듯이 recove-file을 할 때에도 현재 버퍼와 자동 저장된 파일을 비교해서 표시해준다면 확실히 그 자리에서 복구 여부를 판단하기 쉬울 것이다. recover-file의 경우 advice function으로는 정의하기 난감해서 이전에 save-some-buffers때 처럼, 이맥스 기본 lisp library의 files.el을 가져와서 고쳐서 정의하도록 했다.
;; to override recover-file (from 24.5.5 files.el) (defun my:recover-file (file) "Visit file FILE, but get contents from its last auto-save file." ;; Actually putting the file name in the minibuffer should be used ;; only rarely. ;; Not just because users often use the default. (interactive "FRecover file: ") (setq file (expand-file-name file)) (if (auto-save-file-name-p (file-name-nondirectory file)) (error "%s is an auto-save file" (abbreviate-file-name file))) (let ((file-name (let ((buffer-file-name file)) (make-auto-save-file-name)))) (cond ((if (file-exists-p file) (not (file-newer-than-file-p file-name file)) (not (file-exists-p file-name))) (error "Auto-save file %s not current" (abbreviate-file-name file-name))) ((save-window-excursion (with-temp-buffer-window "*Directory*" nil #'(lambda (window _value) (with-selected-window window (unwind-protect (yes-or-no-p (format "Recover auto save file %s? " file-name)) (when (window-live-p window) (quit-restore-window window 'kill))))) (let* ((file-buffer (find-buffer-visiting file)) (diff-switches "-urN")) (display-buffer file-buffer '(display-buffer-same-window)) (delete-other-windows) (diff file-buffer file-name nil 'noasync) (with-current-buffer standard-output (let ((switches dired-listing-switches)) (if (file-symlink-p file) (setq switches (concat switches " -L"))) ;; Use insert-directory-safely, not insert-directory, ;; because these files might not exist. In particular, ;; FILE might not exist if the auto-save file was for ;; a buffer that didn't visit a file, such as "*mail*". ;; The code in v20.x called `ls' directly, so we need ;; to emulate what `ls' did in that case. (insert-directory-safely file switches) (insert-directory-safely file-name switches))) (fit-window-to-buffer (select-window (display-buffer standard-output '(display-buffer-at-bottom))))))) (switch-to-buffer (find-file-noselect file t)) (let ((inhibit-read-only t) ;; Keep the current buffer-file-coding-system. (coding-system buffer-file-coding-system) ;; Auto-saved file should be read with special coding. (coding-system-for-read 'auto-save-coding)) (erase-buffer) (insert-file-contents file-name nil) (set-buffer-file-coding-system coding-system)) (after-find-file nil nil t)) (t (user-error "Recover-file canceled"))))) (eval-after-load 'files `(progn (defalias 'save-some-buffers 'my:save-some-buffers) (defalias 'recover-file 'my:recover-file)))
recover-file의 경우는 각 파일의 저장 시간을 보는 것도 중요하므로 파일의 변경점을 보여 주는 것과 동시에 원래 recover-file의 표시 내용처럼 파일의 directory listing 결과도 함께 보여 줄 필요가 있다. 따라서 alias를 정의 한 이후, 자동 저장이 존재하는 버퍼에서 M-x recover-this-file RET를 실행하면 아래의 이미지와 같이, 왼쪽에는 해당 버퍼, 오른쪽에는 변경점, 그리고 아래쪽에는 원래 보여주던 directory listing 결과를 보여준다.
이제 복구 할 건지 말 건지를 파일을 열어서 일일히 확인할 필요 없이 복구 프롬프트에서 간단하게 결정할 수 있다.
'Emacs' 카테고리의 다른 글
Emacs에서 End of Line 타입 변경을 간편하게! (0) | 2016.05.11 |
---|---|
Emacs 읽기 전용 버퍼에서 모드 키 조합 없이 이동하기 (0) | 2016.04.07 |
Emacs의 버퍼 변경 알림 프롬프트에서 변경 내용 표시하기 (0) | 2016.04.01 |
Emacs spell checker (0) | 2016.03.11 |
Emacs의 ansi color 설정 (0) | 2016.03.07 |