├── .gitignore └── org-magit.el /.gitignore: -------------------------------------------------------------------------------- 1 | *.elc 2 | -------------------------------------------------------------------------------- /org-magit.el: -------------------------------------------------------------------------------- 1 | ;;; org-magit.el --- basic support for magit links 2 | 3 | ;; Copyright (C) 2011, 2012 Yann Hodique. 4 | 5 | ;; Author: Yann Hodique 6 | ;; Keywords: git, magit, outlines 7 | ;; Version: 0.3.0 8 | ;; Package-Requires: ((magit "1.2.0") (org "6.01")) 9 | 10 | ;; This file is free software; you can redistribute it and/or modify 11 | ;; it under the terms of the GNU General Public License as published by 12 | ;; the Free Software Foundation; either version 2, or (at your option) 13 | ;; any later version. 14 | 15 | ;; This file is distributed in the hope that it will be useful, 16 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | ;; GNU General Public License for more details. 19 | 20 | ;; You should have received a copy of the GNU General Public License 21 | ;; along with GNU Emacs; see the file COPYING. If not, write to 22 | ;; the Free Software Foundation, Inc., 59 Temple Place - Suite 330, 23 | ;; Boston, MA 02111-1307, USA. 24 | 25 | ;;; Commentary: 26 | 27 | ;; This package is deprecated. 28 | ;; Instead use the successor `orgit'. 29 | ;; See https://github.com/magit/orgit. 30 | 31 | ;; This module adds support for magit links in org buffers. The following links 32 | ;; are supported: 33 | ;; - magit:/path/to/repo::commit@ 34 | ;; - magit:/path/to/repo::status 35 | ;; - magit:/path/to/repo::log 36 | 37 | ;; Of course those links can be stored as usual with `org-store-link' from the 38 | ;; corresponding magit buffers. By default the path to the repo is abbreviated 39 | ;; with `abbreviate-file-name', just like org-mode does. See 40 | ;; `directory-abbrev-alist' for configuring its behavior. Alternately, you can 41 | ;; customize `org-magit-filename-transformer' and provide your own 42 | ;; transformer function. 43 | 44 | ;; When exporting those links, the variable `org-magit-known-public-providers' 45 | ;; is used to generate meaningful links. This assumes there exists a public 46 | ;; http server that is able to expose those objects. 47 | 48 | ;; Certain settings can be configured directly at the repository level 49 | ;; if needed. For example 50 | ;; 51 | ;; $ git config org-magit.remote upstream 52 | ;; 53 | ;; In this case, html links will point to the "upstream" webserver, instead of 54 | ;; the default "origin". URL templates can also be stored in the 55 | ;; repository. For example 56 | ;; 57 | ;; $ git config org-magit.log http://myserver/plop.git/history 58 | 59 | ;;; Code: 60 | 61 | (eval-when-compile 62 | (require 'cl)) 63 | 64 | (require 'org) 65 | (require 'magit) 66 | 67 | (defvar org-magit-actions 68 | '((status :open current-buffer) 69 | (log :open org-magit-open-log) 70 | (commit :open magit-show-commit))) 71 | 72 | (defgroup org-magit nil 73 | "Magit links for org-mode" 74 | :group 'magit 75 | :group 'org-link) 76 | 77 | (defcustom org-magit-public-remote "origin" 78 | "Default remote to use when exporting links." 79 | :group 'org-magit 80 | :type 'string) 81 | 82 | (defcustom org-magit-config-prefix "org-magit" 83 | "Section to read from in git repository configuration." 84 | :group 'org-magit 85 | :type 'string) 86 | 87 | (defun org-magit-gitweb-provider (base) 88 | `(status ,(concat base "/?p=\\1;a=summary") 89 | log ,(concat base "/?p=\\1;a=log") 90 | commit ,(concat base "/?p=\\1;a=commit;h=%s"))) 91 | 92 | (defun org-magit-gitorious-provider (base) 93 | `(status ,(concat base "/\\1") 94 | log ,(concat base "/\\1/commits") 95 | commit ,(concat base "/\\1/commit/%s"))) 96 | 97 | (defcustom org-magit-known-public-providers 98 | `(;; GitHub 99 | (,(rx bol (or "git@github.com:" 100 | (and (or "git" "ssh" "http" "https") "://" 101 | (* nonl) (? "@") "github.com/")) 102 | (group (* nonl)) ".git") 103 | status "https://github.com/\\1/" 104 | log "https://github.com/\\1/commits" 105 | commit "https://github.com/\\1/commit/%s") 106 | ;; Gitorious 107 | (,(rx bol (or "git@gitorious.org:" 108 | (and (or "git://gitorious.org/" 109 | (and 110 | (or "http" "https") 111 | "://git.gitorious.org/")))) 112 | (group (* nonl)) ".git") 113 | ,@(org-magit-gitorious-provider "https://gitorious.org")) 114 | ;; Bitbucket 115 | (,(rx bol (or "git@bitbucket.org:" 116 | (and (or "ssh" "http" "https") "://" 117 | (* nonl) (? "@") "bitbucket.org")) (group (* nonl))) 118 | status "https://bitbucket.org/\\1" 119 | log "https://bitbucket.org/\\1/changesets" 120 | commit "https://bitbucket.org/\\1/changeset/%s") 121 | ;; org-mode 122 | (,(rx bol "git://orgmode.org/" (group (* nonl) ".git")) 123 | ,@(org-magit-gitweb-provider "http://orgmode.org/w")) 124 | ;; kernel.org 125 | (,(rx bol (or "git" "http" "https") "://git.kernel.org/pub/scm/" 126 | (group (* nonl) ".git")) 127 | ,@(org-magit-gitweb-provider "http://git.kernel.org"))) 128 | "List of git providers, and how to generate links for each 129 | object category." 130 | :group 'org-magit 131 | :type '(repeat (list :tag "Provider identifier" regexp 132 | (set :tag "URL templates" :inline t 133 | (list :inline t 134 | (const :tag "Status" status) 135 | (string :tag "Status URL")) 136 | (list :inline t 137 | (const :tag "Log" log) 138 | (string :tag "Log URL")) 139 | (list :inline t 140 | (const :tag "Commit" commit) 141 | (string :tag "Commit URL")))))) 142 | 143 | (defcustom org-magit-filename-transformer 144 | 'abbreviate-file-name 145 | "Function to call to produce canonical repository name. This 146 | must take a path as input, and provide an equivalent 147 | representation of this path as output." 148 | :group 'org-magit 149 | :type 'function) 150 | 151 | (defun org-magit-split-string (str) 152 | (let* ((strlist (split-string str "::")) 153 | (repo (first strlist)) 154 | (view (second strlist)) 155 | (view-sym nil) 156 | (args nil)) 157 | (cond ((string-match "^status" view) 158 | (setq view-sym 'status)) 159 | ((string-match "^log" view) 160 | (setq view-sym 'log)) 161 | ((string-match "^commit@\\(.*\\)" view) 162 | (setq view-sym 'commit 163 | args (list (match-string 1 view))))) 164 | (list view-sym repo args))) 165 | 166 | (defun org-magit-open-log () 167 | (let ((buffer (current-buffer))) 168 | (funcall (if (fboundp 'magit-log) 'magit-log 'magit-display-log)) 169 | (bury-buffer buffer) 170 | (current-buffer))) 171 | 172 | ;;;###autoload 173 | (defun org-magit-open (str) 174 | (let* ((split (org-magit-split-string str)) 175 | (view (first split)) 176 | (repo (second split)) 177 | (func (plist-get (cdr (assoc view org-magit-actions)) :open)) 178 | (args (third split))) 179 | (let ((default-directory repo)) 180 | (when func 181 | (save-window-excursion 182 | (magit-status repo)) 183 | (apply func args))))) 184 | 185 | (defun org-magit-get (repo &rest keys) 186 | (let ((default-directory repo)) 187 | (apply 'magit-get keys))) 188 | 189 | (defun org-magit-guess-public-url (view url) 190 | (let ((res nil)) 191 | (when url 192 | (dolist (provider org-magit-known-public-providers) 193 | (let ((regexp (car provider))) 194 | (when (string-match regexp url) 195 | (setq res (replace-match (plist-get (cdr provider) view) 196 | nil nil url)))))) 197 | res)) 198 | 199 | (defun org-magit-generate-public-url (path) 200 | (let* ((split (org-magit-split-string path)) 201 | (view (first split)) 202 | (repo (second split)) 203 | (remote (or (org-magit-get 204 | repo (format "%s.remote" org-magit-config-prefix)) 205 | org-magit-public-remote)) 206 | remote-repo 207 | (tpl (or (org-magit-get 208 | repo (format "%s.%s" org-magit-config-prefix view)) 209 | (org-magit-guess-public-url 210 | view (setq remote-repo 211 | (org-magit-get 212 | repo (format "remote.%s.url" remote))))))) 213 | (or (and tpl 214 | (apply 'format tpl (third split))) 215 | (and remote-repo 216 | (concat remote-repo (when (third split) 217 | (concat "@" 218 | (mapconcat 'identity 219 | (third split) "@"))))) 220 | (and (fboundp 'magit-svn-get-ref-info) 221 | (let* ((default-directory repo) 222 | (info (magit-svn-get-ref-info))) 223 | (and info 224 | (cdr (assoc 'url info))))) 225 | path))) 226 | 227 | ;;;###autoload 228 | (defun org-magit-export (path desc format) 229 | (let ((url (or (org-magit-generate-public-url path) path))) 230 | (set-text-properties 0 (length url) nil url) 231 | (set-text-properties 0 (length desc) nil desc) 232 | (cond 233 | ((eq format 'html) (format "%s" url (or desc url))) 234 | ((eq format 'latex) (format "\\href{%s}{%s}" url (or desc url))) 235 | (t (or desc url))))) 236 | 237 | (defun org-magit-make-link (repo &rest components) 238 | (apply 'concat "magit:" repo components)) 239 | 240 | (defun org-magit-clean-repository (repo) 241 | (let ((name (file-name-nondirectory (directory-file-name repo)))) 242 | (when (not (string-match "\\.git" name)) 243 | (setq name (concat name ".git"))) 244 | name)) 245 | 246 | ;; magit used to have only one major-mode: magit-mode, and minor-modes for 247 | ;; status, log and commit. Now they are all major-modes deriving from 248 | ;; magit-mode. Let's have a backward-compatible check for the 249 | ;; current magit mode. 250 | (defmacro org-magit-check-mode (mode) 251 | `(or (and (boundp ',mode) ,mode) 252 | (derived-mode-p ',mode))) 253 | 254 | ;;;###autoload 255 | (defun org-magit-store-link () 256 | (when (derived-mode-p 'magit-mode) 257 | (let* ((repo (or (and org-magit-filename-transformer 258 | (funcall org-magit-filename-transformer 259 | default-directory)) 260 | default-directory)) 261 | (link nil) 262 | (desc (org-magit-clean-repository repo))) 263 | (cond ((org-magit-check-mode magit-status-mode) 264 | (setq link (org-magit-make-link repo "::status") 265 | desc (format "%s status" desc))) 266 | ((org-magit-check-mode magit-log-mode) 267 | (setq link (org-magit-make-link repo "::log") 268 | desc (format "%s log" desc))) 269 | ((or (org-magit-check-mode magit-commit-mode) 270 | (org-magit-check-mode magit-revision-mode)) 271 | (setq link (org-magit-make-link repo "::commit@" 272 | (car magit-refresh-args)) 273 | desc (format "%s commit #%s" desc 274 | (magit-rev-parse 275 | "--short" (car magit-refresh-args)))))) 276 | (org-store-link-props 277 | :type "magit" 278 | :link link 279 | :description desc)))) 280 | 281 | ;;;###autoload 282 | (eval-after-load "org" 283 | '(progn 284 | (org-add-link-type "magit" 'org-magit-open 'org-magit-export) 285 | (add-hook 'org-store-link-functions 'org-magit-store-link))) 286 | 287 | (provide 'org-magit) 288 | ;;; org-magit.el ends here 289 | --------------------------------------------------------------------------------