Skip to content

Commit 1b65504

Browse files
julliardgitster
authored andcommitted
git.el: Keep the status buffer sorted by filename.
This makes insertions and updates much more efficient. Signed-off-by: Alexandre Julliard <julliard@winehq.org> Signed-off-by: Junio C Hamano <gitster@pobox.com>
1 parent 88b7dd4 commit 1b65504

File tree

1 file changed

+65
-38
lines changed

1 file changed

+65
-38
lines changed

contrib/emacs/git.el

Lines changed: 65 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -479,6 +479,27 @@ and returns the process output as a string."
479479
(setf (git-fileinfo->orig-name info) nil)
480480
(setf (git-fileinfo->needs-refresh info) t))))
481481

482+
(defun git-set-filenames-state (status files state)
483+
"Set the state of a list of named files."
484+
(when files
485+
(setq files (sort files #'string-lessp))
486+
(let ((file (pop files))
487+
(node (ewoc-nth status 0)))
488+
(while (and file node)
489+
(let ((info (ewoc-data node)))
490+
(cond ((string-lessp (git-fileinfo->name info) file)
491+
(setq node (ewoc-next status node)))
492+
((string-equal (git-fileinfo->name info) file)
493+
(unless (eq (git-fileinfo->state info) state)
494+
(setf (git-fileinfo->state info) state)
495+
(setf (git-fileinfo->rename-state info) nil)
496+
(setf (git-fileinfo->orig-name info) nil)
497+
(setf (git-fileinfo->needs-refresh info) t))
498+
(setq file (pop files)))
499+
(t (setq file (pop files)))))))
500+
(unless state ;; delete files whose state has been set to nil
501+
(ewoc-filter status (lambda (info) (git-fileinfo->state info))))))
502+
482503
(defun git-state-code (code)
483504
"Convert from a string to a added/deleted/modified state."
484505
(case (string-to-char code)
@@ -532,19 +553,36 @@ and returns the process output as a string."
532553
" " (git-escape-file-name (git-fileinfo->name info))
533554
(git-rename-as-string info))))
534555

535-
(defun git-insert-fileinfo (status info &optional refresh)
536-
"Insert INFO in the status buffer, optionally refreshing an existing one."
537-
(let ((node (and refresh
538-
(git-find-status-file status (git-fileinfo->name info)))))
539-
(setf (git-fileinfo->needs-refresh info) t)
540-
(when node ;preserve the marked flag
541-
(setf (git-fileinfo->marked info) (git-fileinfo->marked (ewoc-data node))))
542-
(if node (setf (ewoc-data node) info) (ewoc-enter-last status info))))
556+
(defun git-insert-info-list (status infolist)
557+
"Insert a list of file infos in the status buffer, replacing existing ones if any."
558+
(setq infolist (sort infolist
559+
(lambda (info1 info2)
560+
(string-lessp (git-fileinfo->name info1)
561+
(git-fileinfo->name info2)))))
562+
(let ((info (pop infolist))
563+
(node (ewoc-nth status 0)))
564+
(while info
565+
(setf (git-fileinfo->needs-refresh info) t)
566+
(cond ((not node)
567+
(ewoc-enter-last status info)
568+
(setq info (pop infolist)))
569+
((string-lessp (git-fileinfo->name (ewoc-data node))
570+
(git-fileinfo->name info))
571+
(setq node (ewoc-next status node)))
572+
((string-equal (git-fileinfo->name (ewoc-data node))
573+
(git-fileinfo->name info))
574+
;; preserve the marked flag
575+
(setf (git-fileinfo->marked info) (git-fileinfo->marked (ewoc-data node)))
576+
(setf (ewoc-data node) info)
577+
(setq info (pop infolist)))
578+
(t
579+
(ewoc-enter-before status node info)
580+
(setq info (pop infolist)))))))
543581

544582
(defun git-run-diff-index (status files)
545583
"Run git-diff-index on FILES and parse the results into STATUS.
546584
Return the list of files that haven't been handled."
547-
(let ((refresh files))
585+
(let (infolist)
548586
(with-temp-buffer
549587
(apply #'git-run-command t nil "diff-index" "-z" "-M" "HEAD" "--" files)
550588
(goto-char (point-min))
@@ -558,13 +596,14 @@ Return the list of files that haven't been handled."
558596
(new-name (match-string 8)))
559597
(if new-name ; copy or rename
560598
(if (eq ?C (string-to-char state))
561-
(git-insert-fileinfo status (git-create-fileinfo 'added new-name old-perm new-perm 'copy name) refresh)
562-
(git-insert-fileinfo status (git-create-fileinfo 'deleted name 0 0 'rename new-name) refresh)
563-
(git-insert-fileinfo status (git-create-fileinfo 'added new-name old-perm new-perm 'rename name)) refresh)
564-
(git-insert-fileinfo status (git-create-fileinfo (git-state-code state) name old-perm new-perm) refresh))
599+
(push (git-create-fileinfo 'added new-name old-perm new-perm 'copy name) infolist)
600+
(push (git-create-fileinfo 'deleted name 0 0 'rename new-name) infolist)
601+
(push (git-create-fileinfo 'added new-name old-perm new-perm 'rename name) infolist))
602+
(push (git-create-fileinfo (git-state-code state) name old-perm new-perm) infolist))
565603
(setq files (delete name files))
566-
(when new-name (setq files (delete new-name files)))))))
567-
files)
604+
(when new-name (setq files (delete new-name files))))))
605+
(git-insert-info-list status infolist)
606+
files))
568607

