Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Merge tag '1.2.0'

  • Loading branch information...
commit bbaa204e8694d3364d206a4dbbc6369ec0a23f61 2 parents 4870cdc + 5ed79cb
John Wiegley authored September 23, 2012
1  .gitignore
@@ -35,3 +35,4 @@
35 35
 /magit-pkg.el
36 36
 magit.spec
37 37
 50magit.el
  38
+/dir
40  CONTRIBUTING.md
Source Rendered
... ...
@@ -0,0 +1,40 @@
  1
+# How to contribute
  2
+
  3
+Contributions to Magit are highly welcome. Here are a few guidelines that will
  4
+help your patches hit upstream as soon as possible.
  5
+
  6
+## Branching scheme
  7
+
  8
+Magit uses 3 main branches for its lifecyle: `maint`, `master` and `next`.
  9
+
  10
+* `maint` contains the set of patches that will definitely make it into the next
  11
+  minor release.
  12
+* `master` contains the set of patches that will highly likely make it into the
  13
+  next major release.
  14
+* `next` contains patches that necessitate some additional checks/improvements
  15
+  before they're integrating into a release.
  16
+
  17
+## Making changes
  18
+
  19
+A good practice is to create a topic branch off Magit, from the branch you
  20
+target for *final* inclusion. This should *always* be either `maint` or
  21
+`master`.
  22
+
  23
+If you branch off `next`, you'll only put more overhead on the maintainer's
  24
+shoulders, and the integration will suffer from additional delays.
  25
+
  26
+Please make sure your commits are well-organized and "atomic" (hitting a single
  27
+well-defined target each).
  28
+
  29
+Please also make sure you check that byte-compilation completes without errors
  30
+of warnings, and that tests run without failures.
  31
+
  32
+Bonus points if you add tests to cover the feature you're hacking.
  33
+
  34
+## Submitting changes
  35
+
  36
+The preferred way of submitting your changes is to open a pull request on the
  37
+central Magit GitHub repository (https://github.com/magit/magit).
  38
+
  39
+Alternately, you can send your patches to the Magit mailing list
  40
+(magit@googlegroups.com), using `git send-email`.
14  Makefile
@@ -8,7 +8,7 @@ ELCS=$(ELS:.el=.elc)
8 8
 ELCS_CONTRIB=$(ELS_CONTRIB:.el=.elc)
9 9
 DIST_FILES=$(ELS) Makefile magit.texi magit.info README.md magit.spec.in magit-pkg.el.in
10 10
 DIST_FILES_CONTRIB=$(ELS_CONTRIB) contrib/magit
11  
-ELPA_FILES=$(ELS) magit.info magit-pkg.el
  11
+ELPA_FILES=$(ELS) magit.info dir magit-pkg.el
12 12
 
13 13
 .PHONY=install
14 14
 
@@ -24,7 +24,7 @@ all: core docs contrib
24 24
 
25 25
 core: $(ELCS) magit.spec magit-pkg.el 50magit.el
26 26
 
27  
-docs: magit.info
  27
+docs: dir
28 28
 
29 29
 contrib: $(ELCS_CONTRIB)
30 30
 
@@ -42,14 +42,18 @@ magit.elc: magit.el
42 42
 	$(BATCH) --eval '(byte-compile-file "magit.tmp.el")' #NO_DIST
43 43
 	mv magit.tmp.elc magit.elc #NO_DIST
44 44
 	rm magit.tmp.el #NO_DIST
  45
+
  46
+dir: magit.info
  47
+	install-info --dir=$@ $<
  48
+
45 49
 magit.info:
46 50
 
47 51
 dist: magit-$(VERSION).tar.gz
48 52
 
49 53
 magit-$(VERSION).tar.gz: $(DIST_FILES) $(DIST_FILES_CONTRIB)
50 54
 	mkdir -p magit-$(VERSION)/contrib
51  
-	cp --preserve=timestamps $(DIST_FILES) magit-$(VERSION)
52  
-	cp --preserve=timestamps $(DIST_FILES_CONTRIB) magit-$(VERSION)/contrib
  55
+	cp -p $(DIST_FILES) magit-$(VERSION)
  56
+	cp -p $(DIST_FILES_CONTRIB) magit-$(VERSION)/contrib
53 57
 	sed -i -e "1 s/=.*/=$(VERSION)/" magit-$(VERSION)/Makefile #NO_DIST
54 58
 	sed -i -e "/NO_DIST/d" magit-$(VERSION)/Makefile #NO_DIST
55 59
 	sed -i -e "s/@GIT_DEV_VERSION@/$(VERSION)/" magit-$(VERSION)/magit.el #NO_DIST
@@ -60,7 +64,7 @@ elpa: magit-$(VERSION).tar
60 64
 
61 65
 magit-$(VERSION).tar: $(ELPA_FILES)
62 66
 	mkdir magit-$(VERSION)
63  
-	cp --preserve=timestamps $(ELPA_FILES) magit-$(VERSION)
  67
+	cp -p $(ELPA_FILES) magit-$(VERSION)
64 68
 	sed -i -e "s/@GIT_DEV_VERSION@/$(VERSION)/" magit-$(VERSION)/magit.el #NO_DIST
65 69
 	tar -cvf magit-$(VERSION).tar magit-$(VERSION)
66 70
 	rm -rf magit-$(VERSION)
6  README.md
Source Rendered
@@ -8,8 +8,7 @@ later.
8 8
 advantage of Git's native features without breaking compatibility with
9 9
 other systems.
10 10
 
11  
-To get started see the [Magit User Manual][manual] or perhaps the
12  
-[cheatsheet][cheatsheet] if you're in a hurry. There's also an
  11
+To get started see the [Magit User Manual][manual]. There's also an
13 12
 excellent [Magit screencast][screencast] by Alex Vollmer which
14 13
 demonstrates some of the major features.
15 14
 
@@ -69,7 +68,7 @@ Development
69 68
 -----------
70 69
 
71 70
 Magit was started by Marius Vollmer. It is now collectively maintained by the
72  
-Magit Owners Team: https://github.com/organizations/magit/teams/53130
  71
+Magit Owners Team: https://github.com/magit?tab=members
73 72
 
74 73
 For a full list of contributors have a look at `magit.el` in the
75 74
 source distribution.
@@ -81,7 +80,6 @@ Magit's canonical source repository is currently
81 80
 [website]: http://magit.github.com/magit
82 81
 [development]: http://github.com/magit/magit
83 82
 [manual]: http://magit.github.com/magit/magit.html
84  
-[cheatsheet]: http://daemianmack.com/magit-cheatsheet.html
85 83
 [screencast]: http://vimeo.com/2871241
86 84
 [download]: http://github.com/magit/magit/downloads
87 85
 [google group]: http://groups.google.com/group/magit/
238  magit-blame.el
@@ -30,6 +30,7 @@
30 30
 
31 31
 ;;; Code:
32 32
 
  33
+(eval-when-compile (require 'cl))
33 34
 (require 'magit)
34 35
 
35 36
 (defface magit-blame-header
@@ -39,7 +40,7 @@
39 40
 
40 41
 (defface magit-blame-sha1
41 42
   '((t :inherit (magit-log-sha1
42  
-		 magit-blame-header)))
  43
+                 magit-blame-header)))
43 44
   "Face for blame sha1."
