changeset 280: |
d398c7d4433d |
parent: |
1169b432cf8e
|
child: |
1c6e8353a855 |
author: |
Richard Westhaver <ellis@rwest.io> |
date: |
Tue, 16 Apr 2024 15:31:40 -0400 |
permissions: |
-rw-r--r-- |
description: |
cleanup |
3 ;; Mercurial is our primary VCS - but we blur the lines by mirroring 4 ;; our code to Git. In a few years mirroring will probably be 5 ;; unnecessary but it's a really useful hack FTTB. 7 ;; Our forge is based on an instance of Heptapod https://heptapod.net/ 8 ;; which is a GitLab fork. Most of the public repos are Mercurial, but 9 ;; there are a few 'reverse-mirrors' which I maintain exclusively as 10 ;; Git repos. Same goes for any fork I maintain - for example, Lust is 11 ;; my fork of rustlang/rust and is just a Git repository. 13 ;; HACK 2023-09-15: hgcmd interface, parsers, metadata object protocols 15 ;; https://wiki.mercurial-scm.org/Design 17 ;; https://wiki.mercurial-scm.org/CommandServer 19 ;; the cmdserver is the obvious solution for Skel to interact with 20 ;; Mercurial so we'll be leaning into it right away without bothering 21 ;; with the standard CLI. I'm unfamiliar with how this is done with 22 ;; Git, or if it's done at all. In Mercurial's case it seems they 23 ;; built it out of licensing issues and to avoid Python cold-start 24 ;; penalty which aren't issues for me ATM anyway. Git is written in C 25 ;; so doesn't suffer a cold-start hit, but it would be nice to 26 ;; interact with repos via a similar lightweight, local, wire 29 ;; BTW It was hard to find the command to start the command server - 30 ;; it's 'hg serve'. Here's the base shell command invoked by chg: 32 ;; hg serve --no-profile --cmdserver chgunix --address @INITSOCKNAME --daemon-postexec chdir:/ @DIR 37 (defvar *default-hg-client-buffer-size* 4096) 38 (defvar *hg-program* (or (find-exe "rhg") (find-exe "hg"))) 40 (defun run-hg-command (cmd &rest args) 41 (sb-ext:run-program *hg-program* (push cmd args) :output :stream)) 44 "Return nil if URL does not look like a URL to a hg valid remote." 45 (let ((url-str (if (typep url 'pathname) 51 (:regex "^https://hg\\.") 55 ;; (describe (make-instance 'hg-repo)) 56 ;; https://repo.mercurial-scm.org/hg/file/tip/mercurial/interfaces/repository.py 57 (defclass hg-repo (vc-repo) 58 ((dirstate) ;; working-directory 62 (defmethod vc-run ((self hg-repo) (cmd string) &rest args) 63 (with-slots (path) self 64 (with-current-directory (path) 65 (with-open-stream (s (sb-ext:process-output (apply #'run-hg-command cmd args))) 66 (with-output-to-string (str) 67 (loop for l = (read-line s nil nil) 69 do (write-line l))))))) 71 (defmethod vc-init ((self (eql :hg))) 72 (make-instance 'hg-repo)) 74 (defmethod vc-init ((self hg-repo)) 75 (with-slots (path) self 76 ;; could throw error here but w/e 77 (sb-ext:process-exit-code (run-hg-command "init" path)))) 79 (defmethod vc-clone ((self hg-repo) remote &key &allow-other-keys) 80 (with-slots (path) self 81 (sb-ext:process-exit-code (run-hg-command "clone" remote path)))) 83 (defmethod vc-pull ((self hg-repo) remote &key &allow-other-keys) 84 (with-slots (path) self 85 (with-current-directory (path) 86 (sb-ext:process-exit-code (run-hg-command "pull" remote))))) 88 (defmethod vc-push ((self hg-repo) remote &key &allow-other-keys) 89 (with-slots (path) self 90 (with-current-directory (path) 91 (sb-ext:process-exit-code (run-hg-command "push" remote))))) 93 (defmethod vc-commit ((self hg-repo) msg &key &allow-other-keys) 94 (with-slots (path) self 95 (with-current-directory (path) 96 (sb-ext:process-exit-code (run-hg-command "commit" "-m" msg))))) 98 (defmethod vc-add ((self hg-repo) &rest files) 99 (with-slots (path) self 100 (with-current-directory (path) 101 (sb-ext:process-exit-code (apply #'run-hg-command "add" files))))) 103 (defmethod vc-remove ((self hg-repo) &rest files) 104 (with-slots (path) self 105 (with-current-directory (path) 106 (sb-ext:process-exit-code (apply #'run-hg-command "remove" files))))) 108 (defmethod vc-addremove ((self hg-repo) &rest files) 109 (with-slots (path) self 110 (with-current-directory (path) 111 (sb-ext:process-exit-code (apply #'run-hg-command "addremove" files))))) 113 (defmethod vc-status ((self hg-repo) &key &allow-other-keys) (vc-run self "status")) 115 (defmethod vc-branch ((self hg-repo) &key cmd branch &allow-other-keys) (vc-run self "branch" cmd branch)) 117 (defmethod vc-diff ((a hg-repo) (b hg-repo) &key &allow-other-keys) 118 (vc-run a "diff" (vc-repo-head a) (vc-repo-head b))) 120 (defmethod vc-id ((self hg-repo)) 121 (with-slots (path) self 122 (with-current-directory (path) 123 (with-open-stream (s (sb-ext:process-output (run-hg-command "id"))) 124 (with-output-to-string (str) 125 (loop for c = (read-char s nil nil) 127 do (write-char c str)) 131 ;; ref: https://wiki.mercurial-scm.org/CommandServer 132 (declaim (inline %make-hg-client)) 133 (defstruct (hg-client (:constructor %make-hg-client)) 134 "hg-client structures contain the client connection state 135 machinery and a handle to the unix socket running Mercurial command 137 (pid 0 :type fixnum :read-only t) 138 (pgid 0 :type fixnum) 139 (cwd (sb-posix:getcwd) :type string) 140 (buffer (make-array *default-hg-client-buffer-size* :element-type 'unsigned-byte :adjustable nil)) 141 (socket nil :type (or local-socket null)) 142 (caps 0 :type fixnum)) 144 (defun make-hg-client (&optional bufsize) 146 :buffer (make-array (or bufsize *default-hg-client-buffer-size*) 147 :element-type 'unsigned-byte 151 ;; all communication with the mercurial cmdserver is done over a 152 ;; socket. byte order is big-endian. 154 ;; data from server is channel-based - (channel length pair sent 155 ;; before data) - 5 byte header total 157 ;; on init, the server will send hello message on channel #\o. the 158 ;; message is a signel chunk consisting of a #\Newline-separated list 159 ;; of lines of the form: 161 <field name>: <field data> 164 ;; fields include: capabilities, encoding, pid 172 (defmethod vc-init ((self hg-client)) 173 "Initialize the hg commandserver client. This method initializes the 174 appropriate process IDs and a socket for communicating with the 176 (with-slots (pid pgid socket caps) self 177 (format nil "pid: ~A, pgid: ~A, socket: ~A, caps: ~A" pid pgid socket caps))) 180 (defmethod vc-run ((self hg-client) cmd &rest args) 181 (declare (ignorable args))) 184 (defstruct hg-nodeid id) 186 (defstruct hg-revlog) 188 (defstruct hg-manifest) 190 (defstruct hg-changeset id) 194 ;; see also: https://wiki.mercurial-scm.org/DirstateFormatImprovementsPlan 198 <p1 binhash><p2 binhash> 199 <list of dirstate entries> 208 variable length entry (length given by the previous length field) with: 209 "<filename>" followed if it's a copy by: "\0<source if copy>" 212 (defstruct dirstate-entry status mode size mtime length filename) 214 ;; (defmethod read-dirstate-file ((self hg-repo))) 217 (entries (make-array 0 :element-type 'dirstate-entry :fill-pointer 0 :adjustable t) :type (vector dirstate-entry)))