update dvc

This commit is contained in:
Kai Tetzlaff 2010-09-12 22:16:14 +02:00
parent 0c9e5fe4b8
commit ec24bef5a4
14 changed files with 2155 additions and 471 deletions

View File

@ -554,10 +554,11 @@ used to get more info about the process.")
(defun dvc-build-dvc-command (dvc list-args)
"Build a shell command to run DVC with args LIST-ARGS.
DVC can be one of 'baz, 'xhg, ..."
(let ((executable (executable-find (dvc-variable dvc "executable"))))
(let* ((name (dvc-variable dvc "executable"))
(executable (executable-find name)))
;; 'executable-find' allows leading ~
(if (not executable)
(error "executable for %s not found" (symbol-name dvc)))
(error "executable %s for %s not found" name (symbol-name dvc)))
(mapconcat 'shell-quote-argument
(cons executable
(remq nil list-args))

View File

@ -54,7 +54,7 @@
;; is options, cdr is the command and arguments. Options are always
;; specified as pairs of keyword and value, and without the leading
;; "--". If an option has no value, use ""; see
;; xmtn-automate-local-changes for an example.
;; xmtn--status-inventory-sync in xmtn-dvc for an example.
;;
;; `xmtn-automate-new-command' returns a command handle. You use this
;; handle to check the error code of the command and obtain its
@ -74,6 +74,9 @@
(require 'xmtn-run)
(require 'xmtn-compat))
(defconst xmtn-automate-arguments (list "--rcfile" (locate-library "xmtn-hooks.lua"))
"Arguments and options for 'mtn automate stdio' sessions.")
(defun xmtn-automate-command-buffer (command)
(xmtn-automate--command-handle-buffer command))
@ -92,7 +95,10 @@
(goto-char (point-max))
(newline)
(insert (format "command: %s" (xmtn-automate--command-handle-command handle)))
(error "mtn error %s" (xmtn-automate--command-handle-error-code handle))))
(error "mtn error %s" (xmtn-automate--command-handle-error-code handle)))
(if (xmtn-automate--command-handle-warnings handle)
(display-buffer (format dvc-error-buffer 'xmtn) t))
)
nil)
(defvar xmtn-automate--*sessions* '()
@ -132,7 +138,7 @@ workspace root."
(xmtn-automate-command-wait-until-finished command-handle)
(xmtn-automate--command-output-as-string command-handle)))
(defun xmtn-automate-simple-command-output-insert-into-buffer
(defun xmtn-automate-command-output-buffer
(root buffer command)
"Send COMMAND to session for ROOT, insert result into BUFFER."
(let* ((session (xmtn-automate-cache-session root))
@ -217,7 +223,8 @@ Signals an error if output contains zero lines or more than one line."
(buffer)
(write-marker)
(finished-p nil)
(error-code nil))
(error-code nil)
(warnings nil))
(defun* xmtn-automate--initialize-session (session &key root name)
(xmtn--assert-optional (equal root (file-name-as-directory root)) t)
@ -281,9 +288,11 @@ Signals an error if output contains zero lines or more than one line."
"Kill session for ROOT."
(interactive)
(let ((temp (assoc (dvc-uniquify-file-name root) xmtn-automate--*sessions*)))
(xmtn-automate--close-session (cdr temp))
(setq xmtn-automate--*sessions*
(delete temp xmtn-automate--*sessions* ))))
;; session may have already been killed
(when temp
(xmtn-automate--close-session (cdr temp))
(setq xmtn-automate--*sessions*
(delete temp xmtn-automate--*sessions* )))))
(defun xmtn-kill-all-sessions ()
"Kill all xmtn-automate sessions."
@ -305,7 +314,7 @@ Signals an error if output contains zero lines or more than one line."
(default-directory root))
(let ((process
(apply 'start-process name buffer xmtn-executable
"automate" "stdio" xmtn-additional-arguments)))
"automate" "stdio" xmtn-automate-arguments)))
(ecase (process-status process)
(run
;; If the process started ok, it outputs the stdio
@ -472,11 +481,14 @@ Return non-nil if some text copied."
(?m
(xmtn-automate--command-handle-buffer command))
((?e ?w ?p ?t)
(if (equal ?w (xmtn-automate--decoder-state-stream state))
(setf (xmtn-automate--command-handle-warnings command) t))
;; probably ought to do something else with p and t, but
;; this is good enough for now.
(get-buffer-create (format dvc-error-buffer 'xmtn)))))
(write-marker
(xmtn-automate--command-handle-write-marker command)))
(with-current-buffer session-buffer
(let* ((end (min (+ (xmtn-automate--decoder-state-read-marker state)
(xmtn-automate--decoder-state-remaining-chars state))
@ -535,16 +547,19 @@ Return non-nil if some text copied."
(if (= (xmtn-automate--decoder-state-read-marker state) write-marker)
(setq tag 'exit-loop)
(setq tag 'again)))
(again
(cond
((> (xmtn-automate--decoder-state-remaining-chars state) 0)
;; copy more output from the current packet
(if (xmtn-automate--process-new-output--copy session)
(setq tag 'again)
(setq tag 'check-for-more)))
(if (= ?l (xmtn-automate--decoder-state-stream state))
;; got the rest of the last packet; process in t branch next loop
(setf (xmtn-automate--decoder-state-remaining-chars state) 0)
(if (xmtn-automate--process-new-output--copy session)
(setq tag 'again)
(setq tag 'check-for-more))))
(t
;; new packet
;; new packet, or final packet
(goto-char (xmtn-automate--decoder-state-read-marker state))
;; A packet has the structure:
;; <command number>:<stream>:<size>:<output>
@ -555,46 +570,50 @@ Return non-nil if some text copied."
;; p progress
;; t ticker
;; l last
;;
;; If size is large, we may not have all of the output in new-string
(cond
((looking-at "\\([0-9]+\\):\\([mewptl]\\):\\([0-9]+\\):")
(let ((command-number (parse-integer (match-string 1)))
(stream (aref (match-string 2) 0))
(let ((stream (aref (match-string 2) 0))
(size (parse-integer (match-string 3))))
(setf (xmtn-automate--decoder-state-read-marker state) (match-end 0))
(setf (xmtn-automate--decoder-state-stream state) stream)
(setf (xmtn-automate--decoder-state-remaining-chars state) size)
(setf (xmtn-automate--decoder-state-stream state) stream)
(ecase stream
((?m ?e ?w ?t ?p)
(setf (xmtn-automate--decoder-state-remaining-chars state) size)
(setf (xmtn-automate--decoder-state-read-marker state) (match-end 0))
(setq tag 'again) )
(?l
(setf (xmtn-automate--decoder-state-read-marker state) (+ size (match-end 0)))
(setf (xmtn-automate--command-handle-error-code command)
(parse-integer
(buffer-substring-no-properties
(match-end 0) (xmtn-automate--decoder-state-read-marker state)) ))
(setf (xmtn-automate--command-handle-finished-p command) t)
(with-no-warnings
;; suppress compiler warning about discarding result
(pop (xmtn-automate--session-remaining-command-handles session)))
(if (xmtn-automate--session-closed-p session)
(setq tag 'exit-loop)
(setq tag 'check-for-more))
(if (> (+ size (match-end 0)) (point-max))
;; do not have the error code yet
(setq tag 'exit-loop)
(setf (xmtn-automate--decoder-state-read-marker state) (+ size (match-end 0)))
(setf (xmtn-automate--command-handle-error-code command)
(parse-integer
(buffer-substring-no-properties
(match-end 0) (xmtn-automate--decoder-state-read-marker state)) ))
(setf (xmtn-automate--command-handle-finished-p command) t)
(with-no-warnings
;; suppress compiler warning about discarding result
(pop (xmtn-automate--session-remaining-command-handles session)))
(if (xmtn-automate--session-closed-p session)
(setq tag 'exit-loop)
(setq tag 'check-for-more)))
)
)))
(t
;; Not a packet. Most likely we are at the end of the
;; buffer, and there is more output coming soon. FIXME:
;; this means the loop logic screwed up.
(if (= (point) (point-max))
;; Not a packet yet, or garbage in the stream from some
;; Lua hook. Most likely we are at the end of the
;; buffer, don't have a complete header, and there is
;; more output coming soon. A packet header has at least
;; 6 bytes; allowing 4 digits per integer takes that to
;; 12.
(if (> 12 (- (point-max) (point)))
(setq tag 'exit-loop)
(error "Unexpected output from mtn at '%s':%d:'%s'"
(current-buffer)
(point)
(buffer-substring (point) (line-end-position)))))))))
(buffer-substring (point) (min (point-max) (+ (point) 100))))
))))))
(exit-loop (return))))))
nil)
@ -652,11 +671,13 @@ Each element of the list is a list; key, signature, name, value, trust."
accu))
(defun xmtn--heads (root branch)
;; apparently stdio automate doesn't default arguments properly;
;; this fails if branch is not passed to mtn.
(xmtn-automate-simple-command-output-lines root (list "heads"
(or branch
(xmtn--tree-default-branch root)))))
(xmtn-automate-simple-command-output-lines
root
(cons
(list "ignore-suspend-certs" "")
(list "heads"
(or branch
(xmtn--tree-default-branch root))))))
(defun xmtn--tree-default-branch (root)
(xmtn-automate-simple-command-output-line root `("get_option" "branch")))
@ -677,24 +698,6 @@ Each element of the list is a list; key, signature, name, value, trust."
(assert (null (funcall next-stanza)))
result))))
(defun xmtn-automate-local-changes (work)
"Summary of status for WORK; 'ok if no changes, 'need-commit if changes."
(let ((default-directory work)
(msg "checking %s for local changes ..."))
(message msg work)
(let ((result (xmtn-automate-simple-command-output-string
default-directory
(list (list "no-unchanged" "" "no-ignored" "")
"inventory"))))
(message (concat msg " done") work)
(if (> (length result) 0)
'need-commit
'ok))))
(provide 'xmtn-automate)
;;; xmtn-automate.el ends here

View File

@ -1,6 +1,6 @@
;;; xmtn-base.el --- Basic definitions for accessing monotone
;; Copyright (C) 2009 Stephen Leake
;; Copyright (C) 2009, 2010 Stephen Leake
;; Copyright (C) 2006, 2007, 2009 Christian M. Ohler
;; Author: Christian M. Ohler
@ -35,11 +35,7 @@
(require 'cl))
(defvar xmtn-executable "mtn"
"*The monotone executable command.
After changing the value of this variable, be sure to run
`xmtn-check-command-version' to clear xmtn's command version
cache.")
"*The monotone executable command.")
(defvar xmtn-additional-arguments '()
"*Additional arguments to pass to monotone.
@ -59,15 +55,16 @@ A list of strings.")
(save-match-data
(string-match "\\`[0-9a-f]\\{40\\}\\'" thing))))
(defun xmtn--filter-non-dir (dir)
"Return list of all directories in DIR, excluding '.', '..'."
(defun xmtn--filter-non-ws (dir)
"Return list of all mtn workspaces in DIR."
(let ((default-directory dir)
(subdirs (directory-files dir)))
(setq subdirs
(mapcar (lambda (filename)
(if (and (file-directory-p filename)
(not (string= "." filename))
(not (string= ".." filename)))
(not (string= ".." filename))
(file-directory-p (concat filename "/_MTN")))
filename))
subdirs))
(delq nil subdirs)))

View File

@ -20,7 +20,7 @@
;; the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
;; Boston, MA 02110-1301 USA.
(eval-and-compile
(eval-when-compile
;; these have macros we use
(require 'cl)
(require 'dvc-utils)
@ -29,7 +29,7 @@
(require 'xmtn-ids)
(require 'xmtn-run))
(eval-when-compile
(eval-and-compile
;; these have functions we use
(require 'dired))
@ -103,8 +103,8 @@
(defstruct (xmtn-conflicts-conflict
(:copier nil))
;; not worth splitting this into a type hierarchy; differences are
;; minor some fields are nil for some conflict types
;; single file conflicts only set left_resolution
;; minor. Some fields are nil for some conflict types.
;; Single file conflicts only set left_resolution
conflict_type ;; 'content | 'duplicate_name | 'orphaned_node
ancestor_name
@ -278,6 +278,16 @@ header."
;; right_type "added directory"
;; right_name "utils"
;;
;; conflict duplicate_name
;; left_type "added file"
;; left_name "build/x86_gnu_windows_release/gds_mms_test.gpr"
;; left_file_id [8d5d8fd099442bfb5d636d6435c241c8cd83f4f9]
;; right_type "renamed file"
;; ancestor_name "build/x86_gnu_windows_release/gds_test.gpr"
;; ancestor_file_id [e2eb7393d9cda23a467622744a392adde63fc850]
;; right_name "build/x86_gnu_windows_release/gds_mms_test.gpr"
;; right_file_id [cec70e80402418bb95dcdeb6abe1356084ff5ece]
;;
;; optional left and right resolutions:
;; resolved_keep{_left | _right}
;; resolved_drop{_left | _right}
@ -286,24 +296,39 @@ header."
(let ((conflict (make-xmtn-conflicts-conflict)))
(setf (xmtn-conflicts-conflict-conflict_type conflict) 'duplicate_name)
(xmtn-basic-io-check-line "left_type" (setf (xmtn-conflicts-conflict-left_type conflict) (cadar value)))
(xmtn-basic-io-check-line "left_name" (setf (xmtn-conflicts-conflict-left_name conflict) (cadar value)))
(xmtn-basic-io-parse-line
(cond
((string= "left_file_id" symbol)
(setf (xmtn-conflicts-conflict-left_file_id conflict) (cadar value))
(xmtn-basic-io-check-line "right_type"
(setf (xmtn-conflicts-conflict-right_type conflict) (cadar value)))
(xmtn-basic-io-check-line "right_name"
(setf (xmtn-conflicts-conflict-right_name conflict) (cadar value)))
(xmtn-basic-io-check-line "right_file_id"
(setf (xmtn-conflicts-conflict-right_file_id conflict) (cadar value))))
(cond
((string= "added file" (xmtn-conflicts-conflict-left_type conflict))
(xmtn-basic-io-check-line "left_name" (setf (xmtn-conflicts-conflict-left_name conflict) (cadar value)))
(xmtn-basic-io-check-line "left_file_id" (setf (xmtn-conflicts-conflict-left_file_id conflict) (cadar value))))
((string= "right_type" symbol)
(setf (xmtn-conflicts-conflict-right_type conflict) (cadar value))
(xmtn-basic-io-check-line "right_name"
(setf (xmtn-conflicts-conflict-right_name conflict) (cadar value))))
(t
(error "found %s" symbol))))
((string= "added directory" (xmtn-conflicts-conflict-left_type conflict))
(xmtn-basic-io-check-line "left_name" (setf (xmtn-conflicts-conflict-left_name conflict) (cadar value))))
((string= "renamed file" (xmtn-conflicts-conflict-left_type conflict))
(xmtn-basic-io-check-line "ancestor_name" (setf (xmtn-conflicts-conflict-ancestor_name conflict) (cadar value)))
(xmtn-basic-io-check-line "ancestor_file_id" (setf (xmtn-conflicts-conflict-ancestor_file_id conflict) (cadar value)))
(xmtn-basic-io-check-line "left_name" (setf (xmtn-conflicts-conflict-left_name conflict) (cadar value)))
(xmtn-basic-io-check-line "left_file_id" (setf (xmtn-conflicts-conflict-left_file_id conflict) (cadar value))))
(t
(error "unsupported left_type %s" (xmtn-conflicts-conflict-left_type conflict))))
(xmtn-basic-io-check-line "right_type" (setf (xmtn-conflicts-conflict-right_type conflict) (cadar value)))
(cond
((string= "added file" (xmtn-conflicts-conflict-right_type conflict))
(xmtn-basic-io-check-line "right_name" (setf (xmtn-conflicts-conflict-right_name conflict) (cadar value)))
(xmtn-basic-io-check-line "right_file_id" (setf (xmtn-conflicts-conflict-right_file_id conflict) (cadar value))))
((string= "added directory" (xmtn-conflicts-conflict-right_type conflict))
(xmtn-basic-io-check-line "right_name" (setf (xmtn-conflicts-conflict-right_name conflict) (cadar value))))
((string= "renamed file" (xmtn-conflicts-conflict-right_type conflict))
(xmtn-basic-io-check-line "ancestor_name" (setf (xmtn-conflicts-conflict-ancestor_name conflict) (cadar value)))
(xmtn-basic-io-check-line "ancestor_file_id" (setf (xmtn-conflicts-conflict-ancestor_file_id conflict) (cadar value)))
(xmtn-basic-io-check-line "right_name" (setf (xmtn-conflicts-conflict-right_name conflict) (cadar value)))
(xmtn-basic-io-check-line "right_file_id" (setf (xmtn-conflicts-conflict-right_file_id conflict) (cadar value))))
(t
(error "unsupported right_type %s" (xmtn-conflicts-conflict-right_type conflict))))
;; look for a left resolution
(case (xmtn-basic-io--peek)
@ -531,13 +556,38 @@ header."
(insert ?\n)
(xmtn-basic-io-write-sym "conflict" "duplicate_name")
(xmtn-basic-io-write-str "left_type" (xmtn-conflicts-conflict-left_type conflict))
(xmtn-basic-io-write-str "left_name" (xmtn-conflicts-conflict-left_name conflict))
(if (xmtn-conflicts-conflict-left_file_id conflict)
(xmtn-basic-io-write-id "left_file_id" (xmtn-conflicts-conflict-left_file_id conflict)))
(cond
((string= "added file" (xmtn-conflicts-conflict-left_type conflict))
(xmtn-basic-io-write-str "left_name" (xmtn-conflicts-conflict-left_name conflict))
(xmtn-basic-io-write-id "left_file_id" (xmtn-conflicts-conflict-left_file_id conflict)))
((string= "added directory" (xmtn-conflicts-conflict-left_type conflict))
(xmtn-basic-io-write-str "left_name" (xmtn-conflicts-conflict-left_name conflict)))
((string= "renamed file" (xmtn-conflicts-conflict-left_type conflict))
(xmtn-basic-io-write-str "ancestor_name" (xmtn-conflicts-conflict-ancestor_name conflict))
(xmtn-basic-io-write-id "ancestor_file_id" (xmtn-conflicts-conflict-ancestor_file_id conflict))
(xmtn-basic-io-write-str "left_name" (xmtn-conflicts-conflict-left_name conflict))
(xmtn-basic-io-write-id "left_file_id" (xmtn-conflicts-conflict-left_file_id conflict)))
(t
(error "unsupported left_type %s" (xmtn-conflicts-conflict-left_type conflict))))
(xmtn-basic-io-write-str "right_type" (xmtn-conflicts-conflict-right_type conflict))
(xmtn-basic-io-write-str "right_name" (xmtn-conflicts-conflict-right_name conflict))
(if (xmtn-conflicts-conflict-right_file_id conflict)
(xmtn-basic-io-write-id "right_file_id" (xmtn-conflicts-conflict-right_file_id conflict)))
(cond
((string= "added file" (xmtn-conflicts-conflict-right_type conflict))
(xmtn-basic-io-write-str "right_name" (xmtn-conflicts-conflict-right_name conflict))
(xmtn-basic-io-write-id "right_file_id" (xmtn-conflicts-conflict-right_file_id conflict)))
((string= "added directory" (xmtn-conflicts-conflict-right_type conflict))
(xmtn-basic-io-write-str "right_name" (xmtn-conflicts-conflict-right_name conflict)))
((string= "renamed file" (xmtn-conflicts-conflict-right_type conflict))
(xmtn-basic-io-write-str "ancestor_name" (xmtn-conflicts-conflict-ancestor_name conflict))
(xmtn-basic-io-write-id "ancestor_file_id" (xmtn-conflicts-conflict-ancestor_file_id conflict))
(xmtn-basic-io-write-str "right_name" (xmtn-conflicts-conflict-right_name conflict))
(xmtn-basic-io-write-id "right_file_id" (xmtn-conflicts-conflict-right_file_id conflict)))
(t
(error "unsupported right_type %s" (xmtn-conflicts-conflict-right_type conflict))))
(if (xmtn-conflicts-conflict-left_resolution conflict)
(ecase (car (xmtn-conflicts-conflict-left_resolution conflict))
@ -987,6 +1037,17 @@ header."
;; if no file_id, it's a directory; can't drop if not empty
(xmtn-conflicts-conflict-right_file_id conflict))))
(defun xmtn-conflicts-left-label ()
"Return 'left: ' or '' as appropriate for current conflict."
(let* ((conflict (ewoc-data (ewoc-locate xmtn-conflicts-ewoc)))
(type (xmtn-conflicts-conflict-conflict_type conflict)))
;; duplicate_name is the only conflict type that needs a right
;; resolution, and thus a 'left' label
(if (equal type 'duplicate_name)
"left: "
"")))
(defvar xmtn-conflicts-resolve-map
(let ((map (make-sparse-keymap "resolution")))
(define-key map [?c] '(menu-item "c) clear resolution"
@ -995,55 +1056,55 @@ header."
;; Don't need 'left' or 'right' in menu, since only one is
;; visible; then this works better for single file conflicts.
(define-key map [?b] '(menu-item "b) drop"
(define-key map [?b] '(menu-item "b) right: drop"
xmtn-conflicts-resolve-drop_right
:visible (xmtn-conflicts-resolve-drop_rightp)))
(define-key map [?a] '(menu-item "a) rename"
(define-key map [?a] '(menu-item "a) right: rename"
(lambda ()
(interactive)
(xmtn-conflicts-resolve-rename 'right))
:visible (xmtn-conflicts-resolve-rename_rightp)))
(define-key map [?9] '(menu-item "9) right file"
(define-key map [?9] '(menu-item "9) right: right file"
(lambda ()
(interactive)
(xmtn-conflicts-resolve-user 'right 'right))
:visible (xmtn-conflicts-resolve-user_rightp)))
(define-key map [?8] '(menu-item "8) left file"
(define-key map [?8] '(menu-item "8) right: left file"
(lambda ()
(interactive)
(xmtn-conflicts-resolve-user 'right 'left))
:visible (xmtn-conflicts-resolve-user_rightp)))
(define-key map [?7] '(menu-item "7) keep"
(define-key map [?7] '(menu-item "7) right: keep"
xmtn-conflicts-resolve-keep_right
:visible (xmtn-conflicts-resolve-keep_rightp)))
(define-key map [?6] '(menu-item "6) ediff"
(define-key map [?6] '(menu-item "6) right: ediff"
(lambda ()
(interactive)
(xmtn-conflicts-resolve-ediff 'right))
:visible (xmtn-conflicts-resolve-user_rightp)))
(define-key map [?5] '(menu-item "5) right file"
(define-key map [?5] '(menu-item (concat "5) " (xmtn-conflicts-left-label) "right file")
(lambda ()
(interactive)
(xmtn-conflicts-resolve-user 'left 'right))
:visible (xmtn-conflicts-resolve-user_leftp)))
(define-key map [?4] '(menu-item "4) left file"
(define-key map [?4] '(menu-item (concat "4) " (xmtn-conflicts-left-label) "left file")
(lambda ()
(interactive)
(xmtn-conflicts-resolve-user 'left 'left))
:visible (xmtn-conflicts-resolve-user_leftp)))
(define-key map [?3] '(menu-item "3) drop"
(define-key map [?3] '(menu-item (concat "3) " (xmtn-conflicts-left-label) "drop")
xmtn-conflicts-resolve-drop_left
:visible (xmtn-conflicts-resolve-drop_leftp)))
(define-key map [?2] '(menu-item "2) rename"
(define-key map [?2] '(menu-item (concat "2) " (xmtn-conflicts-left-label) "rename")
(lambda ()
(interactive)
(xmtn-conflicts-resolve-rename 'left))
:visible (xmtn-conflicts-resolve-rename_leftp)))
(define-key map [?1] '(menu-item "1) keep"
(define-key map [?1] '(menu-item (concat "1) " (xmtn-conflicts-left-label) "keep")
xmtn-conflicts-resolve-keep_left
:visible (xmtn-conflicts-resolve-keep_leftp)))
(define-key map [?0] '(menu-item "0) ediff"
(define-key map [?0] '(menu-item (concat "0) " (xmtn-conflicts-left-label) "ediff")
(lambda ()
(interactive)
(xmtn-conflicts-resolve-ediff 'left))
@ -1147,10 +1208,6 @@ non-nil, show log-edit buffer in other frame."
;; Arrange for `revert-buffer' to do the right thing
(set (make-local-variable 'after-insert-file-functions) '(xmtn-conflicts-after-insert-file))
;; don't do normal clean up stuff
(set (make-local-variable 'before-save-hook) nil)
(set (make-local-variable 'write-file-functions) nil)
(dvc-install-buffer-menu)
(setq buffer-read-only t)
(buffer-disable-undo)

View File

@ -583,12 +583,10 @@ otherwise newer."
(rename-source
(setq more-status
(concat "to " new-path)))
(setq more-status new-path))
(rename-target
(setq more-status
(concat "from " old-path)))
(setq more-status old-path))
(modified
(if (and (equal status '(known))
@ -724,13 +722,7 @@ otherwise newer."
(dvc-save-some-buffers root)
(lexical-let* ((status-buffer status-buffer))
(xmtn--run-command-async
root `("automate" "inventory"
,@(and (xmtn--have-no-ignore)
(not dvc-status-display-known)
'("--no-unchanged"))
,@(and (xmtn--have-no-ignore)
(not dvc-status-display-ignored)
'("--no-ignored")))
root (list "automate" "inventory" "--no-unchanged" "--no-ignored")
:finished (lambda (output error status arguments)
(dvc-status-inventory-done status-buffer)
(with-current-buffer status-buffer
@ -769,6 +761,75 @@ otherwise newer."
arguments)
(current-buffer) error)))))))
(defun xmtn--status-inventory-sync (root)
"Create a status buffer for ROOT; return (buffer status), where status is 'ok or 'need-commit."
(let*
((orig-buffer (current-buffer))
(msg (concat "running inventory for " root " ..."))
(base-revision (xmtn--get-base-revision-hash-id-or-null root))
(branch (xmtn--tree-default-branch root))
(head-revisions (xmtn--heads root branch))
(head-count (length head-revisions))
(output-buffer (generate-new-buffer " *xmtn-inventory*"))
status
(dvc-switch-to-buffer-first nil)
(status-buffer
(dvc-status-prepare-buffer
'xmtn
root
;; base-revision
(if base-revision (format "%s" base-revision) "none")
;; branch
(format "%s" branch)
;; header-more
(lambda ()
(concat
(case head-count
(0 " branch is empty\n")
(1 " branch is merged\n")
(t (dvc-face-add (format " branch has %s heads; need merge\n" head-count) 'dvc-conflict)))
(if (member base-revision head-revisions)
" base revision is a head revision\n"
(dvc-face-add " base revision is not a head revision; need update\n" 'dvc-conflict))))
;; refresh
'xmtn-dvc-status)))
(dvc-save-some-buffers root)
(message msg)
(xmtn-automate-command-output-buffer
root output-buffer
(list (list "no-unchanged" "" "no-ignored" "")
"inventory"))
(with-current-buffer output-buffer
(setq status
(if (> (point-max) (point-min))
'need-commit
'ok)))
(dvc-status-inventory-done status-buffer)
(with-current-buffer status-buffer
(let ((excluded-files (dvc-default-excluded-files)))
(xmtn-basic-io-with-stanza-parser
(parser output-buffer)
(xmtn--parse-inventory
parser
(lambda (path status changes old-path new-path
old-type new-type fs-type)
(xmtn--status-process-entry dvc-fileinfo-ewoc
path status
changes
old-path new-path
old-type new-type
fs-type
excluded-files))))
(when (not (ewoc-locate dvc-fileinfo-ewoc))
(ewoc-enter-last dvc-fileinfo-ewoc
(make-dvc-fileinfo-message
:text (concat " no changes in workspace")))
(ewoc-refresh dvc-fileinfo-ewoc))))
(kill-buffer output-buffer)
(set-buffer orig-buffer)
(message (concat msg " done"))
(list status-buffer status)))
;;;###autoload
(defun xmtn-dvc-status ()
"Display status of monotone tree at `default-directory'."
@ -876,19 +937,8 @@ otherwise newer."
(let ((default-directory root))
(mapcan (lambda (file-name)
(if (or (file-symlink-p file-name)
(xmtn--have-no-ignore)
(not (file-directory-p file-name)))
(list (xmtn--perl-regexp-for-file-name file-name))
;; If mtn automate inventory doesn't support
;; --no-ignore, it also recurses into unknown
;; directories, so we need to ignore files in
;; this directory as well as the directory
;; itself.
(setq file-name (directory-file-name file-name))
(list
(xmtn--perl-regexp-for-file-name file-name)
(xmtn--perl-regexp-for-files-in-directory file-name))))
(list (xmtn--perl-regexp-for-file-name file-name))))
normalized-file-names))
t))))
@ -1117,6 +1167,8 @@ finished."
(progn
"--resolve-conflicts-file=_MTN/conflicts")))
(cmd (list "propagate" other local-branch resolve-conflicts
;; may be resurrecting a suspended branch; doesn't hurt otherwise.
"--ignore-suspend-certs"
(xmtn-dvc-log-message)))
(prompt
(if resolve-conflicts
@ -1431,18 +1483,24 @@ finished."
(defun xmtn--insert-file-contents (root content-hash-id buffer)
(check-type content-hash-id xmtn--hash-id)
(xmtn-automate-simple-command-output-insert-into-buffer
(xmtn-automate-command-output-buffer
root buffer `("get_file" ,content-hash-id)))
(defun xmtn--insert-file-contents-by-name (root backend-id normalized-file-name buffer)
(let* ((resolved-id (xmtn--resolve-backend-id root backend-id))
(hash-id (case (car resolved-id)
(local-tree nil)
(revision (cadr resolved-id))))
(cmd (if hash-id
(revision (cadr resolved-id)))))
(case (car backend-id)
((local-tree last-revision)
;; file may have been renamed but not committed
(setq normalized-file-name (xmtn--get-rename-in-workspace-to root normalized-file-name)))
(t nil))
(let ((cmd (if hash-id
(cons (list "revision" hash-id) (list "get_file_of" normalized-file-name))
(list "get_file_of" normalized-file-name))))
(xmtn-automate-simple-command-output-insert-into-buffer root buffer cmd)))
(xmtn-automate-command-output-buffer root buffer cmd))))
(defun xmtn--same-tree-p (a b)
(equal (file-truename a) (file-truename b)))

40
dvc/lisp/xmtn-hooks.lua Normal file
View File

@ -0,0 +1,40 @@
-- xmtn-hooks.lua --- mtn Lua hook functions used in all xmtn automate
-- stdio sessions
--
-- Copyright (C) 2010 Stephen Leake
--
-- Author: Stephen Leake
-- Keywords: tools
--
-- This file is free software; you can redistribute it and/or modify
-- it under the terms of the GNU General Public License as published by
-- the Free Software Foundation; either version 2 of the License, or
-- (at your option) any later version.
--
-- This file is distributed in the hope that it will be useful,
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-- GNU General Public License for more details.
--
-- You should have received a copy of the GNU General Public License
-- along with this file; see the file COPYING. If not, write to
-- the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
-- Boston, MA 02110-1301 USA.
function get_mtn_command(host)
-- Return a mtn command line for the remote host.
--
-- If the remote host is a Windows machine (ie file: on a Windows
-- machine), we need the Cygwin mtn executable, since the Win32
-- executable does not support file: or ssh:.
--
-- But we have no way to tell what the remote machine is. So we let
-- the lisp code figure that out from user options, and it provides
-- the mtn command to this hook by defining the XMTN_SYNC_MTN
-- environment variable.
return os.getenv("XMTN_SYNC_MTN");
end
-- end of file

View File

@ -20,34 +20,38 @@
;; the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
;; Boston, MA 02110-1301 USA.
(eval-and-compile
(eval-when-compile
;; these have macros we use
(require 'xmtn-ids))
(eval-when-compile
(eval-and-compile
;; these have functions we use
(require 'xmtn-base)
(require 'xmtn-conflicts))
(require 'xmtn-conflicts)
(require 'xmtn-revlist))
(defvar xmtn-status-root ""
"Buffer-local variable holding root directory.")
"Buffer-local variable holding multi-workspace root directory.")
(make-variable-buffer-local 'xmtn-status-root)
(put 'xmtn-status-root 'permanent-local t)
(defvar xmtn-status-ewoc nil
"Buffer-local ewoc for displaying propagations.
"Buffer-local ewoc for displaying multi-workspace status.
All xmtn-status functions operate on this ewoc.
The elements must all be of class xmtn-status-data.")
(make-variable-buffer-local 'xmtn-status-ewoc)
(put 'xmtn-status-ewoc 'permanent-local t)
(defstruct (xmtn-status-data (:copier nil))
work ; directory name relative to xmtn-status-root
branch ; branch name (assumed never changes)
work ; workspace directory name relative to xmtn-status-root
branch ; branch name (all workspaces have same branch; assumed never changes)
need-refresh ; nil | t : if an async process was started that invalidates state data
head-rev ; nil | mtn rev string : current head revision, nil if multiple heads
conflicts-buffer ; *xmtn-conflicts* buffer for merge
heads ; 'need-scan | 'at-head | 'need-update | 'need-merge)
status-buffer ; *xmtn-status* buffer for commit
heads ; 'need-scan | 'at-head | 'need-update | 'need-merge
(update-review
'pending) ; 'pending | 'need-review | 'done
(local-changes
'need-scan) ; 'need-scan | 'need-commit | 'ok
(conflicts
@ -75,13 +79,13 @@ The elements must all be of class xmtn-status-data.")
(insert (dvc-face-add " need refresh\n" 'dvc-conflict))
(ecase (xmtn-status-data-local-changes data)
(need-scan (insert " from local changes unknown\n"))
(need-commit (insert (dvc-face-add " need dvc-status\n" 'dvc-header)))
(need-scan (insert " local changes unknown\n"))
(need-commit (insert (dvc-face-add " need commit\n" 'dvc-header)))
(ok nil))
(ecase (xmtn-status-data-conflicts data)
(need-scan
(insert "conflicts need scan\n"))
(insert " conflicts need scan\n"))
(need-resolve
(insert (dvc-face-add " need resolve conflicts\n" 'dvc-conflict)))
(need-review-resolve-internal
@ -92,10 +96,16 @@ The elements must all be of class xmtn-status-data.")
(ecase (xmtn-status-data-heads data)
(at-head nil)
(need-update (insert (dvc-face-add " need update\n" 'dvc-conflict)))
(need-update
(insert (dvc-face-add " need update\n" 'dvc-conflict)))
(need-merge
(insert (dvc-face-add " need merge\n" 'dvc-conflict)))
)))
(insert (dvc-face-add " need merge\n" 'dvc-conflict))))
(ecase (xmtn-status-data-update-review data)
(pending nil)
(need-review (insert " need update review\n"))
(done nil))
))
(defun xmtn-status-kill-conflicts-buffer (data)
(if (buffer-live-p (xmtn-status-data-conflicts-buffer data))
@ -103,6 +113,10 @@ The elements must all be of class xmtn-status-data.")
(with-current-buffer buffer (save-buffer))
(kill-buffer buffer))))
(defun xmtn-status-kill-status-buffer (data)
(if (buffer-live-p (xmtn-status-data-status-buffer data))
(kill-buffer (xmtn-status-data-status-buffer data))))
(defun xmtn-status-save-conflicts-buffer (data)
(if (buffer-live-p (xmtn-status-data-conflicts-buffer data))
(with-current-buffer (xmtn-status-data-conflicts-buffer data) (save-buffer))))
@ -111,6 +125,7 @@ The elements must all be of class xmtn-status-data.")
"Clean DATA workspace."
(xmtn-automate-kill-session (xmtn-status-work data))
(xmtn-status-kill-conflicts-buffer data)
(xmtn-status-kill-status-buffer data)
(xmtn-conflicts-clean (xmtn-status-work data)))
(defun xmtn-status-clean ()
@ -158,6 +173,7 @@ The elements must all be of class xmtn-status-data.")
(let* ((elem (ewoc-locate xmtn-status-ewoc))
(data (ewoc-data elem)))
(xmtn-status-need-refresh elem data)
(setf (xmtn-status-data-update-review data) 'need-review)
(let ((default-directory (xmtn-status-work data)))
(xmtn-dvc-update))
(xmtn-status-refresh-one data nil)
@ -186,13 +202,13 @@ The elements must all be of class xmtn-status-data.")
'(need-resolve need-review-resolve-internal)))))
(defun xmtn-status-status ()
"Run xmtn-status on current workspace."
"Show status buffer for current workspace."
(interactive)
(let* ((elem (ewoc-locate xmtn-status-ewoc))
(data (ewoc-data elem)))
(xmtn-status-need-refresh elem data)
(setf (xmtn-status-data-local-changes data) 'ok)
(xmtn-status (xmtn-status-work data))
(pop-to-buffer (xmtn-status-data-status-buffer data))
;; IMPROVEME: create a log-edit buffer now, since we have both a
;; status and conflict buffer, and that confuses dvc-log-edit
))
@ -203,11 +219,6 @@ The elements must all be of class xmtn-status-data.")
(let* ((elem (ewoc-locate xmtn-status-ewoc))
(data (ewoc-data elem)))
(setf (xmtn-status-data-local-changes data) 'ok)
(if (buffer-live-p (xmtn-status-data-conflicts-buffer data))
;; creating the log-edit buffer requires a single status/diff/conflicts buffer
(kill-buffer (xmtn-status-data-conflicts-buffer data)))
(ewoc-invalidate xmtn-status-ewoc elem)))
(defun xmtn-status-statusp ()
@ -217,22 +228,23 @@ The elements must all be of class xmtn-status-data.")
(member (xmtn-status-data-local-changes data)
'(need-scan need-commit)))))
(defun xmtn-status-missing ()
"Run xmtn-missing on current workspace."
(defun xmtn-status-review-update ()
"Review last update for current workspace."
(interactive)
(let* ((elem (ewoc-locate xmtn-status-ewoc))
(data (ewoc-data elem)))
(xmtn-status-need-refresh elem data)
(xmtn-missing nil (xmtn-status-work data))))
(setf (xmtn-status-data-update-review data) 'done)
(xmtn-review-update (xmtn-status-work data))))
(defun xmtn-status-missingp ()
"Non-nil if xmtn-missing is appropriate for current workspace."
(defun xmtn-status-review-updatep ()
"Non-nil if xmtn-status-review-update is appropriate for current workspace."
(let* ((data (ewoc-data (ewoc-locate xmtn-status-ewoc))))
(and (not (xmtn-status-data-need-refresh data))
(eq 'need-update (xmtn-status-data-heads data)))))
(eq 'need-review (xmtn-status-data-update-review data)))))
(defun xmtn-status-merge ()
"Run dvc-merge on current workspace."
"Run merge on current workspace."
(interactive)
(let* ((elem (ewoc-locate xmtn-status-ewoc))
(data (ewoc-data elem))
@ -243,7 +255,7 @@ The elements must all be of class xmtn-status-data.")
(ewoc-invalidate xmtn-status-ewoc elem)))
(defun xmtn-status-heads ()
"Run xmtn-heads on current workspace."
"Show heads for current workspace."
(interactive)
(let* ((elem (ewoc-locate xmtn-status-ewoc))
(data (ewoc-data elem))
@ -268,22 +280,22 @@ The elements must all be of class xmtn-status-data.")
(define-key map [?i] '(menu-item "i) ignore local changes"
xmtn-status-status-ok
:visible (xmtn-status-statusp)))
(define-key map [?5] '(menu-item "5) update"
(define-key map [?5] '(menu-item "5) review update"
xmtn-status-review-update
:visible (xmtn-status-review-updatep)))
(define-key map [?4] '(menu-item "4) update"
xmtn-status-update
:visible (xmtn-status-updatep)))
(define-key map [?4] '(menu-item "4) merge"
(define-key map [?3] '(menu-item "3) merge"
xmtn-status-merge
:visible (xmtn-status-headsp)))
(define-key map [?3] '(menu-item "3) show heads"
(define-key map [?2] '(menu-item "2) show heads"
xmtn-status-heads
:visible (xmtn-status-headsp)))
(define-key map [?2] '(menu-item "2) resolve conflicts"
(define-key map [?1] '(menu-item "1) resolve conflicts"
xmtn-status-resolve-conflicts
:visible (xmtn-status-resolve-conflictsp)))
(define-key map [?1] '(menu-item "1) show missing"
xmtn-status-missing
:visible (xmtn-status-missingp)))
(define-key map [?0] '(menu-item "0) status"
(define-key map [?0] '(menu-item "0) commit"
xmtn-status-status
:visible (xmtn-status-statusp)))
map)
@ -319,8 +331,6 @@ The elements must all be of class xmtn-status-data.")
(defun xmtn-status-conflicts (data)
"Return value for xmtn-status-data-conflicts for DATA."
;; Can't check for "current heads", since there could be more than
;; 2, so just recreate conflicts
(let* ((work (xmtn-status-work data))
(default-directory work))
@ -379,11 +389,24 @@ The elements must all be of class xmtn-status-data.")
(message "")
(if refresh-local-changes
(setf (xmtn-status-data-local-changes data) 'need-scan))
(progn
(setf (xmtn-status-data-local-changes data) 'need-scan)
(case (xmtn-status-data-update-review data)
('done (setf (xmtn-status-data-update-review data) 'need-review))
(t nil))))
(case (xmtn-status-data-local-changes data)
(need-scan
(setf (xmtn-status-data-local-changes data) (xmtn-automate-local-changes work)))
(if (buffer-live-p (xmtn-status-data-status-buffer data))
(with-current-buffer (xmtn-status-data-status-buffer data)
(xmtn-dvc-status)
(setf (xmtn-status-data-local-changes data)
(if (not (ewoc-locate dvc-fileinfo-ewoc))
'ok
'need-commit)))
(let ((result (xmtn--status-inventory-sync (xmtn-status-work data))))
(setf (xmtn-status-data-status-buffer data) (car result)
(xmtn-status-data-local-changes data) (cadr result))) ))
(t nil))
(case (xmtn-status-data-conflicts data)
@ -398,7 +421,7 @@ The elements must all be of class xmtn-status-data.")
t)
(defun xmtn-status-refresh ()
"Refresh status of each ewoc element. With prefix arg, reset local changes status to `unknown'."
"Refresh status of each ewoc element. With prefix arg, re-scan for local changes."
(interactive)
(ewoc-map 'xmtn-status-refresh-one xmtn-status-ewoc current-prefix-arg)
(message "done"))
@ -407,9 +430,9 @@ The elements must all be of class xmtn-status-data.")
(defun xmtn-update-multiple (dir &optional workspaces)
"Update all projects under DIR."
(interactive "DUpdate all in (root directory): ")
(let ((root (file-name-as-directory (substitute-in-file-name dir))))
(let ((root (file-name-as-directory (expand-file-name (substitute-in-file-name dir)))))
(if (not workspaces) (setq workspaces (xmtn--filter-non-dir root)))
(if (not workspaces) (setq workspaces (xmtn--filter-non-ws root)))
(dolist (workspace workspaces)
(let ((default-directory (concat root workspace)))
@ -421,8 +444,8 @@ The elements must all be of class xmtn-status-data.")
"Show actions to update all projects under DIR."
(interactive "DStatus for all (root directory): \ni\nP")
(pop-to-buffer (get-buffer-create "*xmtn-multi-status*"))
(setq default-directory (file-name-as-directory (substitute-in-file-name dir)))
(if (not workspaces) (setq workspaces (xmtn--filter-non-dir default-directory)))
(setq default-directory (file-name-as-directory (expand-file-name (substitute-in-file-name dir))))
(if (not workspaces) (setq workspaces (xmtn--filter-non-ws default-directory)))
(setq xmtn-status-root (file-name-as-directory default-directory))
(setq xmtn-status-ewoc (ewoc-create 'xmtn-status-printer))
(let ((inhibit-read-only t)) (delete-region (point-min) (point-max)))
@ -461,6 +484,29 @@ The elements must all be of class xmtn-status-data.")
(xmtn-status-refresh)
(xmtn-status-next))
;;;###autoload
(defun xmtn-status-one-1 (root name head-rev status-buffer heads local-changes)
"Create an xmtn-multi-status buffer from xmtn-propagate."
(pop-to-buffer (get-buffer-create "*xmtn-multi-status*"))
(setq default-directory (concat root "/" name))
(setq xmtn-status-root root)
(setq xmtn-status-ewoc (ewoc-create 'xmtn-status-printer))
(let ((inhibit-read-only t)) (delete-region (point-min) (point-max)))
(ewoc-set-hf xmtn-status-ewoc (format "Root : %s\n" xmtn-status-root) "")
(ewoc-enter-last xmtn-status-ewoc
(make-xmtn-status-data
:work (file-name-nondirectory (directory-file-name default-directory))
:branch (xmtn--tree-default-branch default-directory)
:need-refresh nil
:head-rev head-rev
:conflicts-buffer nil
:status-buffer status-buffer
:heads heads
:local-changes local-changes
:conflicts 'need-scan))
(xmtn-multiple-status-mode)
(xmtn-status-refresh))
(provide 'xmtn-multi-status)
;; end of file

View File

@ -1,6 +1,6 @@
;;; xmtn-propagate.el --- manage multiple propagations for DVC backend for monotone
;; Copyright (C) 2009, 2010 Stephen Leake
;; Copyright (C) 2009 - 2010 Stephen Leake
;; Author: Stephen Leake
;; Keywords: tools
@ -20,12 +20,13 @@
;; the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
;; Boston, MA 02110-1301 USA.
(eval-and-compile
(eval-when-compile
;; these have macros we use
(require 'xmtn-ids))
(eval-when-compile
(eval-and-compile
;; these have functions we use
(require 'xmtn-automate)
(require 'xmtn-base)
(require 'xmtn-conflicts))
@ -49,7 +50,8 @@ The elements must all be of class xmtn-propagate-data.")
(defstruct (xmtn-propagate-data (:copier nil))
from-work ; directory name relative to xmtn-propagate-from-root
to-work ; directory name relative to xmtn-propagate-to-root
from-name ; display name, in buffer and menus
; from-work is often the same as to-work
from-name ; display name, in buffer and menus; usually root dir name
to-name ;
from-branch ; branch name (assumed never changes)
to-branch ;
@ -57,6 +59,8 @@ The elements must all be of class xmtn-propagate-data.")
from-head-rev ; nil | mtn rev string; current head revision; nil if multiple heads
to-head-rev ;
conflicts-buffer ; *xmtn-conflicts* buffer for this propagation
from-status-buffer ; *xmtn-status* buffer for commit in from
to-status-buffer ; *xmtn-status* buffer for commit in to
propagate-needed ; nil | t
from-heads ; 'at-head | 'need-update | 'need-merge)
to-heads ;
@ -66,6 +70,7 @@ The elements must all be of class xmtn-propagate-data.")
'need-scan) ;
(conflicts
'need-scan) ; 'need-scan | 'need-resolve | 'need-review-resolve-internal | 'ok
; for propagate
)
(defun xmtn-propagate-from-work (data)
@ -162,11 +167,44 @@ The elements must all be of class xmtn-propagate-data.")
(if (buffer-live-p (xmtn-propagate-data-conflicts-buffer data))
(with-current-buffer (xmtn-propagate-data-conflicts-buffer data) (save-buffer))))
(defun xmtn-propagate-create-to-status-buffer (data)
"Create to-status buffer for DATA"
(if (buffer-live-p (xmtn-propagate-data-to-status-buffer data))
(with-current-buffer (xmtn-propagate-data-to-status-buffer data)
(xmtn-dvc-status)
(setf (xmtn-propagate-data-to-local-changes data)
(if (not (ewoc-locate dvc-fileinfo-ewoc))
'ok
'need-commit)))
(let ((result (xmtn--status-inventory-sync (xmtn-propagate-to-work data))))
(setf (xmtn-propagate-data-to-status-buffer data) (car result)
(xmtn-propagate-data-to-local-changes data) (cadr result))) ))
(defun xmtn-propagate-create-from-status-buffer (data)
"Create from-status buffer for DATA"
(if (buffer-live-p (xmtn-propagate-data-from-status-buffer data))
(with-current-buffer (xmtn-propagate-data-from-status-buffer data)
(xmtn-dvc-status)
(setf (xmtn-propagate-data-from-local-changes data)
(if (not (ewoc-locate dvc-fileinfo-ewoc))
'ok
'need-commit)))
(let ((result (xmtn--status-inventory-sync (xmtn-propagate-from-work data))))
(setf (xmtn-propagate-data-from-status-buffer data) (car result)
(xmtn-propagate-data-from-local-changes data) (cadr result))) ))
(defun xmtn-propagate-kill-status-buffers (data)
(if (buffer-live-p (xmtn-propagate-data-from-status-buffer data))
(kill-buffer (xmtn-propagate-data-from-status-buffer data)))
(if (buffer-live-p (xmtn-propagate-data-to-status-buffer data))
(kill-buffer (xmtn-propagate-data-to-status-buffer data))))
(defun xmtn-propagate-clean-1 (data)
"Clean DATA workspace"
(xmtn-automate-kill-session (xmtn-propagate-from-work data))
(xmtn-automate-kill-session (xmtn-propagate-to-work data))
(xmtn-propagate-kill-conflicts-buffer data)
(xmtn-propagate-kill-status-buffers data)
(xmtn-conflicts-clean (xmtn-propagate-to-work data)))
(defun xmtn-propagate-clean ()
@ -211,8 +249,36 @@ The elements must all be of class xmtn-propagate-data.")
(eq 'need-scan (xmtn-propagate-data-from-local-changes data))
(eq 'need-scan (xmtn-propagate-data-to-local-changes data)))))
(defun xmtn-propagate-commit-to ()
"Show commit buffer for `to' workspace, so it can be committed, updated, or merged."
(interactive)
(let* ((elem (ewoc-locate xmtn-propagate-ewoc))
(data (ewoc-data elem)))
(xmtn-propagate-need-refresh elem data)
(pop-to-buffer (xmtn-propagate-data-to-status-buffer data))))
(defun xmtn-propagate-commit-top ()
"Non-nil if commit is appropriate for current `to' workspace."
(let* ((data (ewoc-data (ewoc-locate xmtn-propagate-ewoc))))
(and (not (xmtn-propagate-data-need-refresh data))
(eq (xmtn-propagate-data-to-local-changes data) 'need-commit))))
(defun xmtn-propagate-commit-from ()
"Show commit buffer for `from' workspace, so it can be committed, updated, or merged."
(interactive)
(let* ((elem (ewoc-locate xmtn-propagate-ewoc))
(data (ewoc-data elem)))
(xmtn-propagate-need-refresh elem data)
(pop-to-buffer (xmtn-propagate-data-from-status-buffer data))))
(defun xmtn-propagate-commit-fromp ()
"Non-nil if commit is appropriate for current `from' workspace."
(let* ((data (ewoc-data (ewoc-locate xmtn-propagate-ewoc))))
(and (not (xmtn-propagate-data-need-refresh data))
(eq (xmtn-propagate-data-from-local-changes data) 'need-commit))))
(defun xmtn-propagate-update-to ()
"Update current workspace."
"Update current `to' workspace."
(interactive)
(let* ((elem (ewoc-locate xmtn-propagate-ewoc))
(data (ewoc-data elem)))
@ -223,17 +289,12 @@ The elements must all be of class xmtn-propagate-data.")
(xmtn-propagate-refresh-one data nil)
(ewoc-invalidate xmtn-propagate-ewoc elem)))
(defun xmtn-propagate-update-from ()
"Update current workspace."
(interactive)
(let* ((elem (ewoc-locate xmtn-propagate-ewoc))
(data (ewoc-data elem)))
(xmtn-propagate-need-refresh elem data)
(xmtn--update (xmtn-propagate-from-work data)
(xmtn-propagate-data-from-head-rev data)
nil t)
(xmtn-propagate-refresh-one data nil)
(ewoc-invalidate xmtn-propagate-ewoc elem)))
(defun xmtn-propagate-update-top ()
"Non-nil if update is appropriate for current `to' workspace."
(let* ((data (ewoc-data (ewoc-locate xmtn-propagate-ewoc))))
(and (not (xmtn-propagate-data-need-refresh data))
(eq (xmtn-propagate-data-to-heads data)
'need-update))))
(defun xmtn-propagate-propagate ()
"Propagate current workspace."
@ -282,22 +343,7 @@ The elements must all be of class xmtn-propagate-data.")
(member (xmtn-propagate-data-conflicts data)
'(need-resolve need-review-resolve-internal)))))
(defun xmtn-propagate-status-to ()
"Run xmtn-status on current `to' workspace."
(interactive)
(let* ((elem (ewoc-locate xmtn-propagate-ewoc))
(data (ewoc-data elem)))
(xmtn-propagate-need-refresh elem data)
;; can't create log-edit buffer with both conflicts and status
;; buffer open, and we'll be killing this as part of the refresh
;; anyway.
(xmtn-propagate-kill-conflicts-buffer data)
(setf (xmtn-propagate-data-to-local-changes data) 'ok)
(xmtn-status (xmtn-propagate-to-work data))))
(defun xmtn-propagate-status-to-ok ()
(defun xmtn-propagate-local-changes-to-ok ()
"Ignore local changes in current `to' workspace."
(interactive)
(let* ((elem (ewoc-locate xmtn-propagate-ewoc))
@ -305,23 +351,14 @@ The elements must all be of class xmtn-propagate-data.")
(setf (xmtn-propagate-data-to-local-changes data) 'ok)
(ewoc-invalidate xmtn-propagate-ewoc elem)))
(defun xmtn-propagate-status-top ()
"Non-nil if xmtn-status is appropriate for current `to' workspace."
(defun xmtn-propagate-local-changes-top ()
"Non-nil if local-changes-to-ok is appropriate for current `to' workspace."
(let* ((data (ewoc-data (ewoc-locate xmtn-propagate-ewoc))))
(and (not (xmtn-propagate-data-need-refresh data))
(member (xmtn-propagate-data-to-local-changes data)
'(need-scan need-commit)))))
(defun xmtn-propagate-status-from ()
"Run xmtn-status on current `from' workspace."
(interactive)
(let* ((elem (ewoc-locate xmtn-propagate-ewoc))
(data (ewoc-data elem)))
(xmtn-propagate-need-refresh elem data)
(setf (xmtn-propagate-data-from-local-changes data) 'ok)
(xmtn-status (xmtn-propagate-from-work data))))
(defun xmtn-propagate-status-from-ok ()
(defun xmtn-propagate-local-changes-from-ok ()
"Ignore local changes in current `from' workspace."
(interactive)
(let* ((elem (ewoc-locate xmtn-propagate-ewoc))
@ -329,70 +366,63 @@ The elements must all be of class xmtn-propagate-data.")
(setf (xmtn-propagate-data-from-local-changes data) 'ok)
(ewoc-invalidate xmtn-propagate-ewoc elem)))
(defun xmtn-propagate-status-fromp ()
"Non-nil if xmtn-status is appropriate for current `from' workspace."
(defun xmtn-propagate-local-changes-fromp ()
"Non-nil if local-changes-from-ok is appropriate for current `from' workspace."
(let* ((data (ewoc-data (ewoc-locate xmtn-propagate-ewoc))))
(and (not (xmtn-propagate-data-need-refresh data))
(member (xmtn-propagate-data-from-local-changes data)
'(need-scan need-commit)))))
(defun xmtn-propagate-missing-to ()
"Run xmtn-missing on current `to' workspace."
(defun xmtn-propagate-status-to ()
"Show status buffer for `to' workspace, so it can be committed, updated, or merged."
(interactive)
(let* ((elem (ewoc-locate xmtn-propagate-ewoc))
(data (ewoc-data elem)))
(xmtn-propagate-need-refresh elem data)
(xmtn-missing nil (xmtn-propagate-to-work data))))
(xmtn-status-one-1
xmtn-propagate-to-root
(xmtn-propagate-data-to-work data)
(xmtn-propagate-data-to-head-rev data)
(xmtn-propagate-data-to-status-buffer data)
(xmtn-propagate-data-to-heads data)
(xmtn-propagate-data-to-local-changes data))
(defun xmtn-propagate-missing-top ()
"Non-nil if xmtn-missing is appropriate for current `to' workspace."
;; Assume the user completely handles the local changes in the
;; status buffer, so they are now ok
(setf (xmtn-propagate-data-to-local-changes data) 'ok)))
(defun xmtn-propagate-status-top ()
"Non-nil if xmtn-status is appropriate for current `to' workspace."
(let* ((data (ewoc-data (ewoc-locate xmtn-propagate-ewoc))))
(and (not (xmtn-propagate-data-need-refresh data))
(eq 'need-update (xmtn-propagate-data-to-heads data)))))
(or
(member (xmtn-propagate-data-to-heads data)
'(need-update need-merge))
(eq (xmtn-propagate-data-to-local-changes data) 'need-commit)))))
(defun xmtn-propagate-missing-from ()
"Run xmtn-missing on current `from' workspace."
(defun xmtn-propagate-status-from ()
"Show status buffer for `from' workspace, so it can be committed, updated, or merged."
(interactive)
(let* ((elem (ewoc-locate xmtn-propagate-ewoc))
(data (ewoc-data elem)))
(xmtn-propagate-need-refresh elem data)
(xmtn-missing nil (xmtn-propagate-from-work data))))
(xmtn-status-one-1
xmtn-propagate-from-root
(xmtn-propagate-data-from-work data)
(xmtn-propagate-data-from-head-rev data)
(xmtn-propagate-data-from-status-buffer data)
(xmtn-propagate-data-from-heads data)
(xmtn-propagate-data-from-local-changes data))
(setf (xmtn-propagate-data-from-local-changes data) 'ok)))
(defun xmtn-propagate-missing-fromp ()
"Non-nil if xmtn-missing is appropriate for current `from' workspace."
(defun xmtn-propagate-status-fromp ()
"Non-nil if xmtn-status-one is appropriate for current `from' workspace."
(let* ((data (ewoc-data (ewoc-locate xmtn-propagate-ewoc))))
(and (not (xmtn-propagate-data-need-refresh data))
(eq 'need-update (xmtn-propagate-data-from-heads data)))))
(defun xmtn-propagate-heads-to ()
"Run xmtn-heads on current `to' workspace."
(interactive)
(let* ((elem (ewoc-locate xmtn-propagate-ewoc))
(data (ewoc-data elem))
(default-directory (xmtn-propagate-to-work data)))
(xmtn-propagate-need-refresh elem data)
(xmtn-view-heads-revlist)))
(defun xmtn-propagate-heads-top ()
"Non-nil if xmtn-heads is appropriate for current `to' workspace."
(let* ((data (ewoc-data (ewoc-locate xmtn-propagate-ewoc))))
(and (not (xmtn-propagate-data-need-refresh data))
(eq 'need-merge (xmtn-propagate-data-to-heads data)))))
(defun xmtn-propagate-heads-from ()
"Run xmtn-heads on current `from' workspace."
(interactive)
(let* ((elem (ewoc-locate xmtn-propagate-ewoc))
(data (ewoc-data elem))
(default-directory (xmtn-propagate-from-work data)))
(xmtn-propagate-need-refresh elem data)
(xmtn-view-heads-revlist)))
(defun xmtn-propagate-heads-fromp ()
"Non-nil if xmtn-heads is appropriate for current `from' workspace."
(let* ((data (ewoc-data (ewoc-locate xmtn-propagate-ewoc))))
(and (not (xmtn-propagate-data-need-refresh data))
(eq 'need-merge (xmtn-propagate-data-from-heads data)))))
(or
(member (xmtn-propagate-data-from-heads data)
'(need-update need-merge))
(eq (xmtn-propagate-data-from-local-changes data) 'need-commit)))))
(defvar xmtn-propagate-actions-map
(let ((map (make-sparse-keymap "actions")))
@ -402,42 +432,33 @@ The elements must all be of class xmtn-propagate-data.")
(define-key map [?g] '(menu-item "g) refresh"
xmtn-propagate-do-refresh-one
:visible (xmtn-propagate-refreshp)))
(define-key map [?b] '(menu-item "b) propagate"
(define-key map [?8] '(menu-item (concat "8) update " (xmtn-propagate-to-name))
xmtn-propagate-update-to
:visible (xmtn-propagate-update-top)))
(define-key map [?7] '(menu-item (concat "7) commit " (xmtn-propagate-to-name))
xmtn-propagate-commit-to
:visible (xmtn-propagate-commit-top)))
(define-key map [?6] '(menu-item (concat "6) commit " (xmtn-propagate-from-name))
xmtn-propagate-commit-from
:visible (xmtn-propagate-commit-fromp)))
(define-key map [?5] '(menu-item "5) propagate"
xmtn-propagate-propagate
:visible (xmtn-propagate-propagatep)))
(define-key map [?a] '(menu-item "a) resolve conflicts"
(define-key map [?4] '(menu-item "4) resolve conflicts"
xmtn-propagate-resolve-conflicts
:visible (xmtn-propagate-resolve-conflictsp)))
(define-key map [?9] '(menu-item (concat "9) ignore local changes " (xmtn-propagate-to-name))
xmtn-propagate-status-to-ok
:visible (xmtn-propagate-status-top)))
(define-key map [?8] '(menu-item (concat "8) ignore local changes " (xmtn-propagate-from-name))
xmtn-propagate-status-from-ok
:visible (xmtn-propagate-status-fromp)))
(define-key map [?7] '(menu-item (concat "7) show missing " (xmtn-propagate-to-name))
xmtn-propagate-missing-to
:visible (xmtn-propagate-missing-top)))
(define-key map [?6] '(menu-item (concat "6) show missing " (xmtn-propagate-from-name))
xmtn-propagate-missing-from
:visible (xmtn-propagate-missing-fromp)))
(define-key map [?5] '(menu-item (concat "5) update " (xmtn-propagate-to-name))
xmtn-propagate-update-to
:visible (xmtn-propagate-missing-top)))
(define-key map [?4] '(menu-item (concat "4) update " (xmtn-propagate-from-name))
xmtn-propagate-update-from
:visible (xmtn-propagate-missing-fromp)))
(define-key map [?3] '(menu-item (concat "3) commit " (xmtn-propagate-to-name))
(define-key map [?3] '(menu-item (concat "3) ignore local changes " (xmtn-propagate-to-name))
xmtn-propagate-local-changes-to-ok
:visible (xmtn-propagate-local-changes-top)))
(define-key map [?2] '(menu-item (concat "2) ignore local changes " (xmtn-propagate-from-name))
xmtn-propagate-local-changes-from-ok
:visible (xmtn-propagate-local-changes-fromp)))
(define-key map [?1] '(menu-item (concat "1) status " (xmtn-propagate-to-name))
xmtn-propagate-status-to
:visible (xmtn-propagate-status-top)))
(define-key map [?2] '(menu-item (concat "2) commit " (xmtn-propagate-from-name))
(define-key map [?0] '(menu-item (concat "0) status " (xmtn-propagate-from-name))
xmtn-propagate-status-from
:visible (xmtn-propagate-status-fromp)))
(define-key map [?1] '(menu-item (concat "1) show heads " (xmtn-propagate-to-name))
xmtn-propagate-heads-to
:visible (xmtn-propagate-heads-top)))
(define-key map [?0] '(menu-item (concat "0) show heads " (xmtn-propagate-from-name))
xmtn-propagate-heads-from
:visible (xmtn-propagate-heads-fromp)))
map)
"Keyboard menu keymap used to manage propagates.")
@ -572,11 +593,6 @@ The elements must all be of class xmtn-propagate-data.")
(dvc-trace "xmtn-propagate-refresh-one: %s" from-work)
(if refresh-local-changes
(progn
(setf (xmtn-propagate-data-from-local-changes data) 'need-scan)
(setf (xmtn-propagate-data-to-local-changes data) 'need-scan)))
(let ((heads (xmtn--heads from-work (xmtn-propagate-data-from-branch data)))
(from-base-rev (xmtn--get-base-revision-hash-id-or-null from-work)))
(case (length heads)
@ -604,19 +620,24 @@ The elements must all be of class xmtn-propagate-data.")
(setf (xmtn-propagate-data-propagate-needed data)
(xmtn-propagate-needed data))
(if refresh-local-changes
(progn
(setf (xmtn-propagate-data-from-local-changes data) 'need-scan)
(setf (xmtn-propagate-data-to-local-changes data) 'need-scan)))
(if (or refresh-local-changes
(xmtn-propagate-data-propagate-needed data))
;; these checks are slow, so don't do them if they probably are not needed.
(progn
(ecase (xmtn-propagate-data-from-local-changes data)
((need-scan need-commit)
(setf (xmtn-propagate-data-from-local-changes data) (xmtn-automate-local-changes from-work)))
(ok nil))
(need-scan
(xmtn-propagate-create-from-status-buffer data))
(t nil))
(ecase (xmtn-propagate-data-to-local-changes data)
((need-scan need-commit)
(setf (xmtn-propagate-data-to-local-changes data) (xmtn-automate-local-changes to-work)))
(ok nil))))
(need-scan
(xmtn-propagate-create-to-status-buffer data))
(t nil))))
(if (xmtn-propagate-data-propagate-needed data)
(progn
@ -641,7 +662,7 @@ The elements must all be of class xmtn-propagate-data.")
(interactive)
(ewoc-map 'xmtn-propagate-refresh-one xmtn-propagate-ewoc current-prefix-arg)
;; leaves point at (point-min)
(xmtn-propagate-next t)
(xmtn-propagate-next nil t)
(message "done"))
(defun xmtn-propagate-make-data (from-workspace to-workspace from-name to-name)
@ -668,16 +689,16 @@ to TO-DIR. WORKSPACES (default nil) is a list of workspaces
common to from-dir and to-dir; if nil, the directories are
scanned and all common ones found are used."
(interactive "DPropagate all from (root directory): \nDto (root directory): ")
(setq from-dir (substitute-in-file-name from-dir))
(setq to-dir (substitute-in-file-name to-dir))
(pop-to-buffer (get-buffer-create "*xmtn-propagate*"))
;; xmtn-propagate-*-root are buffer-local. Note that we don't care
;; what 'default-directory' is for xmtn-propagate buffer.
(setq xmtn-propagate-from-root (file-name-as-directory (expand-file-name (substitute-in-file-name from-dir))))
(setq xmtn-propagate-to-root (file-name-as-directory (expand-file-name (substitute-in-file-name to-dir))))
(let ((from-workspaces (or workspaces
(xmtn--filter-non-dir from-dir)))
(xmtn--filter-non-ws xmtn-propagate-from-root)))
(to-workspaces (or workspaces
(xmtn--filter-non-dir to-dir))))
(xmtn--filter-non-ws xmtn-propagate-to-root))))
(pop-to-buffer (get-buffer-create "*xmtn-propagate*"))
(setq xmtn-propagate-from-root (file-name-as-directory from-dir))
(setq xmtn-propagate-to-root (file-name-as-directory to-dir))
(setq xmtn-propagate-ewoc (ewoc-create 'xmtn-propagate-printer))
(let ((inhibit-read-only t)) (delete-region (point-min) (point-max)))
(ewoc-set-hf
@ -701,29 +722,27 @@ scanned and all common ones found are used."
(defun xmtn-propagate-one (from-work to-work)
"Show all actions needed to propagate FROM-WORK to TO-WORK."
(interactive "DPropagate all from (workspace): \nDto (workspace): ")
(setq from-work (substitute-in-file-name from-work))
(setq to-work (substitute-in-file-name to-work))
(let ((default-directory to-work))
(pop-to-buffer (get-buffer-create "*xmtn-propagate*"))
;; default-directory is wrong if buffer is reused
(setq default-directory to-work)
(setq xmtn-propagate-from-root (expand-file-name (concat (file-name-as-directory from-work) "../")))
(setq xmtn-propagate-to-root (expand-file-name (concat (file-name-as-directory to-work) "../")))
(setq xmtn-propagate-ewoc (ewoc-create 'xmtn-propagate-printer))
(let ((inhibit-read-only t)) (delete-region (point-min) (point-max)))
(ewoc-set-hf
xmtn-propagate-ewoc
(concat
(format "From root : %s\n" xmtn-propagate-from-root)
(format " To root : %s\n" xmtn-propagate-to-root)
)
"")
(xmtn-propagate-make-data
(file-name-nondirectory (directory-file-name from-work))
(file-name-nondirectory (directory-file-name to-work))
(file-name-nondirectory (directory-file-name from-work))
(file-name-nondirectory (directory-file-name to-work)))
(xmtn-propagate-mode)))
(setq from-work (file-name-as-directory (expand-file-name (substitute-in-file-name from-work))))
(setq to-work (file-name-as-directory (expand-file-name (substitute-in-file-name to-work))))
(pop-to-buffer (get-buffer-create "*xmtn-propagate*"))
(setq default-directory to-work)
(setq xmtn-propagate-from-root (expand-file-name (concat from-work "../")))
(setq xmtn-propagate-to-root (expand-file-name (concat to-work "../")))
(setq xmtn-propagate-ewoc (ewoc-create 'xmtn-propagate-printer))
(let ((inhibit-read-only t)) (delete-region (point-min) (point-max)))
(ewoc-set-hf
xmtn-propagate-ewoc
(concat
(format "From root : %s\n" xmtn-propagate-from-root)
(format " To root : %s\n" xmtn-propagate-to-root)
)
"")
(xmtn-propagate-make-data
(file-name-nondirectory (directory-file-name from-work))
(file-name-nondirectory (directory-file-name to-work))
(file-name-nondirectory (directory-file-name from-work))
(file-name-nondirectory (directory-file-name to-work)))
(xmtn-propagate-mode))
(provide 'xmtn-propagate)

View File

@ -1,6 +1,6 @@
;;; xmtn-revlist.el --- Interactive display of revision histories for monotone
;; Copyright (C) 2008, 2009 Stephen Leake
;; Copyright (C) 2008 - 2010 Stephen Leake
;; Copyright (C) 2006, 2007 Christian M. Ohler
;; Author: Christian M. Ohler
@ -341,6 +341,33 @@ arg; root. Result is of the form:
'()
difference)))
(defun xmtn--revlist--review-update-info (root)
(let* ((branch (xmtn--tree-default-branch root))
(last-update
(xmtn-automate-simple-command-output-line
root
(list "select" "u:")))
(base-revision-hash-id (xmtn--get-base-revision-hash-id root))
(difference
;; FIXME: replace with automate log
(xmtn-automate-simple-command-output-lines
root
(list "ancestry_difference" base-revision-hash-id last-update))))
(list
branch
`(,(format "Tree %s" root)
,(format "Branch %s" branch)
,(format "Base %s" base-revision-hash-id)
nil
,(case (length difference)
(0 "No revisions in last update")
(1 "1 revision in last update:")
(t (format
"%s revisions in last update:"
(length difference)))))
'()
difference)))
(defun xmtn-revlist-show-conflicts ()
"If point is on a revision that has two parents, show conflicts
from the merge."
@ -447,6 +474,17 @@ from the merge."
nil dvc-log-last-n))
nil)
;;;###autoload
(defun xmtn-review-update (root)
"Review revisions in last update of ROOT workspace."
(interactive "D")
(xmtn--setup-revlist
root
'xmtn--revlist--review-update-info
nil ;; first-line-only-p
dvc-log-last-n)
nil)
;;;###autoload
(defun xmtn-view-heads-revlist ()
"Display a revlist buffer showing the heads of the current branch."

View File

@ -112,21 +112,27 @@ Signals an error if more (or fewer) than one line is output."
(first lines)))
(defconst xmtn--minimum-required-command-version '(0 46))
;; see also xmtn-sync.el xmtn-sync-required-command-version
(defconst xmtn--required-automate-format-version "2")
(defun xmtn--have-no-ignore ()
"Non-nil if mtn automate inventory supports --no-ignore, --no-unknown, --no-unchanged options."
(>= (xmtn-dvc-automate-version) 7.0))
(defvar xmtn--*cached-command-version* nil
;; compare with (xmtn-version-<= required)
"(MAJOR MINOR REVISION VERSION-STRING).")
(defvar xmtn--*cached-command-version* nil)
(defvar xmtn--*command-version-cached-for-executable* nil)
(defun xmtn-version-<= (required)
"Nonnil if REQUIRED (list of major, minor) is <= cached version."
(version-list-<= required (butlast (xmtn--cached-command-version) 2)))
(defun xmtn--clear-command-version-cache ()
(setq xmtn--*command-version-cached-for-executable* nil
;; This is redundant but neater.
xmtn--*cached-command-version* nil))
(defun xmtn--cached-command-version ()
"Return mtn version as a list (MAJOR MINOR REVISION VERSION-STRING).
Sets cache if not already set."
(if (equal xmtn--*command-version-cached-for-executable* xmtn-executable)
xmtn--*cached-command-version*
(let ((executable xmtn-executable))
@ -164,127 +170,19 @@ id."
(list major minor revision string)))))
(defun xmtn--check-cached-command-version ()
(let ((minimum-version xmtn--minimum-required-command-version))
(destructuring-bind (major minor revision string)
(xmtn--cached-command-version)
(unless (or (> major (car minimum-version))
(and (= major (car minimum-version))
(>= minor (cadr minimum-version))))
;; Clear cache now since the user is somewhat likely to
;; upgrade mtn (or change the value of `xmtn-executable')
;; after this message.
(xmtn--clear-command-version-cache)
(error (concat "xmtn does not work with mtn versions below %s.%s"
" (%s is %s)")
(car minimum-version) (cadr minimum-version)
xmtn-executable string)))
nil))
;;;###autoload
(defun xmtn-check-command-version ()
"Check and display the version identifier of the mtn command.
This command resets xmtn's command version cache."
(interactive)
(xmtn--clear-command-version-cache)
(destructuring-bind (major minor revision version-string)
(xmtn--cached-command-version)
(let* ((latest (xmtn--latest-mtn-release))
(latest-major (first latest))
(latest-minor (second latest)))
(if (eval `(xmtn--version-case
((and (= ,latest-major latest-minor)
(mainline> latest-major latest-minor))
t)
(t
nil)))
(message "%s (xmtn considers this version newer than %s.%s)"
version-string major minor)
(message "%s" version-string))))
(let ((minimum-version xmtn--minimum-required-command-version)
(string (nth 3 (xmtn--cached-command-version))))
(unless (xmtn-version-<= xmtn--minimum-required-command-version)
;; Clear cache now since the user is somewhat likely to
;; upgrade mtn (or change the value of `xmtn-executable')
;; after this message.
(xmtn--clear-command-version-cache)
(error (concat "xmtn does not work with mtn versions below %s.%s"
" (%s is %s)")
(car minimum-version) (cadr minimum-version)
xmtn-executable string)))
nil)
(defun xmtn--make-version-check-form (version-var condition)
;; The expression (mainline> X Y) matches all command versions
;; strictly newer than X.Y, and, if X.Y is the latest version
;; according to (xmtn--latest-mtn-release), command versions that
;; report version X.Y with a revision ID different from what
;; (xmtn--latest-mtn-release) returns. This is a kludge to attempt
;; to distinguish the latest mtn release from the current
;; bleeding-edge ("mainline") version. (Bleeding-edge mtn versions
;; always report a version equal to the last release, while they
;; generally have syntax and semantics that match the upcoming
;; release; i.e., their syntax and semantics don't match the version
;; number they report.)
(case condition
((t) `t)
((nil) `nil)
(t
(let ((operator (car condition))
(arguments (cdr condition)))
(ecase operator
((< <= > >= = /= mainline>)
(let ((target-version arguments))
(assert (eql (length arguments) 2))
(ecase operator
((=)
`(and (= (car ,version-var) ,(car target-version))
(= (cadr ,version-var) ,(cadr target-version))))
((< >)
`(or (,operator (car ,version-var) ,(car target-version))
(and
(= (car ,version-var) ,(car target-version))
(,operator (cadr ,version-var) ,(cadr target-version)))))
((mainline>)
`(or (> (car ,version-var) ,(car target-version))
(and (= (car ,version-var) ,(car target-version))
(or (> (cadr ,version-var) ,(cadr target-version))
(and (= (cadr ,version-var) ,(cadr target-version))
(let ((-latest- (xmtn--latest-mtn-release)))
(and (= (car -latest-) ,(car target-version))
(= (cadr -latest-)
,(cadr target-version))
(not (equal (caddr ,version-var)
(caddr -latest-))))))))))
((/= <= >=)
(let ((negated-operator (ecase operator
(/= '=)
(<= '>)
(>= '<))))
`(not ,(xmtn--make-version-check-form version-var
`(,negated-operator
,@arguments))))))))
((not)
(assert (eql (length arguments) 1))
`(not ,(xmtn--make-version-check-form version-var (first arguments))))
((and or)
`(,operator
,@(loop for subform in arguments
collect
(xmtn--make-version-check-form version-var subform)))))))))
(defun xmtn--signal-unsupported-version (version supported-conditions)
(error "Operation only implemented for monotone versions matching %S"
;; This message is probably not very helpful to users who
;; don't know xmtn's internals.
`(or ,@supported-conditions)))
(defmacro* xmtn--version-case (&body clauses)
(let ((version (gensym)))
`(let ((,version (xmtn--cached-command-version)))
(cond ,@(loop for (condition . body) in clauses
collect `(,(xmtn--make-version-check-form version
condition)
,@body))
(t (xmtn--signal-unsupported-version
,version
',(loop for (condition . nil) in clauses
collect condition)))))))
(defun xmtn--latest-mtn-release ()
;; Version number and revision id of the latest mtn release at the
;; time of this xmtn release.
'(0 35 "f92dd754bf5c1e6eddc9c462b8d68691cfeb7f8b"))
(provide 'xmtn-run)
;;; xmtn-run.el ends here

175
dvc/lisp/xmtn-sync.el Normal file
View File

@ -0,0 +1,175 @@
;;; xmtn-sync.el --- database sync handling for DVC backend for monotone
;;
;; Copyright (C) 2010 Stephen Leake
;;
;; Author: Stephen Leake
;; Keywords: tools
;;
;; This file is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation; either version 2 of the License, or
;; (at your option) any later version.
;;
;; This file is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;;
;; You should have received a copy of the GNU General Public License
;; along with this file; see the file COPYING. If not, write to
;; the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
;; Boston, MA 02110-1301 USA.
(eval-when-compile
;; these have macros we use
)
(eval-and-compile
;; these have functions we use
(require 'xmtn-automate)
)
;;; User variables
(defvar xmtn-sync-branch-file "~/.dvc/branches"
"File associating branch name with workspace root")
(defvar xmtn-sync-executable
(cond
((equal system-type 'windows-nt)
;; Native MinGW does not support file: or ssh: - assume Cygwin is
;; installed, but not first in path
"c:/bin/mtn")
(t
;; Unix or Cygwin; assume mtn is in path
"mtn"))
"Executable for running sync command on local db; overrides xmtn-executable.")
(defvar xmtn-sync-config "xmtn-sync-config"
"File to store `xmtn-sync-branch-alist' and `xmtn-sync-remote-exec-alist'; relative to `dvc-config-directory'.")
;;; Internal variables
(defconst xmtn-sync-required-command-version '(0 46)
"Minimum mtn version for automate sync; overrides xmtn--minimum-required-command-version.")
(defconst xmtn-sync-remote-exec-default "mtn"
"Default executable command to run on remote host for file: or ssh:; see `xmtn-sync-remote-exec-alist'.")
;; loaded from xmtn-sync-config
(defvar xmtn-sync-branch-alist nil
"Alist associating branch name with workspace root")
(defvar xmtn-sync-remote-exec-alist
(list
(list "file://" xmtn-sync-executable))
"Alist of host and remote command. Overrides `xmtn-sync-remote-exec-default'.")
;; buffer-local
(defvar xmtn-sync-local-db nil
"Absolute path to local database.")
(make-variable-buffer-local 'xmtn-sync-local-db)
(defvar xmtn-sync-remote-db nil
"Absolute path to remote database.")
(make-variable-buffer-local 'xmtn-sync-remote-db)
(defstruct (xmtn-sync-branch
(:copier nil))
;; ewoc element; data for a branch that was received
name)
(defun xmtn-sync-set-hf ()
"Set ewoc header and footer."
(ewoc-set-hf
xmtn-sync-ewoc
(concat
(format " local database : %s\n" xmtn-sync-local-db)
(format "remote database : %s\n" xmtn-sync-remote-db)
)
""))
(defun xmtn-sync-printer (branch)
"Print an ewoc element; BRANCH must be of type xmtn-sync-branch."
(insert "branch: ")
(insert (xmtn-sync-branch-name branch))
(insert "\n")
)
(defvar xmtn-sync-ewoc nil
"Buffer-local ewoc for displaying sync.
All xmtn-sync functions operate on this ewoc.
The elements must all be of type xmtn-sync-sync.")
(make-variable-buffer-local 'xmtn-sync-ewoc)
(defun xmtn-sync-status ()
"Start xmtn-status-one for current ewoc element."
(let* ((data (ewoc-data (ewoc-locate xmtn-sync-ewoc)))
(branch (xmtn-sync-branch-name data))
(work (assoc branch xmtn-sync-branch-alist)))
(if (not work)
(progn
(setq work (read-directory-name (format "workspace root for %s: " branch)))
(push (list branch work) xmtn-sync-branch-alist)))
(xmtn-status-one work)))
(defvar xmtn-sync-ewoc-map
(let ((map (make-sparse-keymap)))
(define-key map [?0] '(menu-item "0) status"
'xmtn-sync-status))
map)
"Keyboard menu keymap for xmtn-sync-ewoc.")
(defvar xmtn-sync-mode-map
(let ((map (make-sparse-keymap)))
(define-key map [?q] 'dvc-buffer-quit)
(define-key map "\M-d" xmtn-sync-ewoc-map)
map)
"Keymap used in `xmtn-sync-mode'.")
(easy-menu-define xmtn-sync-mode-menu xmtn-sync-mode-map
"`xmtn-sync' menu"
`("Xmtn-sync"
["Do the right thing" xmtn-sync-ewoc-map t]
["Quit" dvc-buffer-quit t]
))
;; derive from nil causes no keymap to be used, but still have self-insert keys
;; derive from fundamental-mode causes self-insert keys
(define-derived-mode xmtn-sync-mode fundamental-mode "xmtn-sync"
"Major mode to specify conflict resolutions."
(setq dvc-buffer-current-active-dvc 'xmtn)
(setq buffer-read-only nil)
(setq xmtn-sync-ewoc (ewoc-create 'xmtn-sync-printer))
(setq dvc-buffer-refresh-function nil)
(dvc-install-buffer-menu)
(setq buffer-read-only t)
(buffer-disable-undo)
(set-buffer-modified-p nil))
;;;###autoload
(defun xmtn-sync-sync (local-db remote-host remote-db)
"Sync LOCAL-DB with REMOTE-HOST REMOTE-DB, display sent and received branches.
Remote-db should include branch pattern in URI syntax."
(interactive "flocal db: \nMremote-host: \nMremote-db: ")
(pop-to-buffer (get-buffer-create "*xmtn-sync*"))
(let ((xmtn-executable xmtn-sync-executable)
(xmtn--minimum-required-command-version xmtn-sync-required-command-version))
;; pass remote command to mtn via Lua hook get_mtn_command; see
;; xmtn-hooks.lua
(setenv "XMTN_SYNC_MTN"
(or (cadr (assoc remote-host xmtn-sync-remote-exec-alist))
xmtn-sync-remote-exec-default))
(xmtn-automate-command-output-buffer
default-directory ; root
(current-buffer) ; output-buffer
(list (list
"ticker" "count"
"db" local-db
) ;; options
"sync" (concat remote-host remote-db)) ;; command, args
)))
(provide 'xmtn-sync)
;; end of file

View File

@ -34,7 +34,7 @@ ii = install-info
install: uninstall info
$(MKDIR_P) -m 0755 $(info_dir)
@for i in dvc.info* ; do \
@for i in dvc.info* dvc-intro.info* ; do \
echo Installing $$i ; \
$(INSTALL_DATA) $$i $(info_dir) ; \
done
@ -48,13 +48,16 @@ install: uninstall info
uninstall:
rm -f $(info_dir)/dvc.info*
info: dvc.info
info: dvc.info dvc-intro.info
alldeps = $(srcdir)/dvc.texinfo dvc-version.texinfo
dvc.info: $(alldeps)
$(MAKEINFO) $(srcdir)/dvc.texinfo
dvc-intro.info: $(alldeps)
$(MAKEINFO) $(srcdir)/dvc-intro.texinfo
dvc.html: $(alldeps)
$(MAKEINFO) --html --no-split $(srcdir)/dvc.texinfo

View File

@ -0,0 +1,940 @@
\input texinfo
@c Author : Stephen Leake <stephen_leake@stephe-leake.org>
@c Web : http://www.stephe-leake.org/
@setfilename dvc-intro.info
@settitle DVC: Introduction to the GNU Emacs interface to
distributed version control systems.
@setchapternewpage off
@node Top
@top DVC Intro
@smallexample
@group
Copyright (C) 2007, 2008, 2009, 2010 Stephen Leake
Permission is granted to copy, distribute and/or modify this document
under the terms of the GNU Free Documentation License, Version 1.2
or any later version published by the Free Software Foundation;
with no Invariant Sections, with no Front-Cover Texts, and with no
Back-Cover Texts. A copy of the license is included in the section
entitled ``GNU Free Documentation License''.
@end group
@end smallexample
@menu
* Overview::
* Installing::
* Invoking::
* Status Display::
* Key bindings::
* Previewing updates::
* Merging::
* mtn command line::
* Common Errors and Solutions::
* GNU Free Documentation License::
@detailmenu
--- The Detailed Node Listing ---
Overview
* Basic DVC::
* Compare to CVS::
Invoking
* xmtn-status-one::
* xmtn-propagate-one::
Key bindings
* status buffer keys::
* Ediff keys::
* Log edit keys::
* DVC log keys::
* DVC diff keys::
* mtn conflicts keys::
Common Errors and Solutions
* Attach blocked by unversioned path::
* Revision id does not match conflict file::
@end detailmenu
@end menu
@node Overview
@chapter Overview
DVC is a common interface to several extremely powerful and flexible
version control systems, such as Gnu arch, monotone, bzr, and others
(known as 'backends' to DVC).
DVC provides the same (or at least similar) user interface for each
backend, making it easier to use multiple backends in related
projects. It also automates some tasks, and provides guidance to the
user as to what needs to be done.
DVC is not included with the standard Gnu emacs distribution. It is
provided in source form via a bzr repository (see @ref{Installing}).
If you are not already familiar with version control systems, please
read @ref{Basic DVC}.
One of the most important features of the DVC user interface is that
it identifies what files in a project need attention of some sort; you
have changed them in your working directory, or someone else has
changed them in the repository, or they've been deleted or are new,
etc. DVC presents a list of all such files, and makes it easy to see
what needs to be done for each file.
When committing files, ediff is used to allow reviewing the changes,
so an appropriate change comment can be written.
DVC replaces the command-line interface to the backends for the most
common operations, but it is still necessary to use the command line
at times. Creating a repository, starting a project in a repository,
and managing branches require command line operations.
This manual describes the DVC user interface, and gives examples of
some required command line operations, using the monotone backend.
@menu
* Basic DVC::
* Compare to CVS::
@end menu
@node Basic DVC
@section Basic DVC
Here we give a brief introduction to general concepts of distributed
version control systems, focusing on the concepts that are needed to
use DVC, and providing common terminology.
Each backend will have its own documentation, and terminology that
differs from this. The terms here are taken mostly from the monotone
backend, since it has the most readable user manual.
Let's start with some definitions:
@table @dfn
@item workspace
Each user has a workspace, containing a copy of the files she is
working on. This is typically a directory tree. In the root directory
of the tree there is typically a directory containing backend control
files, used only by the backend.
@item database
The database stores copies of all files in the workspace (and typically
more than one workspace), together with all of the change history and
other meta-information. The database is never edited directly; only
the backends modify it.
@item local database
A database on the user's machine. This database is used to
control all workspaces on the user's machine.
@item remote database
A database on a remote machine. This may be another user's local
database, or a central database set up specifically for sharing files.
The user interacts with the remote database in order to retrieve other
user's files, or deliver files to them.
@item revision
A set of changes to files that are applied together. Most operations
on the database involve revisions, and all changes to files are part
of a revision.
@item branch
A label for distinct trees of revisions. There are two main uses for
branches; parallel development on a single project, and completely
separate projects. Branches of a single project are typically merged
back together (this is called ``propagating''), while completely
separate projects are not.
A database can store any number of branches.
@item heads
The revisions that are the leaves of the history tree on a single
branch. In monotone, there can be any number of heads (see
@ref{Merging}).
@item merge
The process of combining multiple heads of a branch into one
head. This can encounter conflicts that require user resolution; see
@ref{Merging}.
@item propagate
One branch can be ``propagated'' to another. This is a form of
merging; it merges all the changes from one branch into another,
starting from their common ancestor (which is usually the previous
propagate between the two branches).
This is how changes in a development branch are promoted to the main
branch.
Since propagating is a form of merging, it can encounter all of the
same conflicts that merging can.
@item *dvc-status* buffer
A main user interface buffer. It shows all files in the workspace that
need attention. Single keystrokes invoke various operations.
@xref{Status Display}, for more details.
The name of the buffer is not literally @dfn{*dvc-status*}; instead,
@dfn{dvc} is replaced by the backend name; @dfn{xmtn} for monotone,
@dfn{bzr} for bzr, etc. But in this document, we will use the name
@dfn{*dvc-status*}.
@item *dvc-diff* buffer
Another main user interface buffer. It shows the files involved in a
particular revision, together with the diffs of the changes. Single
keystrokes invoke various operations.
@end table
Users edit files in their workspace, then use DVC to synchronize the
workspace with the local database. Later, they use the command line to
synchronize their local database with a remote database. This allows
each user to make changes locally but still under change control,
without affecting other users until they each choose to synchronize.
@node Compare to CVS
@section Compare to CVS
Since many people are familiar with the CVS version control system, we
compare that with DVC, and monotone in particular.
In CVS, each file is committed separately; in DVC, all files in a
workspace are committed together. This makes sure that all changes
that are related are committed together.
This means the commit log message mentions all files that have
changes; it is a much longer message, but there are fewer of them, and
the message can more easily describe changes that affect more than one
file.
In CVS, you must always have access to the remote server. In DVC, you
work with a local database, then separately sync that database with a
remote server. Thus DVC is useful when not on a network; monotone can
even sync via USB disk rather than a network connection.
This means there are two steps to syncing a workspace with the central
server, which can be annoying. On the other hand, the sync process
syncs all projects in the database at once; with monotone, it lets you
know what projects have changes.
Otherwise the primary Emacs interface to CVS and DVC are very similar,
although DVC has many secondary interfaces that CVS does not have.
@node Installing
@chapter Installing
Install bzr; see @url{http://bazaar.canonical.com/en/}.
Retrieve the DCV source; see
@url{https://gna.org/projects/dvc#options} for general information.
In a bash shell:
@example
cd ~
bzr get http://bzr.xsteve.at/dvc/
cd ~/dvc
autoconf
./configure
make
@end example
In your @file{.emacs}, add @code{(load-file (expand-file-name "~/dvc/dvc-load.el"))}
@node Invoking
@chapter Invoking
Before invoking DVC, you may want to ensure that the local database is
synchronized with the central database, via a backend-specific
command line.
You typically invoke DVC with the Emacs command @command{dvc-status}
or @command{dvc-diff}. This prompts for a workspace; it should be the
top level directory in your working directory tree.
You can also create shortcuts in text files to invoke dvc:
@example
(dvc-status (expand-file-name "~/dvc"))
(dvc-diff nil (expand-file-name "~/dvc"))
@end example
These can be executed with @key{C-x C-e}, and are a handy way of
keeping track of several workspaces.
@command{dvc-status} or @command{dvc-diff} run the corresponding
backend command, comparing the workspace against the local database,
and presenting the information in the @dfn{*dvc-status*} or
@dfn{*dvc-diff*} buffer.
For monotone, there are higher-level starting points:
@table @command
@item xmtn-status-one
Summarizes the status of one workspace.
@item xmtn-status-multiple
Similar to @command{xmtn-status-one}, but shows all workspaces
immediately under a root directory.
@item xmtn-propagate-one
Summarizes the status of several workspaces
@item xmtn-propagate-multiple
Supervises propagating several workspaces
@end table
@menu
* xmtn-status-one::
* xmtn-propagate-one::
@end menu
@node xmtn-status-one
@section xmtn-status-one
Summarizes the status of one workspace, in a @dfn{xmtn-multi-status}
buffer. The branch name is shown, followed by possible appropriate
actions. As each action is performed, it is replaced by the next
action, until there are none left.
Similarly, @command{xmtn-status-multiple} shows the status of all
workspaces immediately under a root directory.
Actions are invoked with @key{M-d}.
The possible actions are:
@table @dfn
@item need-refresh
Shown while the backend is computing, or the user is performing
operations in an associated @dfn{*xmtn-multi-status*} buffer.
@item commit
Open an @dfn{*xmtn-status*} buffer to commit changes.
@item resolve conflicts
Open an @dfn{*xmtn-conflicts*} buffer to resolve conflicts; see @ref{Merging}.
@item show heads
Open an @dfn{*xmtn-revlist*} buffer to show the current head revisions.
@item merge
Perform the merge, using the conflict resolutions.
@item update
Update the workspace to the current head revision (must be merged).
@item review update
Open an @dfn{*xmtn-revlist*} buffer to review the revisions in the
most recent update.
@item ignore local changes
Don't show @dfn{commit}.
@item refresh
Recompute the @dfn{*xmtn-multi-status*} display.
@item clean/delete
Delete conflicts and conflict resolution files, and delete
the workspace from the display.
@end table
@node xmtn-propagate-one
@section xmtn-propagate-one
@command{xmtn-propagate-one} supervises the process of propagating
from one workspace to another, in an @dfn{xmtn-propagate} buffer.
The display shows one source and destination branch pair, and possible
appropriate actions. As each action is performed, it is replaced by
the next action, until there are none left.
Similarly, @command{xmtn-propagate-multiple} supervises the
propagation of all workspaces immediately under two root
directories. This is useful when several related projects branch
together.
Before displaying actions, each branch pair is examined to see if
propagate is necessary. If it is not, the workspace is not examined
for changes (since that can take a long time).
In the list of actions, ``from'' stands for the name of the source
branch, ``to'' the name of the destination branch.
Actions are invoked with @key{M-d}.
The possible actions are:
@table @command
@item status ``from''
@itemx status ``to''
Start an @dfn{xmtn-multi-status} buffer for the specified workspace,
to allow commit, update followed by update review, or merge with
conflict resolution.
@itemx update ``to''
Update the specified workspace to the current head revision (must be
merged). This bypasses the @dfn{xmtn-multi-status} buffer, and
therefore does not provide for update review. Useful when you don't
need to review the changes, which is the typical case for propagate.
@item ignore local changes ``from''
@item ignore local changes ``to''
Don't show @dfn{local changes unknown}; assume the workspace is
committed. Useful when you know that any local changes won't interfere
with the propagate.
@item resolve conflicts
Open an @dfn{*xmtn-conflicts*} buffer in the destination workspace to
resolve propagate conflicts; see @ref{Merging}.
@item propagate
Propagate the branch pair, using the conflict resolutions.
@item refresh
Recompute the display. If prefixed with @key{C-u}, force examining
workspaces for local changes.
@item clean/delete
Delete conflicts and conflict resolution files, and delete
the workspace from the display.
@end table
@node Status Display
@chapter Status Display
After invoking @command{dvc-status}, you are presented with the
@dfn{*dvc-status*} buffer.
The detailed format differs depending on the backend. This
presentation is close to the bzr and mtn formats.
The buffer contains a header, such as:
@example
Status for c:/Projects/GDS/common/main/:
base revision : e946839c833b15e6bf12bd1536764e1106c41924
branch : common.main
branch is merged
base revision is a head revision
@end example
The last two lines are important; either may have ``not'' in it.
If the branch is not merged, it must be merged before an update can be
done; see @ref{Merging}. However, commits can be done when the branch
is not merged; this allows saving work before attempting the merge.
If the base revision is not a head revision, there are updates that
need to be applied to the workspace. The updates may be reviewed first
using @key{M m}; they may be applied using @key{M u}.
In the main body of the buffer, there is one line for each file in the
workspace that needs attention. For example:
@example
* modified hardware/gds-hardware-pmrd_wrapper.adb
unknown build/ip1k110_quartus/serv_req_info.txt
E modified hardware/test/test_hardware-one_harness.adb
@end example
Each line has three fields:
@table @dfn
@item Mark
Either blank (not marked), '*' (marked), or 'E' (excluded). Most
commands can apply to a group of marked files, but some cannot (they
warn if a group is marked).
Excluded files are under configuration management, but are excluded
from commits. This is used for files that each user modifies, such as
development test drivers.
@item Status
A phrase indicating the status of the file; see the table below.
@item File name
Gives the file name of the working file, with a path relative to the
root directory.
@end table
In addition, some files will have extra status information that
appears on the next line, indented.
The following table defines each status phrase, and gives the set of
actions that can be taken for each. The action shown is from the DVC
menu; the equivalent key is also given.
Other actions (such as commit) apply to all files; they are discussed
later.
@table @samp
@c the list of status phrases is in
@c /Gnu/dvc/lisp/dvc-fileinfo.el dvc-fileinfo-status-image
@c keep this list in the same order
@item Added
Working file has been added, but not committed.
@table @samp
@item @key{r} Delete
Remove the file from the workspace, do not commit it.
Do this if you've changed your mind.
@end table
@item Conflict
A conflict was detected while merging.
The same lines have been edited differently by different people.
This status does not appear with the monotone back-end.
@table @samp
@item @key{<enter>} Edit the file.
Either resolve the conflict
manually, or use @code{M-x smerge-ediff}. Execute @code{M-x
dvc-resolve} when finished to inform the back-end that the
conflict is resolved.
@item @key{U} Revert
Delete the working copy, replace it with the database copy. Do
this if you decide the changes are not correct.
@end table
@item Deleted
Working file has been marked for deletion, but not committed.
@table @samp
@item @key{a} Add
Undo the removal.
@end table
@item Ignored
Working file is ignored by the back-end. Files with this status
are not typically shown - ignored files are ignored by DVC as well.
They can be enabled by setting @code{dvc-status-display-ignored} to
nil.
@table @samp
@item @key{# e}
Edit the back-end ignore file.
@end table
@item Known
Working file is known to the back-end, and unchanged. Files with
this status are not typically shown. They can be enabled by setting
@code{dvc-status-display-known} to nil. There are no appropriate
actions.
@item Missing
A previously known file has been deleted from the workspace, but
not marked for deletion.
@table @samp
@key{U} Revert
Restore the file to the workspace from the database.
@item @key{r} Delete
Mark the file for deletion.
@end table
@item Modified
A changed file in the workspace.
@table @samp
@item @key{e} ediff
Review differences and collect a change comment.
@item @key{U} Revert
Delete the working copy, replace it with the database copy. Do
this if you decide your changes are not correct.
@end table
@item Rename-source
Working file has been marked as renamed but not committed. No
appropriate actions.
@item Rename-target
Working file has been marked as renamed but not committed. No
appropriate actions.
@item Unknown
Working file is unknown.
@table @samp
@item @key{a} Add
The file is a new source file; add it to the current revision. This will
change the status to 'Added'.
@item @key{i} Ignore
The file is an output file of some sort (ie object file, test output).
Ignore it in all future DVC sessions.
@item @key{I} Ignore extension in dir
Ignore all files with this extension in this directory.
@item @key{M-I} Ignore extension
Ignore all files with this extension in all directories.
@item @key{r} Delete
The file is a scratch file, or was created by mistake. Remove it from
the workspace.
@end table
@end table
Changes are committed all at once; the set of changes to the entire
workspace is called a ``revision''. @key{c} opens the
@code{*dvc-log-edit*} buffer, where you can write a change comment.
Then @key{C-c C-c} commits all changes.
The key @key{M-d} invokes a function called ``Do the Right Thing''. If
there is only a single choice (or an extremely common choice) in the
table above, it does that action. Otherwise, it presents a short list
of the actions, in the message buffer, reminding the user of the
appropriate options. Note that @key{M-d} means meta-d (alt-d on most
PC keyboards))
@node Key bindings
@chapter Key bindings
Here is a summary of the most useful key bindings in the various
buffers associated with DVC.
@menu
* status buffer keys::
* Ediff keys::
* Log edit keys::
* DVC log keys::
* DVC diff keys::
* mtn conflicts keys::
@end menu
@node status buffer keys
@section status buffer keys
In a @code{*dvc-status*} buffer:
@table @key
@item M-d
Do the right thing for the current file.
@item c
Open a @code{*dvc-log-edit*} buffer to accumulate comments for a
commit.
@item M m
Show missing revisions; changes that will be applied by update.
@item M M
Merge current heads; see @ref{Merging}.
@item M u
Update to the current head.
@item R
Rename a missing to an unknown file. The two files must be marked
first, and they must be the only files marked.
@item t
Create an entry in the @code{*dvc-log-edit*} for the current diff.
@end table
@node Ediff keys
@section Ediff keys
In an Ediff control buffer (the small window with Ediff in the title bar):
@table @key
@item a
Copy from buffer A to buffer B.
@item b
Copy from buffer B to buffer A.
@item n
Move to next diff.
@item p
Move to previous diff.
@item q
Quit Ediff.
@item t
Create an entry in the @code{*dvc-log-edit*} for the current diff.
@item $$
Focus on conflicts in a merge.
@item ?
Show the help summary for Ediff. @key{?} hides it again.
@end table
@node Log edit keys
@section log edit keys
In the @code{*dvc-log-edit*} buffer:
@table @key
@item C-c C-c
Commit. Note that this is the only way to actually commit.
@end table
@node DVC log keys
@section DVC log keys
In a @code{*xmtn-log*} buffer:
@table @key
@item n
move to the next revision
@item p
move to the previous revision
@item =
show a diff of the changes in a single revision
@item C-=
show a diff between the revision and the workspace
@end table
@node DVC diff keys
@section DVC diff keys
In a @code{*dvc-diff*} buffer:
@table @key
@item e
show ediff for current file
@item j
jump between file list and diff hunks
@item n
move to the next diff hunk
@item p
move to the previous diff hunk
@end table
@node mtn conflicts keys
@section mtn conflicts keys
In a @code{*xmtn-conflicts*} buffer:
@table @key
@item C
Delete conflicts file and any resolution files.
@item c
Clear the current resolution, so you can specify a different one.
@item n
Move to the next conflict.
@item N
Move to the next unresolved conflict.
@item p
Move to the previous conflict.
@item P
Move to the previous unresolved conflict.
@item q
Quit the @code{*xmtn-conflicts*} buffer. The conflicts file and
associated resolution files are saved.
@item r
Specify a resolution for the current conflict. This prompts with a
choice of resolutions appropriate for the current conflict; select the
appropriate resolution by number. See @ref{Merging}, for information
on the possible resolutions.
@item M-d
Same as @key{r}
@end table
@node Previewing updates
@chapter Previewing updates
To preview updates before applying them to your workspace, use the
@code{dvc-missing} command; it's on the status buffer menu at
@code{DVC | Merge/Update | show missing}.
@code{dvc-missing} can also be invoked via the Emacs command line
(@key{M-x}); that prompts for a local tree.
Invoking @code{dvc-missing} brings up an @code{*dvc-log*} window,
showing revisions that are in your local database but not yet applied
to the workspace.
The revisions are listed oldest first.
You can view the changes made in a single revision, or from that
revision to the current workspace.
See @xref{Log edit keys}, for key bindings.
@key{=} and @key{C-=} bring up a @code{*dvc-diff*} buffer for the
revision selected. The diffs are shown in Gnu diff format; all files
in one @code{*dvc-diff*} buffer. There is a list of the files at the
top of the buffer. See @xref{DVC diff keys}, for key bindings.
Note that you can also review updates after they have been
applied. This is often more useful, because you can edit the workspace
file to fix problems caused by the update, or just to see the final
state after all revisions have been applied.
@node Merging
@chapter Merging
Monotone allows multiple people to each commit to their local
database. Then when the databases are synced, there are multiple heads
for the branch; one head for each developer that commited since the
last sync.
These multiple heads must be merged before a local workspace can be
updated to the head of the branch; there must be only one head to
update to. The monotone command line allows updating to one head of an
unmerged branch, but DVC does not support this.
When the changes in the different heads are to different files, or to
different parts of the same file, monotone can perform the merge
itself. However, when there are changes to the same parts of one file,
it needs help; this is called a content conflict.
An @code{*xmtn-conflicts*} buffer shows all conflicts in a merge or
propagate. You can work thru the list one a time, using @key{M-d}
to specify conflict resolutions. The list is saved in a file, so you
can come back to it later.
The conflicts that monotone knows how to resolve internally have
resolutions of @code{resolved-internal}; the others have no
resolutions.
The conflicts file and associated resolution files are stored in the
monotone bookkeeping area. They must be deleted when you are done with
them; use @key{C C} for that.
@key{M-d} prompts with a list of appropriate resolutions for the
current conflict; select the appropriate resolution by number. The
possible resolutions are:
@table @asis
@item right: drop
@itemx left: drop
Resolve one side of a duplicate name conflict by dropping it.
@itemx drop
Resolve an orphaned node conflict by dropping it.
@item right: rename
@itemx left: rename
Resolve one side of a duplicate name conflict by specifying a new name.
@item rename
Resolve an orphaned node conflict by specifying a new name.
@item right: right file
@itemx right: left file
@itemx left: right file
@itemx left: left file
Resolve one side of a duplicate name conflict by specifying a file.
The other side must be dropped or renamed.
@itemx left file
Resolve a content conflict by specifying a file. The file defaults to
the current workspace file.
@item right: keep
@itemx left: keep
Resolve one side of a duplicate name conflict by keeping it as is.
The other side must be dropped or renamed.
@item right: ediff
@itemx left: ediff
Resolve one side of a duplicate name conflict by ediff. This brings up
an ediff merge of the two files, and saves the result in the
resolution file area.
The other side must be dropped or renamed.
@item ediff
Resolve a content conflict via ediff. This brings up an ediff merge of
the two files, and saves the result in the resolution file area.
@end table
See @xref{mtn conflicts keys}, for a summary of key bindings.
@node mtn command line
@chapter mtn command line
Sometimes, especially over NFS, the Emacs DVC interface can be
painfully slow, and it is appropriate to use the mtn command line
instead.
Other times, the mtn command line is just simpler.
So we list the most useful mtn commands here. See the monotone command
line help or manual for more information.
@table @code
@item status
@code{mtn status}
@item commit
@code{mtn commit --message="<message>"}
@code{mtn commit --message-file=_MTN/log}
@item rename
@code{mtn rename <file> <new-file>}
@item update
@code{mtn update --move-conflicting-paths}
@end table
@node Common Errors and Solutions
@chapter Common Errors and Solutions
@menu
* Attach blocked by unversioned path::
* Revision id does not match conflict file::
@end menu
@node Attach blocked by unversioned path
@section Attach blocked by unversioned path
Problem: When attempting to update a directory, this warning appears:
@example
$ mtn update
...
mtn: warning: attach node 2147486644 blocked by unversioned path '<file path>'
mtn: misuse: 1 workspace conflict
@end example
Explanation: "Unversioned path" means the indicated file is not in the
current revision, however the file already exists on the disk. The
revision you are updating to contains the file, but it can't be
updated because it would overwrite the unknown file on the disk
Solution: Delete the indicated files from the disk and retry the
update, or specify the @command{--move-conflicting-paths} option.
@node Revision id does not match conflict file
@section Revision id does not match conflict file
Problem: When attempting to propagate from one branch to another, this message appears:
@example
$ mtn: propagating common.main -> common.work_user
mtn: [left] 48b675060af47a02bc6f773bd63647726f96cbd5
mtn: [right] 94ffd0b529dfb44c3ab122fe6c514b5f2e857104
mtn: misuse: left revision id does not match conflict file
@end example
Explanation: It means you have some conflict files left over from a
previous propagation or merge.
Solution: In a buffer showing the ``from'' workspace, run: M-x
xmtn-conflicts-clean. Repeat in the ``to'' workspace, then propagate
again.
@node GNU Free Documentation License, , Common Errors and Solutions, Top
@appendix GNU Free Documentation License
@include fdl.texinfo
@bye

409
dvc/texinfo/fdl.texinfo Normal file
View File

@ -0,0 +1,409 @@
@c This file is derived from the GNU file fdl.texi, by deleting the
@c addendum "How to use this License for your documents" and by deleting
@c the @node and @appendixsec commands. The later need to be in the main
@c texinfo file for the Emacs automatic menu-building commands to work.
@cindex FDL, GNU Free Documentation License
@center Version 1.2, November 2002
@display
Copyright @copyright{} 2000,2001,2002 Free Software Foundation, Inc.
51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
@end display
@enumerate 0
@item
PREAMBLE
The purpose of this License is to make a manual, textbook, or other
functional and useful document @dfn{free} in the sense of freedom: to
assure everyone the effective freedom to copy and redistribute it,
with or without modifying it, either commercially or noncommercially.
Secondarily, this License preserves for the author and publisher a way
to get credit for their work, while not being considered responsible
for modifications made by others.
This License is a kind of ``copyleft'', which means that derivative
works of the document must themselves be free in the same sense. It
complements the GNU General Public License, which is a copyleft
license designed for free software.
We have designed this License in order to use it for manuals for free
software, because free software needs free documentation: a free
program should come with manuals providing the same freedoms that the
software does. But this License is not limited to software manuals;
it can be used for any textual work, regardless of subject matter or
whether it is published as a printed book. We recommend this License
principally for works whose purpose is instruction or reference.
@item
APPLICABILITY AND DEFINITIONS
This License applies to any manual or other work, in any medium, that
contains a notice placed by the copyright holder saying it can be
distributed under the terms of this License. Such a notice grants a
world-wide, royalty-free license, unlimited in duration, to use that
work under the conditions stated herein. The ``Document'', below,
refers to any such manual or work. Any member of the public is a
licensee, and is addressed as ``you''. You accept the license if you
copy, modify or distribute the work in a way requiring permission
under copyright law.
A ``Modified Version'' of the Document means any work containing the
Document or a portion of it, either copied verbatim, or with
modifications and/or translated into another language.
A ``Secondary Section'' is a named appendix or a front-matter section
of the Document that deals exclusively with the relationship of the
publishers or authors of the Document to the Document's overall
subject (or to related matters) and contains nothing that could fall
directly within that overall subject. (Thus, if the Document is in
part a textbook of mathematics, a Secondary Section may not explain
any mathematics.) The relationship could be a matter of historical
connection with the subject or with related matters, or of legal,
commercial, philosophical, ethical or political position regarding
them.
The ``Invariant Sections'' are certain Secondary Sections whose titles
are designated, as being those of Invariant Sections, in the notice
that says that the Document is released under this License. If a
section does not fit the above definition of Secondary then it is not
allowed to be designated as Invariant. The Document may contain zero
Invariant Sections. If the Document does not identify any Invariant
Sections then there are none.
The ``Cover Texts'' are certain short passages of text that are listed,
as Front-Cover Texts or Back-Cover Texts, in the notice that says that
the Document is released under this License. A Front-Cover Text may
be at most 5 words, and a Back-Cover Text may be at most 25 words.
A ``Transparent'' copy of the Document means a machine-readable copy,
represented in a format whose specification is available to the
general public, that is suitable for revising the document
straightforwardly with generic text editors or (for images composed of
pixels) generic paint programs or (for drawings) some widely available
drawing editor, and that is suitable for input to text formatters or
for automatic translation to a variety of formats suitable for input
to text formatters. A copy made in an otherwise Transparent file
format whose markup, or absence of markup, has been arranged to thwart
or discourage subsequent modification by readers is not Transparent.
An image format is not Transparent if used for any substantial amount
of text. A copy that is not ``Transparent'' is called ``Opaque''.
Examples of suitable formats for Transparent copies include plain
@sc{ascii} without markup, Texinfo input format, La@TeX{} input
format, @acronym{SGML} or @acronym{XML} using a publicly available
@acronym{DTD}, and standard-conforming simple @acronym{HTML},
PostScript or @acronym{PDF} designed for human modification. Examples
of transparent image formats include @acronym{PNG}, @acronym{XCF} and
@acronym{JPG}. Opaque formats include proprietary formats that can be
read and edited only by proprietary word processors, @acronym{SGML} or
@acronym{XML} for which the @acronym{DTD} and/or processing tools are
not generally available, and the machine-generated @acronym{HTML},
PostScript or @acronym{PDF} produced by some word processors for
output purposes only.
The ``Title Page'' means, for a printed book, the title page itself,
plus such following pages as are needed to hold, legibly, the material
this License requires to appear in the title page. For works in
formats which do not have any title page as such, ``Title Page'' means
the text near the most prominent appearance of the work's title,
preceding the beginning of the body of the text.
A section ``Entitled XYZ'' means a named subunit of the Document whose
title either is precisely XYZ or contains XYZ in parentheses following
text that translates XYZ in another language. (Here XYZ stands for a
specific section name mentioned below, such as ``Acknowledgements'',
``Dedications'', ``Endorsements'', or ``History''.) To ``Preserve the Title''
of such a section when you modify the Document means that it remains a
section ``Entitled XYZ'' according to this definition.
The Document may include Warranty Disclaimers next to the notice which
states that this License applies to the Document. These Warranty
Disclaimers are considered to be included by reference in this
License, but only as regards disclaiming warranties: any other
implication that these Warranty Disclaimers may have is void and has
no effect on the meaning of this License.
@item
VERBATIM COPYING
You may copy and distribute the Document in any medium, either
commercially or noncommercially, provided that this License, the
copyright notices, and the license notice saying this License applies
to the Document are reproduced in all copies, and that you add no other
conditions whatsoever to those of this License. You may not use
technical measures to obstruct or control the reading or further
copying of the copies you make or distribute. However, you may accept
compensation in exchange for copies. If you distribute a large enough
number of copies you must also follow the conditions in section 3.
You may also lend copies, under the same conditions stated above, and
you may publicly display copies.
@item
COPYING IN QUANTITY
If you publish printed copies (or copies in media that commonly have
printed covers) of the Document, numbering more than 100, and the
Document's license notice requires Cover Texts, you must enclose the
copies in covers that carry, clearly and legibly, all these Cover
Texts: Front-Cover Texts on the front cover, and Back-Cover Texts on
the back cover. Both covers must also clearly and legibly identify
you as the publisher of these copies. The front cover must present
the full title with all words of the title equally prominent and
visible. You may add other material on the covers in addition.
Copying with changes limited to the covers, as long as they preserve
the title of the Document and satisfy these conditions, can be treated
as verbatim copying in other respects.
If the required texts for either cover are too voluminous to fit
legibly, you should put the first ones listed (as many as fit
reasonably) on the actual cover, and continue the rest onto adjacent
pages.
If you publish or distribute Opaque copies of the Document numbering
more than 100, you must either include a machine-readable Transparent
copy along with each Opaque copy, or state in or with each Opaque copy
a computer-network location from which the general network-using
public has access to download using public-standard network protocols
a complete Transparent copy of the Document, free of added material.
If you use the latter option, you must take reasonably prudent steps,
when you begin distribution of Opaque copies in quantity, to ensure
that this Transparent copy will remain thus accessible at the stated
location until at least one year after the last time you distribute an
Opaque copy (directly or through your agents or retailers) of that
edition to the public.
It is requested, but not required, that you contact the authors of the
Document well before redistributing any large number of copies, to give
them a chance to provide you with an updated version of the Document.
@item
MODIFICATIONS
You may copy and distribute a Modified Version of the Document under
the conditions of sections 2 and 3 above, provided that you release
the Modified Version under precisely this License, with the Modified
Version filling the role of the Document, thus licensing distribution
and modification of the Modified Version to whoever possesses a copy
of it. In addition, you must do these things in the Modified Version:
@enumerate A
@item
Use in the Title Page (and on the covers, if any) a title distinct
from that of the Document, and from those of previous versions
(which should, if there were any, be listed in the History section
of the Document). You may use the same title as a previous version
if the original publisher of that version gives permission.
@item
List on the Title Page, as authors, one or more persons or entities
responsible for authorship of the modifications in the Modified
Version, together with at least five of the principal authors of the
Document (all of its principal authors, if it has fewer than five),
unless they release you from this requirement.
@item
State on the Title page the name of the publisher of the
Modified Version, as the publisher.
@item
Preserve all the copyright notices of the Document.
@item
Add an appropriate copyright notice for your modifications
adjacent to the other copyright notices.
@item
Include, immediately after the copyright notices, a license notice
giving the public permission to use the Modified Version under the
terms of this License, in the form shown in the Addendum below.
@item
Preserve in that license notice the full lists of Invariant Sections
and required Cover Texts given in the Document's license notice.
@item
Include an unaltered copy of this License.
@item
Preserve the section Entitled ``History'', Preserve its Title, and add
to it an item stating at least the title, year, new authors, and
publisher of the Modified Version as given on the Title Page. If
there is no section Entitled ``History'' in the Document, create one
stating the title, year, authors, and publisher of the Document as
given on its Title Page, then add an item describing the Modified
Version as stated in the previous sentence.
@item
Preserve the network location, if any, given in the Document for
public access to a Transparent copy of the Document, and likewise
the network locations given in the Document for previous versions
it was based on. These may be placed in the ``History'' section.
You may omit a network location for a work that was published at
least four years before the Document itself, or if the original
publisher of the version it refers to gives permission.
@item
For any section Entitled ``Acknowledgements'' or ``Dedications'', Preserve
the Title of the section, and preserve in the section all the
substance and tone of each of the contributor acknowledgements and/or
dedications given therein.
@item
Preserve all the Invariant Sections of the Document,
unaltered in their text and in their titles. Section numbers
or the equivalent are not considered part of the section titles.
@item
Delete any section Entitled ``Endorsements''. Such a section
may not be included in the Modified Version.
@item
Do not retitle any existing section to be Entitled ``Endorsements'' or
to conflict in title with any Invariant Section.
@item
Preserve any Warranty Disclaimers.
@end enumerate
If the Modified Version includes new front-matter sections or
appendices that qualify as Secondary Sections and contain no material
copied from the Document, you may at your option designate some or all
of these sections as invariant. To do this, add their titles to the
list of Invariant Sections in the Modified Version's license notice.
These titles must be distinct from any other section titles.
You may add a section Entitled ``Endorsements'', provided it contains
nothing but endorsements of your Modified Version by various
parties---for example, statements of peer review or that the text has
been approved by an organization as the authoritative definition of a
standard.
You may add a passage of up to five words as a Front-Cover Text, and a
passage of up to 25 words as a Back-Cover Text, to the end of the list
of Cover Texts in the Modified Version. Only one passage of
Front-Cover Text and one of Back-Cover Text may be added by (or
through arrangements made by) any one entity. If the Document already
includes a cover text for the same cover, previously added by you or
by arrangement made by the same entity you are acting on behalf of,
you may not add another; but you may replace the old one, on explicit
permission from the previous publisher that added the old one.
The author(s) and publisher(s) of the Document do not by this License
give permission to use their names for publicity for or to assert or
imply endorsement of any Modified Version.
@item
COMBINING DOCUMENTS
You may combine the Document with other documents released under this
License, under the terms defined in section 4 above for modified
versions, provided that you include in the combination all of the
Invariant Sections of all of the original documents, unmodified, and
list them all as Invariant Sections of your combined work in its
license notice, and that you preserve all their Warranty Disclaimers.
The combined work need only contain one copy of this License, and
multiple identical Invariant Sections may be replaced with a single
copy. If there are multiple Invariant Sections with the same name but
different contents, make the title of each such section unique by
adding at the end of it, in parentheses, the name of the original
author or publisher of that section if known, or else a unique number.
Make the same adjustment to the section titles in the list of
Invariant Sections in the license notice of the combined work.
In the combination, you must combine any sections Entitled ``History''
in the various original documents, forming one section Entitled
``History''; likewise combine any sections Entitled ``Acknowledgements'',
and any sections Entitled ``Dedications''. You must delete all
sections Entitled ``Endorsements.''
@item
COLLECTIONS OF DOCUMENTS
You may make a collection consisting of the Document and other documents
released under this License, and replace the individual copies of this
License in the various documents with a single copy that is included in
the collection, provided that you follow the rules of this License for
verbatim copying of each of the documents in all other respects.
You may extract a single document from such a collection, and distribute
it individually under this License, provided you insert a copy of this
License into the extracted document, and follow this License in all
other respects regarding verbatim copying of that document.
@item
AGGREGATION WITH INDEPENDENT WORKS
A compilation of the Document or its derivatives with other separate
and independent documents or works, in or on a volume of a storage or
distribution medium, is called an ``aggregate'' if the copyright
resulting from the compilation is not used to limit the legal rights
of the compilation's users beyond what the individual works permit.
When the Document is included in an aggregate, this License does not
apply to the other works in the aggregate which are not themselves
derivative works of the Document.
If the Cover Text requirement of section 3 is applicable to these
copies of the Document, then if the Document is less than one half of
the entire aggregate, the Document's Cover Texts may be placed on
covers that bracket the Document within the aggregate, or the
electronic equivalent of covers if the Document is in electronic form.
Otherwise they must appear on printed covers that bracket the whole
aggregate.
@item
TRANSLATION
Translation is considered a kind of modification, so you may
distribute translations of the Document under the terms of section 4.
Replacing Invariant Sections with translations requires special
permission from their copyright holders, but you may include
translations of some or all Invariant Sections in addition to the
original versions of these Invariant Sections. You may include a
translation of this License, and all the license notices in the
Document, and any Warranty Disclaimers, provided that you also include
the original English version of this License and the original versions
of those notices and disclaimers. In case of a disagreement between
the translation and the original version of this License or a notice
or disclaimer, the original version will prevail.
If a section in the Document is Entitled ``Acknowledgements'',
``Dedications'', or ``History'', the requirement (section 4) to Preserve
its Title (section 1) will typically require changing the actual
title.
@item
TERMINATION
You may not copy, modify, sublicense, or distribute the Document except
as expressly provided for under this License. Any other attempt to
copy, modify, sublicense or distribute the Document is void, and will
automatically terminate your rights under this License. However,
parties who have received copies, or rights, from you under this
License will not have their licenses terminated so long as such
parties remain in full compliance.
@item
FUTURE REVISIONS OF THIS LICENSE
The Free Software Foundation may publish new, revised versions
of the GNU Free Documentation License from time to time. Such new
versions will be similar in spirit to the present version, but may
differ in detail to address new problems or concerns. See
@uref{http://www.gnu.org/copyleft/}.
Each version of the License is given a distinguishing version number.
If the Document specifies that a particular numbered version of this
License ``or any later version'' applies to it, you have the option of
following the terms and conditions either of that specified version or
of any later version that has been published (not as a draft) by the
Free Software Foundation. If the Document does not specify a version
number of this License, you may choose any version ever published (not
as a draft) by the Free Software Foundation.
@end enumerate