stgit.el: Add support for showing which files are affected by a patch
[stgit] / contrib / stgit.el
1 ;; stgit.el: An emacs mode for StGit
2 ;;
3 ;; Copyright (C) 2007 David Kågedal <davidk@lysator.liu.se>
4 ;;
5 ;; To install: put this file on the load-path and place the following
6 ;; in your .emacs file:
7 ;;
8 ;; (require 'stgit)
9 ;;
10 ;; To start: `M-x stgit'
11
12 (require 'git nil t)
13
14 (defun stgit (dir)
15 "Manage StGit patches for the tree in DIR."
16 (interactive "DDirectory: \n")
17 (switch-to-stgit-buffer (git-get-top-dir dir))
18 (stgit-reload))
19
20 (unless (fboundp 'git-get-top-dir)
21 (defun git-get-top-dir (dir)
22 "Retrieve the top-level directory of a git tree."
23 (let ((cdup (with-output-to-string
24 (with-current-buffer standard-output
25 (cd dir)
26 (unless (eq 0 (call-process "git" nil t nil
27 "rev-parse" "--show-cdup"))
28 (error "cannot find top-level git tree for %s." dir))))))
29 (expand-file-name (concat (file-name-as-directory dir)
30 (car (split-string cdup "\n")))))))
31
32 (defun stgit-refresh-git-status (&optional dir)
33 "If it exists, refresh the `git-status' buffer belonging to
34 directory DIR or `default-directory'"
35 (when (and (fboundp 'git-find-status-buffer)
36 (fboundp 'git-refresh-status))
37 (let* ((top-dir (git-get-top-dir (or dir default-directory)))
38 (git-status-buffer (and top-dir (git-find-status-buffer top-dir))))
39 (when git-status-buffer
40 (with-current-buffer git-status-buffer
41 (git-refresh-status))))))
42
43 (defun switch-to-stgit-buffer (dir)
44 "Switch to a (possibly new) buffer displaying StGit patches for DIR."
45 (setq dir (file-name-as-directory dir))
46 (let ((buffers (buffer-list)))
47 (while (and buffers
48 (not (with-current-buffer (car buffers)
49 (and (eq major-mode 'stgit-mode)
50 (string= default-directory dir)))))
51 (setq buffers (cdr buffers)))
52 (switch-to-buffer (if buffers
53 (car buffers)
54 (create-stgit-buffer dir)))))
55
56 (defun create-stgit-buffer (dir)
57 "Create a buffer for showing StGit patches.
58 Argument DIR is the repository path."
59 (let ((buf (create-file-buffer (concat dir "*stgit*")))
60 (inhibit-read-only t))
61 (with-current-buffer buf
62 (setq default-directory dir)
63 (stgit-mode)
64 (setq buffer-read-only t))
65 buf))
66
67 (defmacro stgit-capture-output (name &rest body)
68 "Capture StGit output and show it in a window at the end."
69 `(let ((output-buf (get-buffer-create ,(or name "*StGit output*")))
70 (stgit-dir default-directory)
71 (inhibit-read-only t))
72 (with-current-buffer output-buf
73 (erase-buffer)
74 (setq default-directory stgit-dir)
75 (setq buffer-read-only t))
76 (let ((standard-output output-buf))
77 ,@body)
78 (with-current-buffer output-buf
79 (set-buffer-modified-p nil)
80 (setq buffer-read-only t)
81 (if (< (point-min) (point-max))
82 (display-buffer output-buf t)))))
83 (put 'stgit-capture-output 'lisp-indent-function 1)
84
85 (defun stgit-run-silent (&rest args)
86 (apply 'call-process "stg" nil standard-output nil args))
87
88 (defun stgit-run (&rest args)
89 (let ((msgcmd (mapconcat #'identity args " ")))
90 (message "Running stg %s..." msgcmd)
91 (apply 'call-process "stg" nil standard-output nil args)
92 (message "Running stg %s...done" msgcmd)))
93
94 (defun stgit-run-git (&rest args)
95 (let ((msgcmd (mapconcat #'identity args " ")))
96 (message "Running git %s..." msgcmd)
97 (apply 'call-process "git" nil standard-output nil args)
98 (message "Running git %s...done" msgcmd)))
99
100 (defun stgit-reload ()
101 "Update the contents of the StGit buffer."
102 (interactive)
103 (let ((inhibit-read-only t)
104 (curline (line-number-at-pos))
105 (curpatch (stgit-patch-at-point)))
106 (erase-buffer)
107 (insert "Branch: ")
108 (stgit-run-silent "branch")
109 (stgit-run-silent "series" "--description")
110 (stgit-rescan)
111 (if curpatch
112 (stgit-goto-patch curpatch)
113 (goto-line curline)))
114 (stgit-refresh-git-status))
115
116 (defface stgit-description-face
117 '((((background dark)) (:foreground "tan"))
118 (((background light)) (:foreground "dark red")))
119 "The face used for StGit desriptions")
120
121 (defface stgit-top-patch-face
122 '((((background dark)) (:weight bold :foreground "yellow"))
123 (((background light)) (:weight bold :foreground "purple"))
124 (t (:weight bold)))
125 "The face used for the top patch names")
126
127 (defface stgit-applied-patch-face
128 '((((background dark)) (:foreground "light yellow"))
129 (((background light)) (:foreground "purple"))
130 (t ()))
131 "The face used for applied patch names")
132
133 (defface stgit-unapplied-patch-face
134 '((((background dark)) (:foreground "gray80"))
135 (((background light)) (:foreground "orchid"))
136 (t ()))
137 "The face used for unapplied patch names")
138
139 (defun stgit-expand-patch (patchsym)
140 (save-excursion
141 (forward-line)
142 (let ((start (point)))
143 (stgit-run "files" (symbol-name patchsym))
144
145 ;; 'stg files' outputs a single newline for empty patches; it
146 ;; must be destroyed!
147 (when (and (= (1+ start) (point))
148 (= (char-before) ?\n))
149 (delete-backward-char 1))
150
151 (let ((end-marker (point-marker)))
152 (if (= start (point))
153 (insert-string " <no files>\n")
154 (unless (looking-at "^")
155 (insert ?\n))
156 (while (and (zerop (forward-line -1))
157 (>= (point) start))
158 (insert " ")))
159 (put-text-property start end-marker 'stgit-patchsym patchsym)))))
160
161 (defun stgit-rescan ()
162 "Rescan the status buffer."
163 (save-excursion
164 (let ((marked ()))
165 (goto-char (point-min))
166 (while (not (eobp))
167 (cond ((looking-at "Branch: \\(.*\\)")
168 (put-text-property (match-beginning 1) (match-end 1)
169 'face 'bold))
170 ((looking-at "\\([>+-]\\)\\( \\)\\([^ ]+\\) *[|#] \\(.*\\)")
171 (let ((state (match-string 1))
172 (patchsym (intern (match-string 3))))
173 (put-text-property
174 (match-beginning 3) (match-end 3) 'face
175 (cond ((string= state ">") 'stgit-top-patch-face)
176 ((string= state "+") 'stgit-applied-patch-face)
177 ((string= state "-") 'stgit-unapplied-patch-face)))
178 (put-text-property (match-beginning 4) (match-end 4)
179 'face 'stgit-description-face)
180 (when (memq patchsym stgit-marked-patches)
181 (replace-match "*" nil nil nil 2)
182 (setq marked (cons patchsym marked)))
183 (when (memq patchsym stgit-expanded-patches)
184 (stgit-expand-patch patchsym))
185 ))
186 ((or (looking-at "stg series: Branch \".*\" not initialised")
187 (looking-at "stg series: .*: branch not initialized"))
188 (forward-line 1)
189 (insert "Run M-x stgit-init to initialise")))
190 (forward-line 1))
191 (setq stgit-marked-patches (nreverse marked)))))
192
193 (defun stgit-select ()
194 "Expand or collapse the current entry"
195 (interactive)
196 (let ((curpatch (stgit-patch-at-point)))
197 (if (not curpatch)
198 (let ((patched-file (stgit-patched-file-at-point)))
199 (unless patched-file
200 (error "No patch or file on the current line"))
201 (let ((filename (expand-file-name (cdr patched-file))))
202 (unless (file-exists-p filename)
203 (error "File does not exist"))
204 (find-file filename)))
205 (setq curpatch (intern curpatch))
206 (setq stgit-expanded-patches
207 (if (memq curpatch stgit-expanded-patches)
208 (delq curpatch stgit-expanded-patches)
209 (cons curpatch stgit-expanded-patches)))
210 (stgit-reload))))
211
212 (defun stgit-find-file-other-window ()
213 "Open file at point in other window"
214 (interactive)
215 (let ((patched-file (stgit-patched-file-at-point)))
216 (unless patched-file
217 (error "No file on the current line"))
218 (let ((filename (expand-file-name (cdr patched-file))))
219 (unless (file-exists-p filename)
220 (error "File does not exist"))
221 (find-file-other-window filename))))
222
223 (defun stgit-quit ()
224 "Hide the stgit buffer."
225 (interactive)
226 (bury-buffer))
227
228 (defun stgit-git-status ()
229 "Show status using `git-status'."
230 (interactive)
231 (unless (fboundp 'git-status)
232 (error "stgit-git-status requires git-status"))
233 (let ((dir default-directory))
234 (save-selected-window
235 (pop-to-buffer nil)
236 (git-status dir))))
237
238 (defun stgit-next-line (&optional arg try-vscroll)
239 "Move cursor vertically down ARG lines"
240 (interactive "p\np")
241 (next-line arg try-vscroll)
242 (when (looking-at " \\S-")
243 (forward-char 2)))
244
245 (defun stgit-previous-line (&optional arg try-vscroll)
246 "Move cursor vertically up ARG lines"
247 (interactive "p\np")
248 (previous-line arg try-vscroll)
249 (when (looking-at " \\S-")
250 (forward-char 2)))
251
252 (defun stgit-next-patch (&optional arg)
253 "Move cursor down ARG patches"
254 (interactive "p")
255 (unless arg
256 (setq arg 1))
257 (if (< arg 0)
258 (stgit-previous-patch (- arg))
259 (while (not (zerop arg))
260 (setq arg (1- arg))
261 (while (progn (stgit-next-line)
262 (not (stgit-patch-at-point)))))))
263
264 (defun stgit-previous-patch (&optional arg)
265 "Move cursor up ARG patches"
266 (interactive "p")
267 (unless arg
268 (setq arg 1))
269 (if (< arg 0)
270 (stgit-next-patch (- arg))
271 (while (not (zerop arg))
272 (setq arg (1- arg))
273 (while (progn (stgit-previous-line)
274 (not (stgit-patch-at-point)))))))
275
276 (defvar stgit-mode-hook nil
277 "Run after `stgit-mode' is setup.")
278
279 (defvar stgit-mode-map nil
280 "Keymap for StGit major mode.")
281
282 (unless stgit-mode-map
283 (setq stgit-mode-map (make-keymap))
284 (suppress-keymap stgit-mode-map)
285 (mapc (lambda (arg) (define-key stgit-mode-map (car arg) (cdr arg)))
286 '((" " . stgit-mark)
287 ("m" . stgit-mark)
288 ("\d" . stgit-unmark-up)
289 ("u" . stgit-unmark-down)
290 ("?" . stgit-help)
291 ("h" . stgit-help)
292 ("p" . stgit-previous-line)
293 ("n" . stgit-next-line)
294 ("\C-p" . stgit-previous-patch)
295 ("\C-n" . stgit-next-patch)
296 ("\M-{" . stgit-previous-patch)
297 ("\M-}" . stgit-next-patch)
298 ("s" . stgit-git-status)
299 ("g" . stgit-reload)
300 ("r" . stgit-refresh)
301 ("\C-c\C-r" . stgit-rename)
302 ("e" . stgit-edit)
303 ("c" . stgit-coalesce)
304 ("N" . stgit-new)
305 ("R" . stgit-repair)
306 ("C" . stgit-commit)
307 ("U" . stgit-uncommit)
308 ("\r" . stgit-select)
309 ("o" . stgit-find-file-other-window)
310 (">" . stgit-push-next)
311 ("<" . stgit-pop-next)
312 ("P" . stgit-push-or-pop)
313 ("G" . stgit-goto)
314 ("=" . stgit-show)
315 ("D" . stgit-delete)
316 ([(control ?/)] . stgit-undo)
317 ("\C-_" . stgit-undo)
318 ("q" . stgit-quit))))
319
320 (defun stgit-mode ()
321 "Major mode for interacting with StGit.
322 Commands:
323 \\{stgit-mode-map}"
324 (kill-all-local-variables)
325 (buffer-disable-undo)
326 (setq mode-name "StGit"
327 major-mode 'stgit-mode
328 goal-column 2)
329 (use-local-map stgit-mode-map)
330 (set (make-local-variable 'list-buffers-directory) default-directory)
331 (set (make-local-variable 'stgit-marked-patches) nil)
332 (set (make-local-variable 'stgit-expanded-patches) nil)
333 (set-variable 'truncate-lines 't)
334 (run-hooks 'stgit-mode-hook))
335
336 (defun stgit-add-mark (patch)
337 (let ((patchsym (intern patch)))
338 (setq stgit-marked-patches (cons patchsym stgit-marked-patches))))
339
340 (defun stgit-remove-mark (patch)
341 (let ((patchsym (intern patch)))
342 (setq stgit-marked-patches (delq patchsym stgit-marked-patches))))
343
344 (defun stgit-clear-marks ()
345 (setq stgit-marked-patches '()))
346
347 (defun stgit-marked-patches ()
348 "Return the names of the marked patches."
349 (mapcar 'symbol-name stgit-marked-patches))
350
351 (defun stgit-patch-at-point (&optional cause-error allow-file)
352 "Return the patch name on the current line.
353 If CAUSE-ERROR is not nil, signal an error if none found.
354 If ALLOW-FILE is not nil, also handle when point is on a file of
355 a patch."
356 (or (and allow-file
357 (let ((patchsym (get-text-property (point) 'stgit-patchsym)))
358 (and patchsym
359 (symbol-name patchsym))))
360 (save-excursion
361 (beginning-of-line)
362 (and (looking-at "[>+-][ *]\\([^ ]*\\)")
363 (match-string-no-properties 1)))
364 (and cause-error
365 (error "No patch on this line"))))
366
367 (defun stgit-patched-file-at-point ()
368 "Returns a cons of the patchsym and file name at point"
369 (let ((patchsym (get-text-property (point) 'stgit-patchsym)))
370 (when patchsym
371 (save-excursion
372 (beginning-of-line)
373 (when (looking-at " [A-Z] \\(.*\\)")
374 (cons patchsym (match-string-no-properties 1)))))))
375
376 (defun stgit-patches-marked-or-at-point ()
377 "Return the names of the marked patches, or the patch on the current line."
378 (if stgit-marked-patches
379 (stgit-marked-patches)
380 (let ((patch (stgit-patch-at-point)))
381 (if patch
382 (list patch)
383 '()))))
384
385 (defun stgit-goto-patch (patch)
386 "Move point to the line containing PATCH."
387 (let ((p (point)))
388 (goto-char (point-min))
389 (if (re-search-forward (concat "^[>+-][ *]" (regexp-quote patch) " ") nil t)
390 (progn (move-to-column goal-column)
391 t)
392 (goto-char p)
393 nil)))
394
395 (defun stgit-init ()
396 "Run stg init."
397 (interactive)
398 (stgit-capture-output nil
399 (stgit-run "init"))
400 (stgit-reload))
401
402 (defun stgit-mark ()
403 "Mark the patch under point."
404 (interactive)
405 (let ((patch (stgit-patch-at-point t)))
406 (stgit-add-mark patch)
407 (stgit-reload))
408 (stgit-next-patch))
409
410 (defun stgit-unmark-up ()
411 "Remove mark from the patch on the previous line."
412 (interactive)
413 (stgit-previous-patch)
414 (stgit-remove-mark (stgit-patch-at-point t))
415 (stgit-reload))
416
417 (defun stgit-unmark-down ()
418 "Remove mark from the patch on the current line."
419 (interactive)
420 (stgit-remove-mark (stgit-patch-at-point t))
421 (stgit-next-patch)
422 (stgit-reload))
423
424 (defun stgit-rename (name)
425 "Rename the patch under point to NAME."
426 (interactive (list (read-string "Patch name: " (stgit-patch-at-point t))))
427 (let ((old-name (stgit-patch-at-point t)))
428 (stgit-capture-output nil
429 (stgit-run "rename" old-name name))
430 (let ((old-name-sym (intern old-name))
431 (name-sym (intern name)))
432 (when (memq old-name-sym stgit-expanded-patches)
433 (setq stgit-expanded-patches
434 (cons name-sym (delq old-name-sym stgit-expanded-patches))))
435 (when (memq old-name-sym stgit-marked-patches)
436 (setq stgit-marked-patches
437 (cons name-sym (delq old-name-sym stgit-marked-patches)))))
438 (stgit-reload)
439 (stgit-goto-patch name)))
440
441 (defun stgit-repair ()
442 "Run stg repair."
443 (interactive)
444 (stgit-capture-output nil
445 (stgit-run "repair"))
446 (stgit-reload))
447
448 (defun stgit-commit ()
449 "Run stg commit."
450 (interactive)
451 (stgit-capture-output nil (stgit-run "commit"))
452 (stgit-reload))
453
454 (defun stgit-uncommit (arg)
455 "Run stg uncommit. Numeric arg determines number of patches to uncommit."
456 (interactive "p")
457 (stgit-capture-output nil (stgit-run "uncommit" "-n" (number-to-string arg)))
458 (stgit-reload))
459
460 (defun stgit-push-next (npatches)
461 "Push the first unapplied patch.
462 With numeric prefix argument, push that many patches."
463 (interactive "p")
464 (stgit-capture-output nil (stgit-run "push" "-n"
465 (number-to-string npatches)))
466 (stgit-reload)
467 (stgit-refresh-git-status))
468
469 (defun stgit-pop-next (npatches)
470 "Pop the topmost applied patch.
471 With numeric prefix argument, pop that many patches."
472 (interactive "p")
473 (stgit-capture-output nil (stgit-run "pop" "-n" (number-to-string npatches)))
474 (stgit-reload)
475 (stgit-refresh-git-status))
476
477 (defun stgit-applied-at-point ()
478 "Is the patch on the current line applied?"
479 (save-excursion
480 (beginning-of-line)
481 (looking-at "[>+]")))
482
483 (defun stgit-push-or-pop ()
484 "Push or pop the patch on the current line."
485 (interactive)
486 (let ((patch (stgit-patch-at-point t))
487 (applied (stgit-applied-at-point)))
488 (stgit-capture-output nil
489 (stgit-run (if applied "pop" "push") patch))
490 (stgit-reload)))
491
492 (defun stgit-goto ()
493 "Go to the patch on the current line."
494 (interactive)
495 (let ((patch (stgit-patch-at-point t)))
496 (stgit-capture-output nil
497 (stgit-run "goto" patch))
498 (stgit-reload)))
499
500 (defun stgit-id (patch)
501 "Return the git commit id for PATCH"
502 (let ((result (with-output-to-string
503 (stgit-run-silent "id" patch))))
504 (unless (string-match "^\\([0-9A-Fa-f]\\{40\\}\\)$" result)
505 (error "Cannot find commit id for %s" patch))
506 (match-string 1 result)))
507
508 (defun stgit-show ()
509 "Show the patch on the current line."
510 (interactive)
511 (stgit-capture-output "*StGit patch*"
512 (let ((patch (stgit-patch-at-point)))
513 (if (not patch)
514 (let ((patched-file (stgit-patched-file-at-point)))
515 (unless patched-file
516 (error "No patch or file at point"))
517 (let ((id (stgit-id (symbol-name (car patched-file)))))
518 (with-output-to-temp-buffer "*StGit diff*"
519 (stgit-run-git "diff" (concat id "^") id (cdr patched-file))
520 (with-current-buffer standard-output
521 (diff-mode)))))
522 (stgit-run "show" (stgit-patch-at-point))
523 (with-current-buffer standard-output
524 (goto-char (point-min))
525 (diff-mode))))))
526
527 (defun stgit-edit ()
528 "Edit the patch on the current line."
529 (interactive)
530 (let ((patch (stgit-patch-at-point t))
531 (edit-buf (get-buffer-create "*StGit edit*"))
532 (dir default-directory))
533 (log-edit 'stgit-confirm-edit t nil edit-buf)
534 (set (make-local-variable 'stgit-edit-patch) patch)
535 (setq default-directory dir)
536 (let ((standard-output edit-buf))
537 (stgit-run-silent "edit" "--save-template=-" patch))))
538
539 (defun stgit-confirm-edit ()
540 (interactive)
541 (let ((file (make-temp-file "stgit-edit-")))
542 (write-region (point-min) (point-max) file)
543 (stgit-capture-output nil
544 (stgit-run "edit" "-f" file stgit-edit-patch))
545 (with-current-buffer log-edit-parent-buffer
546 (stgit-reload))))
547
548 (defun stgit-new ()
549 "Create a new patch."
550 (interactive)
551 (let ((edit-buf (get-buffer-create "*StGit edit*"))
552 (dir default-directory))
553 (log-edit 'stgit-confirm-new t nil edit-buf)
554 (setq default-directory dir)))
555
556 (defun stgit-confirm-new ()
557 (interactive)
558 (let ((file (make-temp-file "stgit-edit-")))
559 (write-region (point-min) (point-max) file)
560 (stgit-capture-output nil
561 (stgit-run "new" "-f" file))
562 (with-current-buffer log-edit-parent-buffer
563 (stgit-reload))))
564
565 (defun stgit-create-patch-name (description)
566 "Create a patch name from a long description"
567 (let ((patch ""))
568 (while (> (length description) 0)
569 (cond ((string-match "\\`[a-zA-Z_-]+" description)
570 (setq patch (downcase (concat patch (match-string 0 description))))
571 (setq description (substring description (match-end 0))))
572 ((string-match "\\` +" description)
573 (setq patch (concat patch "-"))
574 (setq description (substring description (match-end 0))))
575 ((string-match "\\`[^a-zA-Z_-]+" description)
576 (setq description (substring description (match-end 0))))))
577 (cond ((= (length patch) 0)
578 "patch")
579 ((> (length patch) 20)
580 (substring patch 0 20))
581 (t patch))))
582
583 (defun stgit-delete (patch-names)
584 "Delete the named patches."
585 (interactive (list (stgit-patches-marked-or-at-point)))
586 (if (zerop (length patch-names))
587 (error "No patches to delete")
588 (when (yes-or-no-p (format "Really delete %d patches? "
589 (length patch-names)))
590 (stgit-capture-output nil
591 (apply 'stgit-run "delete" patch-names))
592 (stgit-reload))))
593
594 (defun stgit-coalesce (patch-names)
595 "Run stg coalesce on the named patches."
596 (interactive (list (stgit-marked-patches)))
597 (let ((edit-buf (get-buffer-create "*StGit edit*"))
598 (dir default-directory))
599 (log-edit 'stgit-confirm-coalesce t nil edit-buf)
600 (set (make-local-variable 'stgit-patches) patch-names)
601 (setq default-directory dir)
602 (let ((standard-output edit-buf))
603 (apply 'stgit-run-silent "coalesce" "--save-template=-" patch-names))))
604
605 (defun stgit-confirm-coalesce ()
606 (interactive)
607 (let ((file (make-temp-file "stgit-edit-")))
608 (write-region (point-min) (point-max) file)
609 (stgit-capture-output nil
610 (apply 'stgit-run "coalesce" "-f" file stgit-patches))
611 (with-current-buffer log-edit-parent-buffer
612 (stgit-clear-marks)
613 ;; Go to first marked patch and stay there
614 (goto-char (point-min))
615 (re-search-forward (concat "^[>+-]\\*") nil t)
616 (move-to-column goal-column)
617 (let ((pos (point)))
618 (stgit-reload)
619 (goto-char pos)))))
620
621 (defun stgit-help ()
622 "Display help for the StGit mode."
623 (interactive)
624 (describe-function 'stgit-mode))
625
626 (defun stgit-undo (&optional arg)
627 "Run stg undo.
628 With prefix argument, run it with the --hard flag."
629 (interactive "P")
630 (stgit-capture-output nil
631 (if arg
632 (stgit-run "undo" "--hard")
633 (stgit-run "undo")))
634 (stgit-reload))
635
636 (defun stgit-refresh (&optional arg)
637 "Run stg refresh.
638 With prefix argument, refresh the marked patch or the patch under point."
639 (interactive "P")
640 (let ((patchargs (if arg
641 (let ((patches (stgit-patches-marked-or-at-point)))
642 (cond ((null patches)
643 (error "no patch to update"))
644 ((> (length patches) 1)
645 (error "too many patches selected"))
646 (t
647 (cons "-p" patches))))
648 nil)))
649 (stgit-capture-output nil
650 (apply 'stgit-run "refresh" patchargs))
651 (stgit-refresh-git-status))
652 (stgit-reload))
653
654 (provide 'stgit)