emacs-elpa-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[elpa] externals/ssh-deploy b692b66: Merged changes from GitHub


From: Christian Johansson
Subject: [elpa] externals/ssh-deploy b692b66: Merged changes from GitHub
Date: Thu, 29 Nov 2018 01:00:22 -0500 (EST)

branch: externals/ssh-deploy
commit b692b668520d1682253ff5cbb6bbce0d96243be8
Author: Christian Johansson <address@hidden>
Commit: Christian Johansson <address@hidden>

    Merged changes from GitHub
    
    * pre-defined hydra
    * pre-defined prefix map
    * fixed issue with boolean fallbacks on nil by using integer instead
---
 README.md               | 143 ++++-----
 ssh-deploy-diff-mode.el | 265 +++++++---------
 ssh-deploy.el           | 790 +++++++++++++++++++++++++-----------------------
 3 files changed, 589 insertions(+), 609 deletions(-)

diff --git a/README.md b/README.md
index ef8e619..5e5967a 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,5 @@
-# `emacs-ssh-deploy` 
[![MELPA](http://melpa.org/packages/ssh-deploy-badge.svg)](http://melpa.org/#/ssh-deploy)
 [![MELPA 
Stable](http://stable.melpa.org/packages/ssh-deploy-badge.svg)](http://stable.melpa.org/#/ssh-deploy)
+# `emacs-ssh-deploy`
+[![License GPL 
3](https://img.shields.io/badge/license-GPL_3-green.svg)](https://www.gnu.org/licenses/gpl-3.0.txt)
 
[![MELPA](https://melpa.org/packages/ssh-deploy-badge.svg)](https://melpa.org/#/ssh-deploy)
 [![MELPA 
Stable](https://stable.melpa.org/packages/ssh-deploy-badge.svg)](https://stable.melpa.org/#/ssh-deploy)
 
 The `ssh-deploy` plug-in for Emacs makes it possible to effortlessly deploy 
local files and directories to remote hosts via TRAMP (including but not 
limited to SSH, SFTP, FTP). It tries to provide functions that can be easily 
used by custom scripts.
 
@@ -16,7 +17,7 @@ The `ssh-deploy` plug-in for Emacs makes it possible to 
effortlessly deploy loca
 * Open corresponding file on the remote host
 * Open SQL database-session on remote hosts
 * Run custom deployment scripts
-* All operations support asynchronous mode if `(make-thread`) or `async.el` is 
installed. (You need to setup an automatic authorization for this, i.e. 
`~/.netrc`, `~/.authinfo` or `~/.authinfo.gpg` and/or key-based password-less 
authorization)
+* All operations support asynchronous mode if `(make-thread`) or `async.el` is 
installed. (You need to setup an automatic authorization for this, i.e. 
`~/.authinfo.gpg` and/or key-based password-less authorization)
 
 The idea for this plug-in was to mimic the behavior of **PhpStorm** deployment 
functionality.
 
@@ -28,72 +29,75 @@ Here is a list of other variables you can set globally or 
per directory:
 
 * `ssh-deploy-root-local` The local root that should be under deployment 
*(string)*
 * `ssh-deploy-root-remote` The remote TRAMP root that is used for deployment 
*(string)*
-* `ssh-deploy-debug` Enables debugging messages *(boolean)*
+* `ssh-deploy-debug` Enables debugging messages *(integer)*
 * `ssh-deploy-revision-folder` The folder used for storing local revisions 
*(string)*
-* `ssh-deploy-automatically-detect-remote-changes` Enables automatic detection 
of remote changes *(boolean)*
-* `ssh-deploy-on-explicit-save` Enabled automatic uploads on save *(boolean)*
+* `ssh-deploy-automatically-detect-remote-changes` Enables automatic detection 
of remote changes *(integer)*
+* `ssh-deploy-on-explicit-save` Enabled automatic uploads on save *(integer)*
 * `ssh-deploy-exclude-list` A list defining what paths to exclude from 
deployment *(list)*
-* `ssh-deploy-async` Enables asynchronous transfers (you need to have 
`(make-thread)` or `async.el` installed as well) *(boolean)*
+* `ssh-deploy-async` Enables asynchronous transfers (you need to have 
`(make-thread)` or `async.el` installed as well) *(integer)*
 * `ssh-deploy-remote-sql-database` Default database when connecting to remote 
SQL database *(string)*
 * `ssh-deploy-remote-sql-password` Default password when connecting to remote 
SQL database *(string)*
 * `ssh-deploy-remote-sql-port` - Default port when connecting to remote SQL 
database *(integer)*
 * `ssh-deploy-remote-sql-server` Default server when connecting to remote SQL 
database *(string)*
 * `ssh-deploy-remote-sql-user` Default user when connecting to remote SQL 
database *(string)*
 * `ssh-deploy-remote-shell-executable` Default remote shell executable when 
launching shell on remote host *(string)*
-* `ssh-deploy-verbose` Show messages in message buffer when starting and 
ending actions, default t *(boolean)*
-* `ssh-deploy-script` - Your custom lambda function that will be called using 
(funcall) when running deploy script handler
+* `ssh-deploy-verbose` Show messages in message buffer when starting and 
ending actions *(integer)*
+* `ssh-deploy-script` - Your custom lambda function that will be called using 
(funcall) when running deploy script handler *(function)*
+* `ssh-deploy-async-with-threads` - Whether to use threads (make threads) 
instead of processes (async-start) for asynchronous operations *(integer)*
+
+When integers are used as booleans, above zero means true, zero means false 
and nil means unset and fallback to global settings.
 
 ## Deployment configuration examples
 
-* Download ssh-deploy and place it at `~/.emacs.d/ssh-deploy/` or install via 
`package.el` (`M-x list-packages` or `M-x package-install` + `ssh-deploy`) from 
the `MELPA` repository.
+* Download ssh-deploy and place it at `~/.emacs.d/ssh-deploy/` or install via 
`package.el` (`M-x list-packages` or `M-x package-install` + `ssh-deploy`) from 
the `ELPA` or `MELPA` repository.
 * So if you want to deploy `/Users/username/Web/MySite/` to create this 
`DirectoryVariables` file in your project root at 
`/Users/username/Web/MySite/.dir-locals.el`.
 
 You really need to do a bit of research about how to connect via different 
protocols using TRAMP on your operating system, I think Windows users should 
use `plink` for most protocols. Linux should work out of the box and macOS 
requires a bit of tweaking to get FTP support.
 
-### SSH, automatic uploads, SQL
+### SSH, with automatic uploads and SQL
 
 ``` emacs-lisp
 ((nil . (
   (ssh-deploy-root-local . "/Users/username/Web/MySite/")
   (ssh-deploy-root-remote . "/ssh:address@hidden:/var/www/MySite/")
-  (ssh-deploy-on-explicit-save . t)
+  (ssh-deploy-on-explicit-save . 1)
   (ssh-deploy-remote-sql-database . "myuser")
   (ssh-deploy-remote-sql-password . "mypassword")
   (ssh-deploy-remote-sql-user . "myuser")
 )))
 ```
 
-### SFTP, automatic uploads
+### SFTP, with automatic uploads
 
 ``` emacs-lisp
 ((nil . (
   (ssh-deploy-root-local . "/Users/username/Web/MySite/")
   (ssh-deploy-root-remote . "/sftp:address@hidden:/var/www/MySite/")
-  (ssh-deploy-on-explicit-save . t)
+  (ssh-deploy-on-explicit-save . 1)
 )))
 ```
 
-### SSH, custom port, not asynchronous, without automatic uploads
+### SSH, custom port 2120, not asynchronous and without automatic uploads
 
 ``` emacs-lisp
 ((nil . (
   (ssh-deploy-root-local . "/Users/username/Web/MySite/")
   (ssh-deploy-root-remote . "/ssh:address@hidden:/var/www/MySite/")
-  (ssh-deploy-on-explicit-save . nil)
-  (ssh-deploy-async . nil)
+  (ssh-deploy-on-explicit-save . 0)
+  (ssh-deploy-async . 0)
 )))
 ```
 
 You can pipe remote connections as well like this:
 
-### SSH, not asynchronous, automatic uploads, piped to other user on remote 
server and with custom deployment script.
+### SSH, not asynchronous, with automatic uploads, piped to other user on 
remote server and with custom deployment script.
 
 ``` emacs-lisp
 ((nil . (
   (ssh-deploy-root-local . "/Users/username/Web/MySite/")
   (ssh-deploy-root-remote . 
"/ssh:address@hidden|sudo:address@hidden:/var/www/MySite/")
-  (ssh-deploy-async . nil)
-  (ssh-deploy-on-explicit-save . t)
+  (ssh-deploy-async . 0)
+  (ssh-deploy-on-explicit-save . 1)
   (ssh-deploy-script . (lambda() (let ((default-directory 
ssh-deploy-root-remote))(shell-command "bash compile.sh"))))
 )))
 ```
@@ -106,7 +110,7 @@ If you have a password-less sudo on your remote host you 
should be to do this as
 ((nil . (
   (ssh-deploy-root-local . "/Users/username/Web/MySite/")
   (ssh-deploy-root-remote . "/ftp:address@hidden:/MySite/")
-  (ssh-deploy-on-explicit-save . t)
+  (ssh-deploy-on-explicit-save . 1)
 )))
 ```
 
@@ -127,7 +131,7 @@ Host remote-host
 
 ## Interaction-free password-based setup on *NIX systems
 
-For automatic **FTP** connections you need to setup `~/.netrc`, `~/.authinfo` 
or `~/.authinfo.gpg` with your login credentials. An example of contents:
+For automatic **FTP** connections you need to setup `~/.authinfo.gpg` with 
your login credentials. An example of contents:
 
 ``` shell
 machine myserver.com login myuser port ftp password mypassword
@@ -139,7 +143,7 @@ Set your user and group as owner and file permissions to 
`600`. Emacs should now
 
 ## Interaction-free SSH setup using public-key password-based authorization
 
-By combining a `~/.netrc`, `~/.authinfo` or `~/.authinfo.gpg` setup and a 
`public-key` setup you should be able to have a interaction-free public-key 
password-based authorization that can be used asynchronously.
+By combining a `~/.authinfo.gpg` setup and a `public-key` setup you should be 
able to have a interaction-free public-key password-based authorization that 
can be used asynchronously.
 
 ## Emacs configuration example
 
@@ -149,23 +153,16 @@ By combining a `~/.netrc`, `~/.authinfo` or 
`~/.authinfo.gpg` setup and a `publi
 ;; ssh-deploy - prefix = C-c C-z, f = forced upload, u = upload, d = download, 
x = diff, t = terminal, b = browse, h = shell
 (add-to-list 'load-path "~/.emacs.d/ssh-deploy/")
 (require 'ssh-deploy)
-(add-hook 'after-save-hook (lambda() (if (and (boundp 
'ssh-deploy-on-explicit-save) ssh-deploy-on-explicit-save) 
(ssh-deploy-upload-handler)) ))
-(add-hook 'find-file-hook (lambda() (if (and (boundp 
'ssh-deploy-automatically-detect-remote-changes) 
ssh-deploy-automatically-detect-remote-changes) 
(ssh-deploy-remote-changes-handler)) ))
-(global-set-key (kbd "C-c C-z f") (lambda() 
(interactive)(ssh-deploy-upload-handler-forced) ))
-(global-set-key (kbd "C-c C-z u") (lambda() 
(interactive)(ssh-deploy-upload-handler) ))
-(global-set-key (kbd "C-c C-z D") (lambda() 
(interactive)(ssh-deploy-delete-handler) ))
-(global-set-key (kbd "C-c C-z d") (lambda() 
(interactive)(ssh-deploy-download-handler) ))
-(global-set-key (kbd "C-c C-z x") (lambda() 
(interactive)(ssh-deploy-diff-handler) ))
-(global-set-key (kbd "C-c C-z t") (lambda() 
(interactive)(ssh-deploy-remote-terminal-eshell-base-handler) ))
-(global-set-key (kbd "C-c C-z T") (lambda() 
(interactive)(ssh-deploy-remote-terminal-eshell-handler) ))
-(global-set-key (kbd "C-c C-z h") (lambda() 
(interactive)(ssh-deploy-remote-terminal-shell-base-handler) ))
-(global-set-key (kbd "C-c C-z H") (lambda() 
(interactive)(ssh-deploy-remote-terminal-shell-handler) ))
-(global-set-key (kbd "C-c C-z R") (lambda() 
(interactive)(ssh-deploy-rename-handler) ))
-(global-set-key (kbd "C-c C-z e") (lambda() 
(interactive)(ssh-deploy-remote-changes-handler) ))
-(global-set-key (kbd "C-c C-z b") (lambda() 
(interactive)(ssh-deploy-browse-remote-base-handler) ))
-(global-set-key (kbd "C-c C-z o") (lambda() 
(interactive)(ssh-deploy-open-remote-file-handler) ))
-(global-set-key (kbd "C-c C-z m") (lambda() 
(interactive)(ssh-deploy-remote-sql-mysql-handler) ))
-(global-set-key (kbd "C-c C-z s") (lambda() 
(interactive)(ssh-deploy-run-deploy-script-handler) ))
+(ssh-deploy-line-mode) ;; If you want mode-line feature
+(ssh-deploy-add-menu) ;; If you want menu-bar feature
+(ssh-deploy-add-after-save-hook) ;; If you want automatic upload support
+(ssh-deploy-add-find-file-hook) ;; If you want detecting remote changes support
+(global-set-key (kbd "C-c C-z") 'ssh-deploy-prefix-map)
+```
+
+If you want to use the pre-defined hydra you can use this key-binding instead:
+``` elisp
+(global-set-key (kbd "C-c C-z") 'ssh-deploy-hydra/body)
 ```
 
 * Or use the `use-package` and `hydra-script` I'm using:
@@ -174,51 +171,36 @@ By combining a `~/.netrc`, `~/.authinfo` or 
`~/.authinfo.gpg` setup and a `publi
       (use-package ssh-deploy
         :ensure t
         :demand
-        :bind (("C-c C-z" . hydra-ssh-deploy/body))
-        :hook ((after-save . (lambda() (if (and (boundp 
'ssh-deploy-on-explicit-save) ssh-deploy-on-explicit-save) 
(ssh-deploy-upload-handler)) ))
-               (find-file . (lambda() (if (and (boundp 
'ssh-deploy-automatically-detect-remote-changes) 
ssh-deploy-automatically-detect-remote-changes) 
(ssh-deploy-remote-changes-handler)) )))
+        :bind (("C-c C-z" . ssh-deploy-hydra/body))
+        :hook ((after-save . ssh-deploy-after-save)
+               (find-file . ssh-deploy-find-file))
         :config
-        (defhydra hydra-ssh-deploy (:color red :hint nil)
-          "
-    _u_: Upload                              _f_: Force Upload
-    _d_: Download
-    _D_: Delete
-    _x_: Difference
-    _t_: Eshell Base Terminal                _T_: Eshell Relative Terminal
-    _h_: Shell Base Terminal                 _H_: Shell Relative Terminal
-    _e_: Detect Remote Changes
-    _R_: Rename
-    _b_: Browse Base                         _B_: Browse Relative
-    _o_: Open current file on remote         _m_: Open sql-mysql on remote
-    _s_: Run deploy script
-    "
-          ("f" ssh-deploy-upload-handler-forced)
-          ("u" ssh-deploy-upload-handler)
-          ("d" ssh-deploy-download-handler)
-          ("D" ssh-deploy-delete-handler)
-          ("x" ssh-deploy-diff-handler)
-          ("t" ssh-deploy-remote-terminal-eshell-base-handler)
-          ("T" ssh-deploy-remote-terminal-eshell-handler)
-          ("h" ssh-deploy-remote-terminal-shell-base-handler)
-          ("H" ssh-deploy-remote-terminal-shell-handler)
-          ("e" ssh-deploy-remote-changes-handler)
-          ("R" ssh-deploy-rename-handler)
-          ("b" ssh-deploy-browse-remote-base-handler)
-          ("B" ssh-deploy-browse-remote-handler)
-          ("o" ssh-deploy-open-remote-file-handler)
-          ("m" ssh-deploy-remote-sql-mysql-handler)
-          ("s" ssh-deploy-run-deploy-script-handler)))
+        (ssh-deploy-line-mode) ;; If you want mode-line feature
+        (ssh-deploy-add-menu) ;; If you want menu-bar feature
+      )
 ```
 
-(1) You can remove the `(add-to-list)` and `(require)` lines if you installed 
via `MELPA` repository.
+(1) You can remove the `(add-to-list)` and `(require)` lines if you installed 
via `ELPA` or `MELPA` repository.
 
 * Restart Emacs or re-evaluate your *emacs-init-script*
 
 ## Example usage
 
+File contents `/Users/username/Web/MySite/.dir-locals.el`:
+
+``` emacs-lisp
+((nil . (
+  (ssh-deploy-root-local . "/Users/username/Web/MySite/")
+  (ssh-deploy-root-remote . 
"/ssh:address@hidden|sudo:address@hidden:/var/www/MySite/")
+  (ssh-deploy-async . 1)
+  (ssh-deploy-on-explicit-save . 1)
+  (ssh-deploy-script . (lambda() (let ((default-directory 
ssh-deploy-root-remote))(shell-command "bash compile.sh"))))
+)))
+```
+
 * Now when you save a file somewhere under the directory 
`/Users/username/Web/MySite/`, the script will launch and deploy the file with 
the remote server.
 * If you press `C-c C-z x` and the current buffer is a file, you will launch a 
`ediff` session showing differences between local file and remote file via 
TRAMP, or if current buffer is a directory it will open a buffer showing 
directory differences
-* If you press `C-c C-z f` you will **force** upload local file or directory 
to remote host even if they have external changes.
+w* If you press `C-c C-z f` you will **force** upload local file or directory 
to remote host even if they have external changes.
 * If you press `C-c C-z u` you will upload local file or directory to remote 
host.
 * If you press `C-c C-z d` you will download the current file or directory 
from remote host and then reload current buffer.
 * If you press `C-c C-z D` you will delete the current file or directory after 
a confirmation on local and remote host.
@@ -248,9 +230,18 @@ macOS 10.13 removed the Darwin port of BSD `ftp` which is 
needed for `ange-ftp`,
 4. Type `./configure` then `make` and then `sudo make install`
 5. Type `mv ./src/ftp /usr/local/bin/ftp`
 
+## TRAMP FTP doesn't read my ~/.authinfo.gpg
+
+Ange-FTP defaults to `~/.netrc` so you need to add this to your init script:
+
+``` elisp
+(setq ange-ftp-netrc-filename "~/.authinfo.gpg")
+```
+
 ## Read more
-* <http://www.gnu.org/software/tramp/>
-* <http://melpa.org/>
+* <https://www.gnu.org/software/tramp/>
+* <https://elpa.gnu.org/>
+* <https://melpa.org/>
 * <https://www.emacswiki.org/emacs/DirectoryVariables>
 * <https://www.emacswiki.org/emacs/EdiffMode>
 * <https://github.com/jwiegley/emacs-async>
diff --git a/ssh-deploy-diff-mode.el b/ssh-deploy-diff-mode.el
index e6e077a..c6db269 100644
--- a/ssh-deploy-diff-mode.el
+++ b/ssh-deploy-diff-mode.el
@@ -1,12 +1,12 @@
-;;; ssh-deploy-diff-mode.el --- Mode for interactive directory differences
+;;; ssh-deploy-diff-mode.el --- Mode for interactive directory differences  
-*- lexical-binding:t -*-
 
 ;; Copyright (C) 2017-2018  Free Software Foundation, Inc.
 
-;; Author: Christian Johansson <github.com/cjohansson>
-;; Maintainer: Christian Johansson <github.com/cjohansson>
+;; Author: Christian Johansson <address@hidden>
+;; Maintainer: Christian Johansson <address@hidden>
 ;; Created: 1 Feb 2018
-;; Modified: 23 Feb 2018
-;; Version: 1.14
+;; Modified: 28 Nov 2018
+;; Version: 2.0
 ;; Keywords: tools, convenience
 ;; URL: https://github.com/cjohansson/emacs-ssh-deploy
 
@@ -34,45 +34,34 @@
 ;;; Code:
 
 
-(defvar ssh-deploy-diff-mode nil)
-
-(defconst ssh-deploy-diff-mode--section-directory-a 0 "Section for directory 
a.")
-(defconst ssh-deploy-diff-mode--section-directory-b 1 "Section for directory 
b.")
-(defconst ssh-deploy-diff-mode--section-exclude-list 2 "Section for 
exclude-list.")
-(defconst ssh-deploy-diff-mode--section-only-in-a 3 "Section for only in a.")
-(defconst ssh-deploy-diff-mode--section-only-in-b 4 "Section for only in b.")
-(defconst ssh-deploy-diff-mode--section-in-both 5 "Section for in both.")
-
-(defconst ssh-deploy-diff-mode--action-copy 0 "Action for copy.")
-(defconst ssh-deploy-diff-mode--action-copy-a 1 "Action for copy A.")
-(defconst ssh-deploy-diff-mode--action-copy-b 2 "Action for copy B.")
-(defconst ssh-deploy-diff-mode--action-delete 3 "Action for delete.")
-(defconst ssh-deploy-diff-mode--action-difference 4 "Action for difference.")
-(defconst ssh-deploy-diff-mode--action-refresh 5 "Action for refreshing 
differences.")
-(defconst ssh-deploy-diff-mode--action-open 6 "Action for open file.")
+(autoload 'ssh-deploy-diff-directories "ssh-deploy")
+(autoload 'ssh-deploy-upload "ssh-deploy")
+(autoload 'ssh-deploy-download "ssh-deploy")
+(autoload 'ssh-deploy-delete-both "ssh-deploy")
+(autoload 'ssh-deploy-delete "ssh-deploy")
+(autoload 'ssh-deploy-diff-files "ssh-deploy")
 
 (defconst ssh-deploy-diff-mode--keywords
-  (list
-   "DIRECTORY A"
-   "DIRECTORY B"
-   "EXCLUDE-LIST"
-   "FILES ONLY IN A"
-   "FILES ONLY IN B"
-   "FILES IN BOTH BUT DIFFERS"
-   "HELP"
-   )
+  '(
+    "DIRECTORY A"
+    "DIRECTORY B"
+    "EXCLUDE-LIST"
+    "FILES ONLY IN A"
+    "FILES ONLY IN B"
+    "FILES IN BOTH BUT DIFFERS"
+    "HELP"
+    )
   "Use list of keywords to build regular expression for syntax highlighting.")
 
-(let ((regex (concat "\\<" (regexp-opt ssh-deploy-diff-mode--keywords t) 
"\\>")))
-  (defconst ssh-deploy-diff-mode--font-lock-keywords
+(defconst ssh-deploy-diff-mode--font-lock-keywords
+  (let ((regex (concat "\\<" (regexp-opt ssh-deploy-diff-mode--keywords t) 
"\\>")))
     (list
      `(,regex . font-lock-builtin-face)
-     '("\\('\\w*'\\)" . font-lock-variable-name-face))
-    "Minimal highlighting expressions for SSH Deploy Diff major mode."))
+     '("\\('\\w*'\\)" . font-lock-variable-name-face)))
+  "Minimal highlighting expressions for SSH Deploy Diff major mode.")
 
-(defvar ssh-deploy-diff-mode--map
+(defvar ssh-deploy-diff-mode-map
   (let ((map (make-keymap)))
-    (define-key map "q" 'quit-window)
     (define-key map "C" 'ssh-deploy-diff-mode-copy-handler)
     (define-key map "a" 'ssh-deploy-diff-mode-copy-a-handler)
     (define-key map "b" 'ssh-deploy-diff-mode-copy-b-handler)
@@ -84,13 +73,13 @@
     map)
   "Key-map for SSH Deploy Diff major mode.")
 
-(defun ssh-deploy-diff-mode-copy-handler() "Start the copy action." 
(interactive)(ssh-deploy-diff-mode--action-handler 
ssh-deploy-diff-mode--action-copy))
-(defun ssh-deploy-diff-mode-copy-a-handler() "Start the copy A action." 
(interactive)(ssh-deploy-diff-mode--action-handler 
ssh-deploy-diff-mode--action-copy-a))
-(defun ssh-deploy-diff-mode-copy-b-handler() "Start the copy B action." 
(interactive)(ssh-deploy-diff-mode--action-handler 
ssh-deploy-diff-mode--action-copy-b))
-(defun ssh-deploy-diff-mode-delete-handler() "Start the delete action." 
(interactive)(ssh-deploy-diff-mode--action-handler 
ssh-deploy-diff-mode--action-delete))
-(defun ssh-deploy-diff-mode-difference-handler() "Start the difference 
action." (interactive)(ssh-deploy-diff-mode--action-handler 
ssh-deploy-diff-mode--action-difference))
-(defun ssh-deploy-diff-mode-refresh-handler() "Start the refresh action." 
(interactive)(ssh-deploy-diff-mode--action-handler 
ssh-deploy-diff-mode--action-refresh))
-(defun ssh-deploy-diff-mode-open-handler() "Start the open action." 
(interactive)(ssh-deploy-diff-mode--action-handler 
ssh-deploy-diff-mode--action-open))
+(defun ssh-deploy-diff-mode-copy-handler() "Start the copy action." 
(interactive)(ssh-deploy-diff-mode--action-handler 
#'ssh-deploy-diff-mode--copy))
+(defun ssh-deploy-diff-mode-copy-a-handler() "Start the copy A action." 
(interactive)(ssh-deploy-diff-mode--action-handler 
#'ssh-deploy-diff-mode--copy-a))
+(defun ssh-deploy-diff-mode-copy-b-handler() "Start the copy B action." 
(interactive)(ssh-deploy-diff-mode--action-handler 
#'ssh-deploy-diff-mode--copy-b))
+(defun ssh-deploy-diff-mode-delete-handler() "Start the delete action." 
(interactive)(ssh-deploy-diff-mode--action-handler 
#'ssh-deploy-diff-mode--delete))
+(defun ssh-deploy-diff-mode-difference-handler() "Start the difference 
action." (interactive)(ssh-deploy-diff-mode--action-handler 
#'ssh-deploy-diff-mode--difference))
+(defun ssh-deploy-diff-mode-refresh-handler() "Start the refresh action." 
(interactive)(ssh-deploy-diff-mode--action-handler 
#'ssh-deploy-diff-mode--refresh))
+(defun ssh-deploy-diff-mode-open-handler() "Start the open action." 
(interactive)(ssh-deploy-diff-mode--action-handler 
#'ssh-deploy-diff-mode--open))
 
 (defun ssh-deploy-diff-mode--get-parts ()
   "Return current file and section if any."
@@ -109,14 +98,17 @@
           (let* ((start (line-beginning-position))
                  (end (line-end-position))
                  (section (buffer-substring-no-properties start end)))
-            (setq section (replace-regexp-in-string ": ([0-9]+)$" "" section))
-            (cond ((string= section "DIRECTORY A") (setq section 
ssh-deploy-diff-mode--section-directory-a))
-                  ((string= section "DIRECTORY B") (setq section 
ssh-deploy-diff-mode--section-directory-b))
-                  ((string= section "EXCLUDE-LIST") (setq section 
ssh-deploy-diff-mode--section-exclude-list))
-                  ((string= section "FILES ONLY IN A") (setq section 
ssh-deploy-diff-mode--section-only-in-a))
-                  ((string= section "FILES ONLY IN B") (setq section 
ssh-deploy-diff-mode--section-only-in-b))
-                  ((string= section "FILES IN BOTH BUT DIFFERS") (setq section 
ssh-deploy-diff-mode--section-in-both))
-                  (t (message "Could not find section %s" section)))
+            (setq section (replace-regexp-in-string ": ([0-9]+)\\'" "" 
section))
+            (setq section
+                  (pcase section
+                    ("DIRECTORY A" 'directory-a)
+                    ("DIRECTORY B" 'directory-b)
+                    ("EXCLUDE-LIST" 'exclude-list)
+                    ("FILES ONLY IN A" 'only-in-a)
+                    ("FILES ONLY IN B" 'only-in-b)
+                    ("FILES IN BOTH BUT DIFFERS" 'in-both)
+                    (_ (message "Could not find section %s" section)
+                       section)))
             (while (and (> (line-number-at-pos) 1)
                         (not (looking-at "^DIRECTORY B:")))
               (forward-line -1))
@@ -141,68 +133,50 @@
   (interactive)
   (let ((parts (ssh-deploy-diff-mode--get-parts)))
     (if (not (eq parts nil))
-        (cond ((and (not (null (nth 0 parts))) (= action 
ssh-deploy-diff-mode--action-copy)) (ssh-deploy-diff-mode--copy parts))
-              ((and (not (null (nth 0 parts))) (= action 
ssh-deploy-diff-mode--action-copy-a)) (ssh-deploy-diff-mode--copy-a parts))
-              ((and (not (null (nth 0 parts))) (= action 
ssh-deploy-diff-mode--action-copy-b)) (ssh-deploy-diff-mode--copy-b parts))
-              ((and (not (null (nth 0 parts))) (= action 
ssh-deploy-diff-mode--action-delete)) (ssh-deploy-diff-mode--delete parts))
-              ((and (not (null (nth 0 parts))) (= action 
ssh-deploy-diff-mode--action-difference)) (ssh-deploy-diff-mode--difference 
parts))
-              ((and (not (null (nth 0 parts))) (= action 
ssh-deploy-diff-mode--action-open)) (ssh-deploy-diff-mode--open parts))
-              ((= action ssh-deploy-diff-mode--action-refresh) 
(ssh-deploy-diff-mode--refresh parts))
-              (t (message "Found nothing to do in the section for action %s" 
action)))
-      (message "Found nothing to do"))))
+        (cond
+         ((null parts) (message "Found nothing to do"))
+         ((not (or (nth 0 parts)
+                   ;; FIXME: Comparing equality of functions is bad karma!
+                   (eq action #'ssh-deploy-diff-mode--refresh)))
+          (message "Found nothing to do in the section for action %s"
+                   (replace-regexp-in-string "ssh-deploy-diff-mode--" ""
+                                             (format "%s" action))))
+         (t (funcall action parts))))))
 
 (defun ssh-deploy-diff-mode--refresh (parts)
   "Refresh current difference query based on PARTS."
   (interactive)
-  (require 'ssh-deploy)
-  (if (and (boundp 'ssh-deploy-root-local)
-           (boundp 'ssh-deploy-root-remote)
-           (fboundp 'ssh-deploy-diff-directories))
-      (let ((root-local (nth 2 parts))
-            (root-remote (nth 3 parts))
-            (async (cond ((boundp 'ssh-deploy-async) ssh-deploy-async)(t nil)))
-            (exclude-list (cond ((boundp 'ssh-deploy-exclude-list) 
ssh-deploy-exclude-list)(t nil))))
-        (progn
-          (kill-this-buffer)
-          (ssh-deploy-diff-directories root-local root-remote exclude-list 
async)))))
+  (let ((root-local (nth 2 parts))
+        (root-remote (nth 3 parts)))
+    (kill-this-buffer)
+    (ssh-deploy-diff-directories root-local root-remote)))
 
 (defun ssh-deploy-diff-mode--copy (parts)
   "Perform an upload or download depending on section in PARTS."
-  (require 'ssh-deploy)
   (let* ((file-name (nth 0 parts))
          (root-local (file-truename (nth 2 parts)))
          (root-remote (nth 3 parts))
-         (path-local (file-truename (concat root-local file-name)))
-         (path-remote (concat root-remote file-name))
-         (section (nth 1 parts))
-         (async (cond ((boundp 'ssh-deploy-async) ssh-deploy-async)(t nil)))
-         (revision-folder (cond ((boundp 'ssh-deploy-revision-folder) 
ssh-deploy-revision-folder)(t nil))))
-    (if (and (fboundp 'ssh-deploy-download)
-             (fboundp 'ssh-deploy-upload))
-        (cond ((= section ssh-deploy-diff-mode--section-only-in-a)
-               (ssh-deploy-upload path-local path-remote t async 
revision-folder))
-              ((= section ssh-deploy-diff-mode--section-only-in-b)
-               (ssh-deploy-download path-remote path-local async 
revision-folder))
-              (t (message "Copy is not available in this section")))
-      (display-warning 'ssh-deploy "Function ssh-deploy-download or 
ssh-deploy-upload is missing" :warning))))
+         (path-local (file-truename (expand-file-name file-name root-local)))
+         (path-remote (expand-file-name file-name root-remote))
+         (section (nth 1 parts)))
+    (pcase section
+      ('only-in-a
+       (ssh-deploy-upload path-local path-remote 1))
+      ('only-in-b
+       (ssh-deploy-download path-remote path-local))
+      (_ (message "Copy is not available in this section")))))
 
 (defun ssh-deploy-diff-mode--copy-a (parts)
   "Perform a upload of local-path to remote-path based on PARTS from section A 
or section BOTH."
-  (require 'ssh-deploy)
   (let* ((section (nth 1 parts))
          (file-name (nth 0 parts))
          (root-local (file-truename (nth 2 parts)))
          (root-remote (nth 3 parts))
-         (path-local (file-truename (concat root-local file-name)))
-         (path-remote (concat root-remote file-name))
-         (async (cond ((boundp 'ssh-deploy-async) ssh-deploy-async)(t nil)))
-         (revision-folder (cond ((boundp 'ssh-deploy-revision-folder) 
ssh-deploy-revision-folder)(t nil))))
-    (if (fboundp 'ssh-deploy-upload)
-        (cond ((or (= section ssh-deploy-diff-mode--section-only-in-a)
-                   (= section ssh-deploy-diff-mode--section-in-both))
-               (ssh-deploy-upload path-local path-remote t async 
revision-folder))
-              (t "Copy A is not available in this section"))
-      (display-warning 'ssh-deploy "Function ssh-deploy-upload is missing" 
:warning))))
+         (path-local (file-truename (expand-file-name file-name root-local)))
+         (path-remote (expand-file-name file-name root-remote)))
+    (cond ((memq section '(only-in-a in-both))
+           (ssh-deploy-upload path-local path-remote 1))
+          (t (message "Copy A is not available in this section")))))
 
 (defun ssh-deploy-diff-mode--copy-b (parts)
   "Perform an download of remote-path to local-path based on PARTS from 
section B or section BOTH."
@@ -211,56 +185,42 @@
          (file-name (nth 0 parts))
          (root-local (file-truename (nth 2 parts)))
          (root-remote (nth 3 parts))
-         (path-local (file-truename (concat root-local file-name)))
-         (path-remote (concat root-remote file-name))
-         (async (cond ((boundp 'ssh-deploy-async) ssh-deploy-async)(t nil)))
-         (revision-folder (cond ((boundp 'ssh-deploy-revision-folder) 
ssh-deploy-revision-folder)(t nil))))
-    (if (fboundp 'ssh-deploy-download)
-        (cond ((or (= section ssh-deploy-diff-mode--section-only-in-b)
-                   (= section ssh-deploy-diff-mode--section-in-both))
-               (ssh-deploy-download path-remote path-local async 
revision-folder))
-              (t "Copy B is not available in this section"))
-      (display-warning 'ssh-deploy "Function ssh-deploy-download is missing" 
:warning))))
+         (path-local (file-truename (expand-file-name file-name root-local)))
+         (path-remote (expand-file-name file-name root-remote)))
+    (cond ((memq section '(only-in-b in-both))
+           (ssh-deploy-download path-remote path-local))
+          (t (message "Copy B is not available in this section")))))
 
 (defun ssh-deploy-diff-mode--delete (parts)
   "Delete path in both, only in a or only in b based on PARTS from section A, 
B or BOTH."
-  (require 'ssh-deploy)
   (let* ((section (nth 1 parts))
          (file-name (nth 0 parts))
          (root-local (nth 2 parts))
          (root-remote (nth 3 parts))
-         (path-local (file-truename (concat root-local file-name)))
-         (path-remote (file-truename (concat root-remote file-name)))
-         (async (cond ((boundp 'ssh-deploy-async) ssh-deploy-async)(t nil)))
-         (debug (cond ((boundp 'ssh-deploy-debug) ssh-deploy-debug)(t nil)))
-         (exclude-list (cond ((boundp 'ssh-deploy-exclude-list) 
ssh-deploy-exclude-list)(t nil)))
-         (revision-folder (cond ((boundp 'ssh-deploy-revision-folder) 
ssh-deploy-revision-folder)(t nil))))
-    (if (and (fboundp 'ssh-deploy-delete)
-             (fboundp 'ssh-deploy-delete-both))
-        (cond ((= section ssh-deploy-diff-mode--section-in-both)
-               (let ((yes-no-prompt (read-string (format "Type 'yes' to 
confirm that you want to delete the file '%s': " file-name))))
-                 (if (string= yes-no-prompt "yes")
-                     (ssh-deploy-delete-both path-local root-local root-remote 
async debug exclude-list))))
-              ((= section ssh-deploy-diff-mode--section-only-in-a) 
(ssh-deploy-delete path-local async debug))
-              ((= section ssh-deploy-diff-mode--section-only-in-b) 
(ssh-deploy-delete path-remote async debug))
-              ((= section ssh-deploy-diff-mode--section-in-both) 
(ssh-deploy-delete-both path-local root-local root-remote async debug 
exclude-list))
-              (t (message "Delete is not available in this section")))
-      (display-warning 'ssh-deploy "Function ssh-deploy-delete or 
ssh-deploy-delete-both is missing" :warning))))
+         (path-local (file-truename (expand-file-name file-name root-local)))
+         (path-remote (expand-file-name file-name root-remote)))
+    (pcase section
+      ('in-both
+       (let ((yes-no-prompt (read-string (format "Type 'yes' to confirm that 
you want to delete the file '%s': " file-name))))
+         (if (string= yes-no-prompt "yes")
+             (ssh-deploy-delete-both path-local))))
+      ('only-in-a
+       (ssh-deploy-delete path-local))
+      ('only-in-b
+       (ssh-deploy-delete path-remote))
+      (_ (message "Delete is not available in this section")))))
 
 (defun ssh-deploy-diff-mode--difference (parts)
   "If file exists in both start a difference session based on PARTS."
-  (require 'ssh-deploy)
   (let ((section (nth 1 parts)))
-    (if (= section ssh-deploy-diff-mode--section-in-both)
-        (if (fboundp 'ssh-deploy-diff-files)
-            (let* ((file-name (nth 0 parts))
-                   (root-local (file-truename (nth 2 parts)))
-                   (root-remote (nth 3 parts))
-                   (path-local (file-truename (concat root-local file-name)))
-                   (path-remote (concat root-remote file-name)))
-              (ssh-deploy-diff-files path-local path-remote)))
-      (display-warning 'ssh-deploy "Function ssh-deploy-diff-files is missing" 
:warning))
-    (message "File must exists in both roots to perform a difference 
action.")))
+    (if (eq section 'in-both)
+        (let* ((file-name (nth 0 parts))
+               (root-local (file-truename (nth 2 parts)))
+               (root-remote (nth 3 parts))
+               (path-local (file-truename (expand-file-name file-name 
root-local)))
+               (path-remote (expand-file-name file-name root-remote)))
+          (ssh-deploy-diff-files path-local path-remote))
+      (message "File must exists in both roots to perform a difference 
action."))))
 
 (defun ssh-deploy-diff-mode--open (parts)
   "Perform a open file action based on PARTS from section A or section B."
@@ -269,28 +229,21 @@
          (file-name (nth 0 parts))
          (root-local (file-truename (nth 2 parts)))
          (root-remote (nth 3 parts))
-         (path-local (file-truename (concat root-local file-name)))
-         (path-remote (concat root-remote file-name)))
-    (cond ((= section ssh-deploy-diff-mode--section-only-in-a)
-           (progn
-             (message "Opening file '%s'" path-local)
-             (find-file path-local)))
-          ((= section ssh-deploy-diff-mode--section-only-in-b)
-           (progn
-             (message "Opening file '%s'" path-remote)
-             (find-file path-remote)))
-          (t (message "Open is not available in this section")))))
-
-(defun ssh-deploy-diff-mode ()
+         (path-local (file-truename (expand-file-name file-name root-local)))
+         (path-remote (expand-file-name file-name root-remote)))
+    (pcase section
+      ('only-in-a
+       (message "Opening file '%s'" path-local)
+       (find-file path-local))
+      ('only-in-b
+       (message "Opening file '%s'" path-remote)
+       (find-file path-remote))
+      (_ (message "Open is not available in this section")))))
+
+(define-derived-mode ssh-deploy-diff-mode special-mode "SSH-Deploy-Diff"
   "Major mode for SSH Deploy interactive directory differences."
-  (interactive)
-  (kill-all-local-variables)
-  (use-local-map ssh-deploy-diff-mode--map)
-  (set (make-local-variable 'font-lock-defaults) 
'(ssh-deploy-diff-mode--font-lock-keywords))
-  (setq major-mode 'ssh-deploy-diff-mode)
-  (setq mode-name "SSH-Deploy-Diff")
-  (read-only-mode t)
-  (run-hooks 'ssh-deploy-diff-mode-hook))
+  (set (make-local-variable 'font-lock-defaults)
+       '(ssh-deploy-diff-mode--font-lock-keywords)))
 
 (provide 'ssh-deploy-diff-mode)
 
diff --git a/ssh-deploy.el b/ssh-deploy.el
index c965879..1ee612d 100644
--- a/ssh-deploy.el
+++ b/ssh-deploy.el
@@ -2,11 +2,11 @@
 
 ;; Copyright (C) 2017-2018  Free Software Foundation, Inc.
 
-;; Author: Christian Johansson <github.com/cjohansson>
+;; Author: Christian Johansson <address@hidden>
 ;; Maintainer: Christian Johansson <address@hidden>
 ;; Created: 5 Jul 2016
-;; Modified: 19 Aug 2018
-;; Version: 2.0
+;; Modified: 28 Nov 2018
+;; Version: 3.0
 ;; Keywords: tools, convenience
 ;; URL: https://github.com/cjohansson/emacs-ssh-deploy
 
@@ -40,98 +40,63 @@
 ;; ssh-deploy-root-local,ssh-deploy-root-remote, ssh-deploy-on-explicit-save
 ;; you can setup a directory for TRAMP deployment.
 ;;
-;; For asynchronous transfers you need to setup ~/.netrc, ~/.authinfo or 
~/.authinfo.gpg or key-based authorization or equivalent for automatic 
authentication.
+;; For asynchronous transfers you need to setup ~/.authinfo.gpg or key-based 
authorization or equivalent for automatic authentication.
 ;;
-;; Example contents of ~/.netrc, ~/.authinfo or ~/.authinfo.gpg for 
password-based interaction-free authentication:
+;; Example contents of ~/.authinfo.gpg for password-based interaction-free 
authentication:
 ;; machine myserver.com login myuser port ftp password mypassword
 ;; machine myserver2.com login myuser2 port ssh password mypassword2
 ;; machine myserver3.com login myuser3 port sftp password mypassword3
 ;;
 ;; Set permissions to this file to 600 with your user as the owner.
 ;;
-;; - To setup an upload hook on save do this:
-;;     (add-hook 'after-save-hook
-;;               (lambda () (if (bound-and-true-p ssh-deploy-on-explicit-save)
-;;                              (ssh-deploy-upload-handler))))
+;; If your not using ~/.netrc for FTP information you need to specify what 
file your using with:
+;; (setq ange-ftp-netrc-filename "~/.authinfo.gpg")
+;;
+;; - To setup a upload hook on save do this:
+;; Add to init-script: (ssh-deploy-add-after-save-hook)
 ;;
 ;; - To setup automatic storing of base revisions and detection of remote 
changes do this:
-;;     (add-hook 'find-file-hook
-;;               (lambda ()
-;;                 (if (bound-and-true-p 
ssh-deploy-automatically-detect-remote-changes)
-;;                     (ssh-deploy-remote-changes-handler)) ))
+;; Add to init-script: (ssh-deploy-add-find-file-hook)
+;;
+;; - To enable mode-line feature do this:
+;; (ssh-deploy-line-mode)
 ;;
-;; - To set key-bindings do something like this:
+;; - To enable menu-bar feature do this:
+;; (ssh-deploy-add-menu)
 ;;
-;;     (global-set-key (kbd "C-c C-z f") 'ssh-deploy-upload-handler-forced)
-;;     (global-set-key (kbd "C-c C-z u") 'ssh-deploy-upload-handler)
-;;     (global-set-key (kbd "C-c C-z D") 'ssh-deploy-delete-handler)
-;;     (global-set-key (kbd "C-c C-z d") 'ssh-deploy-download-handler)
-;;     (global-set-key (kbd "C-c C-z x") 'ssh-deploy-diff-handler)
-;;     (global-set-key (kbd "C-c C-z t") 
'ssh-deploy-remote-terminal-eshell-base-handler)
-;;     (global-set-key (kbd "C-c C-z T") 
'ssh-deploy-remote-terminal-eshell-handler)
-;;     (global-set-key (kbd "C-c C-z h") 
'ssh-deploy-remote-terminal-shell-base-handler)
-;;     (global-set-key (kbd "C-c C-z H") 
'ssh-deploy-remote-terminal-shell-handler)
-;;     (global-set-key (kbd "C-c C-z R") 'ssh-deploy-rename-handler)
-;;     (global-set-key (kbd "C-c C-z e") 'ssh-deploy-remote-changes-handler)
-;;     (global-set-key (kbd "C-c C-z b") 
'ssh-deploy-browse-remote-base-handler)
-;;     (global-set-key (kbd "C-c C-z B") 'ssh-deploy-browse-remote-handler)
-;;     (global-set-key (kbd "C-c C-z o") 'ssh-deploy-open-remote-file-handler)
-;;     (global-set-key (kbd "C-c C-z m") 'ssh-deploy-remote-sql-mysql-handler)
-;;     (global-set-key (kbd "C-c C-z s") 'ssh-deploy-run-deploy-script-handler)
+;; - To set global key-bindings do something like this:
+;;     (global-set-key (kbd "C-c C-z") 'ssh-deploy-prefix-map)
+;;
+;; - To set global key-bindings for the pre-defined hydra do something like 
this:
+;;     (global-set-key (kbd "C-c C-z") 'ssh-deploy-hydra/body)
 ;;
 ;; - To install and set-up using use-package and hydra do this:
 ;;   (use-package ssh-deploy
 ;;     :ensure t
 ;;     :demand
-;;     :bind (("C-c C-z" . hydra-ssh-deploy/body))
-;;     :hook ((after-save . (lambda () (if (bound-and-true-p 
ssh-deploy-on-explicit-save) (ssh-deploy-upload-handler)) ))
-;;            (find-file . (lambda () (if (bound-and-true-p 
ssh-deploy-automatically-detect-remote-changes) 
(ssh-deploy-remote-changes-handler)) )))
+;;     :bind (("C-c C-z" . ssh-deploy-hydra/body))
+;;     :hook ((after-save . ssh-deploy-after-save)
+;;            (find-file . ssh-deploy-find-file))
 ;;     :config
-;;     (defhydra hydra-ssh-deploy (:color red :hint nil)
-;;       "
-;; _u_: Upload                              _f_: Force Upload
-;; _d_: Download
-;; _D_: Delete
-;; _x_: Difference
-;; _t_: Eshell Base Terminal                _T_: Eshell Relative Terminal
-;; _h_: Shell Base Terminal                 _H_: Shell Relative Terminal
-;; _e_: Detect Remote Changes
-;; _R_: Rename
-;; _b_: Browse Base                         _B_: Browse Relative
-;; _o_: Open current file on remote         _m_: Open sql-mysql on remote
-;; _s_: Run deploy script
-;; "
-;;       ("f" ssh-deploy-upload-handler-forced)
-;;       ("u" ssh-deploy-upload-handler)
-;;       ("d" ssh-deploy-download-handler)
-;;       ("D" ssh-deploy-delete-handler)
-;;       ("x" ssh-deploy-diff-handler)
-;;       ("t" ssh-deploy-remote-terminal-eshell-base-handler)
-;;       ("T" ssh-deploy-remote-terminal-eshell-handler)
-;;       ("h" ssh-deploy-remote-terminal-shell-base-handler)
-;;       ("H" ssh-deploy-remote-terminal-shell-handler)
-;;       ("e" ssh-deploy-remote-changes-handler)
-;;       ("R" ssh-deploy-rename-handler)
-;;       ("b" ssh-deploy-browse-remote-base-handler)
-;;       ("B" ssh-deploy-browse-remote-handler)
-;;       ("o" ssh-deploy-open-remote-file-handler)
-;;       ("m" ssh-deploy-remote-sql-mysql-handler)
-;;       ("s" ssh-deploy-run-deploy-script-handler)))
+;;     (ssh-deploy-line-mode) ;; If you want mode-line feature
+;;     (ssh-deploy-add-menu) ;; If you want menu-bar feature
+;;    )
+;;
 ;;
 ;; Here is an example for SSH deployment, 
/Users/Chris/Web/Site1/.dir-locals.el:
 ;; ((nil . (
 ;;   (ssh-deploy-root-local . "/Users/Chris/Web/Site1/")
 ;;   (ssh-deploy-root-remote . "/ssh:address@hidden:/var/www/site1/")
-;;   (ssh-deploy-on-explicit-save . t)
-;;   (ssh-deploy-async . t)
+;;   (ssh-deploy-on-explicit-save . 1)
+;;   (ssh-deploy-async . 1)
 ;; )))
 ;;
 ;; Here is an example for SFTP deployment, 
/Users/Chris/Web/Site2/.dir-locals.el:
 ;; ((nil . (
 ;;   (ssh-deploy-root-local . "/Users/Chris/Web/Site2/")
 ;;   (ssh-deploy-root-remote . "/sftp:address@hidden:/var/www/site2/")
-;;   (ssh-deploy-on-explicit-save . nil)
-;;   (ssh-deploy-async . nil)
+;;   (ssh-deploy-on-explicit-save . 0)
+;;   (ssh-deploy-async . 0)
 ;;   (ssh-deploy-script . (lambda() (let ((default-directory 
ssh-deploy-root-remote))(shell-command "bash compile.sh"))))
 ;; )))
 ;;
@@ -142,33 +107,34 @@
 ;; )))
 ;;
 ;;
-;; Now when you are in a directory which is configured for deployment.
-;;
 ;; Here is a list of other variables you can set globally or per directory:
 
 ;; * `ssh-deploy-root-local' - The local root that should be under deployment 
*(string)*
 ;; * `ssh-deploy-root-remote' - The remote TRAMP root that is used for 
deployment *(string)*
-;; * `ssh-deploy-debug' - Enables debugging messages *(boolean)*
+;; * `ssh-deploy-debug' - Enables debugging messages *(integer)*
 ;; * `ssh-deploy-revision-folder' - The folder used for storing local 
revisions *(string)*
-;; * `ssh-deploy-automatically-detect-remote-changes' - Enables automatic 
detection of remote changes *(boolean)*
-;; * `ssh-deploy-on-explicit-save' - Enabled automatic uploads on save 
*(boolean)*
-;; * `ssh-deploy-exclude-list' - A list defining what paths to exclude from 
deployment *(list)*
-;; * `ssh-deploy-async' - Enables asynchronous transfers (you need to have 
`(make-thread)` or `async.el` available as well) *(boolean)*
+;; * `ssh-deploy-automatically-detect-remote-changes' - Enables automatic 
detection of remote changes *(integer)*
+;; * `ssh-deploy-on-explicit-save' - Enabled automatic uploads on save 
*(integer)*
+;; * `ssh-deploy-exclude-list' - A list defining what file names to exclude 
from deployment *(list)*
+;; * `ssh-deploy-async' - Enables asynchronous transfers (you need to have 
`(make-thread)` or `async.el` available as well) *(integer)*
 ;; * `ssh-deploy-remote-sql-database' - Default database when connecting to 
remote SQL database *(string)*
 ;; * `ssh-deploy-remote-sql-password' - Default password when connecting to 
remote SQL database *(string)*
 ;; * `ssh-deploy-remote-sql-port' - Default port when connecting to remote SQL 
database *(integer)*
 ;; * `ssh-deploy-remote-sql-server' - Default server when connecting to remote 
SQL database *(string)*
 ;; * `ssh-deploy-remote-sql-user' - Default user when connecting to remote SQL 
database *(string)*
-;; * `ssh-deploy-remote-shell-executable' - Default shell executable when 
launching shell on remote host
-;; * `ssh-deploy-verbose' - Show messages in message buffer when starting and 
ending actions, default t *(boolean)*
-;; * `ssh-deploy-script' - Our custom lambda function that will be called 
using (funcall) when running deploy script
+;; * `ssh-deploy-remote-shell-executable' - Default shell executable when 
launching shell on remote host *(string)*
+;; * `ssh-deploy-verbose' - Show messages in message buffer when starting and 
ending actions *(integer)*
+;; * `ssh-deploy-script' - Our custom lambda function that will be called 
using (funcall) when running deploy script *(function)*
+;; * `ssh-deploy-async-with-threads' - Whether to use threads (make threads) 
instead of processes (async-start) for asynchronous operations *(integer)*
+;;
+;; When integers are used as booleans, above zero means true, zero means false 
and nil means unset and fallback to global settings.
 ;;
-;; Please see README.md from the same repository for extended documentation.
+;; Please see README.md from the same repository for more extended 
documentation.
 
 ;;; Code:
 
-(autoload 'ssh-deploy-diff-mode "ssh-deploy-diff-mode"
-  "Major mode for SSH Deploy interactive directory differences.")
+
+(autoload 'ssh-deploy-diff-mode "ssh-deploy-diff-mode")
 
 (defgroup ssh-deploy nil
   "Upload, download, difference, browse and terminal handler for files and 
directories on remote hosts via TRAMP."
@@ -187,45 +153,57 @@
 (put 'ssh-deploy-root-remote 'permanent-local t)
 (put 'ssh-deploy-root-remote 'safe-local-variable 'stringp)
 
-(defcustom ssh-deploy-on-explicit-save t
-  "Boolean variable if deploy should be made on explicit save, t by default."
+(defcustom ssh-deploy-on-explicit-save 1
+  "Boolean variable if deploy should be made on explicit save, 1 by default."
   :type 'boolean)
 (put 'ssh-deploy-on-explicit-save 'permanent-local t)
-(put 'ssh-deploy-on-explicit-save 'safe-local-variable 'booleanp)
+(put 'ssh-deploy-on-explicit-save 'safe-local-variable 'integerp)
 
-(defcustom ssh-deploy-debug nil
-  "Boolean variable if debug messages should be shown, nil by default."
+(defcustom ssh-deploy-debug 0
+  "Boolean variable if debug messages should be shown, 0 by default."
   :type 'boolean)
 (put 'ssh-deploy-debug 'permanent-local t)
-(put 'ssh-deploy-debug 'safe-local-variable 'booleanp)
+(put 'ssh-deploy-debug 'safe-local-variable 'integerp)
 
 ;; TODO This flag needs to work better, you should not miss any useful 
notifications when this is on
-(defcustom ssh-deploy-verbose t
-  "Boolean variable if debug messages should be shown, t by default."
+(defcustom ssh-deploy-verbose 1
+  "Boolean variable if debug messages should be shown, 1 by default."
   :type 'boolean)
 (put 'ssh-deploy-verbose 'permanent-local t)
-(put 'ssh-deploy-verbose 'safe-local-variable 'booleanp)
+(put 'ssh-deploy-verbose 'safe-local-variable 'integerp)
 
-(defcustom ssh-deploy-async t
-  "Boolean variable if asynchronous method for transfers should be used, t by 
default."
+(defcustom ssh-deploy-async 0
+  "Boolean variable if asynchronous method for transfers should be used, 0 by 
default."
   :type 'boolean)
 (put 'ssh-deploy-async 'permanent-local t)
-(put 'ssh-deploy-async 'safe-local-variable 'booleanp)
+(put 'ssh-deploy-async 'safe-local-variable 'integerp)
+
+(defcustom ssh-deploy-async-with-threads 0
+  "Boolean variable if asynchronous method should use threads if available, 0 
by default."
+  :type 'boolean)
+(put 'ssh-deploy-async-with-threads 'permanent-local t)
+(put 'ssh-deploy-async-with-threads 'safe-local-variable 'integerp)
+
+(defcustom ssh-deploy-async-with-threads 0
+  "Boolean variable if asynchronous method should use threads if available, 0 
by default."
+  :type 'boolean)
+(put 'ssh-deploy-async-with-threads 'permanent-local t)
+(put 'ssh-deploy-async-with-threads 'safe-local-variable 'integerp)
 
 (defcustom ssh-deploy-revision-folder "~/.ssh-deploy-revisions/"
-  "String variable with path to revisions with trailing slash."
+  "String variable with file name to revisions with trailing slash."
   :type 'string)
 (put 'ssh-deploy-revision-folder 'permanent-local t)
 (put 'ssh-deploy-revision-folder 'safe-local-variable 'stringp)
 
-(defcustom ssh-deploy-automatically-detect-remote-changes t
-  "Detect remote changes and store base revisions automatically, t by default."
+(defcustom ssh-deploy-automatically-detect-remote-changes 1
+  "Detect remote changes and store base revisions automatically, 1 by default."
   :type 'boolean)
 (put 'ssh-deploy-automatically-detect-remote-changes 'permanent-local t)
-(put 'ssh-deploy-automatically-detect-remote-changes 'safe-local-variable 
'booleanp)
+(put 'ssh-deploy-automatically-detect-remote-changes 'safe-local-variable 
'integerp)
 
 (defcustom ssh-deploy-exclude-list '(".git/" ".dir-locals.el")
-  "List of strings that if found in paths will exclude paths from sync, 
'(\"/.git\"/' \".dir-locals.el\") by default."
+  "List of strings that if found in file name will exclude it from sync, 
'(\"/.git\"/' \".dir-locals.el\") by default."
   :type 'list)
 (put 'ssh-deploy-exclude-list 'permanent-local t)
 (put 'ssh-deploy-exclude-list 'safe-local-variable 'listp)
@@ -250,7 +228,8 @@
 
 (defcustom ssh-deploy-remote-sql-server nil
   "String variable of remote sql server, nil by default."
-  :type 'string)
+  :type 'string
+  :group 'ssh-deploy)
 (put 'ssh-deploy-remote-sql-server 'permanent-local t)
 (put 'ssh-deploy-remote-sql-server 'safe-local-variable 'stringp)
 
@@ -261,14 +240,14 @@
 (put 'ssh-deploy-remote-sql-user 'safe-local-variable 'stringp)
 
 (defcustom ssh-deploy-remote-shell-executable nil
-  "String variable of remote shell executable server, nil by default."
+  "String variable of remote server shell executable, nil by default."
   :type 'string)
 (put 'ssh-deploy-remote-shell-executable 'permanent-local t)
 (put 'ssh-deploy-remote-shell-executable 'safe-local-variable 'stringp)
 
 (defcustom ssh-deploy-script nil
   "Lambda function to run with `funcall' when 
`ssh-deploy-run-deploy-script-handler' is executed."
-  :type 'lambda)
+  :type 'function)
 (put 'ssh-deploy-script 'permanent-local t)
 (put 'ssh-deploy-script 'safe-local-variable 'functionp)
 
@@ -306,12 +285,27 @@
 ;; these functions MUST not use module variables in any way.
 
 
-(defun ssh-deploy--async-process (start &optional finish)
-  "Asynchronously do START and then optionally do FINISH."
-  (if (fboundp 'make-thread)
-      (make-thread (lambda () (funcall finish (funcall start))))
+(defun ssh-deploy--async-process (start &optional finish async-with-threads)
+  "Asynchronously do START and then optionally do FINISH, use multi-treading 
if ASYNC-WITH-THREADS is above 0 otherwise use multi processes via async.el."
+  (if (and (fboundp 'make-thread)
+           async-with-threads
+           (> async-with-threads 0))
+      (make-thread (lambda()
+                     (if start
+                         (let ((result (funcall start)))
+                           (if finish
+                               (funcall finish result))))))
     (if (fboundp 'async-start)
-        (async-start start finish)
+        (if start
+            (let ((ftp-netrc nil))
+              (when (boundp 'ange-ftp-netrc-filename)
+                (setq ftp-netrc ange-ftp-netrc-filename))
+              (async-start
+               (lambda()
+                 (if ftp-netrc
+                     (defvar ange-ftp-netrc-filename ftp-netrc))
+                 (funcall start))
+               finish)))
       (display-warning 'ssh-deploy "Neither make-thread nor async-start 
functions are available!"))))
 
 (defun ssh-deploy--mode-line-set-status-and-update (status &optional filename)
@@ -320,26 +314,20 @@
       (let ((buffer (find-buffer-visiting filename)))
         (when buffer
           (with-current-buffer buffer
-            (unless (listp ssh-deploy--mode-line-status)
-              (setq ssh-deploy--mode-line-status '()))
             (push status ssh-deploy--mode-line-status)
             ;; (message "SSH Deploy - Updated status to: %s" 
ssh-deploy--mode-line-status)
             (ssh-deploy--mode-line-status-refresh))))
     (progn
-      (unless (listp ssh-deploy--mode-line-status)
-        (setq ssh-deploy--mode-line-status '()))
       (push status ssh-deploy--mode-line-status)
       ;; (message "SSH Deploy - Updated status to: %s" 
ssh-deploy--mode-line-status)
       (ssh-deploy--mode-line-status-refresh))))
 
 (defun ssh-deploy--mode-line-status-refresh ()
   "Refresh the status text based on the status variable."
-  (unless (listp ssh-deploy--mode-line-status)
-    ;; (message "Resetting status: %s" ssh-deploy--mode-line-status)
-    (setq ssh-deploy--mode-line-status '()))
   (let ((status (pop ssh-deploy--mode-line-status)))
     ;; (message "SSH Deploy - Refreshing status based on: %s" status)
-    (ssh-deploy--mode-line-status-update status)))
+    (when status
+      (ssh-deploy--mode-line-status-update status))))
 
 (defun ssh-deploy--mode-line-status-update (&optional status)
   "Update the local status text variable to a text representation based on 
STATUS."
@@ -356,7 +344,7 @@
       (setq status-text "ul.."))
 
      ((= status ssh-deploy--status-deleting)
-      (setq status-text ".."))
+      (setq status-text "rm.."))
 
      ((= status ssh-deploy--status-renaming)
       (setq status-text "mv.."))
@@ -364,6 +352,9 @@
      ((= status ssh-deploy--status-detecting-remote-changes)
       (setq status-text "diff.."))
 
+     ((and ssh-deploy-root-local ssh-deploy-root-remote)
+      (setq status-text "idle"))
+
      (t (setq status-text ""))
 
      )
@@ -377,7 +368,7 @@
   "Return a formatted string based on TEXT."
   (if (string= text "")
       ""
-    (format " [DPL:%s] " text)))
+    (format " {DPLY:%s} " text)))
 
 (defun ssh-deploy--insert-keyword (text)
   "Insert TEXT as bold text."
@@ -388,7 +379,7 @@
   "Generate revision-path for PATH in ROOT."
   (if (not (file-exists-p root))
       (make-directory root))
-  (concat root (replace-regexp-in-string "\\(/\\|@\\|:\\)" "_" path)))
+  (expand-file-name (replace-regexp-in-string "\\(/\\|@\\|:\\)" "_" path) 
root))
 
 (defun ssh-deploy--file-is-in-path (file path)
   "Return non-nil if FILE is in the path PATH."
@@ -412,18 +403,18 @@
   (and (not (null string))
        (not (zerop (length string)))))
 
-(defun ssh-deploy--upload-via-tramp-async (path-local path-remote force 
revision-folder)
-  "Upload PATH-LOCAL to PATH-REMOTE via TRAMP asynchronously and FORCE upload 
despite remote change, check for revisions in REVISION-FOLDER."
+(defun ssh-deploy--upload-via-tramp-async (path-local path-remote force 
revision-folder async-with-threads)
+  "Upload PATH-LOCAL to PATH-REMOTE via TRAMP asynchronously and FORCE upload 
despite remote change, check for revisions in REVISION-FOLDER.  Use 
multi-treaded async if ASYNC-WITH-THREADS is specified."
   (let ((file-or-directory (not (file-directory-p path-local))))
     (ssh-deploy--mode-line-set-status-and-update ssh-deploy--status-uploading 
path-local)
     (if file-or-directory
         (let ((revision-path (ssh-deploy--get-revision-path path-local 
revision-folder)))
           (when ssh-deploy-verbose (message "Uploading file '%s' to '%s'.. 
(asynchronously)" path-local path-remote))
           (ssh-deploy--async-process
-           (lambda ()
+           (lambda()
              (require 'ediff-util)
              (if (fboundp 'ediff-same-file-contents)
-                 (if (or (eq t force) (not (file-exists-p path-remote)) (and 
(file-exists-p revision-path) (ediff-same-file-contents revision-path 
path-remote)))
+                 (if (or (> force 0) (not (file-exists-p path-remote)) (and 
(file-exists-p revision-path) (ediff-same-file-contents revision-path 
path-remote)))
                      (progn
                        (if (not (file-directory-p (file-name-directory 
path-remote)))
                            (make-directory (file-name-directory path-remote) 
t))
@@ -432,15 +423,16 @@
                        (list 0 (format "Completed upload of file '%s'. 
(asynchronously)" path-remote) path-local))
                    (list 1 (format "Remote file '%s' has changed please 
download or diff. (asynchronously)" path-remote) path-local))
                (list 1 "Function 'ediff-same-file-contents' is missing. 
(asynchronously)" path-local)))
-           (lambda (return)
+           (lambda(return)
              (ssh-deploy--mode-line-set-status-and-update 
ssh-deploy--status-idle (nth 2 return))
              (if (= (nth 0 return) 0)
                  (when ssh-deploy-verbose (message (nth 1 return)))
-               (display-warning 'ssh-deploy (nth 1 return) :warning)))))
+               (display-warning 'ssh-deploy (nth 1 return) :warning)))
+           async-with-threads))
       (progn
         (when ssh-deploy-verbose (message "Uploading directory '%s' to '%s'.. 
(asynchronously)" path-local path-remote))
         (ssh-deploy--async-process
-         (lambda ()
+         (lambda()
            (copy-directory path-local path-remote t t t)
            path-local)
          (lambda(return-path)
@@ -456,7 +448,7 @@
         (progn
           (require 'ediff-util)
           (if (fboundp 'ediff-same-file-contents)
-              (if (or (eq t force)
+              (if (or (> force 0)
                       (not (file-exists-p path-remote))
                       (and (file-exists-p revision-path) 
(ediff-same-file-contents revision-path path-remote)))
                   (progn
@@ -475,13 +467,13 @@
         (ssh-deploy--mode-line-set-status-and-update ssh-deploy--status-idle)
         (when ssh-deploy-verbose (message "Completed upload of '%s'. 
(synchronously)" path-local))))))
 
-(defun ssh-deploy--download-via-tramp-async (path-remote path-local 
revision-folder)
-  "Download PATH-REMOTE to PATH-LOCAL via TRAMP asynchronously and make a copy 
in REVISION-FOLDER."
+(defun ssh-deploy--download-via-tramp-async (path-remote path-local 
revision-folder async-with-threads)
+  "Download PATH-REMOTE to PATH-LOCAL via TRAMP asynchronously and make a copy 
in REVISION-FOLDER, use multi-threading if ASYNC-WITH-THREADS is above zero."
   (let ((revision-path (ssh-deploy--get-revision-path path-local 
revision-folder)))
     (ssh-deploy--mode-line-set-status-and-update 
ssh-deploy--status-downloading path-local)
     (when ssh-deploy-verbose (message "Downloading '%s' to '%s'.. 
(asynchronously)" path-remote path-local))
     (ssh-deploy--async-process
-     (lambda ()
+     (lambda()
        (let ((file-or-directory (not (file-directory-p path-remote))))
          (if file-or-directory
              (progn
@@ -497,7 +489,8 @@
        (let ((local-buffer (find-buffer-visiting return-path)))
          (when local-buffer
            (with-current-buffer local-buffer
-             (revert-buffer t t t))))))))
+             (revert-buffer t t t)))))
+     async-with-threads)))
 
 (defun ssh-deploy--download-via-tramp (path-remote path-local revision-folder)
   "Download PATH-REMOTE to PATH-LOCAL via TRAMP synchronously and store a copy 
in REVISION-FOLDER."
@@ -623,17 +616,10 @@
         (display-warning 'ssh-deploy "Both directories need to exist to 
perform difference generation." :warning))
     (display-warning 'ssh-deploy "Function 'string-remove-prefix' is missing." 
:warning)))
 
-(defun ssh-deploy--diff-directories-present (diff)
-  "Present difference data for directories from DIFF."
-  (let ((buffer (generate-new-buffer "ssh-deploy diff"))
-        (old-ssh-deploy-root-local ssh-deploy-root-local)
-        (old-ssh-deploy-root-remote ssh-deploy-root-remote)
-        (old-ssh-deploy-on-explicit-save ssh-deploy-on-explicit-save)
-        (old-ssh-deploy-debug ssh-deploy-debug)
-        (old-ssh-deploy-async ssh-deploy-async)
-        (old-ssh-deploy-revision-folder ssh-deploy-revision-folder)
-        (old-ssh-deploy-automatically-detect-remote-changes 
ssh-deploy-automatically-detect-remote-changes)
-        (old-ssh-deploy-exclude-list ssh-deploy-exclude-list))
+(defun ssh-deploy--diff-directories-present (diff root-local root-remote 
on-explicit-save debug async async-with-threads revision-folder remote-changes 
exclude-list)
+  "Present difference data for directories from the DIFF, ROOT-LOCAL defines 
local root, ROOT-REMOTE defined remote root, ON-EXPLICIT-SAVE defines automatic 
uploads, DEBUG is the debug flag, ASYNC is for asynchronous, ASYNC-WITH-THREADS 
for threads instead of processes, REVISION-FOLDER is for revisions, 
REMOTE-CHANGES are whether to look for remote change, EXCLUDE-LIST is what 
files to exclude."
+
+  (let ((buffer (generate-new-buffer "*SSH Deploy diff*")))
     (switch-to-buffer buffer)
 
     (ssh-deploy--insert-keyword "DIRECTORY A: ")
@@ -678,14 +664,15 @@
     (ssh-deploy-diff-mode)
 
     ;; Set local variables same as current directories
-    (set (make-local-variable 'ssh-deploy-root-local) 
old-ssh-deploy-root-local)
-    (set (make-local-variable 'ssh-deploy-root-remote) 
old-ssh-deploy-root-remote)
-    (set (make-local-variable 'ssh-deploy-on-explicit-save) 
old-ssh-deploy-on-explicit-save)
-    (set (make-local-variable 'ssh-deploy-debug) old-ssh-deploy-debug)
-    (set (make-local-variable 'ssh-deploy-async) old-ssh-deploy-async)
-    (set (make-local-variable 'ssh-deploy-revision-folder) 
old-ssh-deploy-revision-folder)
-    (set (make-local-variable 'ssh-deploy-automatically-detect-remote-changes) 
old-ssh-deploy-automatically-detect-remote-changes)
-    (set (make-local-variable 'ssh-deploy-exclude-list) 
old-ssh-deploy-exclude-list)))
+    (set (make-local-variable 'ssh-deploy-root-local) root-local)
+    (set (make-local-variable 'ssh-deploy-root-remote) root-remote)
+    (set (make-local-variable 'ssh-deploy-on-explicit-save) on-explicit-save)
+    (set (make-local-variable 'ssh-deploy-debug) debug)
+    (set (make-local-variable 'ssh-deploy-async) async)
+    (set (make-local-variable 'ssh-deploy-async-with-threads) 
async-with-threads)
+    (set (make-local-variable 'ssh-deploy-revision-folder) revision-folder)
+    (set (make-local-variable 'ssh-deploy-automatically-detect-remote-changes) 
remote-changes)
+    (set (make-local-variable 'ssh-deploy-exclude-list) exclude-list)))
 
 
 ;; PUBLIC functions
@@ -707,45 +694,48 @@
     (display-warning 'ssh-deploy "Function 'ediff-same-file-contents' is 
missing." :warning)))
 
 ;;;###autoload
-(defun ssh-deploy-diff-directories (directory-a directory-b &optional 
exclude-list async)
-  "Find difference between DIRECTORY-A and DIRECTORY-B but exclude paths 
matching EXCLUDE-LIST, do it asynchronously is ASYNC is true."
-  ;; FIXME: These next 4 lines don't do anything because the optional args
-  ;; are always "bound" (to nil if the arg was not passed).
-  ;; (if (not (boundp 'async))
-  ;;     (setq async ssh-deploy-async))
-  ;; (if (not (boundp 'exclude-list))
-  ;;     (setq exclude-list ssh-deploy-exclude-list))
-  (if async
-      (let ((script-filename (file-name-directory (symbol-file 
'ssh-deploy-diff-directories))))
-        (message "Calculating differences between directory '%s' and '%s'.. 
(asynchronously)" directory-a directory-b)
-        (ssh-deploy--async-process
-         (lambda ()
-           (add-to-list 'load-path script-filename)
-           (require 'ssh-deploy)
-           (ssh-deploy--diff-directories-data directory-a directory-b 
exclude-list))
-         (lambda(diff)
-           (message "Completed calculation of differences between directory 
'%s' and '%s'. Result: %s only in A, %s only in B, %s differs. 
(asynchronously)" (nth 0 diff) (nth 1 diff) (length (nth 4 diff)) (length (nth 
5 diff)) (length (nth 7 diff)))
-           (if (or (> (length (nth 4 diff)) 0) (> (length (nth 5 diff)) 0) (> 
(length (nth 7 diff)) 0))
-               (ssh-deploy--diff-directories-present diff)))))
-    (progn
-      (message "Calculating differences between directory '%s' and '%s'.. 
(synchronously)" directory-a directory-b)
-      (let ((diff (ssh-deploy--diff-directories-data directory-a directory-b 
exclude-list)))
-        (message "Completed calculation of differences between directory '%s' 
and '%s'. Result: %s only in A, %s only in B, %s differs. (synchronously)" (nth 
0 diff) (nth 1 diff) (length (nth 4 diff)) (length (nth 5 diff)) (length (nth 7 
diff)))
-        (if (or (> (length (nth 4 diff)) 0) (> (length (nth 5 diff)) 0) (> 
(length (nth 7 diff)) 0))
-            (ssh-deploy--diff-directories-present diff))))))
+(defun ssh-deploy-diff-directories (directory-a directory-b &optional 
on-explicit-save debug async async-with-threads revision-folder remote-changes 
exclude-list)
+  "Find difference between DIRECTORY-A and DIRECTORY-B but exclude, 
ON-EXPLICIT-SAVE defines automatic uploads, DEBUG is the debug flag, ASYNC is 
for asynchronous, ASYNC-WITH-THREADS for threads instead of processes, 
REVISION-FOLDER is for revisions, REMOTE-CHANGES are whether to look for remote 
change, EXCLUDE-LIST is what files to exclude."
+  (let ((on-explicit-save (or on-explicit-save ssh-deploy-on-explicit-save))
+        (debug (or debug ssh-deploy-debug))
+        (async (or async ssh-deploy-async))
+        (async-with-threads (or async-with-threads 
ssh-deploy-async-with-threads))
+        (revision-folder (or revision-folder ssh-deploy-revision-folder))
+        (remote-changes (or remote-changes 
ssh-deploy-automatically-detect-remote-changes))
+        (exclude-list (or exclude-list ssh-deploy-exclude-list)))
+    (if (> async 0)
+        (let ((script-filename (file-name-directory (symbol-file 
'ssh-deploy-diff-directories))))
+          (message "Calculating differences between directory '%s' and '%s'.. 
(asynchronously)" directory-a directory-b)
+          (ssh-deploy--async-process
+           (lambda()
+             (add-to-list 'load-path script-filename)
+             (require 'ssh-deploy)
+             (ssh-deploy--diff-directories-data directory-a directory-b 
exclude-list))
+           (lambda(diff)
+             (message "Completed calculation of differences between directory 
'%s' and '%s'. Result: %s only in A %s only in B %s differs. (asynchronously)" 
(nth 0 diff) (nth 1 diff) (length (nth 4 diff)) (length (nth 5 diff)) (length 
(nth 7 diff)))
+             (if (or (> (length (nth 4 diff)) 0) (> (length (nth 5 diff)) 0) 
(> (length (nth 7 diff)) 0))
+                 (ssh-deploy--diff-directories-present diff directory-a 
directory-b on-explicit-save debug async async-with-threads revision-folder 
remote-changes exclude-list)))
+           async-with-threads))
+      (progn
+        (message "Calculating differences between directory '%s' and '%s'.. 
(synchronously)" directory-a directory-b)
+        (let ((diff (ssh-deploy--diff-directories-data directory-a directory-b 
exclude-list)))
+          (message "Completed calculation of differences between directory 
'%s' and '%s'. Result: %s only in A, %s only in B, %s differs. (synchronously)" 
(nth 0 diff) (nth 1 diff) (length (nth 4 diff)) (length (nth 5 diff)) (length 
(nth 7 diff)))
+          (if (or (> (length (nth 4 diff)) 0) (> (length (nth 5 diff)) 0) (> 
(length (nth 7 diff)) 0))
+              (ssh-deploy--diff-directories-present diff directory-a 
directory-b on-explicit-save debug async async-with-threads revision-folder 
remote-changes exclude-list)))))))
 
 ;;;###autoload
-(defun ssh-deploy-remote-changes (path-local &optional root-local root-remote 
async revision-folder exclude-list)
-  "Check if a local revision for PATH-LOCAL on ROOT-LOCAL and if remote file 
has changed on ROOT-REMOTE, do it optionally asynchronously if ASYNC is true, 
check for copies in REVISION-FOLDER and skip if path is in EXCLUDE-LIST."
+(defun ssh-deploy-remote-changes (path-local &optional root-local root-remote 
async revision-folder exclude-list async-with-threads)
+  "Check if a local revision for PATH-LOCAL on ROOT-LOCAL and if remote file 
has changed on ROOT-REMOTE, do it optionally asynchronously if ASYNC is true, 
check for copies in REVISION-FOLDER and skip if path is in EXCLUDE-LIST.  Use 
multi-threading if ASYNC-WITH-THREADS is above zero."
   (let ((root-local (or root-local ssh-deploy-root-local))
-        (root-remote (or root-remote ssh-deploy-root-remote)))
+        (root-remote (or root-remote ssh-deploy-root-remote))
+        (exclude-list (or exclude-list ssh-deploy-exclude-list)))
 
     ;; Is the file inside the local-root and should it not be excluded?
     (if (and (ssh-deploy--file-is-in-path path-local root-local)
              (ssh-deploy--file-is-included path-local exclude-list))
         (let* ((revision-folder (or revision-folder 
ssh-deploy-revision-folder))
                (revision-path (ssh-deploy--get-revision-path path-local 
revision-folder))
-               (path-remote (concat root-remote (ssh-deploy--get-relative-path 
root-local path-local))))
+               (path-remote (expand-file-name (ssh-deploy--get-relative-path 
root-local path-local) root-remote)))
 
           ;; Is the file a regular file?
           (if (not (file-directory-p path-local))
@@ -755,7 +745,7 @@
                 (if (file-exists-p revision-path)
 
                     ;; Local revision exist. Is async enabled?
-                    (if async
+                    (if (> async 0)
                         (progn
 
                           ;; Update buffer status
@@ -774,7 +764,7 @@
                                              (progn
                                                (copy-file path-local 
revision-path t t t t)
                                                (list 0 (format "Remote file 
'%s' is identical to local file '%s' but different to local revision. Updated 
local revision. (asynchronously)" path-remote path-local) path-local))
-                                           (list 1 (format "Remote file '%s' 
has changed, please download or diff. (asynchronously)" path-remote) 
path-local)))
+                                           (list 1 (format "Remote file '%s' 
has changed please download or diff. (asynchronously)" path-remote) 
path-local)))
                                      (list 1 "Function 
'ediff-same-file-contents' is missing. (asynchronously)" path-local)))
                                (list 0 (format "Remote file '%s' doesn't 
exist. (asynchronously)" path-remote) path-local)))
                            (lambda(return)
@@ -784,7 +774,8 @@
 
                              (if (= (nth 0 return) 0)
                                  (when ssh-deploy-verbose (message (nth 1 
return)))
-                               (display-warning 'ssh-deploy (nth 1 return) 
:warning)))))
+                               (display-warning 'ssh-deploy (nth 1 return) 
:warning)))
+                           async-with-threads))
 
                       ;; Async is not enabled - synchronous logic here
                       (progn
@@ -807,7 +798,7 @@
                         (ssh-deploy--mode-line-set-status-and-update 
ssh-deploy--status-idle)))
 
                   ;; Does not have local revision. Is async enabled?
-                  (if async
+                  (if (> async 0)
                       (progn
 
                         ;; Update buffer status
@@ -815,7 +806,8 @@
 
                         ;; Asynchronous logic here
                         (ssh-deploy--async-process
-                         (lambda ()
+                         (lambda()
+
                            ;; Does remote file exist?
                            (if (file-exists-p path-remote)
                                (progn
@@ -825,7 +817,7 @@
                                          (progn
                                            (copy-file path-local revision-path 
t t t t)
                                            (list 0 (format "Remote file '%s' 
has not changed, created base revision. (asynchronously)" path-remote) 
path-local))
-                                       (list 1 (format "Remote file '%s' has 
changed, please download or diff. (asynchronously)" path-remote) path-local))
+                                       (list 1 (format "Remote file '%s' has 
changed please download or diff. (asynchronously)" path-remote) path-local))
                                    (list 1 "Function ediff-file-same-contents 
is missing. (asynchronously)" path-local)))
                              (list 0 (format "Remote file '%s' doesn't exist. 
(asynchronously)" path-remote) path-local)))
                          (lambda(return)
@@ -835,7 +827,8 @@
 
                            (if (= (nth 0 return) 0)
                                (when ssh-deploy-verbose (message (nth 1 
return)))
-                             (display-warning 'ssh-deploy (nth 1 return) 
:warning)))))
+                             (display-warning 'ssh-deploy (nth 1 return) 
:warning)))
+                         async-with-threads))
 
                     ;; Async is not enabled - synchronous logic here
                     (progn
@@ -860,81 +853,81 @@
                       (ssh-deploy--mode-line-set-status-and-update 
ssh-deploy--status-idle)))))
 
             ;; File is a directory
-            (when ssh-deploy-debug (message "File %s is a directory, ignoring 
remote changes check." path-local))))
+            (when (> ssh-deploy-debug 0) (message "File %s is a directory, 
ignoring remote changes check." path-local))))
 
       ;; File is not inside root or is excluded from it
-      (when ssh-deploy-debug (message "File %s is not in root or is excluded 
from it." path-local)))))
+      (when (> ssh-deploy-debug 0) (message "File %s is not in root or is 
excluded from it." path-local)))))
 
-(defun ssh-deploy-delete (path &optional async _debug buffer)
-  "Delete PATH and use flags ASYNC and DEBUG, set status in BUFFER."
-  (if async
-      (progn
-        (when buffer
-          (ssh-deploy--mode-line-set-status-and-update 
ssh-deploy--status-deleting buffer))
-        (ssh-deploy--async-process
-         (lambda ()
-           (if (file-exists-p path)
-               (let ((file-or-directory (not (file-directory-p path))))
-                 (progn
-                   (if file-or-directory
-                       (delete-file path t)
-                     (delete-directory path t t))
-                   (list path 0 buffer)))
-             (list path 1 buffer)))
-         (lambda(response)
-           (when (nth 2 response)
-             (ssh-deploy--mode-line-set-status-and-update 
ssh-deploy--status-idle (nth 2 response))
-             (let ((local-buffer (find-buffer-visiting (nth 2 response))))
-               (when local-buffer
-                 (kill-buffer local-buffer))))
-           (cond ((= 0 (nth 1 response)) (message "Completed deletion of '%s'. 
(asynchronously)" (nth 0 response)))
-                 (t (display-warning 'ssh-deploy (format "Did not find '%s' 
for deletion. (asynchronously)" (nth 0 response)) :warning))))))
-    (if (file-exists-p path)
-        (ssh-deploy--mode-line-set-status-and-update 
ssh-deploy--status-deleting buffer)
-      (let ((file-or-directory (not (file-directory-p path))))
-        (when buffer
-          (ssh-deploy--mode-line-set-status-and-update 
ssh-deploy--status-deleting buffer))
+(defun ssh-deploy-delete (path &optional async async-with-threads)
+  "Delete PATH and use flags ASYNC.  Use multi-threading if ASYNC-WITH-THREADS 
is above zero."
+  (let ((async (or async ssh-deploy-async))
+        (async-with-threads (or async-with-threads 
ssh-deploy-async-with-threads)))
+    (if (> async 0)
         (progn
-          (if file-or-directory
-              (delete-file path t)
-            (delete-directory path t t))
-          (when buffer
-            (ssh-deploy--mode-line-set-status-and-update 
ssh-deploy--status-idle buffer)
-            (let ((local-buffer (find-buffer-visiting buffer)))
-              (when local-buffer
-                (kill-buffer local-buffer))))
-          (message "Completed deletion of '%s'. (synchronously)" path)))
-      (display-warning 'ssh-deploy (format "Did not find '%s' for deletion. 
(synchronously)" path) :warning))))
+          (ssh-deploy--mode-line-set-status-and-update 
ssh-deploy--status-deleting path)
+          (ssh-deploy--async-process
+           (lambda()
+             (if (file-exists-p path)
+                 (let ((file-or-directory (not (file-directory-p path))))
+                   (progn
+                     (if file-or-directory
+                         (delete-file path t)
+                       (delete-directory path t t))
+                     (list path 0)))
+               (list path 1)))
+           (lambda(response)
+             (ssh-deploy--mode-line-set-status-and-update 
ssh-deploy--status-idle (nth 0 response))
+             (let ((local-buffer (find-buffer-visiting (nth 0 response))))
+               (when local-buffer
+                 (kill-buffer local-buffer)))
+             (cond ((= 0 (nth 1 response)) (message "Completed deletion of 
'%s'. (asynchronously)" (nth 0 response)))
+                   (t (display-warning 'ssh-deploy (format "Did not find '%s' 
for deletion. (asynchronously)" (nth 0 response)) :warning))))
+           async-with-threads))
+      (if (file-exists-p path)
+          (let ((file-or-directory (not (file-directory-p path))))
+            (ssh-deploy--mode-line-set-status-and-update 
ssh-deploy--status-deleting path)
+            (progn
+              (if file-or-directory
+                  (delete-file path t)
+                (delete-directory path t t))
+              (ssh-deploy--mode-line-set-status-and-update 
ssh-deploy--status-idle path)
+              (let ((local-buffer (find-buffer-visiting path)))
+                (when local-buffer
+                  (kill-buffer local-buffer)))
+              (message "Completed deletion of '%s'. (synchronously)" path)))
+        (display-warning 'ssh-deploy (format "Did not find '%s' for deletion. 
(synchronously)" path) :warning)))))
 
 ;;;###autoload
-(defun ssh-deploy-delete-both (path-local &optional root-local root-remote 
async debug exclude-list)
-  "Delete PATH-LOCAL relative to ROOT-LOCAL as well as on ROOT-REMOTE, do it 
asynchronously if ASYNC is non-nil, debug if DEBUG is non-nil, check if path is 
excluded in EXCLUDE-LIST."
+(defun ssh-deploy-delete-both (path-local &optional root-local root-remote 
async debug exclude-list async-with-threads)
+  "Delete PATH-LOCAL relative to ROOT-LOCAL as well as on ROOT-REMOTE, do it 
asynchronously if ASYNC is non-nil, debug if DEBUG is non-nil, check if path is 
excluded in EXCLUDE-LIST.  Use async threads is ASYNC-WITH-THREADS is above 
zero."
   (let ((root-local (or root-local ssh-deploy-root-local))
-        (root-remote (or root-remote ssh-deploy-root-remote)))
+        (root-remote (or root-remote ssh-deploy-root-remote))
+        (async (or async ssh-deploy-async))
+        (debug (or debug ssh-deploy-debug))
+        (exclude-list (or exclude-list ssh-deploy-exclude-list))
+        (async-with-threads (or async async-with-threads)))
     (if (and (ssh-deploy--file-is-in-path path-local root-local)
              (ssh-deploy--file-is-included path-local exclude-list))
-        ;; FIXME: Why `concat' instead of `expand-file-name'?
-        (let ((path-remote (concat root-remote (ssh-deploy--get-relative-path 
root-local path-local))))
-          (ssh-deploy-delete path-local async debug path-local)
-          (ssh-deploy-delete path-remote async debug path-local))
-      (when debug (message "Path '%s' is not in the root '%s' or is excluded 
from it." path-local root-local)))))
+        (let ((path-remote (expand-file-name (ssh-deploy--get-relative-path 
root-local path-local) root-remote)))
+          (ssh-deploy-delete path-local async async-with-threads)
+          (ssh-deploy-delete path-remote async async-with-threads))
+      (when (> debug 0) (message "Path '%s' is not in the root '%s' or is 
excluded from it." path-local root-local)))))
 
 ;;;###autoload
-(defun ssh-deploy-rename (old-path-local new-path-local &optional root-local 
root-remote async debug exclude-list)
-  "Rename OLD-PATH-LOCAL to NEW-PATH-LOCAL under ROOT-LOCAL as well as on 
ROOT-REMOTE, do it asynchronously if ASYNC is non-nil, debug if DEBUG is 
non-nil but check if path is excluded in EXCLUDE-LIST first."
-  ;; FIXME: Next 4 lines don't do anything!
-  ;; (if (not (boundp 'debug))
-  ;;     (setq debug ssh-deploy-debug))
-  ;; (if (not (boundp 'async))
-  ;;     (setq async ssh-deploy-async))
+(defun ssh-deploy-rename (old-path-local new-path-local &optional root-local 
root-remote async debug exclude-list async-with-threads)
+  "Rename OLD-PATH-LOCAL to NEW-PATH-LOCAL under ROOT-LOCAL as well as on 
ROOT-REMOTE, do it asynchronously if ASYNC is non-nil, debug if DEBUG is 
non-nil but check if path is excluded in EXCLUDE-LIST first.  Use 
multi-threading if ASYNC-WITH-THREADS is above zero."
   (let ((root-local (or root-local ssh-deploy-root-local))
-        (root-remote (or root-remote ssh-deploy-root-remote)))
+        (root-remote (or root-remote ssh-deploy-root-remote))
+        (async (or async ssh-deploy-async))
+        (debug (or debug ssh-deploy-debug))
+        (exclude-list (or exclude-list ssh-deploy-exclude-list))
+        (async-with-threads (or async-with-threads 
ssh-deploy-async-with-threads)))
     (if (and (ssh-deploy--file-is-in-path old-path-local root-local)
              (ssh-deploy--file-is-in-path new-path-local root-local)
              (ssh-deploy--file-is-included old-path-local exclude-list)
              (ssh-deploy--file-is-included new-path-local exclude-list))
-        (let ((old-path-remote (concat root-remote 
(ssh-deploy--get-relative-path root-local old-path-local)))
-              (new-path-remote (concat root-remote 
(ssh-deploy--get-relative-path root-local new-path-local))))
+        (let ((old-path-remote (expand-file-name 
(ssh-deploy--get-relative-path root-local old-path-local) root-remote))
+              (new-path-remote (expand-file-name 
(ssh-deploy--get-relative-path root-local new-path-local) root-remote)))
           (ssh-deploy--mode-line-set-status-and-update 
ssh-deploy--status-renaming)
           (rename-file old-path-local new-path-local t)
           (if (not (file-directory-p new-path-local))
@@ -944,19 +937,20 @@
                 (set-buffer-modified-p nil))
             (dired new-path-local))
           (message "Renamed '%s' to '%s'." old-path-local new-path-local)
-          (if async
+          (if (> async 0)
               (ssh-deploy--async-process
-               (lambda ()
+               (lambda()
                  (rename-file old-path-remote new-path-remote t)
                  (list old-path-remote new-path-remote new-path-local))
                (lambda(files)
                  (ssh-deploy--mode-line-set-status-and-update 
ssh-deploy--status-idle (nth 2 files))
-                 (message "Renamed '%s' to '%s'. (asynchronously)" (nth 0 
files) (nth 1 files))))
+                 (message "Renamed '%s' to '%s'. (asynchronously)" (nth 0 
files) (nth 1 files)))
+               async-with-threads)
             (progn
               (rename-file old-path-remote new-path-remote t)
               (ssh-deploy--mode-line-set-status-and-update 
ssh-deploy--status-idle)
               (message "Renamed '%s' to '%s'. (synchronously)" old-path-remote 
new-path-remote))))
-      (if debug
+      (if (> debug 0)
           (message "Path '%s' or '%s' is not in the root '%s' or is excluded 
from it." old-path-local new-path-local root-local)))))
 
 ;;;###autoload
@@ -986,24 +980,24 @@
 ;;;###autoload
 (defun ssh-deploy-browse-remote (path-local &optional root-local root-remote 
exclude-list)
   "Browse PATH-LOCAL in `dired-mode' on remote where it is inside ROOT-LOCAL 
and mirrored on ROOT-REMOTE and not in EXCLUDE-LIST."
-  (let ((exclude-list (or exclude-list ssh-deploy-exclude-list))
-        (root-local (or root-local ssh-deploy-root-local))
-        (root-remote (or root-remote ssh-deploy-root-remote)))
+  (let ((root-local (or root-local ssh-deploy-root-local))
+        (root-remote (or root-remote ssh-deploy-root-remote))
+        (exclude-list (or exclude-list ssh-deploy-exclude-list)))
     (if (and (ssh-deploy--file-is-in-path path-local root-local)
              (ssh-deploy--file-is-included path-local exclude-list))
-        (let ((path-remote (concat root-remote (ssh-deploy--get-relative-path 
root-local path-local))))
+        (let ((path-remote (expand-file-name (ssh-deploy--get-relative-path 
root-local path-local) root-remote)))
           (message "Opening '%s' for browsing on remote host.." path-remote)
           (dired path-remote)))))
 
 ;;;###autoload
 (defun ssh-deploy-remote-terminal-eshell (path-local &optional root-local 
root-remote exclude-list)
   "Browse PATH-LOCAL inside ROOT-LOCAL on ROOT-REMOTE in `eshell-mode' if not 
in EXCLUDE-LIST."
-  (let ((exclude-list (or exclude-list ssh-deploy-exclude-list))
-        (root-local (or root-local ssh-deploy-root-local))
-        (root-remote (or root-remote ssh-deploy-root-remote)))
+  (let ((root-local (or root-local ssh-deploy-root-local))
+        (root-remote (or root-remote ssh-deploy-root-remote))
+        (exclude-list (or exclude-list ssh-deploy-exclude-list)))
     (when (and (ssh-deploy--file-is-in-path path-local root-local)
                (ssh-deploy--file-is-included path-local exclude-list))
-      (let ((path-remote (concat root-remote (ssh-deploy--get-relative-path 
root-local path-local))))
+      (let ((path-remote (expand-file-name (ssh-deploy--get-relative-path 
root-local path-local) root-remote)))
         (require 'eshell)
         (message "Opening eshell on '%s'.." path-remote)
         (let ((default-directory path-remote))
@@ -1014,18 +1008,18 @@
 ;;;###autoload
 (defun ssh-deploy-remote-terminal-shell (path-local &optional root-local 
root-remote exclude-list)
   "Browse PATH-LOCAL inside ROOT-LOCAL on ROOT-REMOTE in `eshell-mode' if not 
in EXCLUDE-LIST."
-  (let ((exclude-list (or exclude-list ssh-deploy-exclude-list))
-        (root-local (or root-local ssh-deploy-root-local))
-        (root-remote (or root-remote ssh-deploy-root-remote)))
+  (let ((root-local (or root-local ssh-deploy-root-local))
+        (root-remote (or root-remote ssh-deploy-root-remote))
+        (exclude-list (or exclude-list ssh-deploy-exclude-list)))
     (when (and (ssh-deploy--file-is-in-path path-local root-local)
                (ssh-deploy--file-is-included path-local exclude-list))
-      (let ((path-remote (concat root-remote (ssh-deploy--get-relative-path 
root-local path-local))))
+      (let ((path-remote (expand-file-name (ssh-deploy--get-relative-path 
root-local path-local) root-remote)))
         (require 'shell)
-        (defvar explicit-shell-file-name)
         (message "Opening eshell on '%s'.." path-remote)
         (let ((default-directory path-remote)
               (explicit-shell-file-name ssh-deploy-remote-shell-executable))
-          (shell path-remote))))))
+          (when explicit-shell-file-name ;; NOTE This is only to trick 
flycheck to ignore unused error
+            (shell path-remote)))))))
 
 ;;;###autoload
 (defun ssh-deploy-store-revision (path &optional root)
@@ -1037,46 +1031,43 @@
         (copy-file path revision-path t t t t))))
 
 ;;;###autoload
-(defun ssh-deploy-diff (path-local path-remote &optional root-local debug 
exclude-list async)
-  "Find differences between PATH-LOCAL and PATH-REMOTE, where PATH-LOCAL is 
inside ROOT-LOCAL.  DEBUG enables feedback message, check if PATH-LOCAL is not 
in EXCLUDE-LIST.   ASYNC make the process work asynchronously."
+(defun ssh-deploy-diff (path-local path-remote &optional root-local debug 
exclude-list async async-with-threads on-explicit-save revision-folder 
remote-changes)
+  "Find differences between PATH-LOCAL and PATH-REMOTE, where PATH-LOCAL is 
inside ROOT-LOCAL.  DEBUG enables feedback message, check if PATH-LOCAL is not 
in EXCLUDE-LIST.   ASYNC make the process work asynchronously, if 
ASYNC-WITH-THREADS is above zero use threads, ON-EXPLICIT-SAVE for automatic 
uploads, REVISION-FOLDER for revision-folder, REMOTE-CHANGES for automatic 
notification of remote change."
   (let ((file-or-directory (not (file-directory-p path-local)))
-        (exclude-list (or exclude-list ssh-deploy-exclude-list)))
-    ;; FIXME: next 6 lines don't do anything!
-    ;; (if (not (boundp 'root-local))
-    ;;     (setq root-local ssh-deploy-root-local))
-    ;; (if (not (boundp 'debug))
-    ;;     (setq debug ssh-deploy-debug))
-    ;; (if (not (boundp 'async))
-    ;;     (setq async ssh-deploy-async))
+        (root-local (or root-local ssh-deploy-root-local))
+        (debug (or debug ssh-deploy-debug))
+        (exclude-list (or exclude-list ssh-deploy-exclude-list))
+        (async (or async ssh-deploy-async))
+        (async-with-threads (or async-with-threads 
ssh-deploy-async-with-threads))
+        (on-explicit-save (or on-explicit-save ssh-deploy-on-explicit-save))
+        (revision-folder (or revision-folder ssh-deploy-revision-folder))
+        (remote-changes (or remote-changes 
ssh-deploy-automatically-detect-remote-changes)))
     (if (and (ssh-deploy--file-is-in-path path-local root-local)
              (ssh-deploy--file-is-included path-local exclude-list))
         (if file-or-directory
             (ssh-deploy-diff-files path-local path-remote)
-          (ssh-deploy-diff-directories path-local path-remote exclude-list 
async))
+          (ssh-deploy-diff-directories path-local path-remote on-explicit-save 
debug async async-with-threads revision-folder remote-changes exclude-list))
       (when debug (message "Path '%s' is not in the root '%s' or is excluded 
from it." path-local root-local)))))
 
 ;;;###autoload
-(defun ssh-deploy-upload (path-local path-remote &optional force async 
revision-folder)
-  "Upload PATH-LOCAL to PATH-REMOTE and ROOT-LOCAL via TRAMP, FORCE uploads 
despite remote change, ASYNC determines if transfer should be asynchronously, 
check version in REVISION-FOLDER."
-  ;; FIXME: Next 4 lines don't do anything!
-  ;; (if (not (boundp 'async))
-  ;;     (setq async ssh-deploy-async))
-  ;; (if (not (boundp 'force))
-  ;;     (setq force nil))
-  (let ((revision-folder (or revision-folder ssh-deploy-revision-folder)))
-    (if async
-        (ssh-deploy--upload-via-tramp-async path-local path-remote force 
revision-folder)
+(defun ssh-deploy-upload (path-local path-remote &optional force async 
revision-folder async-with-threads)
+  "Upload PATH-LOCAL to PATH-REMOTE and ROOT-LOCAL via TRAMP, FORCE uploads 
despite remote change, ASYNC determines if transfer should be asynchronously, 
check version in REVISION-FOLDER.  If you want asynchronous threads pass 
ASYNC-WITH-THREADS above zero."
+  (let ((force (or force 0))
+        (async (or async ssh-deploy-async))
+        (revision-folder (or revision-folder ssh-deploy-revision-folder))
+        (async-with-threads (or async-with-threads 
ssh-deploy-async-with-threads)))
+    (if (> async 0)
+        (ssh-deploy--upload-via-tramp-async path-local path-remote force 
revision-folder async-with-threads)
       (ssh-deploy--upload-via-tramp path-local path-remote force 
revision-folder))))
 
 ;;;###autoload
-(defun ssh-deploy-download (path-remote path-local &optional async 
revision-folder)
-  "Download PATH-REMOTE to PATH-LOCAL via TRAMP, ASYNC determines if transfer 
should be asynchrous or not, check for revisions in REVISION-FOLDER."
-  ;; FIXME: Next 2 lines don't do anything!
-  ;; (if (not (boundp 'async))
-  ;;     (setq async ssh-deploy-async))
-  (let ((revision-folder (or revision-folder ssh-deploy-revision-folder)))
-    (if async
-        (ssh-deploy--download-via-tramp-async path-remote path-local 
revision-folder)
+(defun ssh-deploy-download (path-remote path-local &optional async 
revision-folder async-with-threads)
+  "Download PATH-REMOTE to PATH-LOCAL via TRAMP, ASYNC determines if transfer 
should be asynchrous or not, check for revisions in REVISION-FOLDER.  If you 
want asynchronous threads pass ASYNC-WITH-THREADS above zero."
+  (let ((async (or async ssh-deploy-async))
+        (revision-folder (or revision-folder ssh-deploy-revision-folder))
+        (async-with-threads (or async-with-threads 
ssh-deploy-async-with-threads)))
+    (if (> async 0)
+        (ssh-deploy--download-via-tramp-async path-remote path-local 
revision-folder async-with-threads)
       (ssh-deploy--download-via-tramp path-remote path-local 
revision-folder))))
 
 
@@ -1093,10 +1084,8 @@
   (if (and (ssh-deploy--is-not-empty-string ssh-deploy-root-local)
            (ssh-deploy--is-not-empty-string ssh-deploy-root-remote))
       (let ((root-local (file-truename ssh-deploy-root-local))
+            (force (or force 0))
             path-local)
-        ;; FIXME: Next 2 lines don't do anything!
-        ;; (if (not (boundp 'force))
-        ;;     (setq force nil))
         (if (and (ssh-deploy--is-not-empty-string buffer-file-name)
                  (file-exists-p buffer-file-name))
             (setq path-local (file-truename buffer-file-name))
@@ -1106,15 +1095,15 @@
         (if (and (ssh-deploy--is-not-empty-string path-local)
                  (ssh-deploy--file-is-in-path path-local root-local)
                  (ssh-deploy--file-is-included path-local 
ssh-deploy-exclude-list))
-            (let ((path-remote (concat ssh-deploy-root-remote 
(ssh-deploy--get-relative-path root-local path-local))))
-              (ssh-deploy-upload path-local path-remote force ssh-deploy-async 
ssh-deploy-revision-folder))
-          (when ssh-deploy-debug (message "Ignoring upload, path '%s' is 
empty, not in the root '%s' or is excluded from it." path-local root-local))))))
+            (let ((path-remote (expand-file-name 
(ssh-deploy--get-relative-path root-local path-local) ssh-deploy-root-remote)))
+              (ssh-deploy-upload path-local path-remote force ssh-deploy-async 
ssh-deploy-revision-folder ssh-deploy-async-with-threads))
+          (when (> ssh-deploy-debug 0) (message "Ignoring upload, path '%s' is 
empty, not in the root '%s' or is excluded from it." path-local root-local))))))
 
 ;;;###autoload
 (defun ssh-deploy-upload-handler-forced ()
   "Upload current path to remote host if it is configured for deployment."
   (interactive)
-  (ssh-deploy-upload-handler t))
+  (ssh-deploy-upload-handler 1))
 
 ;;;###autoload
 (defun ssh-deploy-remote-changes-handler()
@@ -1124,9 +1113,9 @@
            (ssh-deploy--is-not-empty-string ssh-deploy-root-remote)
            (ssh-deploy--is-not-empty-string buffer-file-name))
       (progn
-        (when ssh-deploy-debug (message "Detecting remote-changes.."))
-        (ssh-deploy-remote-changes (file-truename buffer-file-name) 
(file-truename ssh-deploy-root-local) ssh-deploy-root-remote ssh-deploy-async 
ssh-deploy-revision-folder ssh-deploy-exclude-list))
-    (when ssh-deploy-debug (message "Ignoring remote-changes check since a 
root is empty or the current buffer lacks a file-name."))))
+        (when (> ssh-deploy-debug 0) (message "Detecting remote-changes.."))
+        (ssh-deploy-remote-changes (file-truename buffer-file-name) 
(file-truename ssh-deploy-root-local) ssh-deploy-root-remote ssh-deploy-async 
ssh-deploy-revision-folder ssh-deploy-exclude-list 
ssh-deploy-async-with-threads))
+    (when (> ssh-deploy-debug 0) (message "Ignoring remote-changes check since 
a root is empty or the current buffer lacks a file-name."))))
 
 ;;;###autoload
 (defun ssh-deploy-remote-sql-mysql-handler()
@@ -1135,6 +1124,7 @@
   (when (ssh-deploy--is-not-empty-string ssh-deploy-root-remote)
     (ssh-deploy-remote-sql ssh-deploy-root-remote "mysql")))
 
+;;;###autoload
 (defun ssh-deploy-remote-sql-postgres-handler()
   "Open `sql-postgres' on remote path if path is configured for deployment."
   (interactive)
@@ -1150,7 +1140,7 @@
            (ssh-deploy--is-not-empty-string buffer-file-name))
       (let* ((root-local (file-truename ssh-deploy-root-local))
              (path-local (file-truename buffer-file-name))
-             (path-remote (concat ssh-deploy-root-remote 
(ssh-deploy--get-relative-path root-local path-local))))
+             (path-remote (expand-file-name (ssh-deploy--get-relative-path 
root-local path-local) ssh-deploy-root-remote)))
         (when ssh-deploy-verbose (message "Opening file on remote '%s'" 
path-remote))
         (find-file path-remote))))
 
@@ -1171,9 +1161,9 @@
         (if (and (ssh-deploy--is-not-empty-string path-local)
                  (ssh-deploy--file-is-in-path path-local root-local)
                  (ssh-deploy--file-is-included path-local 
ssh-deploy-exclude-list))
-            (let ((path-remote (concat ssh-deploy-root-remote 
(ssh-deploy--get-relative-path root-local path-local))))
-              (ssh-deploy-download path-remote path-local ssh-deploy-async 
ssh-deploy-revision-folder))
-          (when ssh-deploy-debug (message "Ignoring upload, path '%s' is 
empty, not in the root '%s' or is excluded from it." path-local root-local))))))
+            (let ((path-remote (expand-file-name 
(ssh-deploy--get-relative-path root-local path-local) ssh-deploy-root-remote)))
+              (ssh-deploy-download path-remote path-local ssh-deploy-async 
ssh-deploy-revision-folder ssh-deploy-async-with-threads))
+          (when (> ssh-deploy-debug 0) (message "Ignoring upload, path '%s' is 
empty, not in the root '%s' or is excluded from it." path-local root-local))))))
 
 ;;;###autoload
 (defun ssh-deploy-diff-handler ()
@@ -1185,14 +1175,14 @@
                (file-exists-p buffer-file-name))
           (let* ((path-local (file-truename buffer-file-name))
                  (root-local (file-truename ssh-deploy-root-local))
-                 (path-remote (concat ssh-deploy-root-remote 
(ssh-deploy--get-relative-path root-local path-local))))
-            (ssh-deploy-diff path-local path-remote root-local 
ssh-deploy-debug ssh-deploy-exclude-list ssh-deploy-async))
+                 (path-remote (expand-file-name (ssh-deploy--get-relative-path 
root-local path-local) ssh-deploy-root-remote)))
+            (ssh-deploy-diff path-local path-remote root-local 
ssh-deploy-debug ssh-deploy-exclude-list ssh-deploy-async 
ssh-deploy-async-with-threads ssh-deploy-on-explicit-save 
ssh-deploy-revision-folder ssh-deploy-automatically-detect-remote-changes))
         (if (and (ssh-deploy--is-not-empty-string default-directory)
                  (file-exists-p default-directory))
             (let* ((path-local (file-truename default-directory))
                    (root-local (file-truename ssh-deploy-root-local))
-                   (path-remote (concat ssh-deploy-root-remote 
(ssh-deploy--get-relative-path root-local path-local))))
-              (ssh-deploy-diff path-local path-remote root-local 
ssh-deploy-debug ssh-deploy-exclude-list ssh-deploy-async))))))
+                   (path-remote (expand-file-name 
(ssh-deploy--get-relative-path root-local path-local) ssh-deploy-root-remote)))
+              (ssh-deploy-diff path-local path-remote root-local 
ssh-deploy-debug ssh-deploy-exclude-list ssh-deploy-async 
ssh-deploy-async-with-threads ssh-deploy-on-explicit-save 
ssh-deploy-revision-folder ssh-deploy-automatically-detect-remote-changes))))))
 
 ;;;###autoload
 (defun ssh-deploy-delete-handler ()
@@ -1206,14 +1196,14 @@
                  (root-local (file-truename ssh-deploy-root-local))
                  (yes-no-prompt (read-string (format "Type 'yes' to confirm 
that you want to delete the file '%s': " path-local))))
             (if (string= yes-no-prompt "yes")
-                (ssh-deploy-delete-both path-local root-local 
ssh-deploy-root-remote ssh-deploy-async ssh-deploy-debug)))
+                (ssh-deploy-delete-both path-local root-local 
ssh-deploy-root-remote ssh-deploy-async ssh-deploy-debug 
ssh-deploy-exclude-list ssh-deploy-async-with-threads)))
         (if (and (ssh-deploy--is-not-empty-string default-directory)
                  (file-exists-p default-directory))
             (let* ((path-local (file-truename default-directory))
                    (root-local (file-truename ssh-deploy-root-local))
                    (yes-no-prompt (read-string (format "Type 'yes' to confirm 
that you want to delete the directory '%s': " path-local))))
               (if (string= yes-no-prompt "yes")
-                  (ssh-deploy-delete-both path-local root-local 
ssh-deploy-root-remote ssh-deploy-async ssh-deploy-debug 
ssh-deploy-exclude-list)))))))
+                  (ssh-deploy-delete-both path-local root-local 
ssh-deploy-root-remote ssh-deploy-async ssh-deploy-debug 
ssh-deploy-exclude-list ssh-deploy-async-with-threads)))))))
 
 ;;;###autoload
 (defun ssh-deploy-rename-handler ()
@@ -1229,7 +1219,7 @@
                  (new-path-local-tmp (read-file-name "New file name:" 
(file-name-directory old-path-local) basename nil basename))
                  (new-path-local (file-truename new-path-local-tmp)))
             (if (not (string= old-path-local new-path-local))
-                (ssh-deploy-rename old-path-local new-path-local root-local 
ssh-deploy-root-remote ssh-deploy-async ssh-deploy-debug)))
+                (ssh-deploy-rename old-path-local new-path-local root-local 
ssh-deploy-root-remote ssh-deploy-async ssh-deploy-debug 
ssh-deploy-exclude-list ssh-deploy-async-with-threads)))
         (if (and (ssh-deploy--is-not-empty-string default-directory)
                  (file-exists-p default-directory))
             (let* ((old-path-local (file-truename default-directory))
@@ -1238,7 +1228,7 @@
                    (new-path-local-tmp (read-file-name "New directory name:" 
(file-name-directory old-path-local) basename nil basename))
                    (new-path-local (file-truename new-path-local-tmp)))
               (if (not (string= old-path-local new-path-local))
-                  (ssh-deploy-rename old-path-local new-path-local root-local 
ssh-deploy-root-remote ssh-deploy-async ssh-deploy-debug 
ssh-deploy-exclude-list)))))))
+                  (ssh-deploy-rename old-path-local new-path-local root-local 
ssh-deploy-root-remote ssh-deploy-async ssh-deploy-debug 
ssh-deploy-exclude-list ssh-deploy-async-with-threads)))))))
 
 ;;;###autoload
 (defun ssh-deploy-remote-terminal-eshell-handler ()
@@ -1305,23 +1295,20 @@
   "Run `ssh-deploy-script' with `funcall'."
   (interactive)
   (if ssh-deploy-script
-      (if ssh-deploy-async
+      (if (> ssh-deploy-async 0)
           (progn
             (message "Executing of deployment-script starting... 
(asynchronously)")
             (ssh-deploy--async-process
-             (let ((local ssh-deploy-root-local)
-                   (remote ssh-deploy-root-remote)
-                   (script ssh-deploy-script))
-               (lambda ()
-                 (let ((ssh-deploy-root-local local)
-                       (ssh-deploy-root-remote remote))
-                   (funcall script))))
-             (lambda(result) (message "Completed execution of 
deployment-script. '%s'(asynchronously)" result))))
+             `(lambda() (let ((ssh-deploy-root-local ,ssh-deploy-root-local)
+                              (ssh-deploy-root-remote ,ssh-deploy-root-remote))
+                          (funcall ,ssh-deploy-script)))
+             (lambda(result) (message "Completed execution of 
deployment-script. Return: '%s' (asynchronously)" result))
+             ssh-deploy-async-with-threads))
         (progn
           (message "Executing of deployment-script starting... 
(synchronously)")
-          (funcall ssh-deploy-script)
-          (message "Completed execution of deployment-script. 
(synchronously)")))
-    (display-warning 'ssh-deploy (format "ssh-deploy-script lacks 
definition!") :warning)))
+          (let ((ret (funcall ssh-deploy-script)))
+            (message "Completed execution of deployment-script. Return: '%s' 
(synchronously)" ret))))
+    (display-warning 'ssh-deploy "ssh-deploy-script lacks definition!" 
:warning)))
 
 
 ;;; Menu-bar
@@ -1330,64 +1317,45 @@
 ;; This is particularly useful when key-bindings are not working because of 
some mode
 ;; overriding them.
 
+
 (defvar ssh-deploy-menu-map
   (let ((map (make-sparse-keymap "Menu for SSH Deploy")))
-    (define-key map [pq]
-      '("PostgreSQL" . ssh-deploy-remote-sql-postgres-handler))
-    (define-key map [mq]
-      '("MySQL" . ssh-deploy-remote-sql-mysql-handler))
+    (define-key map [pq] '("PostgreSQL" . 
ssh-deploy-remote-sql-postgres-handler))
+    (define-key map [mq] '("MySQL" . ssh-deploy-remote-sql-mysql-handler))
     (define-key map [sep1] '("--"))
-
-    (define-key map [sb]
-      '("Shell Base" . ssh-deploy-remote-terminal-shell-base-handler))
-    (define-key map [ss]
-      '("Shell" . ssh-deploy-remote-terminal-shell-handler))
+    (define-key map [sb] '("Shell Base" . 
ssh-deploy-remote-terminal-shell-base-handler))
+    (define-key map [ss] '("Shell" . ssh-deploy-remote-terminal-shell-handler))
     (define-key map [sep2] '("--"))
-
-    (define-key map [eb]
-      '("Eshell Base" . ssh-deploy-remote-terminal-eshell-base-handler))
-    (define-key map [es]
-      '("Eshell" . ssh-deploy-remote-terminal-eshell-handler))
-    (define-key map [sep3]
-      '("--"))
-
-    (define-key map [bb]
-      '("Browse Base" . ssh-deploy-browse-remote-base-handler))
-    (define-key map [br]
-      '("Browse" . ssh-deploy-browse-remote-handler))
+    (define-key map [eb] '("Eshell Base" . 
ssh-deploy-remote-terminal-eshell-base-handler))
+    (define-key map [es] '("Eshell" . 
ssh-deploy-remote-terminal-eshell-handler))
+    (define-key map [sep3] '("--"))
+    (define-key map [bb] '("Browse Base" . 
ssh-deploy-browse-remote-base-handler))
+    (define-key map [br] '("Browse" . ssh-deploy-browse-remote-handler))
     (define-key map [sep4] '("--"))
-
-    (define-key map [df]
-      '("Difference" . ssh-deploy-diff-handler))
-    (define-key map [rc]
-      '("Detect Remote Changes" . ssh-deploy-remote-changes-handler))
+    (define-key map [df] '("Difference" . ssh-deploy-diff-handler))
+    (define-key map [rc] '("Detect Remote Changes" . 
ssh-deploy-remote-changes-handler))
     (define-key map [sep5] '("--"))
-
-    (define-key map [de]
-      '("Delete" . ssh-deploy-delete-handler))
-    (define-key map [rn]
-      '("Rename" . ssh-deploy-rename-handler))
-    (define-key map [op]
-      '("Open" . ssh-deploy-open-remote-file-handler))
+    (define-key map [de] '("Delete" . ssh-deploy-delete-handler))
+    (define-key map [rn] '("Rename" . ssh-deploy-rename-handler))
+    (define-key map [op] '("Open" . ssh-deploy-open-remote-file-handler))
     (define-key map [sep6] '("--"))
-
-    (define-key map [sc]
-      '("Run script" . ssh-deploy-run-deploy-script-handler))
+    (define-key map [sc] '("Run script" . 
ssh-deploy-run-deploy-script-handler))
     (define-key map [sep7] '("--"))
-
-    (define-key map [ulf]
-      '("Forced Upload" . ssh-deploy-upload-handler-forced))
-    (define-key map [ul]
-      '("Upload" . ssh-deploy-upload-handler))
-    (define-key map [dl]
-      '("Download" . ssh-deploy-download-handler))
+    (define-key map [ulf] '("Forced Upload" . 
ssh-deploy-upload-handler-forced))
+    (define-key map [ul] '("Upload" . ssh-deploy-upload-handler))
+    (define-key map [dl] '("Download" . ssh-deploy-download-handler))
     map))
 
-(define-key-after
-  global-map
-  [menu-bar sshdeploy]
-  (cons "Deployment" ssh-deploy-menu-map)
-  'tools)
+(defun ssh-deploy-menu-map-update ()
+  "Update menu map and only show menu if deployment is active."
+  (if (and ssh-deploy-root-local ssh-deploy-root-remote)
+      (define-key-after global-map [menu-bar sshdeploy] (cons "Deployment" 
ssh-deploy-menu-map) 'tools)
+    (define-key-after global-map [menu-bar sshdeploy] 'undefined 'tools)))
+
+(defun ssh-deploy-add-menu ()
+  "Add menu-bar support."
+  (add-hook 'menu-bar-update-hook 'ssh-deploy-menu-map-update))
+
 
 ;;; Mode Line
 
@@ -1395,15 +1363,83 @@
 (define-minor-mode ssh-deploy-line-mode
   "Show SSH Deploy status in mode line"
   :global t
+  :group 'ssh-deploy
   (add-to-list 'global-mode-string 'ssh-deploy--mode-line-status-text t))
+
 (ssh-deploy--mode-line-status-refresh)
 
-;; Start mode line by default
-;; FIXME: By convention, loading  an Elisp file should not affect
-;; Emacs's behavior!
-(ssh-deploy-line-mode)
+
+;;; Usability shortcuts
+
+
+(defun ssh-deploy-after-save () "Logic for automatic uploads."
+       (when (and (boundp 'ssh-deploy-on-explicit-save) 
ssh-deploy-on-explicit-save (> ssh-deploy-on-explicit-save 0)) 
(ssh-deploy-upload-handler)))
+
+(defun ssh-deploy-add-after-save-hook () "Add the `after-save-hook'."
+       (when (fboundp 'ssh-deploy-after-save)
+         (add-hook 'after-save-hook 'ssh-deploy-after-save)))
+
+(defun ssh-deploy-find-file () "Logic for detecting remote change."
+       (when (and (boundp 'ssh-deploy-automatically-detect-remote-changes) 
ssh-deploy-automatically-detect-remote-changes (> 
ssh-deploy-automatically-detect-remote-changes 0)) 
(ssh-deploy-remote-changes-handler)))
+
+(defun ssh-deploy-add-find-file-hook () "Add the `find-file-hook'."
+       (when (fboundp 'ssh-deploy-find-file) (add-hook 'find-file-hook 
'ssh-deploy-find-file)))
+
+(when (fboundp 'defhydra)
+  (defhydra ssh-deploy-hydra (:color red :hint nil)
+    "
+    SSH Deploy Menu
+    
+    _u_: Upload                              _f_: Force Upload
+    _d_: Download
+    _D_: Delete
+    _x_: Difference
+    _t_: Eshell Base Terminal                _T_: Eshell Relative Terminal
+    _h_: Shell Base Terminal                 _H_: Shell Relative Terminal
+    _e_: Detect Remote Changes
+    _R_: Rename
+    _b_: Browse Base                         _B_: Browse Relative
+    _o_: Open current file on remote         _m_: Open sql-mysql on remote
+    _s_: Run deploy script
+    "
+    ("f" ssh-deploy-upload-handler-forced)
+    ("u" ssh-deploy-upload-handler)
+    ("d" ssh-deploy-download-handler)
+    ("D" ssh-deploy-delete-handler)
+    ("x" ssh-deploy-diff-handler)
+    ("t" ssh-deploy-remote-terminal-eshell-base-handler)
+    ("T" ssh-deploy-remote-terminal-eshell-handler)
+    ("h" ssh-deploy-remote-terminal-shell-base-handler)
+    ("H" ssh-deploy-remote-terminal-shell-handler)
+    ("e" ssh-deploy-remote-changes-handler)
+    ("R" ssh-deploy-rename-handler)
+    ("b" ssh-deploy-browse-remote-base-handler)
+    ("B" ssh-deploy-browse-remote-handler)
+    ("o" ssh-deploy-open-remote-file-handler)
+    ("m" ssh-deploy-remote-sql-mysql-handler)
+    ("s" ssh-deploy-run-deploy-script-handler)))
+
+(defvar ssh-deploy-prefix-map
+  (let ((map (make-sparse-keymap)))
+    (define-key map "f" 'ssh-deploy-upload-handler-force)
+    (define-key map "u" 'ssh-deploy-upload-handle)
+    (define-key map "D" 'ssh-deploy-delete-handler)
+    (define-key map "d" 'ssh-deploy-download-handler)
+    (define-key map "x" 'ssh-deploy-diff-handler)
+    (define-key map "t" 'ssh-deploy-remote-terminal-eshell-base-handler)
+    (define-key map "T" 'ssh-deploy-remote-terminal-eshell-handler)
+    (define-key map "h" 'ssh-deploy-remote-terminal-shell-base-handler)
+    (define-key map "H" 'ssh-deploy-remote-terminal-shell-handler)
+    (define-key map "R" 'ssh-deploy-rename-handler)
+    (define-key map "e" 'ssh-deploy-remote-changes-handler)
+    (define-key map "b" 'ssh-deploy-browse-remote-base-handler)
+    (define-key map "B" 'ssh-deploy-browse-remote-handler)
+    (define-key map "o" 'ssh-deploy-open-remote-file-handler)
+    (define-key map "m" 'ssh-deploy-remote-sql-mysql-handler)
+    (define-key map "s" 'ssh-deploy-run-deploy-script-handler)
+    map))
+(fset 'ssh-deploy-prefix-map ssh-deploy-prefix-map)
 
 
 (provide 'ssh-deploy)
 ;;; ssh-deploy.el ends here
-



reply via email to

[Prev in Thread] Current Thread [Next in Thread]