;;; majjit.el --- Clone jujutsu repositories with Magit's interface -*- lexical-binding: t -*- ;; Author: Lucien Cartier-Tilet ;; Maintainer: Lucien Cartier-Tilet ;; Version: 0.1.0 ;; Package-Requires: ((emacs "28.1") (magit "4.5.0")) ;; Homepage: https://labs.phundrak.com/phundrak/majjit ;; Keywords: git tools vcs ;; This file is not part of GNU Emacs ;; This program is free software: you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation, either version 3 of the License, or ;; (at your option) any later version. ;; This program is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License ;; along with this program. If not, see . ;;; Commentary: ;; commentary ;;; Code: (require 'magit) (defgroup majjit nil "Clone your jj repositories with Magit." :group 'tools :prefix "majjit-" :link '(url-link :tag "https://labs.phundrak.com/phundrak/majjit")) (defcustom majjit-default-directory nil "See `magit-clone-default-directory'." :package-version '(majjit . "0.1.0") :group 'majjit :type '(choice (const :tag "Value of default-directory") (directory :tag "Constant directory") (function :tag "Function's value"))) (defcustom majjit-executable "jj" "Local executable of jj." :package-version '(majjit . "0.1.0") :group 'majjit :type 'path) (defcustom majjit-remote-executable "jj" "Remote executable of jj." :package-version '(majjit . "0.1.0") :group 'majjit :type 'path) (defcustom majjit-global-arguments '("--color=never") "Global jj arguments." :package-version '(majjit . "0.1.0") :group 'majjit :type '(repeat string)) (defun majjit-sentinel (process event) "Default sentinel used by `majjit-run-jj-async'." (when (memq (process-status process) '(exit signal)) (setq event (substring event 0 -1)) (when (string-match "^finished" event) (message (concat (process-name process) " finished"))) (when (eq process magit-this-process) (setq magit-this-process nil)))) (defun majjit-executable () "Return executable for jj. See `magit-git-executable'." (if (file-remote-p default-directory) majjit-remote-executable majjit-executable)) (defun majjit-run-jj-async (directory &rest args) "Start jujutsu and return the process object. ARGS is flattened and then used as arguments to jujutsu. Once cloning is done, open DIRECTORY with `find-file'. Inspired by `magit-start-git'." (let ((default-process-coding-system (magit--process-coding-system)) (magit-git-global-arguments majjit-global-arguments)) (apply #'magit-start-process (majjit-executable) nil (magit-process-git-arguments args)) ;; Don't refresh the buffer we're calling from. (process-put magit-this-process 'inhibit-refresh t) (set-process-sentinel magit-this-process (lambda (process event) (majjit-sentinel process event) (find-file directory))))) (defun majjit-read-args () "Get all the necessary args for cloning the repository." (let* ((magit-clone-default-directory majjit-default-directory) (magit-args (magit-clone-read-args))) (list (car magit-args) (cadr magit-args) (yes-or-no-p "Colocate the repository? ")))) (defun majjit--check-valid-clone-directory (repository directory) "Check whether the target DIRECTORY is a valid target. Taken from `magit-clone-internal'. Refer to it for use of REPOSITORY and DIRECTORY." (let ((directory (file-name-as-directory (expand-file-name directory)))) (when (file-exists-p directory) (if (file-directory-p directory) (when (length> (directory-files directory) 2) (let ((name (magit-clone--url-to-name repository))) (unless (and name (setq directory (file-name-as-directory (expand-file-name name directory))) (not (file-exists-p directory))) (user-error "%s already exists" directory)))) (user-error "%s already exists and is not a directory" directory))))) (defun majjit--internal (repository directory colocate-p) "Clone REPOSITORY to DIRECTORY using jj. If COLOCATE-P is t, colocate the repository with git." (let ((directory (file-name-as-directory (expand-file-name directory)))) (majjit--check-valid-clone-directory repository directory) (majjit-run-jj-async directory "git" "clone" (if colocate-p "--colocate" nil) "--" repository (magit-convert-filename-for-git directory)))) ;;;###autoload (defun majjit-clone-from-url (url) "Clone a repository from a known URL." (let ((directory (read-directory-name "Clone to: " (if (functionp majjit-default-directory) (funcall majjit-default-directory) majjit-default-directory) nil nil (magit-clone--url-to-name url))) (colocate-p (yes-or-no-p "Colocate? "))) (majjit--internal url directory colocate-p))) ;;;###autoload (defun majjit-clone (repository directory colocate-p) "Clone a git REPOSITORY using jujutsu into a DIRECTORY. If COLOCATE-P is t, then colocate the repository with git. Depends on Magit. Inspired by `magit-clone-regular'." (interactive (majjit-read-args)) (majjit--internal repository directory colocate-p)) (provide 'majjit) ;;; majjit.el ends here