View the file you're editing in Emacs on GitHub

Searching for an easy way to point my coworkers to specific lines of a file on GitHub I’ve stumbled upon magithub. Unfortunately it is not compatible with latest versions of magit so I’ve taken the liberty of copying it’s file browsing functionality and re-implementing it without magit dependency and with additional checks for the current branch.

If you need a more thorough interaction with GitHub I’d suggest you to dive into magithub’s source and send it’s author some pull requests.

You can use use osener/github-browse-file with an active region to specify highlighted lines. With a prefix argument (C-u) you can force it to use the “master” branch.

Update: I’ve created a GitHub repository for github-browse-file and uploaded it to Marmalade. You can install it using M-x package-install github-browse-file.

Screenshots

Emacs Screenshot

GitHub Screenshot

Source

(autoload 'vc-git-root "vc-git")

(defvar osener/github-force-master nil
  "Whether to use \"master\" regardless of current branch
This should only ever be `let'-bound, not set outright.")

(defun osener/github-relative-url ()
  "Return \"username/repo\" for current repository.

Error out if this isn't a GitHub repo."
  (let ((url (vc-git--run-command-string nil "config" "remote.origin.url")))
    (unless url (error "Not in a GitHub repo"))
    (when (and url (string-match "github.com:?/?\\(.*\\)" url))
      (replace-regexp-in-string "\\.git" "" (match-string 1 url)))))

(defun osener/github-repo-relative-path ()
  "Return the path to the current file relative to the repository root."
  (let* ((root (ignore-errors (vc-git-root buffer-file-name))))
    (and root (file-relative-name buffer-file-name root))))

(defun osener/github-ahead-p ()
  "Return non-nil if current git HEAD is ahead of origin/master"
  (let ((rev (vc-git--run-command-string
              nil "rev-list" "--left-right" "origin/master...HEAD")))
    (and (> (length rev) 0)
         (string-equal (substring rev 0 1) ">"))))

(defun osener/github-current-rev ()
  "Return the SHA1 of HEAD if it is not ahead of origin/master.
If osener/github-force-master is non-nil, return \"master\".
Otherwise, return the name of the current  branch."
  (cond
   (osener/github-force-master "master")
   ((osener/github-ahead-p) (car (vc-git-branches)))
   (t (let ((rev (vc-git--run-command-string nil "rev-parse" "HEAD")))
        (and rev (replace-regexp-in-string "\n" "" rev))))))

(defun osener/github-url (&optional anchor)
  "Load http://github.com/user/repo/file#ANCHOR in a web browser and add it to
the kill ring."
  (let ((url (concat "https://github.com/"
                     (osener/github-relative-url)
                     "/blob/" (osener/github-current-rev) "/"
                     (osener/github-repo-relative-path)
                     (when anchor (concat "#" anchor)))))
    (kill-new url)
    (browse-url url)))

(defun osener/github-browse-file (force-master)
  "Show the GitHub webpage for the current file. The URL for the webpage is
added to the kill ring.

In Transient Mark mode, if the mark is active, highlight the contents of the
region."
  (interactive "P")
  (let ((path (osener/github-repo-relative-path))
        (osener/github-force-master force-master)
        start
        end)
    (when mark-ring
      (setq start (line-number-at-pos (region-beginning))
            end (line-number-at-pos (region-end)))
      (when (eq (char-before (region-end)) ?\n) (decf end)))

    (osener/github-url
     (when (and transient-mark-mode mark-active)
       (if (eq start end) (format "L%d" start)
         (format "L%d-%d" start end))))))

You can bind it to something like C-c ' b (magithub default) or C-c o using a line similar to the following:

(global-set-key (kbd "C-c o") 'osener/github-browse-file)

Published: April 06 2013

blog comments powered by Disqus