# # patch "contrib/monotone.el" # from [a4d4e79c12a521038d669310c3a86de36f1e252c] # to [9d7a96ba10fdbfe14cafb7845b64f5a9a7510816] # --- contrib/monotone.el +++ contrib/monotone.el @@ -12,35 +12,29 @@ ;; To use monotone from within Emacs, all you should need to ;; do is require the package. There are many options but ;; then only one you should have to set is the prefix key for -;; the keybindings. Something like the following should work: +;; the keybindings. Something like the following should work: ;; ;; (require 'monotone) ;; (monotone-set-vc-prefix-key [f5]) ;; or "\C-xv" ;; (setq monotone-passwd-remember t) ;; +;; You may want to put "monotone-grab-id" on a handy function key. +;; ;; Monotone prefers to work with the global working set. ;; "monotone.el" has its defaults set to match. ;; Commands run without a prefix work on the global working set. ;; One C-u is the subtree (".") and C-u C-u is the current file. ;; (There are exceptions) ;; - - +;; Some of the function names follow Emacs "vc-" names, +;; others follow monotone names. I havent decided which I +;; like better. ;; -;; This defines `monotone-diff', `monotone-status', `monotone-add', -;; `monotone-drop', `monotone-revert' and `monotone-commit'. These -;; functions call the corresponding monotone command, restricted to -;; the current file. With a prefix argument (C-u) the command is -;; applied unrestricted (on the whole tree). As an exception, -;; `monotone-status' has the opposite behaviour: it is unrestricted by -;; default, restricted with a prefix argument. -;; -;; /!\ beware of bugs: `monotone-commit' is more dangerous than the -;; others since it writes to the database. -;; +;; This mode was written and tested with GNU Emacs 21.3.50.1 ;; FIXME: handle aborts better and kill monotone. -;; FEATURE: given an id, suck out the file with "monotone cat" +;; FIXME: given an id, suck out the file with "monotone cat" +;; FIXME: handle diff --revision XXX path/to/file ;;; User vars: ;; These vars are likley to be changed by the user. @@ -70,10 +64,22 @@ ;;; System Vars: -;; It is unlikely for users to change these. +;; It is unlikely for users to need to change these. +(defvar monotone-last-id nil + "The last id which was worked with or grabbed. +This could be a file, manifest or revision. +It is also stuffed into the kill ring. +This is used for defaults.") +(defvar monotone-last-fileid nil + "The last file id.") +(defvar monotone-last-manifestid nil + "The last manifest id.") +(defvar monotone-last-revisionid nil + "The last revision id.") + (defvar monotone-buffer "*monotone*" - "The buffer used for displaying monotone output.") + "The buffer used for running monotone commands.") (defvar monotone-commit-buffer "*monotone commit*" "The name of the buffer for the commit message.") @@ -152,12 +158,12 @@ (defvar monotone-vc-prefix-map (let ((map (make-sparse-keymap))) (define-key map "=" 'monotone-vc-diff) - (define-key map "P" 'monotone-vc-push) + (define-key map "P" 'monotone-push) (define-key map "\C-q" 'monotone-vc-commit) (define-key map "i" 'monotone-vc-register) - (define-key map "i" 'monotone-vc-register) + (define-key map "6" 'monotone-grab-id) (define-key map "l" 'monotone-vc-print-log) - (define-key map "p" 'monotone-vc-pull) + (define-key map "p" 'monotone-pull) (define-key map "q" 'monotone-vc-commit) ;; i am a lazy typist (define-key map "s" 'monotone-vc-status) map)) @@ -178,7 +184,7 @@ (defun monotone-toggle-vc-prefix-map (&optional arg) "Toggle between the default and monotone vc-maps, ARG set map. With arg 0 use the default variable `vc-prefix-map'. -With t use monotone-vc-prefix-map. +With t use `monotone-vc-prefix-map'. This permits quick switches between the classic vc and monotone keymaps." (interactive "P") (message "Arg: %s" arg) @@ -216,18 +222,23 @@ (defun monotone-extract-MT-path (path &optional mt-top) "Get the PATH minus the MT-TOP." + ;; cast and check + (when (bufferp path) + (setq path (buffer-file-name path))) + (when (not (stringp path)) + (error "path is not a string.")) (let ((mt-top (or mt-top monotone-MT-top (monotone-find-MT-top path)))) ;; work with full names (setq path (expand-file-name path) mt-top (expand-file-name mt-top)) - ;; (if (not mt-top) nil (substring path (length mt-top))))) -;;(monotone-extract-MT-path "/disk/amelie1/harley/monotone-dev/contrib/monotone.el") -;;(monotone-find-MT-dir "/disk/amelie1/harley") -;; +;; (monotone-extract-MT-path "/disk/amelie1/harley/monotone-dev/contrib/monotone.el") +;; (monotone-find-MT-dir "/disk/amelie1/harley") +;; (monotone-extract-MT-path (current-buffer)) + (defun monotone-output-mode () "In the future this will provide some fontification. Nothing for now." @@ -237,29 +248,6 @@ ;;(define-derived-mode monotone-shell-mode comint-mode "Monotone") -(defun monotone-arg-decode (arg) - "Decode the arg into the scope monotone should work on." - (interactive "p") - (message "%s" arg) - (cond - ((= arg 1) 'global) - ((= arg 4) 'tree) - ((= arg 16) 'file) - (t (error "Prefix should be in (1,4,16)")))) -;; (monotone-arg-decode 4) - -(defun monotone-arg-scope (scope filename) - (when (numberp scope) - (setq scope (monotone-arg-decode scope))) - (when (bufferp filename) - (setq filename (buffer-file-name filename))) - (cond - ((eq scope 'global) nil) - ((eq scope 'tree) ".") - ((eq scope 'file) (monotone-extract-MT-path filename)) - (t (error "Bad scope: %s" scope)))) -;; (monotone-arg-scope 'file (current-buffer)) - ;; Run a monotone command (defun monotone-cmd (args) "Execute the monotone command with ARGS in the monotone top directory." @@ -347,25 +335,10 @@ (interactive) (let ((args monotone-cmd-last-args)) (when (or (null args) (not (listp args))) - (error "no last args to rerun")) + (error "No last args to rerun")) (monotone-cmd args))) ;; (monotone-cmd "list known") -;; check for common errors and args. -(defun monotone-cmd-buf (global buf cmds) - "Run a simple monotone command for this buffer. (passwordless) -GLOBAL runs the command without the buffer. -BUF is the buffer if not global. -CMD is the command to execute." - (let ((bfn (buffer-file-name))) - (when (not bfn) - (error "No file-name for buffer")) - (let* ((monotone-MT-top (monotone-find-MT-top bfn)) - (bmn (monotone-extract-MT-path bfn))) - (if global - (monotone-cmd cmds) - (monotone-cmd (append cmds (list bmn)))) ))) - (defun monotone-cmd-is-running () "Return if monotone is running." (save-window-excursion @@ -375,19 +348,20 @@ ;;;;;;;;;; -(defun monotone-passwd-remember (keypairid passwd) - "Remember the password." +(defun monotone-passwd-remember (keypairid password) + "Remember the PASSWORD for KEYPAIRID." (let ((rec (assoc keypairid monotone-passwd-alist))) (if rec - (setcdr rec passwd) + (setcdr rec password) (progn - (setq rec (cons keypairid passwd)) + (setq rec (cons keypairid password)) (setq monotone-passwd-alist (cons rec monotone-passwd-alist)))) rec)) ;; (monotone-passwd-remember "foo" "bar") ;; (setq monotone-passwd-alist nil) (defun monotone-passwd-find (keypairid) + "Return the password for KEYPAIRID or nil." (cdr (assoc keypairid monotone-passwd-alist))) ;; (monotone-passwd-find "foo") @@ -410,12 +384,29 @@ (interactive) (monotone-cmd '("list" "branches"))) -(defun monotone-pull (&optional server collection) - "Pull data from the optinal SERVER and COLLECTION." - ;;(interactive "sServer: \nsCollection: \n") - (let ((cmd (list "--ticker=dot" "pull")) - (svr (or server monotone-server "")) - (col (or collection monotone-collection ""))) +;;;;;;;;;; + +(defun monotone-db-prompt () + "Prompt for the server and collection, defaulting to the prior values." + ;; read-string docs say not to use initial-input but "compile" does. + (setq monotone-server + (read-string "Monotone server [host:port]: " monotone-server + 'monotone-server-hist)) + (setq monotone-collection + (read-string "Monotone collection: " monotone-collection + 'monotone-collection-hist))) + +(defun monotone-db-action (prefix action) + "Preform the db ACTION requested. With PREFIX prompt for info." + (when (equal prefix 0) + (setq monotone-server nil + monotone-collection nil)) + (when prefix + (monotone-db-prompt)) + ;; + (let ((cmd (list "--ticker=dot" (format "%s" action))) + (svr (or monotone-server "")) + (col (or monotone-collection ""))) ;; given address? (when (and (stringp svr) (not (string= svr ""))) (setq cmd (append cmd (list svr))) @@ -424,40 +415,42 @@ (setq cmd (append cmd (list col))))) ;; (monotone-cmd cmd))) -;; (monotone-pull) -(defun monotone-vc-pull () - "Pull updates from a remote server. With ARG prompt for server and collection. -With an arg of 0, clear default server and collection." - (interactive) - ;; read-string docs say not to use initial-input but "compile" does. - (setq monotone-server - (read-string "Monotone server: " monotone-server - 'monotone-server-hist)) - (setq monotone-collection - (read-string "Monotone collection: " monotone-collection - 'monotone-collection-hist)) - (monotone-pull monotone-server monotone-collection)) -;; (monotone-vc-pull) +(defun monotone-pull (arg) + "Pull updates from a remote server. ARG prompts. +With ARG prompt for server and collection. +With ARG of 0, clear default server and collection." + (interactive "P") + (monotone-db-action arg "pull")) +(defun monotone-push () + "Push the DB contents to a remote server. ARG prompts." + (interactive "P") + (monotone-db-action arg "push")) -(defun monotone-vc-push () - "FIXME: This is a lame-ass push without args. -I want to do the first push of monotone.el from within emacs." - (interactive) - (monotone-cmd '("--ticker=dot" "push"))) +(defun monotone-sync (arg) + "Sync the DB with a remote server. ARG prompts." + (interactive "P") + (monotone-db-action arg "sync")) +;;;;;;;;;; + ;;; Start if the commit process... ;; FIXME: the default should be a global commit. -(defun monotone-vc-commit (&rest args) - "Commit the current buffer. With ARGS do a global commit." - (interactive "P") +(defun monotone-vc-commit (args) + "Do a commit." + (interactive "p") + (setq args (monotone-arg-decode args)) + (when (eq args 'file) + (when (not (setq args (buffer-file-name))) + (error "Cant commit a buffer without a filename"))) ;; dont run two processes (when (monotone-cmd-is-running) (switch-to-buffer (get-buffer-create monotone-buffer)) - (error "You have a monotone process running.")) + (error "You have a monotone process running")) ;; flush buffers (save-some-buffers) + ;; (let ((buf (get-buffer-create monotone-commit-buffer)) (monotone-MT-top (monotone-find-MT-top))) ;; found MT? @@ -489,7 +482,8 @@ (insert "\n")) (let ((eo-message (point))) ;; what is being commited? - (mapc (function (lambda (a) (insert "args: " (format "%s" a) "\n"))) args) + ;;(mapc (function (lambda (a) (insert "args: " (format "%s" a) "\n"))) args) + (insert (format "%s\n" args)) ;;(insert (format "Commit arg = %s" arg) "\n") ;; instructional text (when (stringp monotone-commit-instructions) @@ -510,7 +504,7 @@ monotone-commit-args args))) (defun monotone-commit-mode (&optional arg) - "Mode for editing a monotone commit message. ARG turns on" + "Mode for editing a monotone commit message. ARG turns on." (interactive "p") (fundamental-mode) ;; (text-mode) (run-hooks monotone-commit-mode-hook) @@ -526,17 +520,27 @@ (defun monotone-commit-complete () "Complete the message and commit the work." (interactive) + (when (not (eq monotone-commit-edit-status 'started)) + (error "The commit in this buffer is '%s'" monotone-commit-edit-status)) (monotone-remove-MT-lines) (let ((buf (current-buffer)) (message (buffer-substring-no-properties (point-min) (point-max))) + (mca monotone-commit-args) ;; copy of buffer-local-var (args (list "commit"))) (switch-to-buffer (get-buffer-create monotone-buffer)) ;; assemble and run the command (setq args (append args (list "--message" message))) ;; FIXME: global subtree file list... - (when monotone-commit-args - (setq args (append args (list ".")))) - (monotone-cmd args) + (cond + ((equal mca 'global) + (monotone-cmd args)) ;; no spec + ((equal mca 'tree) + (error "Monotone tree scope sucks for commit!")) + ((stringp mca) ;; file + (setq args (append args (list (monotone-extract-MT-path mca)))) + (monotone-cmd args)) + (t + (error "unknown monotone-commit-args"))) ;; mark it done (set-buffer buf) (setq monotone-commit-edit-status 'done))) @@ -549,42 +553,104 @@ (beginning-of-line) (kill-line 1))) +;;;;;;;;;; +(defun monotone-arg-decode (arg) + "Decode the ARG into the scope monotone should work on." + (interactive "p") + (message "%s" arg) + (cond + ((member arg '(global tree file)) arg) ;; identity + ((= arg 1) 'global) + ((= arg 4) 'tree) + ((= arg 16) 'file) + (t (error "Prefix should be in (1,4,16) or (global tree file)")))) +;; (monotone-arg-decode 4) +;; (monotone-arg-decode 'file) + +;;(defun monotone-arg-scope (scope filename) +;; "Turn the SCOPE and FILENAME into and arg for monotone." +;; (when (numberp scope) +;; (setq scope (monotone-arg-decode scope))) +;; (when (bufferp filename) +;; (setq filename (buffer-file-name filename))) +;; (cond +;; ((eq scope 'global) nil) +;; ((eq scope 'tree) ".") +;; ((eq scope 'file) +;; (if filename +;; (monotone-extract-MT-path filename +;; (t (error "Bad scope: %s" scope)))) +;;;; (monotone-arg-scope 'file (current-buffer)) + +;; check for common errors and args. +(defun monotone-cmd-buf (prefix cmds &optional buf) + "Run a simple monotone command for this buffer. +PREFIX selects the scope. CMDS is the command to execute. BUF is +the buffer if not global." + (setq prefix (monotone-arg-decode prefix)) ;; what is the scope? + (setq buf (or buf (current-buffer))) ;; default + (cond + ((eq prefix 'global) + (monotone-cmd cmds)) + ((eq prefix 'tree) + ;; MONOTONE BUG: when using "." the command must be run in the dir + ;; all other commands are run in MT-top + ;; FIXME: cd to the correct place in the tree + (monotone-cmd (append cmds (list ".")))) + ((eq prefix 'file) + (let ((name (buffer-file-name buf))) + (if name + (monotone-cmd (append cmds (list (monotone-extract-MT-path name)))) + (error "This buffer is not a file")))) + (t + (error "Bad prefix")))) + +;; runs the command without the buffer. +;; (let ((bfn (buffer-file-name))) +;; (when (not bfn) +;; (error "No file-name for buffer")) +;; (let* ((monotone-MT-top (monotone-find-MT-top bfn)) +;; (bmn (monotone-extract-MT-path bfn))) +;; + ;; NOTE: The command names are modeled after the vc command names. -(defun monotone-log-depth (arg) +(defun monotone-set-log-depth (arg) + "Set the max number of entries displayed in log output to ARG." (interactive "NEnter max depth of log entries to report (0=all): ") (setq monotone-log-depth arg)) -;; (monotone-log-depth 10) +;; (monotone-set-log-depth 10) (defun monotone-vc-print-log (&optional arg) "Print the log for this buffer. With prefix ARG the global log." (interactive "p") - (let ((cmd (list "log"))) + (let ((cmds (list "log"))) (when (and (numberp monotone-log-depth) (< 0 monotone-log-depth)) - (setq cmd (append cmd (list (format "--depth=%d" monotone-log-depth))))) - (monotone-cmd-buf arg (current-buffer) cmd) + (setq cmds (append cmds (list (format "--depth=%d" monotone-log-depth))))) + (monotone-cmd-buf arg cmds) (rename-buffer "*monotone log*" t))) ;; (monotone-print-log) (defun monotone-vc-diff (&optional arg) "Print the diffs for this buffer. With prefix ARG, the global diffs." - (interactive "P") + (interactive "p") (save-some-buffers) - (let ((mt-top (monotone-find-MT-top)) - (bfn (buffer-file-name)) - (args (list "diff"))) - (let ((what (if bfn (monotone-extract-MT-path bfn mt-top) "."))) - (setq args (append args (list what))) - (monotone-cmd args) - (rename-buffer (format "*monotone diff %s*" what) t) - (diff-mode)))) + (let ((what (monotone-arg-decode arg)) + (name (buffer-file-name))) + (monotone-cmd-buf what '("diff")) + (diff-mode) + (rename-buffer + (format "*monotone diff %s*" + (case what + ('file (monotone-extract-MT-path name)) + (t what))) t))) (defun monotone-vc-register () "Register this file with monotone for the next commit." (interactive) (if buffer-file-name - (monotone-cmd-buf nil (current-buffer) "add") + (monotone-cmd-buf 'file "add" (current-buffer)) (error "This buffer does not have a file name"))) (defun monotone-vc-status () @@ -602,7 +668,9 @@ ;; (monotone-vc-update-change-log) -(defun monotone-vc-revision () +(defun monotone-cat-revision () + "Display the current revision." + (interactive) (monotone-cmd '("cat" "revision"))) ;;;;;;;;;; @@ -611,69 +679,102 @@ "A regexp matching a monotone id.") (defun monotone-id-at-point () - (interactive) + "Return the ID under the point." (save-excursion (skip-chars-backward "0-9A-Fa-f" (- (point) 40)) (if (looking-at monotone-id-regexp) (match-string 1) nil))) -(defun monotone-id-at-point-prompt (what) - "Get the id at point or prompt for one." +(defun monotone-grab-id () + "Grab the id under point and put it in the kill buffer for later use. +Grab the ids you want from the buffer and then yank back when needed." + (interactive) (let ((id (monotone-id-at-point))) (when (not id) + (error "Point is not on a monotone id")) + (setq monotone-last-id id) + (kill-new id))) + +(defun monotone-id-at-point-prompt (what defaultid) + "Get the id at point. Prompt for WHAT not found, defaulting to DEFAULTID." + (let ((id (monotone-id-at-point))) + (when (not id) (let ((prompt (capitalize (format "%s: " what)))) - (setq id (read-string prompt)))) + (setq id (read-string prompt (or defaultid monotone-last-id))))) id)) ;; (monotone-id-at-point-prompt 'file) (defun monotone-cat-id (what id) + "Display the item WHAT which has ID." (when id (let ((what (format "%s" what)) (name (format "*monotone %s %s*" what id))) (monotone-cmd (list "cat" what id)) + ;; remember it + (setq monotone-last-id id) ;; dont duplicate the buffers (if (get-buffer name) - (kill-buffer name)) + (kill-buffer name)) (rename-buffer name)))) - (defun monotone-cat-fileid (&optional id) "Display the file with ID." (interactive) - (monotone-cat-id 'file (or id (monotone-id-at-point-prompt 'file)))) + (monotone-cat-id 'file (or id (monotone-id-at-point-prompt 'file monotone-last-fileid))) + (setq monotone-last-fileid monotone-last-id)) (defun monotone-cat-manifestid (&optional id) "Display the manifest with ID." (interactive) - (monotone-cat-id 'manifest (or id (monotone-id-at-point-prompt 'manifest)))) + (monotone-cat-id 'manifest (or id (monotone-id-at-point-prompt 'manifest monotone-last-manifestid))) + (setq monotone-last-revisionid monotone-last-id)) (defun monotone-cat-revisionid (&optional id) "Display the revision with ID." (interactive) - (monotone-cat-id 'revision (or id (monotone-id-at-point-prompt 'revision)))) + (monotone-cat-id 'revision (or id (monotone-id-at-point-prompt 'revision monotone-last-revisionid))) + (setq monotone-last-revisionid monotone-last-id)) - ;;;;;;;;;; (defvar monotone-menu (let ((map (make-sparse-keymap "Monotone"))) ;;(define-key map [monotone-vc-] '(menu-item "" monotone-vc-)) - (define-key map [monotone-vc-diff] '(menu-item "Diff" monotone-vc-diff)) - (define-key map [monotone-vc-log] '(menu-item "Log" monotone-vc-log)) - (define-key map [monotone-vc-status] '(menu-item "Status" monotone-vc-status)) + ;; The menu items are defined in REVERSE order. So we reverse them here. + ;; (reverse + ;; (list + (define-key map [monotone-vc-log] + '(menu-item "Log" monotone-vc-log)) + (define-key map [monotone-vc-status] + '(menu-item "Status" monotone-vc-status)) + (define-key map [monotone-vc-diff] + '(menu-item "Diff" monotone-vc-diff)) + (define-key map [monotone-grab-id] + '(menu-item "Grab ID" monotone-grab-id)) + ;; (define-key map [monotone-separator] '("--")) - (define-key map [monotone-cat-fid] '(menu-item "Cat this file id" monotone-cat-fileid)) - (define-key map [monotone-cat-mid] '(menu-item "Cat this manifest id" monotone-cat-manifestid)) - (define-key map [monotone-cat-rid] '(menu-item "Cat this revision id" monotone-cat-revisionid)) + (define-key map [monotone-cat-fid] + '(menu-item "Cat this file id" monotone-cat-fileid)) + (define-key map [monotone-cat-mid] + '(menu-item "Cat this manifest id" monotone-cat-manifestid)) + (define-key map [monotone-cat-rid] + '(menu-item "Cat this revision id" monotone-cat-revisionid)) + ;; (define-key map [monotone-separator] '("--")) - (define-key map [monotone-vc-pull] '(menu-item "DB Pull" monotone-vc-pull)) - (define-key map [monotone-vc-push] '(menu-item "DB Push" monotone-vc-push)) - (define-key map [monotone-vc-sync] '(menu-item "DB Sync" monotone-vc-sync)) + (define-key map [monotone-pull] + '(menu-item "DB Pull" monotone-pull)) + (define-key map [monotone-push] + '(menu-item "DB Push" monotone-push)) + (define-key map [monotone-sync] + '(menu-item "DB Sync" monotone-sync)) + ;;)) map)) +;; FIXME: an error was reported with define-key-after -- why? (when monotone-menu-name - (define-key-after global-map [menu-bar monotone] (cons monotone-menu-name monotone-menu))) + (define-key-after global-map [menu-bar monotone] + (cons monotone-menu-name monotone-menu))) (provide 'monotone) ;;; monotone.el ends here