=== 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