diff options
| author | Tom Tromey | 2017-02-13 18:09:36 -0700 |
|---|---|---|
| committer | Tom Tromey | 2017-02-14 14:07:53 -0700 |
| commit | 3fb9f5452fbd0458f90115b0a95151b8e7a482a1 (patch) | |
| tree | 0821ca2fdd6fe0a65b36a35eb2f30a0a4938393b | |
| parent | 71b90192dab8de9825904faaabbaf9548d3db2ab (diff) | |
| download | emacs-3fb9f5452fbd0458f90115b0a95151b8e7a482a1.tar.gz emacs-3fb9f5452fbd0458f90115b0a95151b8e7a482a1.zip | |
Make vc-git detect conflict state for vc-dir
* lisp/vc/vc-git.el (vc-git-dir-status-state): New struct.
(vc-git-dir-status-update-file): New function.
(vc-git-after-dir-status-stage, vc-git-dir-status-goto-stage): Use
vc-git-dir-status-state; add 'ls-files-conflict state.
(vc-git-dir-status-files): Create a vc-git-dir-status-state.
| -rw-r--r-- | lisp/vc/vc-git.el | 179 |
1 files changed, 110 insertions, 69 deletions
diff --git a/lisp/vc/vc-git.el b/lisp/vc/vc-git.el index 24dabb6f9f3..0f58892eb4e 100644 --- a/lisp/vc/vc-git.el +++ b/lisp/vc/vc-git.el | |||
| @@ -401,11 +401,30 @@ or an empty string if none." | |||
| 401 | (vc-git-file-type-as-string old-perm new-perm) | 401 | (vc-git-file-type-as-string old-perm new-perm) |
| 402 | (vc-git-rename-as-string state extra)))) | 402 | (vc-git-rename-as-string state extra)))) |
| 403 | 403 | ||
| 404 | (defun vc-git-after-dir-status-stage (stage files update-function) | 404 | (cl-defstruct (vc-git-dir-status-state |
| 405 | (:copier nil) | ||
| 406 | (:conc-name vc-git-dir-status-state->)) | ||
| 407 | ;; Current stage. | ||
| 408 | stage | ||
| 409 | ;; List of files still to be processed. | ||
| 410 | files | ||
| 411 | ;; Update function to be called at the end. | ||
| 412 | update-function | ||
| 413 | ;; Hash table of entries for files we've computed so far. | ||
| 414 | (hash (make-hash-table :test 'equal))) | ||
| 415 | |||
| 416 | (defsubst vc-git-dir-status-update-file (state filename file-state file-info) | ||
| 417 | (puthash filename (list file-state file-info) | ||
| 418 | (vc-git-dir-status-state->hash state)) | ||
| 419 | (setf (vc-git-dir-status-state->files state) | ||
| 420 | (delete filename (vc-git-dir-status-state->files state)))) | ||
| 421 | |||
| 422 | (defun vc-git-after-dir-status-stage (git-state) | ||
| 405 | "Process sentinel for the various dir-status stages." | 423 | "Process sentinel for the various dir-status stages." |
| 406 | (let (next-stage result) | 424 | (let (next-stage |
| 425 | (files (vc-git-dir-status-state->files git-state))) | ||
| 407 | (goto-char (point-min)) | 426 | (goto-char (point-min)) |
| 408 | (pcase stage | 427 | (pcase (vc-git-dir-status-state->stage git-state) |
| 409 | (`update-index | 428 | (`update-index |
| 410 | (setq next-stage (if (vc-git--empty-db-p) 'ls-files-added 'diff-index))) | 429 | (setq next-stage (if (vc-git--empty-db-p) 'ls-files-added 'diff-index))) |
| 411 | (`ls-files-added | 430 | (`ls-files-added |
| @@ -413,29 +432,40 @@ or an empty string if none." | |||
| 413 | (while (re-search-forward "\\([0-7]\\{6\\}\\) [0-9a-f]\\{40\\} 0\t\\([^\0]+\\)\0" nil t) | 432 | (while (re-search-forward "\\([0-7]\\{6\\}\\) [0-9a-f]\\{40\\} 0\t\\([^\0]+\\)\0" nil t) |
| 414 | (let ((new-perm (string-to-number (match-string 1) 8)) | 433 | (let ((new-perm (string-to-number (match-string 1) 8)) |
| 415 | (name (match-string 2))) | 434 | (name (match-string 2))) |
| 416 | (push (list name 'added (vc-git-create-extra-fileinfo 0 new-perm)) | 435 | (vc-git-dir-status-update-file |
| 417 | result)))) | 436 | git-state name 'added |
| 437 | (vc-git-create-extra-fileinfo 0 new-perm))))) | ||
| 418 | (`ls-files-up-to-date | 438 | (`ls-files-up-to-date |
| 419 | (setq next-stage 'ls-files-unknown) | 439 | (setq next-stage 'ls-files-unknown) |
| 420 | (while (re-search-forward "\\([0-7]\\{6\\}\\) [0-9a-f]\\{40\\} 0\t\\([^\0]+\\)\0" nil t) | 440 | (while (re-search-forward "\\([0-7]\\{6\\}\\) [0-9a-f]\\{40\\} \\([0-3]\\)\t\\([^\0]+\\)\0" nil t) |
| 441 | (let ((perm (string-to-number (match-string 1) 8)) | ||
| 442 | (state (match-string 2)) | ||
| 443 | (name (match-string 3))) | ||
| 444 | (vc-git-dir-status-update-file | ||
| 445 | git-state name (if (equal state "0") | ||
| 446 | 'up-to-date | ||
| 447 | 'conflict) | ||
| 448 | (vc-git-create-extra-fileinfo perm perm))))) | ||
| 449 | (`ls-files-conflict | ||
| 450 | (setq next-stage 'ls-files-unknown) | ||
| 451 | ;; It's enough to look for "3" to notice a conflict. | ||
| 452 | (while (re-search-forward "\\([0-7]\\{6\\}\\) [0-9a-f]\\{40\\} 3\t\\([^\0]+\\)\0" nil t) | ||
| 421 | (let ((perm (string-to-number (match-string 1) 8)) | 453 | (let ((perm (string-to-number (match-string 1) 8)) |
| 422 | (name (match-string 2))) | 454 | (name (match-string 2))) |
| 423 | (push (list name 'up-to-date | 455 | (vc-git-dir-status-update-file |
| 424 | (vc-git-create-extra-fileinfo perm perm)) | 456 | git-state name 'conflict |
| 425 | result)))) | 457 | (vc-git-create-extra-fileinfo perm perm))))) |
| 426 | (`ls-files-unknown | 458 | (`ls-files-unknown |
| 427 | (when files (setq next-stage 'ls-files-ignored)) | 459 | (when files (setq next-stage 'ls-files-ignored)) |
| 428 | (while (re-search-forward "\\([^\0]*?\\)\0" nil t 1) | 460 | (while (re-search-forward "\\([^\0]*?\\)\0" nil t 1) |
| 429 | (push (list (match-string 1) 'unregistered | 461 | (vc-git-dir-status-update-file git-state (match-string 1) 'unregistered |
| 430 | (vc-git-create-extra-fileinfo 0 0)) | 462 | (vc-git-create-extra-fileinfo 0 0)))) |
| 431 | result))) | ||
| 432 | (`ls-files-ignored | 463 | (`ls-files-ignored |
| 433 | (while (re-search-forward "\\([^\0]*?\\)\0" nil t 1) | 464 | (while (re-search-forward "\\([^\0]*?\\)\0" nil t 1) |
| 434 | (push (list (match-string 1) 'ignored | 465 | (vc-git-dir-status-update-file git-state (match-string 1) 'ignored |
| 435 | (vc-git-create-extra-fileinfo 0 0)) | 466 | (vc-git-create-extra-fileinfo 0 0)))) |
| 436 | result))) | ||
| 437 | (`diff-index | 467 | (`diff-index |
| 438 | (setq next-stage (if files 'ls-files-up-to-date 'ls-files-unknown)) | 468 | (setq next-stage (if files 'ls-files-up-to-date 'ls-files-conflict)) |
| 439 | (while (re-search-forward | 469 | (while (re-search-forward |
| 440 | ":\\([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" | 470 | ":\\([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" |
| 441 | nil t 1) | 471 | nil t 1) |
| @@ -446,30 +476,34 @@ or an empty string if none." | |||
| 446 | (new-name (match-string 8))) | 476 | (new-name (match-string 8))) |
| 447 | (if new-name ; Copy or rename. | 477 | (if new-name ; Copy or rename. |
| 448 | (if (eq ?C (string-to-char state)) | 478 | (if (eq ?C (string-to-char state)) |
| 449 | (push (list new-name 'added | 479 | (vc-git-dir-status-update-file |
| 450 | (vc-git-create-extra-fileinfo old-perm new-perm | 480 | git-state new-name 'added |
| 451 | 'copy name)) | 481 | (vc-git-create-extra-fileinfo old-perm new-perm |
| 452 | result) | 482 | 'copy name)) |
| 453 | (push (list name 'removed | 483 | (vc-git-dir-status-update-file |
| 454 | (vc-git-create-extra-fileinfo 0 0 | 484 | git-state name 'removed |
| 455 | 'rename new-name)) | 485 | (vc-git-create-extra-fileinfo 0 0 'rename new-name)) |
| 456 | result) | 486 | (vc-git-dir-status-update-file |
| 457 | (push (list new-name 'added | 487 | git-state new-name 'added |
| 458 | (vc-git-create-extra-fileinfo old-perm new-perm | 488 | (vc-git-create-extra-fileinfo old-perm new-perm |
| 459 | 'rename name)) | 489 | 'rename name))) |
| 460 | result)) | 490 | (vc-git-dir-status-update-file |
| 461 | (push (list name (vc-git--state-code state) | 491 | git-state name (vc-git--state-code state) |
| 462 | (vc-git-create-extra-fileinfo old-perm new-perm)) | 492 | (vc-git-create-extra-fileinfo old-perm new-perm))))))) |
| 463 | result)))))) | 493 | ;; If we had files but now we don't, it's time to stop. |
| 464 | (when result | 494 | (when (and files (not (vc-git-dir-status-state->files git-state))) |
| 465 | (setq result (nreverse result)) | 495 | (setq next-stage nil)) |
| 466 | (when files | 496 | (setf (vc-git-dir-status-state->stage git-state) next-stage) |
| 467 | (dolist (entry result) (setq files (delete (car entry) files))) | 497 | (setf (vc-git-dir-status-state->files git-state) files) |
| 468 | (unless files (setq next-stage nil)))) | 498 | (if next-stage |
| 469 | (when (or result (not next-stage)) | 499 | (vc-git-dir-status-goto-stage git-state) |
| 470 | (funcall update-function result next-stage)) | 500 | (funcall (vc-git-dir-status-state->update-function git-state) |
| 471 | (when next-stage | 501 | (let ((result nil)) |
| 472 | (vc-git-dir-status-goto-stage next-stage files update-function)))) | 502 | (maphash (lambda (key value) |
| 503 | (push (cons key value) result)) | ||
| 504 | (vc-git-dir-status-state->hash git-state)) | ||
| 505 | result) | ||
| 506 | nil)))) | ||
| 473 | 507 | ||
| 474 | ;; Follows vc-git-command (or vc-do-async-command), which uses vc-do-command | 508 | ;; Follows vc-git-command (or vc-do-async-command), which uses vc-do-command |
| 475 | ;; from vc-dispatcher. | 509 | ;; from vc-dispatcher. |
| @@ -477,41 +511,48 @@ or an empty string if none." | |||
| 477 | ;; Follows vc-exec-after. | 511 | ;; Follows vc-exec-after. |
| 478 | (declare-function vc-set-async-update "vc-dispatcher" (process-buffer)) | 512 | (declare-function vc-set-async-update "vc-dispatcher" (process-buffer)) |
| 479 | 513 | ||
| 480 | (defun vc-git-dir-status-goto-stage (stage files update-function) | 514 | (defun vc-git-dir-status-goto-stage (git-state) |
| 481 | (erase-buffer) | 515 | (let ((files (vc-git-dir-status-state->files git-state))) |
| 482 | (pcase stage | 516 | (erase-buffer) |
| 483 | (`update-index | 517 | (pcase (vc-git-dir-status-state->stage git-state) |
| 484 | (if files | 518 | (`update-index |
| 485 | (vc-git-command (current-buffer) 'async files "add" "--refresh" "--") | 519 | (if files |
| 486 | (vc-git-command (current-buffer) 'async nil | 520 | (vc-git-command (current-buffer) 'async files "add" "--refresh" "--") |
| 487 | "update-index" "--refresh"))) | 521 | (vc-git-command (current-buffer) 'async nil |
| 488 | (`ls-files-added | 522 | "update-index" "--refresh"))) |
| 489 | (vc-git-command (current-buffer) 'async files | 523 | (`ls-files-added |
| 490 | "ls-files" "-z" "-c" "-s" "--")) | 524 | (vc-git-command (current-buffer) 'async files |
| 491 | (`ls-files-up-to-date | 525 | "ls-files" "-z" "-c" "-s" "--")) |
| 492 | (vc-git-command (current-buffer) 'async files | 526 | (`ls-files-up-to-date |
| 493 | "ls-files" "-z" "-c" "-s" "--")) | 527 | (vc-git-command (current-buffer) 'async files |
| 494 | (`ls-files-unknown | 528 | "ls-files" "-z" "-c" "-s" "--")) |
| 495 | (vc-git-command (current-buffer) 'async files | 529 | (`ls-files-conflict |
| 496 | "ls-files" "-z" "-o" "--directory" | 530 | (vc-git-command (current-buffer) 'async files |
| 497 | "--no-empty-directory" "--exclude-standard" "--")) | 531 | "ls-files" "-z" "-c" "-s" "--")) |
| 498 | (`ls-files-ignored | 532 | (`ls-files-unknown |
| 499 | (vc-git-command (current-buffer) 'async files | 533 | (vc-git-command (current-buffer) 'async files |
| 500 | "ls-files" "-z" "-o" "-i" "--directory" | 534 | "ls-files" "-z" "-o" "--directory" |
| 501 | "--no-empty-directory" "--exclude-standard" "--")) | 535 | "--no-empty-directory" "--exclude-standard" "--")) |
| 502 | ;; --relative added in Git 1.5.5. | 536 | (`ls-files-ignored |
| 503 | (`diff-index | 537 | (vc-git-command (current-buffer) 'async files |
| 504 | (vc-git-command (current-buffer) 'async files | 538 | "ls-files" "-z" "-o" "-i" "--directory" |
| 505 | "diff-index" "--relative" "-z" "-M" "HEAD" "--"))) | 539 | "--no-empty-directory" "--exclude-standard" "--")) |
| 506 | (vc-run-delayed | 540 | ;; --relative added in Git 1.5.5. |
| 507 | (vc-git-after-dir-status-stage stage files update-function))) | 541 | (`diff-index |
| 542 | (vc-git-command (current-buffer) 'async files | ||
| 543 | "diff-index" "--relative" "-z" "-M" "HEAD" "--"))) | ||
| 544 | (vc-run-delayed | ||
| 545 | (vc-git-after-dir-status-stage git-state)))) | ||
| 508 | 546 | ||
| 509 | (defun vc-git-dir-status-files (_dir files update-function) | 547 | (defun vc-git-dir-status-files (_dir files update-function) |
| 510 | "Return a list of (FILE STATE EXTRA) entries for DIR." | 548 | "Return a list of (FILE STATE EXTRA) entries for DIR." |
| 511 | ;; Further things that would have to be fixed later: | 549 | ;; Further things that would have to be fixed later: |
| 512 | ;; - how to handle unregistered directories | 550 | ;; - how to handle unregistered directories |
| 513 | ;; - how to support vc-dir on a subdir of the project tree | 551 | ;; - how to support vc-dir on a subdir of the project tree |
| 514 | (vc-git-dir-status-goto-stage 'update-index files update-function)) | 552 | (vc-git-dir-status-goto-stage |
| 553 | (make-vc-git-dir-status-state :stage 'update-index | ||
| 554 | :files files | ||
| 555 | :update-function update-function))) | ||
| 515 | 556 | ||
| 516 | (defvar vc-git-stash-map | 557 | (defvar vc-git-stash-map |
| 517 | (let ((map (make-sparse-keymap))) | 558 | (let ((map (make-sparse-keymap))) |