All of lore.kernel.org
 help / color / mirror / Atom feed
From: Alexandre Julliard <julliard@winehq.org>
To: git@vger.kernel.org
Subject: [PATCH] git.el: Support for incremental status updates.
Date: Tue, 24 Jul 2007 12:12:47 +0200	[thread overview]
Message-ID: <87sl7ekt40.fsf@wine.dyndns.org> (raw)

When we know which files have been modified, we can now run diff-index
or ls-files with a file list to refresh only the specified files
instead of the whole project.

This also allows proper refreshing of files upon add/delete/resolve,
instead of making assumptions about the new file state.

Signed-off-by: Alexandre Julliard <julliard@winehq.org>
---
 contrib/emacs/git.el |  224 +++++++++++++++++++++++++-------------------------
 1 files changed, 113 insertions(+), 111 deletions(-)

diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el
index d7f4316..53dd703 100644
--- a/contrib/emacs/git.el
+++ b/contrib/emacs/git.el
@@ -314,8 +314,8 @@ and returns the process output as a string."
     (sort-lines nil (point-min) (point-max))
     (save-buffer))
   (when created
-    (git-run-command nil nil "update-index" "--info-only" "--add" "--" (file-relative-name ignore-name)))
-  (git-add-status-file (if created 'added 'modified) (file-relative-name ignore-name))))
+    (git-run-command nil nil "update-index" "--add" "--" (file-relative-name ignore-name)))
+  (git-update-status-files (list (file-relative-name ignore-name)) 'unknown)))
 
 ; propertize definition for XEmacs, stolen from erc-compat
 (eval-when-compile
@@ -523,23 +523,39 @@ and returns the process output as a string."
                   "  " (git-escape-file-name (git-fileinfo->name info))
                   (git-rename-as-string info))))
 
-(defun git-parse-status (status)
-  "Parse the output of git-diff-index in the current buffer."
-  (goto-char (point-min))
-  (while (re-search-forward
-          ":\\([0-7]\\{6\\}\\) \\([0-7]\\{6\\}\\) [0-9a-f]\\{40\\} [0-9a-f]\\{40\\} \\(\\([ADMU]\\)\0\\([^\0]+\\)\\|\\([CR]\\)[0-9]*\0\\([^\0]+\\)\0\\([^\0]+\\)\\)\0"
-          nil t 1)
-    (let ((old-perm (string-to-number (match-string 1) 8))
-          (new-perm (string-to-number (match-string 2) 8))
-          (state (or (match-string 4) (match-string 6)))
-          (name (or (match-string 5) (match-string 7)))
-          (new-name (match-string 8)))
-      (if new-name  ; copy or rename
-          (if (eq ?C (string-to-char state))
-              (ewoc-enter-last status (git-create-fileinfo 'added new-name old-perm new-perm 'copy name))
-            (ewoc-enter-last status (git-create-fileinfo 'deleted name 0 0 'rename new-name))
-            (ewoc-enter-last status (git-create-fileinfo 'added new-name old-perm new-perm 'rename name)))
-        (ewoc-enter-last status (git-create-fileinfo (git-state-code state) name old-perm new-perm))))))
+(defun git-insert-fileinfo (status info &optional refresh)
+  "Insert INFO in the status buffer, optionally refreshing an existing one."
+  (let ((node (and refresh
+                   (git-find-status-file status (git-fileinfo->name info)))))
+    (setf (git-fileinfo->needs-refresh info) t)
+    (when node   ;preserve the marked flag
+      (setf (git-fileinfo->marked info) (git-fileinfo->marked (ewoc-data node))))
+    (if node (ewoc-set-data node info) (ewoc-enter-last status info))))
+
+(defun git-run-diff-index (status files)
+  "Run git-diff-index on FILES and parse the results into STATUS.
+Return the list of files that haven't been handled."
+  (let ((refresh files))
+    (with-temp-buffer
+      (apply #'git-run-command t nil "diff-index" "-z" "-M" "HEAD" "--" files)
+      (goto-char (point-min))
+      (while (re-search-forward
+              ":\\([0-7]\\{6\\}\\) \\([0-7]\\{6\\}\\) [0-9a-f]\\{40\\} [0-9a-f]\\{40\\} \\(\\([ADMU]\\)\0\\([^\0]+\\)\\|\\([CR]\\)[0-9]*\0\\([^\0]+\\)\0\\([^\0]+\\)\\)\0"
+              nil t 1)
+        (let ((old-perm (string-to-number (match-string 1) 8))
+              (new-perm (string-to-number (match-string 2) 8))
+              (state (or (match-string 4) (match-string 6)))
+              (name (or (match-string 5) (match-string 7)))
+              (new-name (match-string 8)))
+          (if new-name  ; copy or rename
+              (if (eq ?C (string-to-char state))
+                  (git-insert-fileinfo status (git-create-fileinfo 'added new-name old-perm new-perm 'copy name) refresh)
+                (git-insert-fileinfo status (git-create-fileinfo 'deleted name 0 0 'rename new-name) refresh)
+                (git-insert-fileinfo status (git-create-fileinfo 'added new-name old-perm new-perm 'rename name)) refresh)
+            (git-insert-fileinfo status (git-create-fileinfo (git-state-code state) name old-perm new-perm) refresh))
+          (setq files (delete name files))
+          (when new-name (setq files (delete new-name files)))))))
+  files)
 
 (defun git-find-status-file (status file)
   "Find a given file in the status ewoc and return its node."
@@ -548,32 +564,59 @@ and returns the process output as a string."
       (setq node (ewoc-next status node)))
     node))
 