44 45
   :group 'magit-faces)
45 46
 
@@ -63,6 +64,8 @@
63 64
     (define-key map (kbd "l") 'magit-blame-locate-commit)
64 65
     (define-key map (kbd "RET") 'magit-blame-locate-commit)
65 66
     (define-key map (kbd "q") 'magit-blame-mode)
  67
+    (define-key map (kbd "n") 'magit-blame-next-chunk)
  68
+    (define-key map (kbd "p") 'magit-blame-previous-chunk)
66 69
     map)
67 70
   "Keymap for an annotated section.\\{magit-blame-map}")
68 71
 
@@ -77,15 +80,15 @@
77 80
   (unless (buffer-file-name)
78 81
     (error "Current buffer has no associated file!"))
79 82
   (when (and (buffer-modified-p)
80  
-	     (y-or-n-p (format "save %s first? " (buffer-file-name))))
  83
+             (y-or-n-p (format "save %s first? " (buffer-file-name))))
81 84
     (save-buffer))
82 85
 
83 86
   (if magit-blame-mode
84 87
       (progn
85  
-	(setq magit-blame-buffer-read-only buffer-read-only)
86  
-	(magit-blame-file-on (current-buffer))
87  
-	(set-buffer-modified-p nil)
88  
-	(setq buffer-read-only t))
  88
+        (setq magit-blame-buffer-read-only buffer-read-only)
  89
+        (magit-blame-file-on (current-buffer))
  90
+        (set-buffer-modified-p nil)
  91
+        (setq buffer-read-only t))
89 92
     (magit-blame-file-off (current-buffer))
90 93
     (set-buffer-modified-p nil)
91 94
     (setq buffer-read-only magit-blame-buffer-read-only)))
@@ -94,47 +97,83 @@
94 97
   (save-excursion
95 98
     (save-restriction
96 99
       (with-current-buffer buffer
97  
-	(widen)
98  
-	(mapc (lambda (ov)
99  
-		(if (overlay-get ov :blame)
100  
-		    (delete-overlay ov)))
101  
-	      (overlays-in (point-min) (point-max)))))))
  100
+        (widen)
  101
+        (mapc (lambda (ov)
  102
+                (if (overlay-get ov :blame)
  103
+                    (delete-overlay ov)))
  104
+              (overlays-in (point-min) (point-max)))))))
102 105
 
103 106
 (defun magit-blame-file-on (buffer)
104 107
   (magit-blame-file-off buffer)
105 108
   (save-excursion
106 109
     (with-current-buffer buffer
107 110
       (save-restriction
108  
-	(with-temp-buffer
109  
-	  (magit-git-insert (list "blame" "--porcelain" "--"
110  
-				  (file-name-nondirectory
111  
-				   (buffer-file-name buffer))))
112  
-	  (magit-blame-parse buffer (current-buffer)))))))
  111
+        (with-temp-buffer
  112
+          (magit-git-insert (list "blame" "--porcelain" "--"
  113
+                                  (file-name-nondirectory
  114
+                                   (buffer-file-name buffer))))
  115
+          (magit-blame-parse buffer (current-buffer)))))))
113 116
 
114 117
 (defun magit-blame-locate-commit (pos)
115 118
   "Jump to a commit in the branch history from an annotated blame section."
116 119
   (interactive "d")
117 120
   (let ((overlays (overlays-at pos))
118  
-	sha1)
  121
+        sha1)
119 122
     (dolist (ov overlays)
120 123
       (if (overlay-get ov :blame)
121  
-	  (setq sha1 (plist-get (nth 3 (overlay-get ov :blame)) :sha1))))
  124
+          (setq sha1 (plist-get (nth 3 (overlay-get ov :blame)) :sha1))))
122 125
     (if sha1
123  
-	(magit-show-commit sha1))))
  126
+        (magit-show-commit sha1))))
  127
+
  128
