123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222 |
- (require 'org)
- (defcustom org-git-program "git"
- "Name of the git executable used to follow git links."
- :type '(string)
- :group 'org)
- (org-add-link-type "gitbare" 'org-gitbare-open)
- (defun org-gitbare-open (str)
- (let* ((strlist (org-git-split-string str))
- (gitdir (first strlist))
- (object (second strlist)))
- (org-git-open-file-internal gitdir object)))
- (defun org-git-open-file-internal (gitdir object)
- (let* ((sha (org-git-blob-sha gitdir object))
- (tmpdir (concat temporary-file-directory "org-git-" sha))
- (filename (org-git-link-filename object))
- (tmpfile (expand-file-name filename tmpdir)))
- (unless (file-readable-p tmpfile)
- (make-directory tmpdir)
- (with-temp-file tmpfile
- (org-git-show gitdir object (current-buffer))))
- (org-open-file tmpfile)
- (set-buffer (get-file-buffer tmpfile))
- (setq buffer-read-only t)))
- (org-add-link-type "git" 'org-git-open)
- (defun org-git-open (str)
- (let* ((strlist (org-git-split-string str))
- (filepath (first strlist))
- (commit (second strlist))
- (dirlist (org-git-find-gitdir (file-truename filepath)))
- (gitdir (first dirlist))
- (relpath (second dirlist)))
- (org-git-open-file-internal gitdir (concat commit ":" relpath))))
- (defun org-git-split-dirpath (dirpath)
- "Given a directory name, return '(dirname basname)"
- (let ((dirname (file-name-directory (directory-file-name dirpath)))
- (basename (file-name-nondirectory (directory-file-name dirpath))))
- (list dirname basename)))
- (defun org-git-find-gitdir (path)
- "Given a file (not necessarily existing) file path, return the
- a pair (gitdir relpath), where gitdir is the path to the first
- .git subdirectory found updstream and relpath is the rest of
- the path. Example: (org-git-find-gitdir
- \"~/gitrepos/foo/bar.txt\") returns
- '(\"/home/user/gitrepos/.git\" \"foo/bar.txt\"). When not in a git repository, return nil."
- (let ((dir (file-name-directory path))
- (relpath (file-name-nondirectory path)))
- (catch 'toplevel
- (while (not (file-exists-p (expand-file-name ".git" dir)))
- (let ((dirlist (org-git-split-dirpath dir)))
- (when (string= (second dirlist) "")
- (throw 'toplevel nil))
- (setq dir (first dirlist)
- relpath (concat (file-name-as-directory (second dirlist)) relpath))))
- (list (expand-file-name ".git" dir) relpath))))
- (eval-and-compile
- (if (featurep 'xemacs)
- (defalias 'org-git-gitrepos-p 'org-git-find-gitdir)
- (defalias 'org-git-gitrepos-p 'org-git-find-gitdir
- "Return non-nil if path is in git repository")))
- (defun org-git-split-string (str)
- "Given a string of the form \"str1::str2\", return a list of
- two substrings \'(\"str1\" \"str2\"). If the double colon is mising, take str2 to be the empty string."
- (let ((strlist (split-string str "::")))
- (cond ((= 1 (length strlist))
- (list (car strlist) ""))
- ((= 2 (length strlist))
- strlist)
- (t (error "org-git-split-string: only one :: allowed: %s" str)))))
- (defun org-git-link-filename (str)
- "Given an object description (see the man page of
- git-rev-parse), return the nondirectory part of the referenced
- filename, if it can be extracted. Otherwise, return a valid
- filename."
- (let* ((match (and (string-match "[^:]+$" str)
- (match-string 0 str)))
- (filename (and match (file-name-nondirectory match))))
- filename))
- (defun org-git-create-searchstring (branch timestring)
- (concat branch "@{" timestring "}"))
- (defun org-git-create-git-link (file)
- "Create git link part to file at specific time"
- (interactive "FFile: ")
- (let* ((gitdir (first (org-git-find-gitdir (file-truename file))))
- (branchname (org-git-get-current-branch gitdir))
- (timestring (format-time-string "%Y-%m-%d" (current-time))))
- (concat "git:" file "::" (org-git-create-searchstring branchname timestring))))
- (defun org-git-store-link ()
- "Store git link to current file."
- (when (buffer-file-name)
- (let ((file (abbreviate-file-name (buffer-file-name))))
- (when (org-git-gitrepos-p file)
- (org-store-link-props
- :type "git"
- :link (org-git-create-git-link file))))))
- (add-hook 'org-store-link-functions 'org-git-store-link)
- (defun org-git-insert-link-interactively (file searchstring &optional description)
- (interactive "FFile: \nsSearch string: \nsDescription: ")
- (insert (org-make-link-string (concat "git:" file "::" searchstring) description)))
- (defun org-git-show (gitdir object buffer)
- "Show the output of git --git-dir=gitdir show object in buffer."
- (unless
- (zerop (call-process org-git-program nil buffer nil
- "--no-pager" (concat "--git-dir=" gitdir) "show" object))
- (error "git error: %s " (with-current-buffer buffer (buffer-string)))))
- (defun org-git-blob-sha (gitdir object)
- "Return sha of the referenced object"
- (with-temp-buffer
- (if (zerop (call-process org-git-program nil t nil
- "--no-pager" (concat "--git-dir=" gitdir) "rev-parse" object))
- (buffer-substring (point-min) (1- (point-max)))
- (error "git error: %s " (buffer-string)))))
- (defun org-git-get-current-branch (gitdir)
- "Return the name of the current branch."
- (with-temp-buffer
- (if (not (zerop (call-process org-git-program nil t nil
- "--no-pager" (concat "--git-dir=" gitdir) "symbolic-ref" "-q" "HEAD")))
- (error "git error: %s " (buffer-string))
- (goto-char (point-min))
- (if (looking-at "^refs/heads/")
- (buffer-substring 12 (1- (point-max)))))))
- (provide 'org-git-link)
|