diff options
| author | Dan Nicolaescu | 2008-03-30 15:44:34 +0000 |
|---|---|---|
| committer | Dan Nicolaescu | 2008-03-30 15:44:34 +0000 |
| commit | 236b5827555913694962172b56a3fa91c3b2de6b (patch) | |
| tree | 3552d15c5edd75c659f5addb01c245f00ac43469 | |
| parent | 58f10bffac5fa90a35685a06f284f5a38d279987 (diff) | |
| download | emacs-236b5827555913694962172b56a3fa91c3b2de6b.tar.gz emacs-236b5827555913694962172b56a3fa91c3b2de6b.zip | |
* vc-git.el: Make vc-status display information about copies,
renames and permission changes.
(vc-git-extra-fileinfo): New defstruct.
(vc-git-escape-file-name, vc-git-file-type-as-string)
(vc-git-rename-as-string, vc-git-permissions-as-string)
(vc-git-status-printer): New functions.
(vc-git-after-dir-status-stage2): Also return vc-git-extra-fileinfo.
(vc-git-after-dir-status-stage1): Look for copies, renames and
permission changes.
(vc-git-after-dir-status-stage1-empty-db): Set permissions.
(vc-git-dir-status): Ask for staged files and renames.
| -rw-r--r-- | lisp/ChangeLog | 14 | ||||
| -rw-r--r-- | lisp/vc-git.el | 130 |
2 files changed, 135 insertions, 9 deletions
diff --git a/lisp/ChangeLog b/lisp/ChangeLog index e6bfdbac49a..fa92b9c04d1 100644 --- a/lisp/ChangeLog +++ b/lisp/ChangeLog | |||
| @@ -1,3 +1,17 @@ | |||
| 1 | 2008-03-30 Alexandre Julliard <julliard@winehq.org> | ||
| 2 | |||
| 3 | * vc-git.el: Make vc-status display information about copies, | ||
| 4 | renames and permission changes. | ||
| 5 | (vc-git-extra-fileinfo): New defstruct. | ||
| 6 | (vc-git-escape-file-name, vc-git-file-type-as-string) | ||
| 7 | (vc-git-rename-as-string, vc-git-permissions-as-string) | ||
| 8 | (vc-git-status-printer): New functions. | ||
| 9 | (vc-git-after-dir-status-stage2): Also return vc-git-extra-fileinfo. | ||
| 10 | (vc-git-after-dir-status-stage1): Look for copies, renames and | ||
| 11 | permission changes. | ||
| 12 | (vc-git-after-dir-status-stage1-empty-db): Set permissions. | ||
| 13 | (vc-git-dir-status): Ask for staged files and renames. | ||
| 14 | |||
| 1 | 2008-03-30 Dan Nicolaescu <dann@ics.uci.edu> | 15 | 2008-03-30 Dan Nicolaescu <dann@ics.uci.edu> |
| 2 | 16 | ||
| 3 | * vc.el: Allow backends to display backend specific information in | 17 | * vc.el: Allow backends to display backend specific information in |
diff --git a/lisp/vc-git.el b/lisp/vc-git.el index 795f57c245d..f3765aaba6f 100644 --- a/lisp/vc-git.el +++ b/lisp/vc-git.el | |||
| @@ -208,23 +208,133 @@ | |||
| 208 | (propertize def-ml | 208 | (propertize def-ml |
| 209 | 'help-echo (concat help-echo "\nCurrent branch: " branch))))) | 209 | 'help-echo (concat help-echo "\nCurrent branch: " branch))))) |
| 210 | 210 | ||
| 211 | (defstruct (vc-git-extra-fileinfo | ||
| 212 | (:copier nil) | ||
| 213 | (:constructor vc-git-create-extra-fileinfo (old-perm new-perm &optional rename-state orig-name)) | ||
| 214 | (:conc-name vc-git-extra-fileinfo->)) | ||
| 215 | old-perm new-perm ;; permission flags | ||
| 216 | rename-state ;; rename or copy state | ||
| 217 | orig-name) ;; original name for renames or copies | ||
| 218 | |||
| 219 | (defun vc-git-escape-file-name (name) | ||
| 220 | "Escape a file name if necessary." | ||
| 221 | (if (string-match "[\n\t\"\\]" name) | ||
| 222 | (concat "\"" | ||
| 223 | (mapconcat (lambda (c) | ||
| 224 | (case c | ||
| 225 | (?\n "\\n") | ||
| 226 | (?\t "\\t") | ||
| 227 | (?\\ "\\\\") | ||
| 228 | (?\" "\\\"") | ||
| 229 | (t (char-to-string c)))) | ||
| 230 | name "") | ||
| 231 | "\"") | ||
| 232 | name)) | ||
| 233 | |||
| 234 | (defun vc-git-file-type-as-string (old-perm new-perm) | ||
| 235 | "Return a string describing the file type based on its permissions." | ||
| 236 | (let* ((old-type (lsh (or old-perm 0) -9)) | ||
| 237 | (new-type (lsh (or new-perm 0) -9)) | ||
| 238 | (str (case new-type | ||
| 239 | (?\100 ;; file | ||
| 240 | (case old-type | ||
| 241 | (?\100 nil) | ||
| 242 | (?\120 " (type change symlink -> file)") | ||
| 243 | (?\160 " (type change subproject -> file)"))) | ||
| 244 | (?\120 ;; symlink | ||
| 245 | (case old-type | ||
| 246 | (?\100 " (type change file -> symlink)") | ||
| 247 | (?\160 " (type change subproject -> symlink)") | ||
| 248 | (t " (symlink)"))) | ||
| 249 | (?\160 ;; subproject | ||
| 250 | (case old-type | ||
| 251 | (?\100 " (type change file -> subproject)") | ||
| 252 | (?\120 " (type change symlink -> subproject)") | ||
| 253 | (t " (subproject)"))) | ||
| 254 | (?\110 nil) ;; directory (internal, not a real git state) | ||
| 255 | (?\000 ;; deleted or unknown | ||
| 256 | (case old-type | ||
| 257 | (?\120 " (symlink)") | ||
| 258 | (?\160 " (subproject)"))) | ||
| 259 | (t (format " (unknown type %o)" new-type))))) | ||
| 260 | (cond (str (propertize str 'face 'font-lock-comment-face)) | ||
| 261 | ((eq new-type ?\110) "/") | ||
| 262 | (t "")))) | ||
| 263 | |||
| 264 | (defun vc-git-rename-as-string (state extra) | ||
| 265 | "Return a string describing the copy or rename associated with INFO, or an empty string if none." | ||
| 266 | (let ((rename-state (when extra | ||
| 267 | (vc-git-extra-fileinfo->rename-state extra)))) | ||
| 268 | (if rename-state | ||
| 269 | (propertize | ||
| 270 | (concat " (" | ||
| 271 | (if (eq rename-state 'copy) "copied from " | ||
| 272 | (if (eq state 'added) "renamed from " | ||
| 273 | "renamed to ")) | ||
| 274 | (vc-git-escape-file-name (vc-git-extra-fileinfo->orig-name extra)) | ||
| 275 | ")") 'face 'font-lock-comment-face) | ||
| 276 | ""))) | ||
| 277 | |||
| 278 | (defun vc-git-permissions-as-string (old-perm new-perm) | ||
| 279 | "Format a permission change as string." | ||
| 280 | (propertize | ||
| 281 | (if (or (not old-perm) | ||
| 282 | (not new-perm) | ||
| 283 | (eq 0 (logand ?\111 (logxor old-perm new-perm)))) | ||
| 284 | " " | ||
| 285 | (if (eq 0 (logand ?\111 old-perm)) "+x" "-x")) | ||
| 286 | 'face 'font-lock-type-face)) | ||
| 287 | |||
| 288 | (defun vc-git-status-printer (info) | ||
| 289 | "Pretty-printer for the vc-status-fileinfo structure." | ||
| 290 | (let* ((state (vc-status-fileinfo->state info)) | ||
| 291 | (extra (vc-status-fileinfo->extra info)) | ||
| 292 | (old-perm (when extra (vc-git-extra-fileinfo->old-perm extra))) | ||
| 293 | (new-perm (when extra (vc-git-extra-fileinfo->new-perm extra)))) | ||
| 294 | (insert | ||
| 295 | " " | ||
| 296 | (propertize (format "%c" (if (vc-status-fileinfo->marked info) ?* ? )) | ||
| 297 | 'face 'font-lock-type-face) | ||
| 298 | " " | ||
| 299 | (propertize | ||
| 300 | (format "%-12s" state) | ||
| 301 | 'face (cond ((eq state 'up-to-date) 'font-lock-builtin-face) | ||
| 302 | ((eq state 'missing) 'font-lock-warning-face) | ||
| 303 | (t 'font-lock-variable-name-face)) | ||
| 304 | 'mouse-face 'highlight) | ||
| 305 | " " (vc-git-permissions-as-string old-perm new-perm) | ||
| 306 | " " | ||
| 307 | (propertize (vc-git-escape-file-name (vc-status-fileinfo->name info)) | ||
| 308 | 'face 'font-lock-function-name-face | ||
| 309 | 'mouse-face 'highlight) | ||
| 310 | (vc-git-file-type-as-string old-perm new-perm) | ||
| 311 | (vc-git-rename-as-string state extra)))) | ||
| 312 | |||
| 211 | ;; Variable used to keep the intermediate results for vc-git-status. | 313 | ;; Variable used to keep the intermediate results for vc-git-status. |
| 212 | (defvar vc-git-status-result nil) | 314 | (defvar vc-git-status-result nil) |
| 213 | 315 | ||
| 214 | (defun vc-git-after-dir-status-stage2 (update-function status-buffer) | 316 | (defun vc-git-after-dir-status-stage2 (update-function status-buffer) |
| 215 | (goto-char (point-min)) | 317 | (goto-char (point-min)) |
| 216 | (while (re-search-forward "\\([^\0]*?\\)\0" nil t 1) | 318 | (while (re-search-forward "\\([^\0]*?\\)\0" nil t 1) |
| 217 | (push (cons (match-string 1) 'unregistered) vc-git-status-result)) | 319 | (push (list (match-string 1) 'unregistered (vc-git-create-extra-fileinfo 0 0)) vc-git-status-result)) |
| 218 | (funcall update-function (nreverse vc-git-status-result) status-buffer)) | 320 | (funcall update-function (nreverse vc-git-status-result) status-buffer)) |
| 219 | 321 | ||
| 220 | (defun vc-git-after-dir-status-stage1 (update-function status-buffer) | 322 | (defun vc-git-after-dir-status-stage1 (update-function status-buffer) |
| 221 | (goto-char (point-min)) | 323 | (goto-char (point-min)) |
| 222 | (while (re-search-forward | 324 | (while (re-search-forward |
| 223 | ":[0-7]\\{6\\} [0-7]\\{6\\} [0-9a-f]\\{40\\} [0-9a-f]\\{40\\} \\([ADMUT]\\)\0\\([^\0]+\\)\0" | 325 | ":\\([0-7]\\{6\\}\\) \\([0-7]\\{6\\}\\) [0-9a-f]\\{40\\} [0-9a-f]\\{40\\} \\(\\([ADMUT]\\)\0\\([^\0]+\\)\\|\\([CR]\\)[0-9]*\0\\([^\0]+\\)\0\\([^\0]+\\)\\)\0" |
| 224 | nil t 1) | 326 | nil t 1) |
| 225 | (let ((filename (match-string 2)) | 327 | (let ((old-perm (string-to-number (match-string 1) 8)) |
| 226 | (status (vc-git--state-code (match-string 1)))) | 328 | (new-perm (string-to-number (match-string 2) 8)) |
| 227 | (push (cons filename status) vc-git-status-result))) | 329 | (state (or (match-string 4) (match-string 6))) |
| 330 | (name (or (match-string 5) (match-string 7))) | ||
| 331 | (new-name (match-string 8))) | ||
| 332 | (if new-name ; copy or rename | ||
| 333 | (if (eq ?C (string-to-char state)) | ||
| 334 | (push (list new-name 'added (vc-git-create-extra-fileinfo old-perm new-perm 'copy name)) vc-git-status-result) | ||
| 335 | (push (list name 'removed (vc-git-create-extra-fileinfo 0 0 'rename new-name)) vc-git-status-result) | ||
| 336 | (push (list new-name 'added (vc-git-create-extra-fileinfo old-perm new-perm 'rename name)) vc-git-status-result)) | ||
| 337 | (push (list name (vc-git--state-code state) (vc-git-create-extra-fileinfo old-perm new-perm)) vc-git-status-result)))) | ||
| 228 | (erase-buffer) | 338 | (erase-buffer) |
| 229 | (vc-git-command (current-buffer) 'async nil "ls-files" "-z" "-o" | 339 | (vc-git-command (current-buffer) 'async nil "ls-files" "-z" "-o" |
| 230 | "--directory" "--no-empty-directory" "--exclude-standard") | 340 | "--directory" "--no-empty-directory" "--exclude-standard") |
| @@ -233,8 +343,10 @@ | |||
| 233 | 343 | ||
| 234 | (defun vc-git-after-dir-status-stage1-empty-db (update-function status-buffer) | 344 | (defun vc-git-after-dir-status-stage1-empty-db (update-function status-buffer) |
| 235 | (goto-char (point-min)) | 345 | (goto-char (point-min)) |
| 236 | (while (re-search-forward "\\([^\0]*?\\)\0" nil t 1) | 346 | (while (re-search-forward "\\([0-7]\\{6\\}\\) [0-9a-f]\\{40\\} 0\t\\([^\0]+\\)\0" nil t) |
| 237 | (push (cons (match-string 1) 'added) vc-git-status-result)) | 347 | (let ((new-perm (string-to-number (match-string 1) 8)) |
| 348 | (name (match-string 2))) | ||
| 349 | (push (list name 'added (vc-git-create-extra-fileinfo 0 new-perm)) vc-git-status-result))) | ||
| 238 | (erase-buffer) | 350 | (erase-buffer) |
| 239 | (vc-git-command (current-buffer) 'async nil "ls-files" "-z" "-o" | 351 | (vc-git-command (current-buffer) 'async nil "ls-files" "-z" "-o" |
| 240 | "--directory" "--no-empty-directory" "--exclude-standard") | 352 | "--directory" "--no-empty-directory" "--exclude-standard") |
| @@ -249,11 +361,11 @@ | |||
| 249 | (set (make-local-variable 'vc-git-status-result) nil) | 361 | (set (make-local-variable 'vc-git-status-result) nil) |
| 250 | (if (vc-git--empty-db-p) | 362 | (if (vc-git--empty-db-p) |
| 251 | (progn | 363 | (progn |
| 252 | (vc-git-command (current-buffer) 'async nil "ls-files" "-z" "-c") | 364 | (vc-git-command (current-buffer) 'async nil "ls-files" "-z" "-c" "-s") |
| 253 | (vc-exec-after | 365 | (vc-exec-after |
| 254 | `(vc-git-after-dir-status-stage1-empty-db | 366 | `(vc-git-after-dir-status-stage1-empty-db |
| 255 | (quote ,update-function) ,status-buffer))) | 367 | (quote ,update-function) ,status-buffer))) |
| 256 | (vc-git-command (current-buffer) 'async nil "diff-index" "-z" "HEAD") | 368 | (vc-git-command (current-buffer) 'async nil "diff-index" "-z" "-M" "HEAD") |
| 257 | (vc-exec-after | 369 | (vc-exec-after |
| 258 | `(vc-git-after-dir-status-stage1 (quote ,update-function) ,status-buffer)))) | 370 | `(vc-git-after-dir-status-stage1 (quote ,update-function) ,status-buffer)))) |
| 259 | 371 | ||