569608
(defun git-find-status-file (status file)
570609
"Find a given file in the status ewoc and return its node."
@@ -576,16 +615,16 @@ Return the list of files that haven't been handled."
576615
(defun git-run-ls-files (status files default-state &rest options)
577616
"Run git-ls-files on FILES and parse the results into STATUS.
578617
Return the list of files that haven't been handled."
579-
(let ((refresh files))
618+
(let (infolist)
580619
(with-temp-buffer
581-
(apply #'git-run-command t nil "ls-files" "-z" "-t" (append options (list "--") files))
620+
(apply #'git-run-command t nil "ls-files" "-z" (append options (list "--") files))
582621
(goto-char (point-min))
583-
(while (re-search-forward "\\([HMRCK?]\\) \\([^\0]*\\)\0" nil t 1)
584-
(let ((state (match-string 1))
585-
(name (match-string 2)))
586-
(git-insert-fileinfo status (git-create-fileinfo (or (git-state-code state) default-state) name) refresh)
587-
(setq files (delete name files))))))
588-
files)
622+
(while (re-search-forward "\\([^\0]*\\)\0" nil t 1)
623+
(let ((name (match-string 1)))
624+
(push (git-create-fileinfo default-state name) infolist)
625+
(setq files (delete name files)))))
626+
(git-insert-info-list status infolist)
627+
files))
589628

590629
(defun git-run-ls-unmerged (status files)
591630
"Run git-ls-files -u on FILES and parse the results into STATUS."
@@ -594,9 +633,8 @@ Return the list of files that haven't been handled."
594633
(goto-char (point-min))
595634
(let (unmerged-files)
596635
(while (re-search-forward "[0-7]\\{6\\} [0-9a-f]\\{40\\} [123]\t\\([^\0]+\\)\0" nil t)
597-
(let ((node (git-find-status-file status (match-string 1))))
598-
(when node (push (ewoc-data node) unmerged-files))))
599-
(git-set-files-state unmerged-files 'unmerged))))
636+
(push (match-string 1) unmerged-files))
637+
(git-set-filenames-state status unmerged-files 'unmerged))))
600638

601639
(defun git-get-exclude-files ()
602640
"Get the list of exclude files to pass to git-ls-files."
@@ -622,18 +660,7 @@ Return the list of files that haven't been handled."
622660
(setq remaining-files (apply #'git-run-ls-files status remaining-files 'unknown "-o"
623661
(concat "--exclude-per-directory=" git-per-dir-ignore-file)
624662
(mapcar (lambda (f) (concat "--exclude-from=" f)) exclude-files)))))
625-
; mark remaining files with the default state (or remove them if nil)
626-
(when remaining-files
627-
(if default-state
628-
(ewoc-map (lambda (info)
629-
(when (member (git-fileinfo->name info) remaining-files)
630-
(git-set-files-state (list info) default-state))
631-
nil)
632-
status)
633-
(ewoc-filter status
634-
(lambda (info files)
635-
(not (member (git-fileinfo->name info) files)))
636-
remaining-files)))
663+
(git-set-filenames-state status remaining-files default-state)
637664
(git-refresh-files)
638665
(git-refresh-ewoc-hf status)))
639666

0 commit comments

Comments
 (0)