git.el: Added a command to amend a commit.
It reverts the commit and sets up the status and edit log buffer to allow making changes and recommitting it. Bound to C-c C-a. Signed-off-by: Alexandre Julliard <julliard@winehq.org> Signed-off-by: Junio C Hamano <gitster@pobox.com>
Alexandre Julliard committed
Feb 7, 2008 at 13:50 UTC
76127b3a0d7cf359b12be80971f023b6ee07f7f9
1 file changed
+73
-1
contrib/emacs/git.el
+73
-1
@@ -740,6 +740,27 @@ Return the list of files that haven't been handled."
740
(git-refresh-files)
741
(git-refresh-ewoc-hf git-status)))
742
743
+(defun git-mark-files (status files)
744
+ "Mark all the specified FILES, and unmark the others."
745
+ (setq files (sort files #'string-lessp))
746
+ (let ((file (and files (pop files)))
747
+ (node (ewoc-nth status 0)))
748
+ (while node
749
+ (let ((info (ewoc-data node)))
750
+ (if (and file (string-equal (git-fileinfo->name info) file))
751
+ (progn
752
+ (unless (git-fileinfo->marked info)
753
+ (setf (git-fileinfo->marked info) t)
754
+ (setf (git-fileinfo->needs-refresh info) t))
755
+ (setq file (pop files))
756
+ (setq node (ewoc-next status node)))
757
+ (when (git-fileinfo->marked info)
758
+ (setf (git-fileinfo->marked info) nil)
759
+ (setf (git-fileinfo->needs-refresh info) t))
760
+ (if (and file (string-lessp file (git-fileinfo->name info)))
761
+ (setq file (pop files))
762
+ (setq node (ewoc-next status node))))))))
763
+
764
(defun git-marked-files ()
765
"Return a list of all marked files, or if none a list containing just the file at cursor position."
766
(unless git-status (error "Not in git-status buffer."))
@@ -1218,7 +1239,8 @@ Return the list of files that haven't been handled."
1239
(goto-char (point-min))
1240
(when (re-search-forward "\n+\\'" nil t)
1241
(replace-match "\n" t t))
1221
- (when sign-off (git-append-sign-off committer-name committer-email)))))
1242
+ (when sign-off (git-append-sign-off committer-name committer-email)))
1243
+ buffer))
1244
1245
(defun git-commit-file ()
1246
"Commit the marked file(s), asking for a commit message."
@@ -1251,6 +1273,52 @@ Return the list of files that haven't been handled."
1273
(setq buffer-file-coding-system coding-system)
1274
(re-search-forward (regexp-quote (concat git-log-msg-separator "\n")) nil t))))
1275
1276
+(defun git-setup-commit-buffer (commit)
1277
+ "Setup the commit buffer with the contents of COMMIT."
1278
+ (let (author-name author-email subject date msg)
1279
+ (with-temp-buffer
1280
+ (let ((coding-system (git-get-logoutput-coding-system)))
1281
+ (git-call-process-env t nil "log" "-1" commit)
1282
+ (goto-char (point-min))
1283
+ (when (re-search-forward "^Author: *\\(.*\\) <\\(.*\\)>$" nil t)
1284
+ (setq author-name (match-string 1))
1285
+ (setq author-email (match-string 2)))
1286
+ (when (re-search-forward "^Date: *\\(.*\\)$" nil t)
1287
+ (setq date (match-string 1)))
1288
+ (while (re-search-forward "^ \\(.*\\)$" nil t)
1289
+ (push (match-string 1) msg))
1290
+ (setq msg (nreverse msg))
1291
+ (setq subject (pop msg))
1292
+ (while (and msg (zerop (length (car msg))) (pop msg)))))
1293
+ (git-setup-log-buffer (get-buffer-create "*git-commit*")
1294
+ author-name author-email subject date
1295
+ (mapconcat #'identity msg "\n"))))
1296
+
1297
+(defun git-get-commit-files (commit)
1298
+ "Retrieve the list of files modified by COMMIT."
1299
+ (let (files)
1300
+ (with-temp-buffer
1301
+ (git-call-process-env t nil "diff-tree" "-r" "-z" "--name-only" "--no-commit-id" commit)
1302
+ (goto-char (point-min))
1303
+ (while (re-search-forward "\\([^\0]*\\)\0" nil t 1)
1304
+ (push (match-string 1) files)))
1305
+ files))
1306
+
1307
+(defun git-amend-commit ()
1308
+ "Undo the last commit on HEAD, and set things up to commit an
1309
+amended version of it."
1310
+ (interactive)
1311
+ (unless git-status (error "Not in git-status buffer."))
1312
+ (when (git-empty-db-p) (error "No commit to amend."))
1313
+ (let* ((commit (git-rev-parse "HEAD"))
1314
+ (files (git-get-commit-files commit)))
1315
+ (git-call-process-env nil nil "reset" "--soft" "HEAD^")
1316
+ (git-update-status-files (copy-sequence files) 'uptodate)
1317
+ (git-mark-files git-status files)
1318
+ (git-refresh-files)
1319
+ (git-setup-commit-buffer commit)
1320
+ (git-commit-file)))
1321
+
1322
(defun git-find-file ()
1323
"Visit the current file in its own buffer."
1324
(interactive)
@@ -1329,6 +1397,7 @@ Return the list of files that haven't been handled."
1397
1398
(unless git-status-mode-map
1399
(let ((map (make-keymap))
1400
+ (commit-map (make-sparse-keymap))
1401
(diff-map (make-sparse-keymap))
1402
(toggle-map (make-sparse-keymap)))
1403
(suppress-keymap map)
@@ -1337,6 +1406,7 @@ Return the list of files that haven't been handled."
1406
(define-key map " " 'git-next-file)
1407
(define-key map "a" 'git-add-file)
1408
(define-key map "c" 'git-commit-file)
1409
+ (define-key map "\C-c" commit-map)
1410
(define-key map "d" diff-map)
1411
(define-key map "=" 'git-diff-file)
1412
(define-key map "f" 'git-find-file)
@@ -1362,6 +1432,8 @@ Return the list of files that haven't been handled."
1432
(define-key map "x" 'git-remove-handled)
1433
(define-key map "\C-?" 'git-unmark-file-up)
1434
(define-key map "\M-\C-?" 'git-unmark-all)
1435
+ ; the commit submap
1436
+ (define-key commit-map "\C-a" 'git-amend-commit)
1437
; the diff submap
1438
(define-key diff-map "b" 'git-diff-file-base)
1439
(define-key diff-map "c" 'git-diff-file-combined)