unofficial mirror of bug-gnu-emacs@gnu.org 
 help / color / mirror / code / Atom feed
From: Dmitry Dzhus <dima@dzhus.org>
To: 8756@debbugs.gnu.org
Subject: bug#8756: vc-git.el doesn't use --follow argument in vc-git-print-log
Date: Thu, 31 Jul 2014 00:35:32 +0400	[thread overview]
Message-ID: <3258071406752532@web22g.yandex.ru> (raw)
In-Reply-To: <87sjrxl0r1.fsf@dod.no>

[-- Attachment #1: Type: text/plain, Size: 1164 bytes --]

How are you gentlemen,

Following renames is nice no matter what Git upstream thinks,
so I gave this bug a try. I'd like to share some progress.

Diffing (`d`), paging (`f`) and annotating a file (`a`) revision works.
I've tested it with Git version 1.8.5.4 and 2.0.2.

Parallel git log with hash/file information is run when
a log is queried. (vc-git-file-shalist) provides access to
a list of SHA1's/historical file names for the file open
in the *vc-change-log* buffer.

The bad part is that whole-changeset (`D`) diffing is broken
(wrong revisions are selected for diff).
The culprit is `vc-git-previous-revision`.
Using HASH^ to obtain the parent of HASH revision produces
unexpected results when file renames are involved. However,
for whole-changeset diffing HASH^ seems to be exactly what
we need. However, `vc-git-previous-revision` doesn't seem
to enable any inspection of the context in which we use it
(in other words, if we hit `d` or `D` in the log buffer.)
Any ideas?

I also have not tackled revision navigation in the annotation
mode, but this can likely be solved by plugging calls to 
`(vc-git-rev-to-filename)` somewhere in the annotation mode.

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: vc-git-follow-renames.patch --]
[-- Type: text/x-diff; name="vc-git-follow-renames.patch", Size: 6450 bytes --]

