Compare commits

...

2 Commits

Author SHA1 Message Date
9b566ec98e chore: rename project to majjit 2026-02-24 13:17:45 +01:00
99a419c6bb feat: initial implementation 2026-02-24 13:17:05 +01:00
3 changed files with 159 additions and 3 deletions

View File

@@ -208,7 +208,7 @@ If you develop a new program, and you want it to be of the greatest possible use
To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the “copyright” line and a pointer to where the full notice is found. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the “copyright” line and a pointer to where the full notice is found.
magit-clone-jj majjit
Copyright (C) 2026 phundrak Copyright (C) 2026 phundrak
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 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.
@@ -221,7 +221,7 @@ Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode:
magit-clone-jj Copyright (C) 2026 phundrak majjit Copyright (C) 2026 phundrak
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details.

View File

@@ -1,3 +1,3 @@
# magit-clone-jj # majjit
Simple package to clone repositories in Emacs using Magit and Jujutsu. Simple package to clone repositories in Emacs using Magit and Jujutsu.

156
majjit.el Normal file
View File

@@ -0,0 +1,156 @@
;;; majjit.el --- Clone jujutsu repositories with Magit's interface -*- lexical-binding: t -*-
;; Author: Lucien Cartier-Tilet <lucien@phundrak.com>
;; Maintainer: Lucien Cartier-Tilet <lucien@phundrak.com>
;; 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 <https://www.gnu.org/licenses/>.
;;; 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 (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