+(defun magit-find-next-overlay-change (BEG END PROP)
  129
+  "Return the next position after BEG where an overlay matching a
  130
+property PROP starts or ends. If there are no matching overlay
  131
+boundaries from BEG to END, the return value is nil."
  132
+  (save-excursion
  133
+    (goto-char BEG)
  134
+    (catch 'found
  135
+      (flet ((overlay-change (pos)
  136
+                             (if (< BEG END) (next-overlay-change pos)
  137
+                               (previous-overlay-change pos)))
  138
+             (within-bounds-p (pos)
  139
+                              (if (< BEG END) (< pos END)
  140
+                                (> pos END))))
  141
+        (let ((ov-pos BEG))
  142
+          ;; iterate through overlay changes from BEG to END
  143
+          (while (within-bounds-p ov-pos)
  144
+            (let* ((next-ov-pos (overlay-change ov-pos))
  145
+                   ;; search for an overlay with a PROP property
  146
+                   (next-ov
  147
+                    (let ((overlays (overlays-at next-ov-pos)))
  148
+                      (while (and overlays
  149
+                                  (not (overlay-get (car overlays) PROP)))
  150
+                        (setq overlays (cdr overlays)))
  151
+                      (car overlays))))
  152
+              (if next-ov
  153
+                  ;; found the next overlay with prop PROP at next-ov-pos
  154
+                  (throw 'found next-ov-pos)
  155
+                ;; no matching overlay found, keep looking
  156
+                (setq ov-pos next-ov-pos)))))))))
  157
+
  158
+(defun magit-blame-next-chunk (pos)
  159
+  "Go to the next blame chunk."
  160
+  (interactive "d")
  161
+  (let ((next-chunk-pos (magit-find-next-overlay-change pos (point-max) :blame)))
  162
+    (when next-chunk-pos
  163
+      (goto-char next-chunk-pos))))
  164
+
  165
+(defun magit-blame-previous-chunk (pos)
  166
+  "Go to the previous blame chunk."
  167
+  (interactive "d")
  168
+  (let ((prev-chunk-pos (magit-find-next-overlay-change pos (point-min) :blame)))
  169
+    (when prev-chunk-pos
  170
+      (goto-char prev-chunk-pos))))
124 171
 
125 172
 (defcustom magit-time-format-string "%Y-%m-%dT%T%z"
126 173
   "How to format time in magit-blame header."
127 174
   :group 'magit
128 175
   :type 'string)
129 176
 
130  
-(defun magit-blame-split-time (unixtime)
131  
-  "Split UNIXTIME into (HIGH LOW) format expected by Emacs's time functions."
132  
-  (list (lsh unixtime -16) (logand unixtime #xFFFF)))
133  
-
134  
-(defun magit-blame-unsplit-time (unixtime)
135  
-  "Convert UNIXTIME from (HIGH LOW) format to single number."
136  
-  (+ (lsh (car unixtime) 16) (cadr unixtime)))
137  
-
138 177
 (defun magit-blame-decode-time (unixtime &optional tz)
139 178
   "Decode UNIXTIME into (HIGH LOW) format.
140 179
 
@@ -144,14 +183,14 @@ containing seconds since epoch or Emacs's (HIGH LOW
144 183
 . IGNORED) format."
145 184
   (when (numberp tz)
146 185
     (unless (numberp unixtime)
147  
-      (setq unixtime (magit-blame-unsplit-time unixtime)))
  186
+      (setq unixtime (float-time unixtime)))
148 187
     (let* ((ptz (abs tz))
149 188
            (min (+ (* (/ ptz 100) 60)
150 189
                    (mod ptz 100))))
151 190
       (setq unixtime (+ (* (if (< tz 0) (- min) min) 60) unixtime))))
152 191
 
153 192
   (when (numberp unixtime)
154  
-    (setq unixtime (magit-blame-split-time unixtime)))
  193
+    (setq unixtime (seconds-to-time unixtime)))
155 194
   unixtime)
156 195
 
157 196
 (defun magit-blame-format-time-string (format &optional unixtime tz)
@@ -175,93 +214,90 @@ officially supported at the moment."
175 214
   "Parse blame-info in buffer BLAME-BUF and decorate TARGET-BUF buffer."
176 215
   (save-match-data
177 216
     (let ((blank (propertize " " 'face 'magit-blame-header))
178  
-	  (nl (propertize "\n" 'face 'magit-blame-header))
179  
-	  (commit-hash (make-hash-table :test 'equal :size 577))
180  
-	  commit commit-info old-line new-line num old-file subject author
181  
-	  author-time author-timezone info ov beg end blame)
  217
+          (nl (propertize "\n" 'face 'magit-blame-header))
  218
+          (commit-hash (make-hash-table :test 'equal :size 577))
  219
+          commit commit-info old-line new-line num old-file subject author
  220
+          author-time author-timezone info ov beg end blame)
182 221
       (with-current-buffer blame-buf
183  
-	(goto-char (point-min))
184  
-	;; search for a ful commit info
185  
-	(while (re-search-forward "^\\([0-9a-f]\\{40\\}\\) \\([0-9]+\\) \\([0-9]+\\) \\([0-9]+\\)$" nil t)
186  
-	  (setq commit (match-string-no-properties 1)
187  
-		old-line (string-to-number
188  
-			  (match-string-no-properties 2))
189  
-		new-line (string-to-number
190  
-			  (match-string-no-properties 3))
191  
-		num (string-to-number
192  
-		     (match-string-no-properties 4)))
193  
-	  ;; was this commit already seen (and stored in the hash)?
194  
-	  (setq commit-info (gethash commit commit-hash))
195  
-	  ;; Nope, this is the 1st time, the full commit-info follow.
196  
-	  (unless commit-info
197  
-	    (re-search-forward "^author \\(.+\\)$")
198  
-	    (setq author (match-string-no-properties 1))
  222
+        (goto-char (point-min))
  223
+        ;; search for a ful commit info
  224
+        (while (re-search-forward "^\\([0-9a-f]\\{40\\}\\) \\([0-9]+\\) \\([0-9]+\\) \\([0-9]+\\)$" nil t)
  225
+          (setq commit (match-string-no-properties 1)
  226
+                old-line (string-to-number
  227
+                          (match-string-no-properties 2))
  228
+                new-line (string-to-number
  229
+                          (match-string-no-properties 3))
  230
+                num (string-to-number
  231
+                     (match-string-no-properties 4)))
  232
+          ;; was this commit already seen (and stored in the hash)?
  233
+          (setq commit-info (gethash commit commit-hash))
  234
+          ;; Nope, this is the 1st time, the full commit-info follow.
  235
+          (unless commit-info
  236
+            (re-search-forward "^author \\(.+\\)$")
  237
+            (setq author (match-string-no-properties 1))
199 238
             (re-search-forward "^author-time \\(.+\\)$")
200  
-            (setq author-time (magit-blame-split-time
201  
-                               (truncate
202  
-                                (string-to-number
203  
-                                 (match-string-no-properties 1)))))
  239
+            (setq author-time (string-to-number
  240
+                               (match-string-no-properties 1)))
204 241
             (re-search-forward "^author-tz \\(.+\\)$")
205  
-            (setq author-timezone (truncate
206  
-                                   (string-to-number
207  
-                                    (match-string-no-properties 1))))
208  
-	    (re-search-forward "^summary \\(.+\\)$")
209  
-	    (setq subject (match-string-no-properties 1))
210  
-	    (re-search-forward "^filename \\(.+\\)$")
211  
-	    (setq old-file (match-string-no-properties 1))
212  
-	    (setq commit-info (list :sha1 commit :author author
  242
+            (setq author-timezone (string-to-number
  243
+                                   (match-string-no-properties 1)))
  244
+            (re-search-forward "^summary \\(.+\\)$")
  245
+            (setq subject (match-string-no-properties 1))
  246
+            (re-search-forward "^filename \\(.+\\)$")
  247
+            (setq old-file (match-string-no-properties 1))
  248
+            (setq commit-info (list :sha1 commit :author author
213 249
                                     :author-time author-time
214 250
                                     :author-timezone author-timezone
215  
-				    :subject subject :file old-file))
216  
-	    ;; save it in the hash
217  
-	    (puthash commit commit-info commit-hash))
218  
-	  ;; add the current blame-block into the list INFO.
219  
-	  (setq info (cons (list old-line new-line num commit-info)
220  
-			   info))))
  251
+                                    :subject subject :file old-file))
  252
+            ;; save it in the hash
  253
+            (puthash commit commit-info commit-hash))
  254
+          ;; add the current blame-block into the list INFO.
  255
+          (setq info (cons (list old-line new-line num commit-info)
  256
+                           info))))
221 257
       ;; now do from beginning
222 258
       (setq info (nreverse info))
223 259
       (with-current-buffer target-buf
224  
-	;; for every blame chunk
225  
-	(dolist (chunk info)
226  
-	  (setq commit-info (nth 3 chunk)
227  
-		old-line (nth 0 chunk)
228  
-		new-line (nth 1 chunk)
229  
-		num (nth 2 chunk)
230  
-		commit (plist-get commit-info :sha1)
231  
-		author (plist-get commit-info :author)
  260
+        ;; for every blame chunk
  261
+        (dolist (chunk info)
  262
+          (setq commit-info (nth 3 chunk)
  263
+                old-line (nth 0 chunk)
  264
+                new-line (nth 1 chunk)
  265
+                num (nth 2 chunk)
  266
+                commit (plist-get commit-info :sha1)
  267
+                author (plist-get commit-info :author)
232 268
                 author-time (plist-get commit-info :author-time)
233 269
                 author-timezone (plist-get commit-info :author-timezone)
234  
-		subject (plist-get commit-info :subject))
235  
-
236  
-	  (goto-char (point-min))
237  
-	  (forward-line (1- new-line))
238  
-
239  
-	  (setq beg (line-beginning-position)
240  
-		end (save-excursion
241  
-		      (forward-line num)
242  
-		      (line-beginning-position)))
243  
-	  ;; mark the blame chunk
244  
-	  (put-text-property beg end :blame chunk)
245  
-
246  
-	  ;; make an overlay with blame info as 'before-string
247  
-	  ;; on the current chunk.
248  
-	  (setq ov (make-overlay beg end))
249  
-	  (overlay-put ov :blame chunk)
250  
-	  (setq blame (concat
251  
-		       (propertize (substring-no-properties commit 0 8)
252  
-				   'face 'magit-blame-sha1)
253  
-		       blank
254  
-		       (propertize (format "%-20s" author)
255  
-				   'face 'magit-blame-culprit)
  270
+                subject (plist-get commit-info :subject))
  271
+
  272
+          (goto-char (point-min))
  273
+          (forward-line (1- new-line))
  274
+
  275
+          (setq beg (line-beginning-position)
  276
+                end (save-excursion
  277
+                      (forward-line num)
  278
+                      (line-beginning-position)))
  279
+          ;; mark the blame chunk
  280
+          (put-text-property beg end :blame chunk)
  281
+
  282
+          ;; make an overlay with blame info as 'before-string
  283
+          ;; on the current chunk.
  284
+          (setq ov (make-overlay beg end))
  285
+          (overlay-put ov :blame chunk)
  286
+          (setq blame (concat
  287
+                       (propertize (substring-no-properties commit 0 8)
  288
+                                   'face 'magit-blame-sha1)
  289
+                       blank
  290
+                       (propertize (format "%-20s" author)
  291
+                                   'face 'magit-blame-culprit)
256 292
                        blank
257 293
                        (propertize (magit-blame-format-time-string
258 294
                                     magit-time-format-string
259 295
                                     author-time author-timezone)
260 296
                                    'face 'magit-blame-time)
261  
-		       blank
262  
-		       (propertize subject 'face 'magit-blame-subject)
263  
-		       blank nl))
264  
-	  (overlay-put ov 'before-string blame))))))
  297
+                       blank
  298
+                       (propertize subject 'face 'magit-blame-subject)
  299
+                       blank nl))
  300
+          (overlay-put ov 'before-string blame))))))
265 301
 
266 302
 (provide 'magit-blame)
267 303
 ;;; magit-blame.el ends here
1  magit-key-mode.el
@@ -120,7 +120,6 @@
120 120
      (switches
121 121
       ("-ff" "Fast-forward only" "--ff-only")
122 122
       ("-nf" "No fast-forward" "--no-ff")
123  
-      ("-nc" "No commit" "--no-commit")
124 123
       ("-sq" "Squash" "--squash"))
125 124
      (arguments
126 125
       ("-st" "Strategy" "--strategy=" read-from-minibuffer)))
135  magit-wip.el
... ...
@@ -1,7 +1,10 @@
1 1
 ;;; magit-wip.el --- git-wip plug-in for Magit
2 2
 
  3
+;; Copyright (C) 2012  Jonas Bernoulli
3 4
 ;; Copyright (C) 2012  Ryan C. Thompson
4  
-;;
  5
+
  6
+;; Maintainer: Jonas Bernoulli <jonas@bernoul.li>
  7
+
5 8
 ;; Magit is free software; you can redistribute it and/or modify it
6 9
 ;; under the terms of the GNU General Public License as published by
7 10
 ;; the Free Software Foundation; either version 3, or (at your option)
@@ -17,29 +20,36 @@
17 20
 
18 21
 ;;; Commentary:
19 22
 
20  
-;; This plug-in provides special highlighting of git-wip refs in Magit
21  
-;; log buffers.
  23
+;; This plug-in provides support for special work-in-progress refs.
22 24
 
23  
-;; Available commands:
24  
-;; - `magit-wip-mode': Enable or disable the mode
  25
+;; This requires the third-party git command "git wip" which is available
  26
+;; from https://github.com/bartman/git-wip.
25 27
 
26  
-;; TODO:
27  
-;; - Provide an interface for `git wip log'
  28
+;; The global mode `magit-wip-mode' provides highlighting of wip refs in
  29
+;; Magit buffers while the local mode `magit-wip-save-mode' commits to
  30
+;; such a ref when saving a file-visiting buffer.
28 31
 
29  
-;;; Code:
  32
+;; To enable `magit-wip-save-mode' enable `global-magit-wip-save-mode'
  33
+;; and use the Magit extension mechanism to select the repositories in
  34
+;; which you want to use a work-in-progress ref.  Usually you also want
  35
+;; to enable `magit-wip-mode'.
  36
+;;
  37
+;;   (magit-wip-mode 1)
  38
+;;   (global-magit-wip-save-mode 1)
  39
+;;
  40
+;;   $ git config --add magit.extension wip-save           # or
  41
+;;   $ git config --global --add magit.extension wip-save
30 42
 
31  
-(require 'magit)
  43
+;; Note that `global-magit-wip-save-mode' is the only mode that uses the
  44
+;; extension mechanism for file-visiting buffers all other global modes
  45
+;; making use of it to turn on local modes in Magit buffers.
32 46
 
33  
-;;; Customizables:
  47
+;;; Code:
34 48
 
35  
-(define-minor-mode magit-wip-mode
36  
-  "Magit support fot git-wip.
  49
+(require 'magit)
  50
+(require 'format-spec)
37 51
 
38  
-Currently, this will just give git-wip refs a special appearance
39  
-in Magit log buffers."
40  
-  :group 'magit
41  
-  :global t
42  
-  (magit-wip-setup-namespaces))
  52
+;;; Magit Wip Mode.
43 53
 
44 54
 (defface magit-log-head-label-wip
45 55
   '((((class color) (background light))
@@ -53,22 +63,91 @@ in Magit log buffers."
53 63
   "Face for git-wip labels shown in log buffer."
54 64
   :group 'magit-faces)
55 65
 
56  
-;;; Common code:
57  
-
58 66
 (defun magit-log-get-wip-color (suffix)
59 67
   (list (concat "(WIP) " suffix)
60 68
         'magit-log-head-label-wip))
61 69
 
62  
-(defconst magit-wip-refs-namespaces
63  
-  '(("wip" magit-log-get-wip-color))
64  
-  "A list like `magit-refs-namespaces' but specific to git-wip.")
  70
+(defconst magit-wip-refs-namespace
  71
+  '("wip" magit-log-get-wip-color))
  72
+
  73
+;;;###autoload
  74
+(define-minor-mode magit-wip-mode
  75
+  "In Magit log buffers; give wip refs a special appearance."
  76
+  :group 'magit
  77
+  :global t
  78
+  (if magit-wip-mode
  79
+      (add-to-list 'magit-refs-namespaces magit-wip-refs-namespace 'append)
  80
+    (setq magit-refs-namespaces
  81
+          (delete magit-wip-refs-namespace magit-refs-namespaces))))
65 82
 
66  
-(defun magit-wip-setup-namespaces ()
67  
-  (let ((adder (lambda (ns) (add-to-list 'magit-refs-namespaces ns 'append)))
68  
-        (remover (lambda (ns) (setq magit-refs-namespaces (delete ns magit-refs-namespaces)))))
69  
-    (mapc (if magit-wip-mode adder remover)
70  
-          magit-wip-refs-namespaces)))
  83
+;;; Magit Wip Save Mode.
71 84
 
72  
-(magit-wip-setup-namespaces)
  85
+(defcustom magit-wip-commit-message "WIP %r"
  86
+  "Commit message for git-wip commits.
  87
+
  88
+The following `format'-like specs are supported:
  89
+%f the full name of the file being saved, and
  90
+%r the name of the file being saved, relative to the repository root
  91
+%g the root of the git repository."
  92
+  :group 'magit
  93
+  :type 'string)
  94
+
  95
+(defcustom magit-wip-echo-area-message "Wrote %f (wip)"
  96
+  "Message shown in the echo area after creating a git-wip commit.
  97
+
  98
+The following `format'-like specs are supported:
  99
+%f the full name of the file being saved, and
  100
+%r the name of the file being saved, relative to the repository root.
  101
+%g the root of the git repository."
  102
+  :group 'magit
  103
+  :type '(choice (const :tag "No message" nil) string))
  104
+
  105
+(defvar magit-wip-save-mode-lighter " Wip")
  106
+
  107
+;;;###autoload
  108
+(define-minor-mode magit-wip-save-mode
  109
+  "Magit support for committing to a work-in-progress ref.
  110
+
  111
+When this minor mode is turned on and a file is saved inside a writable
  112
+git repository then it is also committed to a special work-in-progress
  113
+ref."
  114
+  :lighter magit-wip-save-mode-lighter
  115
+  (if magit-wip-save-mode
  116
+      (add-hook  'after-save-hook 'magit-wip-save-safe t t)
  117
+    (remove-hook 'after-save-hook 'magit-wip-save-safe t)))
  118
+
  119
+;;;###autoload
  120
+(define-globalized-minor-mode global-magit-wip-save-mode
  121
+  magit-wip-save-mode turn-on-magit-wip-save
  122
+  :group 'magit)
  123
+
  124
+(defun turn-on-magit-wip-save ()
  125
+  (when (and (buffer-file-name)
  126
+             (magit-get-top-dir default-directory)
  127
+             (member "wip-save" (magit-get-all "magit.extension")))
  128
+    (if (= (magit-git-exit-code "wip" "-h") 0)
  129
+        (magit-wip-save-mode 1)
  130
+      (message "Git command 'git wip' cannot be found"))))
  131
+
  132
+(defun magit-wip-save-safe ()
  133
+  (condition-case err
  134
+      (magit-wip-save)
  135
+    (error
  136
+     (message "Magit WIP got an error: %S" err))))
  137
+
  138
+(defun magit-wip-save ()
  139
+  (let* ((top-dir (magit-get-top-dir default-directory))
  140
+         (name (file-truename (buffer-file-name)))
  141
+         (spec `((?r . ,(file-relative-name name top-dir))
  142
+                 (?f . ,(buffer-file-name))
  143
+                 (?g . ,top-dir))))
  144
+    (when (and top-dir (file-writable-p top-dir))
  145
+      (save-excursion ; kludge see https://github.com/magit/magit/issues/441
  146
+        (magit-run-git "wip" "save"
  147
+                       (format-spec magit-wip-commit-message spec)
  148
+                       "--editor" "--" name))
  149
+      (when magit-wip-echo-area-message
  150
+        (message (format-spec magit-wip-echo-area-message spec))))))
73 151
 
74 152
 (provide 'magit-wip)
  153
+;;; magit-wip.el ends here
85  magit.el
@@ -651,6 +651,9 @@ operation after commit).")
651 651
     (define-key map (kbd "z") 'magit-key-mode-popup-stashing)
652 652
     map))
653 653
 
  654
+(eval-after-load 'dired-x
  655
+  '(define-key magit-status-mode-map [remap dired-jump] 'magit-dired-jump))
  656
+
654 657
 (defvar magit-log-mode-map
655 658
   (let ((map (make-sparse-keymap)))
656 659
     (define-key map (kbd ".") 'magit-mark-item)
@@ -2154,17 +2157,29 @@ function can be enriched by magit extension like magit-topgit and magit-svn"
2154 2157
   "Checks if git/ssh asks for a password and ask the user for it."
2155 2158
   (let (ask)
2156 2159
     (cond ((or (string-match "^Enter passphrase for key '\\\(.*\\\)': $" string)
2157  
-               (string-match "^\\\(.*\\\)'s password:" string))
  2160
+               (string-match "^\\\(.*\\\)'s password:" string)
  2161
+               (string-match "^Password for '\\\(.*\\\)':" string))
2158 2162
            (setq ask (format "Password for '%s': " (match-string 1 string))))
2159 2163
           ((string-match "^[pP]assword:" string)
2160 2164
            (setq ask "Password:")))
2161 2165
     (when ask
2162 2166
       (process-send-string proc (concat (read-passwd ask nil) "\n")))))
2163 2167
 
  2168
+(defun magit-username (proc string)
  2169
+  "Checks if git asks for a username and ask the user for it."
  2170
+  (when (string-match "^Username for '\\\(.*\\\)':" string)
  2171
+    (process-send-string proc
  2172
+                         (concat
  2173
+                          (read-string (format "Username for '%s': "
  2174
+                                               (match-string 1 string))
  2175
+                                       nil nil (user-login-name))
  2176
+                          "\n"))))
  2177
+
2164 2178
 (defun magit-process-filter (proc string)
2165 2179
   (save-current-buffer
2166 2180
     (set-buffer (process-buffer proc))
2167 2181
     (let ((inhibit-read-only t))
  2182
+      (magit-username proc string)
2168 2183
       (magit-password proc string)
2169 2184
       (goto-char (process-mark proc))
2170 2185
       ;; Find last ^M in string.  If one was found, ignore everything
@@ -3545,7 +3560,7 @@ FULLY-QUALIFIED-NAME is non-nil."
3545 3560
 (defun magit-init (dir)
3546 3561
   "Initialize git repository in the DIR directory."
3547 3562
   (interactive (list (read-directory-name "Directory for Git repository: ")))
3548  
-  (let* ((dir (expand-file-name dir))
  3563
+  (let* ((dir (file-name-as-directory (expand-file-name dir)))
3549 3564
          (topdir (magit-get-top-dir dir)))
3550 3565
     (when (or (not topdir)
3551 3566
               (yes-or-no-p
@@ -3557,7 +3572,8 @@ FULLY-QUALIFIED-NAME is non-nil."
3557 3572
       (unless (file-directory-p dir)
3558 3573
         (and (y-or-n-p (format "Directory %s does not exists.  Create it? " dir))
3559 3574
              (make-directory dir)))
3560  
-      (magit-run* (list magit-git-executable "init" dir)))))
  3575
+      (let ((default-directory dir))
  3576
+        (magit-run* (list magit-git-executable "init"))))))
3561 3577
 
3562 3578
 (define-derived-mode magit-status-mode magit-mode "Magit"
3563 3579
   "Mode for looking at git status.
@@ -3646,7 +3662,10 @@ user input."
3646 3662
 \('git merge REVISION')."
3647 3663
   (interactive (list (magit-read-rev "Merge" (magit-guess-branch))))
3648 3664
   (when revision
3649  
-    (magit-run-git "merge" "--no-commit" (magit-rev-to-git revision))
  3665
+    (apply 'magit-run-git
  3666
+           "merge" "--no-commit"
  3667
+           (magit-rev-to-git revision)
  3668
+           magit-custom-options)
3650 3669
     (when (file-exists-p ".git/MERGE_MSG")
3651 3670
         (magit-log-edit))))
3652 3671
 
@@ -4553,9 +4572,9 @@ This means that the eventual commit does 'git commit --allow-empty'."
4553 4572
     (setq magit-pre-log-edit-window-configuration
4554 4573
           (current-window-configuration))
4555 4574
     (pop-to-buffer buf)
  4575
+    (setq default-directory dir)
4556 4576
     (when (file-exists-p (concat (magit-git-dir) "MERGE_MSG"))
4557 4577
       (insert-file-contents (concat (magit-git-dir) "MERGE_MSG")))
4558  
-    (setq default-directory dir)
4559 4578
     (magit-log-edit-mode)
4560 4579
     (make-local-variable 'magit-buffer-internal)
4561 4580
     (setq magit-buffer-internal magit-buf)
@@ -5057,7 +5076,7 @@ restore the window state that was saved before ediff was called."
5057 5076
       (magit-git-section 'diffbuf
5058 5077
                          (magit-rev-range-describe range "Changes")
5059 5078
                          'magit-wash-diffs
5060  
-                         "diff" (magit-diff-U-arg) args))))
  5079
+                         "diff" (magit-diff-U-arg) args "--"))))
5061 5080
 
5062 5081
 (define-derived-mode magit-diff-mode magit-mode "Magit Diff"
5063 5082
   "Mode for looking at a git diff.
@@ -5184,7 +5203,7 @@ the current git repository."
5184 5203
   (let ((topdir (expand-file-name
5185 5204
                  (magit-get-top-dir (or (file-name-directory filename)
5186 5205
                                         default-directory))))
5187  
-        (file (expand-file-name filename)))
  5206
+        (file (file-truename filename)))
5188 5207
     (when (and (not (string= topdir ""))
5189 5208
                ;; FILE must start with the git repository path
5190 5209
                (zerop (string-match-p (concat "\\`" topdir) file)))
@@ -5292,9 +5311,14 @@ values (such as wildcards) that might be of interest.
5292 5311
 
5293 5312
 If LOCAL is nil, the `.gitignore' file is updated.
5294 5313
 Otherwise, it is `.git/info/exclude'."
5295  
-  (let ((ignore-file (if local (concat (magit-git-dir) "info/exclude") ".gitignore")))
  5314
+  (let* ((local-ignore-dir (concat (magit-git-dir) "info/"))
  5315
+         (ignore-file (if local
  5316
+                          (concat local-ignore-dir "exclude")
  5317
+                        ".gitignore")))
5296 5318
     (if edit
5297 5319
       (setq file (magit-ignore-modifiable-file file edit)))
  5320
+    (if (and local (not (file-exists-p local-ignore-dir)))
  5321
+        (make-directory local-ignore-dir t))
5298 5322
     (with-temp-buffer
5299 5323
       (when (file-exists-p ignore-file)
5300 5324
         (insert-file-contents ignore-file))
@@ -5436,6 +5460,22 @@ The name of the change log file is set by variable change-log-default-name."
5436 5460
   (interactive)
5437 5461
   (magit-visiting-file-item (call-interactively 'add-change-log-entry-other-window)))
5438 5462
 
  5463
+(eval-after-load 'dired-x
  5464
+  '(defun magit-dired-jump (&optional other-window)
  5465
+    "Visit current item.
  5466
+With a prefix argument, visit in other window."
  5467
+    (interactive "P")
  5468
+    (require 'dired-x)
  5469
+    (magit-section-action (item info "dired-jump")
  5470
+      ((untracked file)
  5471
+       (dired-jump other-window (file-truename info)))
  5472
+      ((diff)
  5473
+       (dired-jump other-window (file-truename (magit-diff-item-file item))))
  5474
+      ((hunk)
  5475
+       (dired-jump other-window
  5476
+                   (file-truename (magit-diff-item-file
  5477
+                                   (magit-hunk-item-diff item))))))))
  5478
+
5439 5479
 (defun magit-visit-file-item (&optional other-window)
5440 5480
   "Visit current file associated with item.
5441 5481
 With a prefix argument, visit in other window."
@@ -5575,7 +5615,8 @@ Return values:
5575 5615
          (commit (and (member 'commit (magit-section-context-type section))
5576 5616
                       (magit-section-info section)))
5577 5617
          (old-editor (getenv "GIT_EDITOR")))
5578  
-    (setenv "GIT_EDITOR" (locate-file "emacsclient" exec-path))
  5618
+    (setenv "GIT_EDITOR" (concat (locate-file "emacsclient" exec-path)
  5619
+                                 " -s " server-name))
5579 5620
     (unwind-protect
5580 5621
         (magit-run-git-async "rebase" "-i"
5581 5622
                              (or (and commit (concat commit "^"))
@@ -5612,14 +5653,13 @@ These are the branch names with the remote name stripped."
5612 5653
 (defvar magit-branches-buffer-name "*magit-branches*")
5613 5654
 
5614 5655
 (defun magit--is-branch-at-point-remote ()
5615  
-  "Return t if the branch at point is a remote tracking branch"
  5656
+  "Return non-nil if the branch at point is a remote tracking branch"
5616 5657
   (magit-remote-part-of-branch (magit--branch-name-at-point)))
5617 5658
 
5618 5659
 (defun magit-remote-part-of-branch (branch)
5619 5660
   (when (string-match-p "^\\(?:refs/\\)?remotes\\/" branch)
5620 5661
     (loop for remote in (magit-git-lines "remote")
5621  
-          until (string-match-p (format "^\\(?:refs/\\)?remotes\\/%s\\/" (regexp-quote remote)) branch)
5622  
-          finally return remote)))
  5662
+          when (string-match-p (format "^\\(?:refs/\\)?remotes\\/%s\\/" (regexp-quote remote)) branch) return remote)))
5623 5663
 
5624 5664
 (defun magit-branch-no-remote (branch)
5625 5665
   (let ((remote (magit-remote-part-of-branch branch)))
@@ -5754,23 +5794,26 @@ These are the branch names with the remote name stripped."
5754 5794
          (markers
5755 5795
           (append (mapcar (lambda (remote)
5756 5796
                             (save-excursion
5757  
-                              (search-forward-regexp (concat "^  remotes\\/" remote))
5758  
-                              (beginning-of-line)
5759  
-                              (point-marker)))
  5797
+                              (when (search-forward-regexp
  5798
+                                     (concat "^  remotes\\/" remote) nil t)
  5799
+                                (beginning-of-line)
  5800
+                                (point-marker))))
5760 5801
                           remotes)
5761 5802
                   (list (save-excursion
5762 5803
                           (goto-char (point-max))
5763 5804
                           (point-marker)))))
5764 5805
          ; list of remote elements to display in the buffer
5765 5806
          (remote-groups (loop for remote in remotes
5766  
-                              for end-marker in (cdr markers)
5767  
-                              collect (list remote end-marker))))
  5807
+                              for end-markers on (cdr markers)
  5808
+                              for marker = (loop for x in end-markers thereis x)
  5809
+                              collect (list remote marker))))
5768 5810
 
5769 5811
     ; actual displaying of information
5770 5812
     (magit-with-section "local" nil
5771 5813
       (insert-before-markers (propertize "Local:" 'face 'magit-section-title) "\n")
5772 5814
       (magit-set-section-info ".")
5773  
-      (magit-wash-branches-between-point-and-marker (car markers)))
  5815
+      (magit-wash-branches-between-point-and-marker
  5816
+       (loop for x in markers thereis x)))
5774 5817
 
5775 5818
     (insert-before-markers "\n")
5776 5819
 
@@ -5778,7 +5821,8 @@ These are the branch names with the remote name stripped."
5778 5821
 
5779 5822
     ; make sure markers point to nil so that they can be garbage collected
5780 5823
     (mapc (lambda (marker)
5781  
-            (set-marker marker nil))
  5824
+            (when marker
  5825
+             (set-marker marker nil)))
5782 5826
           markers)))
5783 5827
 
5784 5828
 (defun magit-refresh-branch-manager ()
@@ -5950,7 +5994,8 @@ With a prefix arg, do a submodule update --init"
5950 5994
 layer. This can be added to `magit-mode-hook' for example"
5951 5995
   (dolist (ext (magit-get-all "magit.extension"))
5952 5996
     (let ((sym (intern (format "magit-%s-mode" ext))))
5953  
-      (when (fboundp sym)
  5997
+      (when (and (fboundp sym)
  5998
+                 (not (eq sym 'magit-wip-save-mode)))
5954 5999
         (funcall sym 1)))))
5955 6000
 
5956 6001
 (provide 'magit)
27  magit.texi
@@ -52,6 +52,7 @@ maintain compatibility are still welcome.
52 52
 * Wazzup::
53 53
 * Merging::
54 54
 * Rebasing::
  55
+* Interactive Rebasing::
55 56
 * Rewriting::
56 57
 * Pushing and Pulling::
57 58
 * Bisecting::
@@ -663,6 +664,30 @@ Of course, you can initiate a rebase in any number of ways, by
663 664
 configuring @code{git pull} to rebase instead of merge, for example.
664 665
 Such a rebase can be finished with @kbd{R} as well.
665 666
 
  667
+@node Interactive Rebasing
  668
+@chapter Interactive Rebasing
  669
+
  670
+Typing @kbd{E} in the status buffer will initiate an interactive
  671
+rebase. This is equivalent to running @code{git rebase --interactive}
  672
+at the command line. The @file{git-rebase-todo} file will be opened in
  673
+an Emacs buffer for you to edit. This file is opened using
  674
+@code{emacsclient}, so just edit this file as you normally would, then
  675
+call the @code{server-edit} function (typically bound to @kbd{C-x #})
  676
+to tell Emacs you are finished editing, and the rebase will proceed as
  677
+usual.
  678
+
  679
+If you have loaded @file{rebase-mode.el} (which is included in the
  680
+Magit distribution), the @file{git-rebase-todo} buffer will be in
  681
+@code{rebase-mode}. This mode disables normal text editing but instead
  682
+provides single-key commands (shown in the buffer) to perform all the
  683
+edits that you would normally do manually, including changing the
  684
+operation to be performed each commit (``pick'', ``squash'', etc.),
  685
+deleting (commenting out) commits from the list, and reordering
  686
+commits. You can finish editing the buffer and proceed with the rebase
  687
+by pressing @kbd{C-c C-c}, which is bound to @code{server-edit} in
  688
+this mode, and you can abort the rebase with @kbd{C-c C-k}, just like
  689
+when editing a commit message in Magit.
  690
+
666 691
 @node Rewriting
667 692
 @chapter Rewriting
668 693
 
@@ -715,7 +740,7 @@ keeping information from the status buffer.
715 740
 
716 741
 If you rather wish to start over, type @kbd{r a}.  This will abort the
717 742
 rewriting, resetting the current head back to the value it had before
718  
-the rewrite was started with @kbd{r s}.
  743
+the rewrite was started with @kbd{r b}.
719 744
 
720 745
 Typing @kbd{r f} will @emph{finish} the rewrite: it will apply all
721 746
 unused commits one after the other, as if you would us @kbd{A} with
36  tests/magit-tests.el
@@ -8,6 +8,7 @@
8 8
       (error "Skipping tests, mocker.el is not available"))))
9 9
 
10 10
 (require 'magit)
  11
+(require 'magit-blame)
11 12
 
12 13
 (defmacro with-temp-git-repo (repo &rest body)
13 14
   (declare (indent 1) (debug t))
@@ -17,8 +18,17 @@
17 18
          (progn
18 19
            (magit-init repo)
19 20
            ,@body)
20  
-       (delete-directory ,repo t)
21  
-       )))
  21
+       (delete-directory ,repo t))))
  22
+
  23
+(defmacro with-opened-file (file &rest body)
  24
+  (declare (indent 1) (debug t))
  25
+  (let ((buffer (make-symbol "*buffer*")))
  26
+    `(let (,buffer)
  27
+       (unwind-protect
  28
+           (progn
  29
+             (setq ,buffer (find-file-literally ,file))
  30
+             ,@body)
  31
+         (when ,buffer (kill-buffer ,buffer))))))
22 32
 
23 33
 (defun magit-tests-section-has-item-title (title &optional section-path)
24 34
   (let ((children (magit-section-children
@@ -28,6 +38,8 @@
28 38
     (should (member title
29 39
                     (mapcar 'magit-section-title children)))))
30 40
 
  41
+;;; magit.el tests
  42
+
31 43
 (ert-deftest magit-init-test ()
32 44
   (with-temp-git-repo repo
33 45
     (should (magit-git-repo-p repo))))
@@ -48,7 +60,7 @@
48 60
 
49 61
 (ert-deftest magit-init-test-expansion ()
50 62
   (let* ((dir "~/plop")
51  
-         (exp-dir (expand-file-name dir)))
  63
+         (exp-dir (file-name-as-directory (expand-file-name dir))))
52 64
     (mocker-let
53 65
         ;; make sure all steps have the expanded version of dir
54 66
         ((magit-get-top-dir (dir)
@@ -56,7 +68,7 @@
56 68
          (file-directory-p (dir)
57 69
                            ((:input `(,exp-dir) :output t)))
58 70
          (magit-run* (args)
59  
-                     ((:input `((,magit-git-executable "init" ,exp-dir))
  71
+                     ((:input `((,magit-git-executable "init"))
60 72
                        :output t))))
61 73
       (should (magit-init dir)))))
62 74
 
@@ -85,3 +97,19 @@
85 97
 
86 98
     (magit-run* '("git" "config" "core.safecrlf" "false"))
87 99
     (should-not (magit-get-boolean "core.safecrlf"))))
  100
+
  101
+;;; magit-blame.el tests
  102
+
  103
+(ert-deftest magit-blame-mode ()
  104
+  (let ((dummy-filename "foo"))
  105
+    (with-temp-git-repo repo
  106
+      (with-temp-buffer
  107
+        (insert "dummy content")
  108
+        (write-file (format "%s/%s" repo dummy-filename)))
  109
+      (magit-status repo)
  110
+      (magit-stage-all t)
  111
+      (magit-log-edit)
  112
+      (insert "dummy message")
  113
+      (magit-log-edit-commit)
  114
+      (with-opened-file (format "%s/%s" repo dummy-filename)
  115
+        (should (magit-blame-mode))))))

0 notes on commit bbaa204

Please sign in to comment.
Something went wrong with that request. Please try again.