=== modified file 'lisp/vc/vc-git.el'
--- lisp/vc/vc-git.el	2014-06-29 20:48:55 +0000
+++ lisp/vc/vc-git.el	2014-07-30 20:32:01 +0000
@@ -691,13 +691,7 @@
 	 (coding-system-for-read 'binary)
 	 (coding-system-for-write 'binary)
 	 (fullname
-	  (let ((fn (vc-git--run-command-string
-		     file "ls-files" "-z" "--full-name" "--")))
-	    ;; ls-files does not return anything when looking for a
-	    ;; revision of a file that has been renamed or removed.
-	    (if (string= fn "")
-		(file-relative-name file (vc-git-root default-directory))
-	      (substring fn 0 -1)))))
+          (vc-git-rev-to-filename rev)))
     (vc-git-command
      buffer 0
      nil
@@ -786,20 +780,53 @@
     ;; If the buffer exists from a previous invocation it might be
     ;; read-only.
     (let ((inhibit-read-only t))
+      ;; Clean SHA1 list caches whenever we query a new change log
       (with-current-buffer
           buffer
-	(apply 'vc-git-command buffer
-	       'async files
-	       (append
-		'("log" "--no-color")
-		(when shortlog
-		  `("--graph" "--decorate" "--date=short"
+        (if (boundp 'vc-git-file-shalist-raw)
+            (setq vc-git-file-shalist-raw nil)
+          (set (make-local-variable 'vc-git-file-shalist-raw) nil))
+        (if (boundp 'vc-git-file-shalist)
+            (setq vc-git-file-shalist nil))
+        (when (vc-git-single-file files)
+          ;; Store newline-separated list of revision hashes and file
+          ;; names in vc-git-file-shalist-raw buffer-local variable
+          (with-temp-buffer
+            (set-process-filter
+             (apply 'vc-git-command nil
+                    'async files
+                    (append
+                     '("log"
+                       "--follow"
+                       "--name-only"
+                       "--pretty=tformat:%H"
+                       "--no-color")
+                     ;; Tail revision must now its parent
+                     (when limit (list "-n" (format "%s" (1+ limit))))
+                     (when start-revision (lsit start-revision))
+                     '("--")))
+             (lambda (p s)
+               (with-current-buffer buffer
+                 (setq
+                  vc-git-file-shalist-raw
+                  (replace-regexp-in-string
+                   "\n\n" "\n"
+                   (concat (if (boundp 'vc-git-file-shalist-raw)
+                               vc-git-file-shalist-raw "") s))))))))
+        (apply 'vc-git-command buffer
+               'async files
+               (append
+                (if (vc-git-single-file files)
+                    '("log" "--follow" "--no-color")
+                  '("log" "--no-color"))
+                (when shortlog
+                  `("--graph" "--decorate" "--date=short"
                     ,(format "--pretty=tformat:%s"
-			     (car vc-git-root-log-format))
-		    "--abbrev-commit"))
-		(when limit (list "-n" (format "%s" limit)))
-		(when start-revision (list start-revision))
-		'("--")))))))
+                             (car vc-git-root-log-format))
+                    "--abbrev-commit"))
+                (when limit (list "-n" (format "%s" limit)))
+                (when start-revision (list start-revision))
+                '("--")))))))
 
 (defun vc-git-log-outgoing (buffer remote-location)
   (interactive)
@@ -904,11 +931,21 @@
 (defun vc-git-diff (files &optional rev1 rev2 buffer)
   "Get a difference report using Git between two revisions of FILES."
   (let (process-file-side-effects)
-    (apply #'vc-git-command (or buffer "*vc-diff*") 1 files
-	   (if (and rev1 rev2) "diff-tree" "diff-index")
-	   "--exit-code"
-	   (append (vc-switches 'git 'diff)
-		   (list "-p" (or rev1 "HEAD") rev2 "--")))))
+    (with-current-buffer (or buffer "*vc-diff*")
+      ;; Run diff from the repository root because our file names are
+      ;; relative to it
+      (setq default-directory (vc-git-root default-directory))
+      (apply #'vc-git-command (or buffer "*vc-diff*") 1
+             (if (vc-git-single-file files)
+                 (list
+                  (vc-git-rev-to-filename rev1)
+                  (vc-git-rev-to-filename rev2))
+               files)
+             (if (and rev1 rev2) "diff-tree" "diff-index")
+             "--exit-code"
+             "-M"
+             (append (vc-switches 'git 'diff)
+                     (list "-p" (or rev1 "HEAD") rev2 "--"))))))
 
 (defun vc-git-revision-table (_files)
   ;; What about `files'?!?  --Stef
@@ -928,7 +965,8 @@
     table))
 
 (defun vc-git-annotate-command (file buf &optional rev)
-  (let ((name (file-relative-name file)))
+  (setq default-directory (vc-git-root default-directory))
+  (let ((name (vc-git-rev-to-filename rev)))
     (vc-git-command buf 'async nil "blame" "--date=iso" "-C" "-C" rev "--" name)))
 
 (declare-function vc-annotate-convert-time "vc-annotate" (time))
@@ -987,7 +1025,11 @@
                            (point)
                            (1- (point-max)))))))
         (or (vc-git-symbolic-commit prev-rev) prev-rev))
-    (vc-git--rev-parse (concat rev "^"))))
+    ;; Use historical data for the file if possible.
+    ;; FIXME: This breaks whole-changeset diffing.
+    (if (vc-git-file-shalist)
+        (car (cddr (member rev (vc-git-file-shalist))))
+      (vc-git--rev-parse (concat rev "^")))))
 
 (defun vc-git--rev-parse (rev)
   (with-temp-buffer
@@ -995,6 +1037,26 @@
      (vc-git--out-ok "rev-parse" rev)
      (buffer-substring-no-properties (point-min) (+ (point-min) 40)))))
 
+(defun vc-git-single-file (files)
+  "Return t if FILES contains a single non-directory file."
+  (and (eq (length files) 1)
+       (not (file-directory-p (car files)))))
+
+(defun vc-git-file-shalist ()
+  "Return alternating list of SHA1 hashes and file names.
+The list contains commit hashes and historical names for a file
+in the current change log buffer."
+  (cond
+   ((and (boundp 'vc-git-file-shalist) vc-git-file-shalist)
+    vc-git-file-shalist)
+   ((and (boundp 'vc-git-file-shalist-raw) vc-git-file-shalist-raw)
+    (set (make-local-variable 'vc-git-file-shalist)
+         (split-string vc-git-file-shalist-raw "\n")))))
+
+(defun vc-git-rev-to-filename (rev)
+  "Return a historical file name for the file in REV."
+  (cadr (member rev (vc-git-file-shalist))))
+
 (defun vc-git-next-revision (file rev)
   "Git-specific version of `vc-next-revision'."
   (let* ((default-directory (file-name-directory


  parent reply	other threads:[~2014-07-30 20:35 UTC|newest]

Thread overview: 20+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2011-05-29 20:17 bug#8756: 23.3; vc-git.el doesn't use --follow argument in vc-git-print-log Steinar Bang
2011-05-31  5:25 ` Dan Nicolaescu
2011-11-28 20:21   ` Steinar Bang
2011-11-28 21:08     ` Dan Nicolaescu
2011-11-29 17:29       ` Steinar Bang
2011-12-01 18:15         ` Dan Nicolaescu
2011-12-01 21:44           ` Steinar Bang
2011-12-01 21:57             ` Steinar Bang
2014-01-09 19:21               ` Glenn Morris
2014-01-11  1:40                 ` Dan Nicolaescu
2014-01-11 14:15                   ` Steinar Bang
2014-01-11 23:32                     ` Steinar Bang
2014-01-12  1:46                     ` Dan Nicolaescu
2014-01-12  3:57                       ` Eli Zaretskii
2014-01-12 10:15                       ` Steinar Bang
2014-01-12 10:45                         ` Steinar Bang
2014-07-30 20:35 ` Dmitry Dzhus [this message]
2019-11-03 14:32   ` bug#8756: " Lars Ingebrigtsen
2019-11-03 18:48     ` Dmitry Gutov
2019-11-08 20:44       ` Lars Ingebrigtsen

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

  List information: https://www.gnu.org/software/emacs/

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

  git send-email \
    --in-reply-to=3258071406752532@web22g.yandex.ru \
    --to=dima@dzhus.org \
    --cc=8756@debbugs.gnu.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.
Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).