@@ -356,7 +356,7 @@ the process output as a string, or nil if the git command failed."
356356 (save-buffer ))
357357 (when created
358358 (git-call-process nil " update-index" " --add" " --" (file-relative-name ignore-name)))
359- (git-update-status-files (list (file-relative-name ignore-name)) 'unknown )))
359+ (git-update-status-files (list (file-relative-name ignore-name)))))
360360
361361; propertize definition for XEmacs, stolen from erc-compat
362362(eval-when-compile
@@ -497,21 +497,19 @@ the process output as a string, or nil if the git command failed."
497497 old-perm new-perm ; ; permission flags
498498 rename-state ; ; rename or copy state
499499 orig-name ; ; original name for renames or copies
500+ needs-update ; ; whether file needs to be updated
500501 needs-refresh) ; ; whether file needs to be refreshed
501502
502503(defvar git-status nil )
503504
504- (defun git-clear-status (status )
505- " Remove everything from the status list."
506- (ewoc-filter status (lambda (info ) nil )))
507-
508505(defun git-set-fileinfo-state (info state )
509506 " Set the state of a file info."
510507 (unless (eq (git-fileinfo->state info) state)
511508 (setf (git-fileinfo->state info) state
512509 (git-fileinfo->new-perm info) (git-fileinfo->old-perm info)
513510 (git-fileinfo->rename-state info) nil
514511 (git-fileinfo->orig-name info) nil
512+ (git-fileinfo->needs-update info) nil
515513 (git-fileinfo->needs-refresh info) t )))
516514
517515(defun git-status-filenames-map (status func files &rest args )
@@ -521,10 +519,11 @@ the process output as a string, or nil if the git command failed."
521519 (let ((file (pop files ))
522520 (node (ewoc-nth status 0 )))
523521 (while (and file node)
524- (let ((info (ewoc-data node)))
525- (if (string-lessp (git-fileinfo->name info) file)
522+ (let* ((info (ewoc-data node))
523+ (name (git-fileinfo->name info)))
524+ (if (string-lessp name file)
526525 (setq node (ewoc-next status node))
527- (if (string-equal (git-fileinfo-> name info) file)
526+ (if (string-equal name file)
528527 (apply func info args))
529528 (setq file (pop files ))))))))
530529
@@ -622,37 +621,50 @@ the process output as a string, or nil if the git command failed."
622621 (git-file-type-as-string old-perm new-perm)
623622 (git-rename-as-string info)))))
624623
625- (defun git-insert-info-list (status infolist )
626- " Insert a list of file infos in the status buffer, replacing existing ones if any."
627- (setq infolist (sort infolist
628- (lambda (info1 info2 )
629- (string-lessp (git-fileinfo->name info1)
630- (git-fileinfo->name info2)))))
631- (let ((info (pop infolist))
632- (node (ewoc-nth status 0 )))
624+ (defun git-update-node-fileinfo (node info )
625+ " Update the fileinfo of the specified node. The names are assumed to match already."
626+ (let ((data (ewoc-data node)))
627+ (setf
628+ ; ; preserve the marked flag
629+ (git-fileinfo->marked info) (git-fileinfo->marked data)
630+ (git-fileinfo->needs-update data) nil )
631+ (when (not (equal info data))
632+ (setf (git-fileinfo->needs-refresh info) t
633+ (ewoc-data node) info))))
634+
635+ (defun git-insert-info-list (status infolist files )
636+ " Insert a sorted list of file infos in the status buffer, replacing existing ones if any."
637+ (let* ((info (pop infolist))
638+ (node (ewoc-nth status 0 ))
639+ (name (and info (git-fileinfo->name info)))
640+ remaining)
633641 (while info
634- (cond ((not node)
635- (setq node (ewoc-enter-last status info))
636- (setq info (pop infolist)))
637- ((string-lessp (git-fileinfo->name (ewoc-data node))
638- (git-fileinfo->name info))
639- (setq node (ewoc-next status node)))
640- ((string-equal (git-fileinfo->name (ewoc-data node))
641- (git-fileinfo->name info))
642- ; ; preserve the marked flag
643- (setf (git-fileinfo->marked info) (git-fileinfo->marked (ewoc-data node)))
644- (setf (git-fileinfo->needs-refresh info) t )
645- (setf (ewoc-data node) info)
646- (setq info (pop infolist)))
647- (t
648- (setq node (ewoc-enter-before status node info))
649- (setq info (pop infolist)))))))
642+ (let ((nodename (and node (git-fileinfo->name (ewoc-data node)))))
643+ (while (and files (string-lessp (car files ) name))
644+ (push (pop files ) remaining))
645+ (when (and files (string-equal (car files ) name))
646+ (setq files (cdr files )))
647+ (cond ((not nodename)
648+ (setq node (ewoc-enter-last status info))
649+ (setq info (pop infolist))
650+ (setq name (and info (git-fileinfo->name info))))
651+ ((string-lessp nodename name)
652+ (setq node (ewoc-next status node)))
653+ ((string-equal nodename name)
654+ ; ; preserve the marked flag
655+ (git-update-node-fileinfo node info)
656+ (setq info (pop infolist))
657+ (setq name (and info (git-fileinfo->name info))))
658+ (t
659+ (setq node (ewoc-enter-before status node info))
660+ (setq info (pop infolist))
661+ (setq name (and info (git-fileinfo->name info)))))))
662+ (nconc (nreverse remaining) files )))
650663
651664(defun git-run-diff-index (status files )
652665 " Run git-diff-index on FILES and parse the results into STATUS.
653666Return the list of files that haven't been handled."
654- (let ((remaining (copy-sequence files ))
655- infolist)
667+ (let (infolist)
656668 (with-temp-buffer
657669 (apply #'git-call-process t " diff-index" " -z" " -M" " HEAD" " --" files )
658670 (goto-char (point-min ))
@@ -669,11 +681,12 @@ Return the list of files that haven't been handled."
669681 (push (git-create-fileinfo 'added new-name old-perm new-perm 'copy name) infolist)
670682 (push (git-create-fileinfo 'deleted name 0 0 'rename new-name) infolist)
671683 (push (git-create-fileinfo 'added new-name old-perm new-perm 'rename name) infolist))
672- (push (git-create-fileinfo (git-state-code state) name old-perm new-perm) infolist))
673- (setq remaining (delete name remaining))
674- (when new-name (setq remaining (delete new-name remaining))))))
675- (git-insert-info-list status infolist)
676- remaining))
684+ (push (git-create-fileinfo (git-state-code state) name old-perm new-perm) infolist)))))
685+ (setq infolist (sort (nreverse infolist)
686+ (lambda (info1 info2 )
687+ (string-lessp (git-fileinfo->name info1)
688+ (git-fileinfo->name info2)))))
689+ (git-insert-info-list status infolist files )))
677690
678691(defun git-find-status-file (status file )
679692 " Find a given file in the status ewoc and return its node."
@@ -693,27 +706,24 @@ Return the list of files that haven't been handled."
693706 (let ((name (match-string 1 )))
694707 (push (git-create-fileinfo default-state name 0
695708 (if (string-equal " /" (match-string 2 )) (lsh ?\110 9 ) 0 ))
696- infolist)
697- (setq files (delete name files )))))
698- (git-insert-info-list status infolist)
699- files ))
709+ infolist))))
710+ (setq infolist (nreverse infolist)) ; ; assume it is sorted already
711+ (git-insert-info-list status infolist files )))
700712
701713(defun git-run-ls-files-cached (status files default-state )
702714 " Run git-ls-files -c on FILES and parse the results into STATUS.
703715Return the list of files that haven't been handled."
704- (let ((remaining (copy-sequence files ))
705- infolist)
716+ (let (infolist)
706717 (with-temp-buffer
707718 (apply #'git-call-process t " ls-files" " -z" " -s" " -c" " --" files )
708719 (goto-char (point-min ))
709720 (while (re-search-forward " \\ ([0-7]\\ {6\\ }\\ ) [0-9a-f]\\ {40\\ } 0\t \\ ([^\0 ]+\\ )\0 " nil t )
710721 (let* ((new-perm (string-to-number (match-string 1 ) 8 ))
711722 (old-perm (if (eq default-state 'added ) 0 new-perm))
712723 (name (match-string 2 )))
713- (push (git-create-fileinfo default-state name old-perm new-perm) infolist)
714- (setq remaining (delete name remaining)))))
715- (git-insert-info-list status infolist)
716- remaining))
724+ (push (git-create-fileinfo default-state name old-perm new-perm) infolist))))
725+ (setq infolist (nreverse infolist)) ; ; assume it is sorted already
726+ (git-insert-info-list status infolist files )))
717727
718728(defun git-run-ls-unmerged (status files )
719729 " Run git-ls-files -u on FILES and parse the results into STATUS."
@@ -742,11 +752,17 @@ Return the list of files that haven't been handled."
742752 (concat " --exclude-per-directory=" git-per-dir-ignore-file)
743753 (append options (mapcar (lambda (f ) (concat " --exclude-from=" f)) exclude-files)))))
744754
745- (defun git-update-status-files (files &optional default-state )
755+ (defun git-update-status-files (&optional files )
746756 " Update the status of FILES from the index."
747757 (unless git-status (error " Not in git-status buffer. " ))
748- (when (or git-show-uptodate files )
749- (git-run-ls-files-cached git-status files 'uptodate ))
758+ ; ; set the needs-update flag on existing files
759+ (if (setq files (sort files #'string-lessp ))
760+ (git-status-filenames-map
761+ git-status (lambda (info ) (setf (git-fileinfo->needs-update info) t )) files )
762+ (ewoc-map (lambda (info ) (setf (git-fileinfo->needs-update info) t ) nil ) git-status)
763+ (git-call-process nil " update-index" " --refresh" )
764+ (when git-show-uptodate
765+ (git-run-ls-files-cached git-status nil 'uptodate )))
750766 (let* ((remaining-files
751767 (if (git-empty-db-p) ; we need some special handling for an empty db
752768 (git-run-ls-files-cached git-status files 'added )
@@ -756,7 +772,11 @@ Return the list of files that haven't been handled."
756772 (setq remaining-files (git-run-ls-files-with-excludes git-status remaining-files 'unknown " -o" )))
757773 (when (or remaining-files (and git-show-ignored (not files )))
758774 (setq remaining-files (git-run-ls-files-with-excludes git-status remaining-files 'ignored " -o" " -i" )))
759- (git-set-filenames-state git-status remaining-files default-state)
775+ (unless files
776+ (setq remaining-files (git-get-filenames (ewoc-collect git-status #'git-fileinfo->needs-update ))))
777+ (when remaining-files
778+ (setq remaining-files (git-run-ls-files-cached git-status remaining-files 'uptodate )))
779+ (git-set-filenames-state git-status remaining-files nil )
760780 (git-refresh-files)
761781 (git-refresh-ewoc-hf git-status)))
762782
@@ -891,11 +911,9 @@ Return the list of files that haven't been handled."
891911 (condition-case nil (delete-file " .git/MERGE_HEAD" ) (error nil ))
892912 (condition-case nil (delete-file " .git/MERGE_MSG" ) (error nil ))
893913 (with-current-buffer buffer (erase-buffer ))
894- (git-update-status-files (git-get-filenames files ) 'uptodate )
914+ (git-update-status-files (git-get-filenames files ))
895915 (git-call-process nil " rerere" )
896916 (git-call-process nil " gc" " --auto" )
897- (git-refresh-files)
898- (git-refresh-ewoc-hf git-status)
899917 (message " Committed %s . " commit)
900918 (git-run-hook " post-commit" nil )))
901919 (message " Commit aborted. " ))))
@@ -1009,7 +1027,7 @@ Return the list of files that haven't been handled."
10091027 (unless files
10101028 (push (file-relative-name (read-file-name " File to add: " nil nil t )) files ))
10111029 (when (apply 'git-call-process-display-error " update-index" " --add" " --" files )
1012- (git-update-status-files files 'uptodate )
1030+ (git-update-status-files files )
10131031 (git-success-message " Added" files ))))
10141032
10151033(defun git-ignore-file ()
@@ -1019,7 +1037,7 @@ Return the list of files that haven't been handled."
10191037 (unless files
10201038 (push (file-relative-name (read-file-name " File to ignore: " nil nil t )) files ))
10211039 (dolist (f files ) (git-append-to-ignore f))
1022- (git-update-status-files files 'ignored )
1040+ (git-update-status-files files )
10231041 (git-success-message " Ignored" files )))
10241042
10251043(defun git-remove-file ()
@@ -1037,7 +1055,7 @@ Return the list of files that haven't been handled."
10371055 (delete-directory name)
10381056 (delete-file name))))
10391057 (when (apply 'git-call-process-display-error " update-index" " --remove" " --" files )
1040- (git-update-status-files files nil )
1058+ (git-update-status-files files )
10411059 (git-success-message " Removed" files )))
10421060 (message " Aborting " ))))
10431061
@@ -1065,7 +1083,7 @@ Return the list of files that haven't been handled."
10651083 (apply 'git-call-process-display-error " update-index" " --force-remove" " --" added))
10661084 (or (not modified)
10671085 (apply 'git-call-process-display-error " checkout" " HEAD" modified)))))
1068- (git-update-status-files (append added modified) 'uptodate )
1086+ (git-update-status-files (append added modified))
10691087 (when ok
10701088 (dolist (file modified)
10711089 (let ((buffer (get-file-buffer file)))
@@ -1078,7 +1096,7 @@ Return the list of files that haven't been handled."
10781096 (let ((files (git-get-filenames (git-marked-files-state 'unmerged ))))
10791097 (when files
10801098 (when (apply 'git-call-process-display-error " update-index" " --" files )
1081- (git-update-status-files files 'uptodate )
1099+ (git-update-status-files files )
10821100 (git-success-message " Resolved" files )))))
10831101
10841102(defun git-remove-handled ()
@@ -1348,7 +1366,7 @@ amended version of it."
13481366 (git-call-process-display-error " reset" " --soft" " HEAD^" )
13491367 (and (git-update-ref " ORIG_HEAD" commit)
13501368 (git-update-ref " HEAD" nil commit)))
1351- (git-update-status-files (copy-sequence files ) 'uptodate )
1369+ (git-update-status-files (copy-sequence files ))
13521370 (git-mark-files git-status files )
13531371 (git-refresh-files)
13541372 (git-setup-commit-buffer commit)
@@ -1391,27 +1409,10 @@ amended version of it."
13911409(defun git-refresh-status ()
13921410 " Refresh the git status buffer."
13931411 (interactive )
1394- (let* ((status git-status)
1395- (pos (ewoc-locate status))
1396- (marked-files (git-get-filenames (ewoc-collect status (lambda (info ) (git-fileinfo->marked info)))))
1397- (cur-name (and pos (git-fileinfo->name (ewoc-data pos)))))
1398- (unless status (error " Not in git-status buffer. " ))
1399- (message " Refreshing git status... " )
1400- (git-call-process nil " update-index" " --refresh" )
1401- (git-clear-status status)
1402- (git-update-status-files nil )
1403- ; restore file marks
1404- (when marked-files
1405- (git-status-filenames-map status
1406- (lambda (info )
1407- (setf (git-fileinfo->marked info) t )
1408- (setf (git-fileinfo->needs-refresh info) t ))
1409- marked-files)
1410- (git-refresh-files))
1411- ; move point to the current file name if any
1412- (message " Refreshing git status...done " )
1413- (let ((node (and cur-name (git-find-status-file status cur-name))))
1414- (when node (ewoc-goto-node status node)))))
1412+ (unless git-status (error " Not in git-status buffer. " ))
1413+ (message " Refreshing git status... " )
1414+ (git-update-status-files)
1415+ (message " Refreshing git status...done " ))
14151416
14161417(defun git-status-quit ()
14171418 " Quit git-status mode."
@@ -1591,7 +1592,7 @@ Meant to be used in `after-save-hook'."
15911592 ; skip files located inside the .git directory
15921593 (unless (string-match " ^\\ .git/" filename)
15931594 (git-call-process nil " add" " --refresh" " --" filename)
1594- (git-update-status-files (list filename) 'uptodate )))))))
1595+ (git-update-status-files (list filename))))))))
15951596
15961597(defun git-help ()
15971598 " Display help for Git mode."
0 commit comments