+(defun vc-clear-headers ()
+ ;; Clear all version headers in the current buffer, i.e. reset them
+ ;; to the nonexpanded form. Only implemented for RCS, yet.
+ ;; Don't lose point and mark during this.
+ (let ((context (vc-buffer-context))
+ (case-fold-search nil))
+ ;; save-excursion may be able to relocate point and mark properly.
+ ;; If it fails, vc-restore-buffer-context will give it a second try.
+ (save-excursion
+ (goto-char (point-min))
+ (while (re-search-forward
+ (concat "\\$\\(Author\\|Date\\|Header\\|Id\\|Locker\\|Name\\|"
+ "RCSfile\\|Revision\\|Source\\|State\\): [^$\n]+\\$")
+ nil t)
+ (replace-match "$\\1$")))
+ (vc-restore-buffer-context context)))
+
+;;;###autoload
+(defun vc-merge ()
+ (interactive)
+ (vc-ensure-vc-buffer)
+ (vc-buffer-sync)
+ (let* ((file buffer-file-name)
+ (backend (vc-backend file))
+ first-version second-version locking-user)
+ (if (eq backend 'SCCS)
+ (error "Sorry, merging is not implemented for SCCS")
+ (setq locking-user (vc-locking-user file))
+ (if (eq (vc-checkout-model file) 'manual)
+ (if (not locking-user)
+ (if (not (y-or-n-p
+ (format "File must be %s for merging. %s now? "
+ (if (eq backend 'RCS) "locked" "writable")
+ (if (eq backend 'RCS) "Lock" "Check out"))))
+ (error "Merge aborted")
+ (vc-checkout file t))
+ (if (not (string= locking-user (vc-user-login-name)))
+ (error "File is locked by %s" locking-user))))
+ (setq first-version (read-string "Branch or version to merge from: "))
+ (if (and (>= (elt first-version 0) ?0)
+ (<= (elt first-version 0) ?9))
+ (if (not (vc-branch-p first-version))
+ (setq second-version
+ (read-string "Second version: "
+ (concat (vc-branch-part first-version) ".")))
+ ;; We want to merge an entire branch. Set versions
+ ;; accordingly, so that vc-backend-merge understands us.
+ (setq second-version first-version)
+ ;; first-version must be the starting point of the branch
+ (setq first-version (vc-branch-part first-version))))
+ (let ((status (vc-backend-merge file first-version second-version)))
+ (if (and (eq (vc-checkout-model file) 'implicit)
+ (not (vc-locking-user file)))
+ (vc-file-setprop file 'vc-locking-user nil))
+ (vc-resynch-buffer file t t)
+ (if (not (zerop status))
+ (if (y-or-n-p "Conflicts detected. Resolve them now? ")
+ (vc-resolve-conflicts "WORKFILE" "MERGE SOURCE")
+ (message "File contains conflict markers"))
+ (message "Merge successful"))))))
+
+;;;###autoload
+(defun vc-resolve-conflicts (&optional name-A name-B)
+ "Invoke ediff to resolve conflicts in the current buffer.
+The conflicts must be marked with rcsmerge conflict markers."
+ (interactive)
+ (vc-ensure-vc-buffer)
+ (let* ((found nil)
+ (file-name (file-name-nondirectory buffer-file-name))
+ (your-buffer (generate-new-buffer
+ (concat "*" file-name
+ " " (or name-A "WORKFILE") "*")))
+ (other-buffer (generate-new-buffer
+ (concat "*" file-name
+ " " (or name-B "CHECKED-IN") "*")))
+ (result-buffer (current-buffer)))
+ (save-excursion
+ (set-buffer your-buffer)
+ (erase-buffer)
+ (insert-buffer result-buffer)
+ (goto-char (point-min))
+ (while (re-search-forward (concat "^<<<<<<< "
+ (regexp-quote file-name) "\n") nil t)
+ (setq found t)
+ (replace-match "")
+ (if (not (re-search-forward "^=======\n" nil t))
+ (error "Malformed conflict marker"))
+ (replace-match "")
+ (let ((start (point)))
+ (if (not (re-search-forward "^>>>>>>> [0-9.]+\n" nil t))
+ (error "Malformed conflict marker"))
+ (delete-region start (point))))
+ (if (not found)
+ (progn
+ (kill-buffer your-buffer)
+ (kill-buffer other-buffer)
+ (error "No conflict markers found")))
+ (set-buffer other-buffer)
+ (erase-buffer)
+ (insert-buffer result-buffer)
+ (goto-char (point-min))
+ (while (re-search-forward (concat "^<<<<<<< "
+ (regexp-quote file-name) "\n") nil t)
+ (let ((start (match-beginning 0)))
+ (if (not (re-search-forward "^=======\n" nil t))
+ (error "Malformed conflict marker"))
+ (delete-region start (point))
+ (if (not (re-search-forward "^>>>>>>> [0-9.]+\n" nil t))
+ (error "Malformed conflict marker"))
+ (replace-match "")))
+ (let ((config (current-window-configuration))
+ (ediff-default-variant 'default-B))
+
+ ;; Fire up ediff.
+
+ (set-buffer (ediff-merge-buffers your-buffer other-buffer))
+
+ ;; Ediff is now set up, and we are in the control buffer.
+ ;; Do a few further adjustments and take precautions for exit.
+
+ (make-local-variable 'vc-ediff-windows)
+ (setq vc-ediff-windows config)
+ (make-local-variable 'vc-ediff-result)
+ (setq vc-ediff-result result-buffer)
+ (make-local-variable 'ediff-quit-hook)
+ (setq ediff-quit-hook
+ (function
+ (lambda ()
+ (let ((buffer-A ediff-buffer-A)
+ (buffer-B ediff-buffer-B)
+ (buffer-C ediff-buffer-C)
+ (result vc-ediff-result)
+ (windows vc-ediff-windows))
+ (ediff-cleanup-mess)
+ (set-buffer result)
+ (erase-buffer)
+ (insert-buffer buffer-C)
+ (kill-buffer buffer-A)
+ (kill-buffer buffer-B)
+ (kill-buffer buffer-C)
+ (set-window-configuration windows)
+ (message "Conflict resolution finished; you may save the buffer")))))
+ (message "Please resolve conflicts now; exit ediff when done")
+ nil))))
+
+;; The VC directory major mode. Coopt Dired for this.