-(defun git-parse-ls-files (status default-state &optional skip-existing)
-  "Parse the output of git-ls-files in the current buffer."
-  (goto-char (point-min))
-  (let (infolist)
-    (while (re-search-forward "\\([HMRCK?]\\) \\([^\0]*\\)\0" nil t 1)
-      (let ((state (match-string 1))
-            (name (match-string 2)))
-        (unless (and skip-existing (git-find-status-file status name))
-          (push (git-create-fileinfo (or (git-state-code state) default-state) name) infolist))))
-    (dolist (info (nreverse infolist))
-      (ewoc-enter-last status info))))
-
-(defun git-parse-ls-unmerged (status)
-  "Parse the output of git-ls-files -u in the current buffer."
-  (goto-char (point-min))
-  (let (files)
-    (while (re-search-forward "[0-7]\\{6\\} [0-9a-f]\\{40\\} [123]\t\\([^\0]+\\)\0" nil t)
-      (let ((node (git-find-status-file status (match-string 1))))
-        (when node (push (ewoc-data node) files))))
-    (git-set-files-state files 'unmerged)))
-
-(defun git-add-status-file (state name)
-  "Add a new file to the status list (if not existing already) and return its node."
+(defun git-run-ls-files (status files default-state &rest options)
+  "Run git-ls-files on FILES and parse the results into STATUS.
+Return the list of files that haven't been handled."
+  (let ((refresh files))
+    (with-temp-buffer
+      (apply #'git-run-command t nil "ls-files" "-z" "-t" (append options (list "--") files))
+      (goto-char (point-min))
+      (while (re-search-forward "\\([HMRCK?]\\) \\([^\0]*\\)\0" nil t 1)
+        (let ((state (match-string 1))
+              (name (match-string 2)))
+          (git-insert-fileinfo status (git-create-fileinfo (or (git-state-code state) default-state) name) refresh)
+          (setq files (delete name files))))))
+  files)
+
+(defun git-run-ls-unmerged (status files)
+  "Run git-ls-files -u on FILES and parse the results into STATUS."
+  (with-temp-buffer
+    (apply #'git-run-command t nil "ls-files" "-z" "-u" "--" files)
+    (goto-char (point-min))
+    (let (unmerged-files)
+      (while (re-search-forward "[0-7]\\{6\\} [0-9a-f]\\{40\\} [123]\t\\([^\0]+\\)\0" nil t)
+        (let ((node (git-find-status-file status (match-string 1))))
+          (when node (push (ewoc-data node) unmerged-files))))
+      (git-set-files-state unmerged-files 'unmerged))))
+
+(defun git-update-status-files (files &optional default-state)
+  "Update the status of FILES from the index."
   (unless git-status (error "Not in git-status buffer."))
-  (or (git-find-status-file git-status name)
-      (ewoc-enter-last git-status (git-create-fileinfo state name))))
+  (let* ((status git-status)
+         (remaining-files
+          (if (git-empty-db-p) ; we need some special handling for an empty db
+              (git-run-ls-files status files 'added "-c")
+            (git-run-diff-index status files))))
+    (git-run-ls-unmerged status files)
+    (when (and (or (not files) remaining-files)
+               (file-readable-p ".git/info/exclude"))
+      (setq remaining-files (git-run-ls-files status remaining-files
+                                              'unknown "-o" "--exclude-from=.git/info/exclude"
+                                              (concat "--exclude-per-directory=" git-per-dir-ignore-file))))
+    ; mark remaining files with the default state (or remove them if nil)
+    (when remaining-files
+      (if default-state
+          (ewoc-map (lambda (info)
+                      (when (member (git-fileinfo->name info) remaining-files)
+                        (git-set-files-state (list info) default-state))
+                      nil)
+                    status)
+        (ewoc-filter status
+                     (lambda (info files)
+                       (not (member (git-fileinfo->name info) files)))
+                     remaining-files)))
+    (git-refresh-files)
+    (git-refresh-ewoc-hf status)))
 
 (defun git-marked-files ()
   "Return a list of all marked files, or if none a list containing just the file at cursor position."
@@ -789,54 +832,34 @@ and returns the process output as a string."
 (defun git-add-file ()
   "Add marked file(s) to the index cache."
   (interactive)
-  (let ((files (git-marked-files-state 'unknown)))
+  (let ((files (git-get-filenames (git-marked-files-state 'unknown))))
     (unless files
-      (push (ewoc-data
-             (git-add-status-file 'added (file-relative-name
-                                          (read-file-name "File to add: " nil nil t))))
-            files))
-    (apply #'git-run-command nil nil "update-index" "--info-only" "--add" "--" (git-get-filenames files))
-    (git-set-files-state files 'added)
-    (git-refresh-files)))
+      (push (file-relative-name (read-file-name "File to add: " nil nil t)) files))
+    (apply #'git-run-command nil nil "update-index" "--add" "--" files)
+    (git-update-status-files files 'uptodate)))
 
 (defun git-ignore-file ()
   "Add marked file(s) to the ignore list."
   (interactive)
-  (let ((files (git-marked-files-state 'unknown)))
+  (let ((files (git-get-filenames (git-marked-files-state 'unknown))))
     (unless files
-      (push (ewoc-data
-             (git-add-status-file 'unknown (file-relative-name
-                                            (read-file-name "File to ignore: " nil nil t))))
-            files))
-    (dolist (info files) (git-append-to-ignore (git-fileinfo->name info)))
-    (git-set-files-state files 'ignored)
-    (git-refresh-files)))
+      (push (file-relative-name (read-file-name "File to ignore: " nil nil t)) files))
+    (dolist (f files) (git-append-to-ignore f))
+    (git-update-status-files files 'ignored)))
 
 (defun git-remove-file ()
   "Remove the marked file(s)."
   (interactive)
-  (let ((files (git-marked-files-state 'added 'modified 'unknown 'uptodate)))
+  (let ((files (git-get-filenames (git-marked-files-state 'added 'modified 'unknown 'uptodate))))
     (unless files
-      (push (ewoc-data
-             (git-add-status-file 'unknown (file-relative-name
-                                            (read-file-name "File to remove: " nil nil t))))
-            files))
+      (push (file-relative-name (read-file-name "File to remove: " nil nil t)) files))
     (if (yes-or-no-p
          (format "Remove %d file%s? " (length files) (if (> (length files) 1) "s" "")))
         (progn
-          (dolist (info files)
-            (let ((name (git-fileinfo->name info)))
-              (when (file-exists-p name) (delete-file name))))
-          (apply #'git-run-command nil nil "update-index" "--info-only" "--remove" "--" (git-get-filenames files))
-          ; remove unknown files from the list, set the others to deleted
-          (ewoc-filter git-status
-                       (lambda (info files)
-                         (not (and (memq info files) (eq (git-fileinfo->state info) 'unknown))))
-                       files)
-          (git-set-files-state files 'deleted)
-          (git-refresh-files)
-          (unless (ewoc-nth git-status 0)  ; refresh header if list is empty
-            (git-refresh-ewoc-hf git-status)))
+          (dolist (name files)
+            (when (file-exists-p name) (delete-file name)))
+          (apply #'git-run-command nil nil "update-index" "--remove" "--" files)
+          (git-update-status-files files nil))
       (message "Aborting"))))
 
 (defun git-revert-file ()
@@ -849,26 +872,23 @@ and returns the process output as a string."
                 (format "Revert %d file%s? " (length files) (if (> (length files) 1) "s" ""))))
       (dolist (info files)
         (case (git-fileinfo->state info)
-          ('added (push info added))
-          ('deleted (push info modified))
-          ('unmerged (push info modified))
-          ('modified (push info modified))))
+          ('added (push (git-fileinfo->name info) added))
+          ('deleted (push (git-fileinfo->name info) modified))
+          ('unmerged (push (git-fileinfo->name info) modified))
+          ('modified (push (git-fileinfo->name info) modified))))
       (when added
-          (apply #'git-run-command nil nil "update-index" "--force-remove" "--" (git-get-filenames added))
-          (git-set-files-state added 'unknown))
+        (apply #'git-run-command nil nil "update-index" "--force-remove" "--" added))
       (when modified
-          (apply #'git-run-command nil nil "checkout" "HEAD" (git-get-filenames modified))
-          (git-set-files-state modified 'uptodate))
-      (git-refresh-files))))
+        (apply #'git-run-command nil nil "checkout" "HEAD" modified))
+      (git-update-status-files (append added modified) 'uptodate))))
 
 (defun git-resolve-file ()
   "Resolve conflicts in marked file(s)."
   (interactive)
-  (let ((files (git-marked-files-state 'unmerged)))
+  (let ((files (git-get-filenames (git-marked-files-state 'unmerged))))
     (when files
-      (apply #'git-run-command nil nil "update-index" "--" (git-get-filenames files))
-      (git-set-files-state files 'modified)
-      (git-refresh-files))))
+      (apply #'git-run-command nil nil "update-index" "--" files)
+      (git-update-status-files files 'uptodate))))
 
 (defun git-remove-handled ()
   "Remove handled files from the status list."
@@ -1071,27 +1091,9 @@ and returns the process output as a string."
          (pos (ewoc-locate status))
          (cur-name (and pos (git-fileinfo->name (ewoc-data pos)))))
     (unless status (error "Not in git-status buffer."))
+    (git-run-command nil nil "update-index" "--refresh")
     (git-clear-status status)
-    (git-run-command nil nil "update-index" "--info-only" "--refresh")
-    (if (git-empty-db-p)
-        ; we need some special handling for an empty db
-        (with-temp-buffer
-          (git-run-command t nil "ls-files" "-z" "-t" "-c")
-          (git-parse-ls-files status 'added))
-      (with-temp-buffer
-        (git-run-command t nil "diff-index" "-z" "-M" "HEAD")
-        (git-parse-status status)))
-      (with-temp-buffer
-        (git-run-command t nil "ls-files" "-z" "-u")
-        (git-parse-ls-unmerged status))
-      (when (file-readable-p ".git/info/exclude")
-        (with-temp-buffer
-          (git-run-command t nil "ls-files" "-z" "-t" "-o"
-                           "--exclude-from=.git/info/exclude"
-                           (concat "--exclude-per-directory=" git-per-dir-ignore-file))
-          (git-parse-ls-files status 'unknown)))
-    (git-refresh-files)
-    (git-refresh-ewoc-hf status)
+    (git-update-status-files nil)
     ; move point to the current file name if any
     (let ((node (and cur-name (git-find-status-file status cur-name))))
       (when node (ewoc-goto-node status node)))))
-- 
1.5.3.rc2.39.g82303f

-- 
Alexandre Julliard
julliard@winehq.org

             reply	other threads:[~2007-07-24 10:12 UTC|newest]

Thread overview: 6+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2007-07-24 10:12 Alexandre Julliard [this message]
2007-07-30 23:22 ` [PATCH] git.el: Support for incremental status updates Karl Hasselström
2007-07-31  9:19   ` David Kågedal
2007-07-31 11:14     ` Karl Hasselström
2007-07-31 18:48       ` Alexandre Julliard
2007-07-31 19:58         ` Karl Hasselström

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=87sl7ekt40.fsf@wine.dyndns.org \
    --to=julliard@winehq.org \
    --cc=git@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.