diff --git a/Makefile b/Makefile
index 888384e..80c0089 100644
--- a/Makefile
+++ b/Makefile
@@ -21,15 +21,15 @@ INIT_PACKAGES="(progn \
all: compile test package-lint clean-elc
test:
- ${EMACS} -Q --eval ${INIT_PACKAGES} --batch -l h.el -l h-tests.el -f ert-run-tests-batch-and-exit
+ ${EMACS} -Q --eval ${INIT_PACKAGES} --batch -l my-repo-pins.el -l my-repo-pins-tests.el -f ert-run-tests-batch-and-exit
package-lint:
- ${EMACS} -Q --eval ${INIT_PACKAGES} --batch -f package-lint-batch-and-exit h.el
+ ${EMACS} -Q --eval ${INIT_PACKAGES} --batch -f package-lint-batch-and-exit my-repo-pins.el
compile: clean-elc
${EMACS} -Q --eval ${INIT_PACKAGES} -L . --batch -f batch-byte-compile *.el
-clean-elc:
+clean:
rm -f f.elc
.PHONY: all compile clean-elc package-lint test
diff --git a/README.md b/README.md
index 6d160b9..7bd4b46 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,9 @@
-# H.el [![CI](https://github.com/NinjaTrappeur/h.el/actions/workflows/test.yml/badge.svg)](https://github.com/NinjaTrappeur/h.el/actions/workflows/test.yml)
+# My Repo Pins [![CI](https://github.com/NinjaTrappeur/my-repo-pins/actions/workflows/test.yml/badge.svg)](https://github.com/NinjaTrappeur/my-repo-pins/actions/workflows/test.yml)
+
+![Project logo](./doc/assets/logo-white.svg#gh-dark-mode-only)
+![Project logo](./doc/assets/logo-black.svg#gh-light-mode-only)
+
+
This Emacs plugin is all about helping you to keep your git repositories organized in a single unified tree.
@@ -18,7 +23,7 @@ IE., having a directory structure like that:
│ └── mpv
└── NinjaTrappeur
├── cinny
- └── h.el
+ └── my-repo-pins.el
```
This Emacs plugin aims to help you navigate this repository tree **and** clone new repositories at the right place in the tree.
@@ -29,89 +34,87 @@ As always, a small demo is worth a thousand words!
**Jump to a local repository you already cloned:**
-![Screen capture showcasing h.el jumping to a already checked out repository](./doc/assets/jump-local.webp)
+![Screen capture showcasing my-repo-pins.el jumping to a already checked out repository](./doc/assets/jump-local.webp)
**Find a repository in a remote forge, clone it, and jump to it:**
-![Screen capture showcasing h.el cloning a git repository from a remote forge before jumping to it](./doc/assets/clone-project.webp)
+![Screen capture showcasing my-repo-pins.el cloning a git repository from a remote forge before jumping to it](./doc/assets/clone-project.webp)
**Alternatively, you can also specify a absolute git URL you want to clone:**
-![Screen capture showcasing h.el cloning a git repository using a absolute git url before jumping to it](./doc/assets/clone-absolute-url.webp)
-
-This plugin is heavily inspired by [**Zimbatm's h**](https://github.com/zimbatm/h).
+![Screen capture showcasing my-repo-pins.el cloning a git repository using a absolute git url before jumping to it](./doc/assets/clone-absolute-url.webp)
## Quick Start
-The minimal configuration consists in setting the directory in which you want to clone all your git repositories via the `h-code-root` variable.
+The minimal configuration consists in setting the directory in which you want to clone all your git repositories via the `my-repo-pins-code-root` variable.
Let's say you'd like to store all your git repositories in the `~/code-root` directory. You'll want to add the following snippet in your Emacs configuration file:
```elisp
(require 'h)
-(setq h-code-root "~/code-root")
+(setq my-repo-pins-code-root "~/code-root")
```
-You can then call the `M-x h-jump-to-project` command to open a project living in your `~/code-root` directory **or** clone a new project in your code root.
+You can then call the `M-x my-repo-pins` command to open a project living in your `~/code-root` directory **or** clone a new project in your code root.
Binding this command to a global key binding might make things a bit more convenient. I personally like to bind it to `M-h`. You can add the following snippet to your Emacs configuration to set up this key binding:
```elisp
-(global-set-key (kbd "M-h") 'h-jump-to-project)
+(global-set-key (kbd "M-h") 'my-repo-pins)
```
## Customization
-### h-code-root - REQUIRED
+### my-repo-pins-code-root - REQUIRED
-Path to the directory containing all your projects. `h.el` organize the git repos you'll clone in a tree fashion.
+Path to the directory containing all your projects. `my-repo-pins.el` organize the git repos you'll clone in a tree fashion.
-All the code fetched using `h.el` will end up in this root directory. A tree of subdirectories will be created mirroring the remote clone URL.
+All the code fetched using `my-repo-pins.el` will end up in this root directory. A tree of subdirectories will be created mirroring the remote clone URL.
-For instance, after checking out https://git.savannah.gnu.org/git/emacs/org-mode.git, the source code will live in the h-code-root/git.savannah.gnu.org/git/emacs/org-mode/ local directory
+For instance, after checking out https://git.savannah.gnu.org/git/emacs/org-mode.git, the source code will live in the my-repo-pins-code-root/git.savannah.gnu.org/git/emacs/org-mode/ local directory
-### h-git-bin
+### my-repo-pins-git-bin
Path pointing to the git binary. By default, it'll look for git in the current `$PATH`.
-### h-forge-fetchers
+### my-repo-pins-forge-fetchers
Alist in the form of `("FORGE NAME" . FETCH-FUNCTION)` where `FETCH-FUNCTION` is a function in charge of retrieving a potential remote clone URL. More about this function in the [Fetchers](#fetchers) section.
## Fetchers
-When a repository cannot be found in the code root directory, `h.el` will try to download it from different forges. By default, it'll try to find it on github.com, gitlab.com, git.sr.ht, and codeberg.org.
+When a repository cannot be found in the code root directory, `my-repo-pins.el` will try to download it from different forges. By default, it'll try to find it on github.com, gitlab.com, git.sr.ht, and codeberg.org.
### Re-Using the Default Fetchers for your own Forge Instance
-H.el provides some generic fetchers for Gitlab, Sourcehut, and Gitea.
+My-repo-pins.el provides some generic fetchers for Gitlab, Sourcehut, and Gitea.
You can re-use these generic fetchers for your own forge instance using the following functions:
-- GitLab: `h--query-gitlab-owner-repo`
-- SourceHut: `h--query-sourcehut-owner-repo`
-- Gitea: `h--query-gitea-owner-repo`
+- GitLab: `my-repo-pins--query-gitlab-owner-repo`
+- SourceHut: `my-repo-pins--query-sourcehut-owner-repo`
+- Gitea: `my-repo-pins--query-gitea-owner-repo`
These functions share the same 4 input arguments:
- `instance-url`: your instance [FQDN](https://fr.wikipedia.org/wiki/Fully_qualified_domain_name). For instance: `gitlab.gnome.org`, `git.alternativebit.fr`, …
- `user-name`: the user name for which we want to clone the repository.
- `repo-name`: name of the repository we want to clone.
-- `callback`: function `h.el` will use to clone the repository once we retrieved the various clone URLs. The callback takes an alist as parameter. The alist being of the form of : `((ssh . SSH-CHECKOUT-URL) (https . HTTPS-CHECKOUT-URL))`.
+- `callback`: function `My-repo-pins.el` will use to clone the repository once we retrieved the various clone URLs. The callback takes an alist as parameter. The alist being of the form of : `((ssh . SSH-CHECKOUT-URL) (https . HTTPS-CHECKOUT-URL))`.
-You can re-use these functions by instantiating them for a specific forge, then by appending this instantiation to the `h-forge-fetchers` variable in your Emacs configuration.
+You can re-use these functions by instantiating them for a specific forge, then by appending this instantiation to the `my-repo-pins-forge-fetchers` variable in your Emacs configuration.
Let's say you want to retrieve repositories from the Gnome Gitlab instance living at `gitlab.gnome.org`. You'll have to add the following snippet to your Emacs configuration:
```elisp
-(setq h-forge-fetchers
- `(("gitlab.gnome.org" (lambda (owner repo cb)(h--query-gitlab-owner-repo "gitlab.gnome.org" owner repo cb)))
- ,h-forge-fetchers))
+(setq my-repo-pins-forge-fetchers
+ `(("gitlab.gnome.org" (lambda (owner repo cb)(my-repo-pins--query-gitlab-owner-repo "gitlab.gnome.org" owner repo cb)))
+ ,my-repo-pins-forge-fetchers))
```
### Writing your Forge Fetcher from Scratch
-You may also want to support a forge for which `h.el` currently does not provide any generic fetcher. In that case, you'll have to write a function in the form of:
+You may also want to support a forge for which `my-repo-pins.el` currently does not provide any generic fetcher. In that case, you'll have to write a function in the form of:
```elisp
(defun your-custom-fetcher (owner repo)
@@ -121,8 +124,8 @@ You may also want to support a forge for which `h.el` currently does not provide
The function needs to accept two input parameters:
-- `owner`: string containing the name of the owner of the query repository. IE. `ninjatrappeur` for the `ninjatrappeur/h.el` query.
-- `repository`: string containing the name of the query repository. IE. `h.el` for the `ninjatrappeur/h.el` query.
+- `owner`: string containing the name of the owner of the query repository. IE. `ninjatrappeur` for the `ninjatrappeur/my-repo-pins.el` query.
+- `repository`: string containing the name of the query repository. IE. `my-repo-pins.el` for the `ninjatrappeur/my-repo-pins.el` query.
This function will return either `nil` in case the query couldn't be found on the remote forge. An alist containing the SSH and HTTPS clone URLs in the form of:
@@ -130,3 +133,8 @@ This function will return either `nil` in case the query couldn't be found on th
'((ssh . SSH-CHECKOUT-URL)
(https . HTTPS-CHECKOUT-URL))
```
+
+## Aknowledgements
+
+- This plugin is heavily inspired by [**Zimbatm's h**](https://github.com/zimbatm/h). Thanks a lot for this amazing tool, it made my life easier for years!
+- Thanks a lot to Wiwi who found the **terrible** pun we used to name this project.
diff --git a/doc/assets/logo-black.svg b/doc/assets/logo-black.svg
new file mode 100644
index 0000000..66a17a3
--- /dev/null
+++ b/doc/assets/logo-black.svg
@@ -0,0 +1,71 @@
+
+
+
+
diff --git a/doc/assets/logo-inkscape.svg b/doc/assets/logo-inkscape.svg
new file mode 100644
index 0000000..1771440
--- /dev/null
+++ b/doc/assets/logo-inkscape.svg
@@ -0,0 +1,95 @@
+
+
+
+
diff --git a/doc/assets/logo-white.svg b/doc/assets/logo-white.svg
new file mode 100644
index 0000000..b299337
--- /dev/null
+++ b/doc/assets/logo-white.svg
@@ -0,0 +1,71 @@
+
+
+
+
diff --git a/h-tests.el b/h-tests.el
deleted file mode 100644
index 5134143..0000000
--- a/h-tests.el
+++ /dev/null
@@ -1,356 +0,0 @@
-;;; h-tests.el --- Project navigation and remote checkout -*- lexical-binding: t; -*-
-
-;;; Copyright (C) 2022 Félix Baylac Jacqué
-;;; Author: Félix Baylac Jacqué
-;;; Maintainer: Félix Baylac Jacqué
-;;; Version: 1.14.0
-
-;;; License:
-
-;;; 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:
-
-;;; TODO before publish
-
-;;; Code:
-
-(require 'ert)
-(require 'h)
-
-;; Test Helpers
-;;;;;;;;;;;;;;
-
-(defun h--tests-with-temp-dir (func)
- "Run the FUNC function in a temporary directory.
-
-FUNC gets called with the temp dir as parameter.
-The directory gets deleted once we exit FUNC."
- (let ((temp-dir (make-temp-file "h-test-" t)))
- (unwind-protect
- (funcall func (file-name-as-directory temp-dir))
- (delete-directory temp-dir t))))
-
-(defun h--tests-init-fake-git-repo (dir)
- "Create a dummy git repo at DIR.
-
-If DIR doesn't exists, we create it first."
- (let* ((d (file-name-as-directory dir))
- (git-process
- (progn
- (make-directory d t)
- (h--call-git-in-dir d
- nil
- "init"))))
- (progn
- (unless (file-directory-p d) (make-directory d t))
- ;; ERT does not handle async processes gracefully for the time
- ;; being. Blocking and waiting for the git process to exit
- ;; before moving on.
- (while (accept-process-output git-process)))))
-
-;; Test Dirs Setup
-;;;;;;;;;;;;;;;;;
-
-(defun h--tests-run-on-testroot-1 (func)
- "Run the FUNC function on testroot1.
-
-FUNC is called with the directory cotaining test root 1 as parameter.
-
-For reference: test-root-1 looks like this:
- test-root-1
- ├── example1.tld
- │ ├── user1
- │ │ ├── proj1
- │ │ └── proj2
- │ └── user2
- │ └── proj1
- └── example2.tld
- └── user1
- └── proj1"
- (h--tests-with-temp-dir
- (lambda (temp-dir)
- (progn
- (h--tests-init-fake-git-repo (concat temp-dir "example1.tld/user1/proj1"))
- (h--tests-init-fake-git-repo (concat temp-dir "example1.tld/user1/proj2"))
- (h--tests-init-fake-git-repo (concat temp-dir "example1.tld/user2/proj1"))
- (h--tests-init-fake-git-repo (concat temp-dir "example2.tld/user1/proj1"))
- (funcall func temp-dir)
- ))))
-
-(defun h--tests-run-on-testroot-2 (func)
- "Run the FUNC function on testroot2.
-
-FUNC is called with the directory cotaining test root 2 as parameter.
-
-For reference: test-root-2 looks like this:
- test-root-2
- ├── example1.tld
- │ ├── user1
- │ │ ├── proj1
- │ │ └── proj2 (NOT A GIT REPO)
- │ └── user2
- │ └── proj1
- └── example2.tld
- └── user1
- └── proj1"
- (h--tests-with-temp-dir
- (lambda (temp-dir)
- (progn
- (h--tests-init-fake-git-repo (concat temp-dir "example1.tld/user1/proj1"))
- (make-directory (concat (file-name-as-directory temp-dir) "example1.tld/user1/proj2"))
- (h--tests-init-fake-git-repo (concat temp-dir "example1.tld/user2/proj1"))
- (h--tests-init-fake-git-repo (concat temp-dir "example2.tld/user1/proj1"))
- (funcall func temp-dir)))))
-
-
-(defun h--tests-run-on-empty-testroot (func)
- "Run the FUNC function on testroot1.
-
-FUNC is called with a empty test root.
-
-For reference: a empty test root looks like this:
- test-root"
- (h--tests-with-temp-dir
- (lambda (temp-dir)
- (progn
- (funcall func temp-dir)))))
-
-;; Tests
-;;;;;;;
-
-(ert-deftest h--tests-get-code-root-projects-coderoot-1 ()
- "Test the `h--get-code-root-projects with test-root-1 setup."
- (let
- ((results
- (h--tests-run-on-testroot-1 (lambda (root) (h--get-code-root-projects root))))
- )
- (should (member "example1.tld/user1/proj1" results))
- (should (member "example1.tld/user1/proj2" results))
- (should (member "example1.tld/user2/proj1" results))
- (should (member "example2.tld/user1/proj1" results))
- (should (eq (length results) 4))))
-
-
-(ert-deftest h--tests-find-git-dirs-recursively-coderoot-1 ()
- "Test the `h--get-code-root-projects with test-root-1 setup."
- (let*
- ((r nil)
- (results
- (h--tests-run-on-testroot-1
- (lambda (root)
- (progn (setq r root)
- (h--find-git-dirs-recursively root))))))
- (should (member (concat r "example1.tld/user1/proj1/") results))
- (should (member (concat r "example1.tld/user1/proj2/") results))
- (should (member (concat r "example1.tld/user2/proj1/") results))
- (should (member (concat r "example2.tld/user1/proj1/") results))
- (should (eq (length results) 4))))
-
-(ert-deftest h--tests-get-code-root-projects-coderoot-2 ()
- "Test the `h--get-code-root-projects with test-root-2 setup."
- (let
- ((results
- (h--tests-run-on-testroot-2 (lambda (root) (h--get-code-root-projects root))))
- )
- (should (member "example1.tld/user1/proj1" results))
- (should (member "example1.tld/user2/proj1" results))
- (should (member "example2.tld/user1/proj1" results))
- (should (eq (length results) 3))))
-
-(ert-deftest h--tests-find-git-dirs-recursively-coderoot-2 ()
- "Test the `h--get-code-root-projects with test-root-2 setup."
- (let*
- ((r nil)
- (results
- (h--tests-run-on-testroot-2
- (lambda (root)
- (progn (setq r root)
- (h--find-git-dirs-recursively root))))))
- (should (member (concat r "example1.tld/user1/proj1/") results))
- (should (member (concat r "example1.tld/user2/proj1/") results))
- (should (member (concat r "example2.tld/user1/proj1/") results))
- (should (eq (length results) 3))))
-
-(ert-deftest h--tests-get-code-root-projects-empty-coderoot ()
- "Test the `h--get-code-root-projects with a empty coderoot."
- (let
- ((results
- (h--tests-run-on-empty-testroot (lambda (root) (h--get-code-root-projects root))))
- )
- (should (seq-empty-p results))))
-
-(ert-deftest h--tests-find-git-dirs-recursively-empty-coderoot ()
- "Test the `h--get-code-root-projects with a empty coderoot."
- (let
- ((results
- (h--tests-run-on-empty-testroot (lambda (root) (h--find-git-dirs-recursively root))))
- )
- (should (seq-empty-p results))))
-
-(ert-deftest h--tests-get-code-root-projects-no-coderoot ()
- "Test the `h--get-code-root-projects with a non-existing coderoot."
- (let
- ((results (h--get-code-root-projects "/does/not/exist")))
- (should (seq-empty-p results))))
-
-
-;; Test Fetchers
-;;;;;;;;;;;;;;;;;
-
-;; Github
-
-(ert-deftest h--tests-fetch-github-parse-response-ok ()
- "Test h--tests-fetch-github-parse-response with a fixture."
- (with-temp-buffer
- (insert-file-contents "./tests/fixtures/github-get-request-ok.txt")
- (should (equal (h--fetch-github-parse-response (current-buffer))
- '((ssh . "git@github.com:NinjaTrappeur/h.el.git")
- (https . "https://github.com/NinjaTrappeur/h.el.git"))))))
-
-(ert-deftest h--tests-fetch-github-parse-response-ko ()
- "Test h--tests-fetch-github-parse-response with a fixture."
- (with-temp-buffer
- (insert-file-contents "./tests/fixtures/github-get-request-ko.txt")
- (should (equal (h--fetch-github-parse-response (current-buffer)) nil))))
-
-;; Gitea
-
-(ert-deftest h--tests-fetch-gitea-parse-response-ok ()
- "Test h--tests-fetch-gitea-parse-response with a fixture."
- (with-temp-buffer
- (insert-file-contents "./tests/fixtures/gitea-get-request-ok.txt")
- (should (equal (h--fetch-gitea-parse-response (current-buffer))
- '((ssh . "gitea@git.alternativebit.fr:NinjaTrappeur/h.el.git")
- (https . "https://git.alternativebit.fr/NinjaTrappeur/h.el.git"))))))
-
-(ert-deftest h--tests-fetch-gitea-parse-response-ko ()
- "Test h--tests-fetch-gitea-parse-response with a fixture."
- (with-temp-buffer
- (insert-file-contents "./tests/fixtures/gitea-get-request-ko.txt")
- (should (equal (h--fetch-gitea-parse-response (current-buffer)) nil))))
-
-;; Test repo URI parser
-;;;;;;;;;;;;;;;;;
-
-(ert-deftest h--test-parse-repo-identifier ()
- "Test h--parse-repo-identifier."
- (should (equal
- (h--parse-repo-identifier "https://github.com/Ninjatrappeur/h.el")
- '((tag . full-url) (full-url . "https://github.com/Ninjatrappeur/h.el"))))
- (should (equal
- (h--parse-repo-identifier "github.com/Ninjatrappeur/h.el")
- '((tag . full-url) (full-url . "github.com/Ninjatrappeur/h.el"))))
- (should (equal
- (h--parse-repo-identifier "Ninjatrappeur/h.el")
- '((tag . owner-repo) (owner . "Ninjatrappeur") (repo . "h.el"))))
- (should (equal
- (h--parse-repo-identifier "h.el")
- '((tag . repo) (repo . "h.el")))))
-
-(ert-deftest h--test-filepath-from-clone-url ()
- "Test h--filepath-from-clone-url."
- ;; HTTP/HTTPS
- (should (equal (h--filepath-from-clone-url "http://github.com/NinjaTrappeur/h.el.git") "github.com/NinjaTrappeur/h.el"))
- (should (equal (h--filepath-from-clone-url "http://github.com/NinjaTrappeur/h.el") "github.com/NinjaTrappeur/h.el"))
- (should (equal (h--filepath-from-clone-url "https://github.com/NinjaTrappeur/h.el.git") "github.com/NinjaTrappeur/h.el"))
- (should (equal (h--filepath-from-clone-url "https://github.com/NinjaTrappeur/h.el") "github.com/NinjaTrappeur/h.el"))
- (should (equal (h--filepath-from-clone-url "http://git.savannah.gnu.org/cgit/emacs/elpa.git") "git.savannah.gnu.org/cgit/emacs/elpa"))
- (should (equal (h--filepath-from-clone-url "https://git.savannah.gnu.org/git/emacs.git") "git.savannah.gnu.org/git/emacs"))
- ;; SSH
- (should (equal (h--filepath-from-clone-url "ssh://git@github.com:NinjaTrappeur/h.el.git") "github.com/NinjaTrappeur/h.el"))
- (should (equal (h--filepath-from-clone-url "ssh://git@github.com:NinjaTrappeur/h.el") "github.com/NinjaTrappeur/h.el"))
- (should (equal (h--filepath-from-clone-url "git@github.com:NinjaTrappeur/h.el.git") "github.com/NinjaTrappeur/h.el"))
- (should (equal (h--filepath-from-clone-url "git@github.com:NinjaTrappeur/h.el") "github.com/NinjaTrappeur/h.el")))
-
-(ert-deftest h--test-git-clone-in-dir ()
- "Test the h--git-clone-in-dir function."
- (h--tests-run-on-testroot-1
- (lambda (dir)
- (let*
- ((tmpdir (make-temp-file "h-test-" t))
- (git-process (h--git-clone-in-dir
- (format "file://%s" (concat dir "example1.tld/user1/proj1/"))
- tmpdir)))
- (progn
- (while (accept-process-output git-process))
- (should (file-exists-p (format "%s/.git" tmpdir)))
- (delete-directory tmpdir t))))))
-
-;;; State Management tests
-
-(ert-deftest h--test-init-forges-state ()
- "Test the h--init-forges-state function."
- (let* ((forge-fetchers
- '(("GitHub.com" .
- ((query-user-repo . h--query-github-owner-repo)))
- ("GitLab.com" .
- ((query-user-repo . (lambda (owner repo cb) (h--query-gitlab-owner-repo "gitlab.com" owner repo cb)))))
- ("git.sr.ht" .
- ((query-user-repo . (lambda (owner repo cb) (h--query-sourcehut-owner-repo "git.sr.ht" owner repo cb)))))
- ("Codeberg.org" .
- ((query-user-repo . (lambda (owner repo cb) (h--query-gitea-owner-repo "codeberg.org" owner repo cb)))))))
- (result (h--init-forges-state forge-fetchers)))
- (should (equal (alist-get "GitHub.com" result nil nil 'equal) 'loading))
- (should (equal (alist-get "GitLab.com" result nil nil 'equal) 'loading))
- (should (equal (alist-get "git.sr.ht" result nil nil 'equal) 'loading))
- (should (equal (alist-get "Codeberg.org" result nil nil 'equal) 'loading))))
-
-;;; UI-related tests
-
-(ert-deftest h--test-add-keys-to-forge-status ()
- "Test the h--add-keys-to-forge-status function."
- (let
- ((dummy-forge-query-status-one-result
- '(("GitHub"
- (ssh . "git@github.com:NinjaTrappeur/h.el.git")
- (https . "https://github.com/NinjaTrappeur/h.el.git"))
- ("GitLab" . not-found)))
- (expected-forge-query-status-with-keys-one-result
- `(("GitHub"
- (status
- (ssh . "git@github.com:NinjaTrappeur/h.el.git")
- (https . "https://github.com/NinjaTrappeur/h.el.git"))
- (key . ,?1))
- ("GitLab" (status . not-found))))
- (dummy-forge-query-status-two-results
- '(("GitHub"
- (ssh . "git@github.com:NinjaTrappeur/h.el.git")
- (https . "https://github.com/NinjaTrappeur/h.el.git"))
- ("Codeberg" . not-found)
- ("GitLab"
- (ssh . "git@gitlab.com:NinjaTrappeur/h.el.git")
- (https . "https://gitlab.com/NinjaTrappeur/h.el.git"))))
- (expected-forge-query-status-with-keys-two-results
- `(("GitHub"
- (status
- (ssh . "git@github.com:NinjaTrappeur/h.el.git")
- (https . "https://github.com/NinjaTrappeur/h.el.git"))
- (key . ,'?1))
- ("Codeberg" (status . not-found))
- ("GitLab"
- (status
- (ssh . "git@gitlab.com:NinjaTrappeur/h.el.git")
- (https . "https://gitlab.com/NinjaTrappeur/h.el.git"))
- (key . ,'?2)))))
-
- (should (equal
- expected-forge-query-status-with-keys-one-result
- (h--add-keys-to-forge-status dummy-forge-query-status-one-result)))
- (should (equal
- expected-forge-query-status-with-keys-two-results
- (h--add-keys-to-forge-status dummy-forge-query-status-two-results)))))
-
-(provide 'h-tests)
-;;; h-tests.el ends here
diff --git a/my-repo-pins-tests.el b/my-repo-pins-tests.el
new file mode 100644
index 0000000..aee417d
--- /dev/null
+++ b/my-repo-pins-tests.el
@@ -0,0 +1,356 @@
+;;; my-repo-pins-tests.el --- Project navigation and remote checkout -*- lexical-binding: t; -*-
+
+;;; Copyright (C) 2022 Félix Baylac Jacqué
+;;; Author: Félix Baylac Jacqué
+;;; Maintainer: Félix Baylac Jacqué
+;;; Version: 1.14.0
+
+;;; License:
+
+;;; 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:
+
+;;; TODO before publish
+
+;;; Code:
+
+(require 'ert)
+(require 'my-repo-pins)
+
+;; Test Helpers
+;;;;;;;;;;;;;;
+
+(defun my-repo-pins--tests-with-temp-dir (func)
+ "Run the FUNC function in a temporary directory.
+
+FUNC gets called with the temp dir as parameter.
+The directory gets deleted once we exit FUNC."
+ (let ((temp-dir (make-temp-file "my-repo-pins-test-" t)))
+ (unwind-protect
+ (funcall func (file-name-as-directory temp-dir))
+ (delete-directory temp-dir t))))
+
+(defun my-repo-pins--tests-init-fake-git-repo (dir)
+ "Create a dummy git repo at DIR.
+
+If DIR doesn't exists, we create it first."
+ (let* ((d (file-name-as-directory dir))
+ (git-process
+ (progn
+ (make-directory d t)
+ (my-repo-pins--call-git-in-dir d
+ nil
+ "init"))))
+ (progn
+ (unless (file-directory-p d) (make-directory d t))
+ ;; ERT does not handle async processes gracefully for the time
+ ;; being. Blocking and waiting for the git process to exit
+ ;; before moving on.
+ (while (accept-process-output git-process)))))
+
+;; Test Dirs Setup
+;;;;;;;;;;;;;;;;;
+
+(defun my-repo-pins--tests-run-on-testroot-1 (func)
+ "Run the FUNC function on testroot1.
+
+FUNC is called with the directory cotaining test root 1 as parameter.
+
+For reference: test-root-1 looks like this:
+ test-root-1
+ ├── example1.tld
+ │ ├── user1
+ │ │ ├── proj1
+ │ │ └── proj2
+ │ └── user2
+ │ └── proj1
+ └── example2.tld
+ └── user1
+ └── proj1"
+ (my-repo-pins--tests-with-temp-dir
+ (lambda (temp-dir)
+ (progn
+ (my-repo-pins--tests-init-fake-git-repo (concat temp-dir "example1.tld/user1/proj1"))
+ (my-repo-pins--tests-init-fake-git-repo (concat temp-dir "example1.tld/user1/proj2"))
+ (my-repo-pins--tests-init-fake-git-repo (concat temp-dir "example1.tld/user2/proj1"))
+ (my-repo-pins--tests-init-fake-git-repo (concat temp-dir "example2.tld/user1/proj1"))
+ (funcall func temp-dir)
+ ))))
+
+(defun my-repo-pins--tests-run-on-testroot-2 (func)
+ "Run the FUNC function on testroot2.
+
+FUNC is called with the directory cotaining test root 2 as parameter.
+
+For reference: test-root-2 looks like this:
+ test-root-2
+ ├── example1.tld
+ │ ├── user1
+ │ │ ├── proj1
+ │ │ └── proj2 (NOT A GIT REPO)
+ │ └── user2
+ │ └── proj1
+ └── example2.tld
+ └── user1
+ └── proj1"
+ (my-repo-pins--tests-with-temp-dir
+ (lambda (temp-dir)
+ (progn
+ (my-repo-pins--tests-init-fake-git-repo (concat temp-dir "example1.tld/user1/proj1"))
+ (make-directory (concat (file-name-as-directory temp-dir) "example1.tld/user1/proj2"))
+ (my-repo-pins--tests-init-fake-git-repo (concat temp-dir "example1.tld/user2/proj1"))
+ (my-repo-pins--tests-init-fake-git-repo (concat temp-dir "example2.tld/user1/proj1"))
+ (funcall func temp-dir)))))
+
+
+(defun my-repo-pins--tests-run-on-empty-testroot (func)
+ "Run the FUNC function on testroot1.
+
+FUNC is called with a empty test root.
+
+For reference: a empty test root looks like this:
+ test-root"
+ (my-repo-pins--tests-with-temp-dir
+ (lambda (temp-dir)
+ (progn
+ (funcall func temp-dir)))))
+
+;; Tests
+;;;;;;;
+
+(ert-deftest my-repo-pins--tests-get-code-root-projects-coderoot-1 ()
+ "Test the `my-repo-pins--get-code-root-projects with test-root-1 setup."
+ (let
+ ((results
+ (my-repo-pins--tests-run-on-testroot-1 (lambda (root) (my-repo-pins--get-code-root-projects root))))
+ )
+ (should (member "example1.tld/user1/proj1" results))
+ (should (member "example1.tld/user1/proj2" results))
+ (should (member "example1.tld/user2/proj1" results))
+ (should (member "example2.tld/user1/proj1" results))
+ (should (eq (length results) 4))))
+
+
+(ert-deftest my-repo-pins--tests-find-git-dirs-recursively-coderoot-1 ()
+ "Test the `my-repo-pins--get-code-root-projects with test-root-1 setup."
+ (let*
+ ((r nil)
+ (results
+ (my-repo-pins--tests-run-on-testroot-1
+ (lambda (root)
+ (progn (setq r root)
+ (my-repo-pins--find-git-dirs-recursively root))))))
+ (should (member (concat r "example1.tld/user1/proj1/") results))
+ (should (member (concat r "example1.tld/user1/proj2/") results))
+ (should (member (concat r "example1.tld/user2/proj1/") results))
+ (should (member (concat r "example2.tld/user1/proj1/") results))
+ (should (eq (length results) 4))))
+
+(ert-deftest my-repo-pins--tests-get-code-root-projects-coderoot-2 ()
+ "Test the `my-repo-pins--get-code-root-projects with test-root-2 setup."
+ (let
+ ((results
+ (my-repo-pins--tests-run-on-testroot-2 (lambda (root) (my-repo-pins--get-code-root-projects root))))
+ )
+ (should (member "example1.tld/user1/proj1" results))
+ (should (member "example1.tld/user2/proj1" results))
+ (should (member "example2.tld/user1/proj1" results))
+ (should (eq (length results) 3))))
+
+(ert-deftest my-repo-pins--tests-find-git-dirs-recursively-coderoot-2 ()
+ "Test the `my-repo-pins--get-code-root-projects with test-root-2 setup."
+ (let*
+ ((r nil)
+ (results
+ (my-repo-pins--tests-run-on-testroot-2
+ (lambda (root)
+ (progn (setq r root)
+ (my-repo-pins--find-git-dirs-recursively root))))))
+ (should (member (concat r "example1.tld/user1/proj1/") results))
+ (should (member (concat r "example1.tld/user2/proj1/") results))
+ (should (member (concat r "example2.tld/user1/proj1/") results))
+ (should (eq (length results) 3))))
+
+(ert-deftest my-repo-pins--tests-get-code-root-projects-empty-coderoot ()
+ "Test the `my-repo-pins--get-code-root-projects with a empty coderoot."
+ (let
+ ((results
+ (my-repo-pins--tests-run-on-empty-testroot (lambda (root) (my-repo-pins--get-code-root-projects root))))
+ )
+ (should (seq-empty-p results))))
+
+(ert-deftest my-repo-pins--tests-find-git-dirs-recursively-empty-coderoot ()
+ "Test the `my-repo-pins--get-code-root-projects with a empty coderoot."
+ (let
+ ((results
+ (my-repo-pins--tests-run-on-empty-testroot (lambda (root) (my-repo-pins--find-git-dirs-recursively root))))
+ )
+ (should (seq-empty-p results))))
+
+(ert-deftest my-repo-pins--tests-get-code-root-projects-no-coderoot ()
+ "Test the `my-repo-pins--get-code-root-projects with a non-existing coderoot."
+ (let
+ ((results (my-repo-pins--get-code-root-projects "/does/not/exist")))
+ (should (seq-empty-p results))))
+
+
+;; Test Fetchers
+;;;;;;;;;;;;;;;;;
+
+;; Github
+
+(ert-deftest my-repo-pins--tests-fetch-github-parse-response-ok ()
+ "Test my-repo-pins--tests-fetch-github-parse-response with a fixture."
+ (with-temp-buffer
+ (insert-file-contents "./tests/fixtures/github-get-request-ok.txt")
+ (should (equal (my-repo-pins--fetch-github-parse-response (current-buffer))
+ '((ssh . "git@github.com:NinjaTrappeur/my-repo-pins.el.git")
+ (https . "https://github.com/NinjaTrappeur/my-repo-pins.el.git"))))))
+
+(ert-deftest my-repo-pins--tests-fetch-github-parse-response-ko ()
+ "Test my-repo-pins--tests-fetch-github-parse-response with a fixture."
+ (with-temp-buffer
+ (insert-file-contents "./tests/fixtures/github-get-request-ko.txt")
+ (should (equal (my-repo-pins--fetch-github-parse-response (current-buffer)) nil))))
+
+;; Gitea
+
+(ert-deftest my-repo-pins--tests-fetch-gitea-parse-response-ok ()
+ "Test my-repo-pins--tests-fetch-gitea-parse-response with a fixture."
+ (with-temp-buffer
+ (insert-file-contents "./tests/fixtures/gitea-get-request-ok.txt")
+ (should (equal (my-repo-pins--fetch-gitea-parse-response (current-buffer))
+ '((ssh . "gitea@git.alternativebit.fr:NinjaTrappeur/my-repo-pins.el.git")
+ (https . "https://git.alternativebit.fr/NinjaTrappeur/my-repo-pins.el.git"))))))
+
+(ert-deftest my-repo-pins--tests-fetch-gitea-parse-response-ko ()
+ "Test my-repo-pins--tests-fetch-gitea-parse-response with a fixture."
+ (with-temp-buffer
+ (insert-file-contents "./tests/fixtures/gitea-get-request-ko.txt")
+ (should (equal (my-repo-pins--fetch-gitea-parse-response (current-buffer)) nil))))
+
+;; Test repo URI parser
+;;;;;;;;;;;;;;;;;
+
+(ert-deftest my-repo-pins--test-parse-repo-identifier ()
+ "Test my-repo-pins--parse-repo-identifier."
+ (should (equal
+ (my-repo-pins--parse-repo-identifier "https://github.com/Ninjatrappeur/my-repo-pins.el")
+ '((tag . full-url) (full-url . "https://github.com/Ninjatrappeur/my-repo-pins.el"))))
+ (should (equal
+ (my-repo-pins--parse-repo-identifier "github.com/Ninjatrappeur/my-repo-pins.el")
+ '((tag . full-url) (full-url . "github.com/Ninjatrappeur/my-repo-pins.el"))))
+ (should (equal
+ (my-repo-pins--parse-repo-identifier "Ninjatrappeur/my-repo-pins.el")
+ '((tag . owner-repo) (owner . "Ninjatrappeur") (repo . "my-repo-pins.el"))))
+ (should (equal
+ (my-repo-pins--parse-repo-identifier "my-repo-pins.el")
+ '((tag . repo) (repo . "my-repo-pins.el")))))
+
+(ert-deftest my-repo-pins--test-filepath-from-clone-url ()
+ "Test my-repo-pins--filepath-from-clone-url."
+ ;; HTTP/HTTPS
+ (should (equal (my-repo-pins--filepath-from-clone-url "http://github.com/NinjaTrappeur/my-repo-pins.el.git") "github.com/NinjaTrappeur/my-repo-pins.el"))
+ (should (equal (my-repo-pins--filepath-from-clone-url "http://github.com/NinjaTrappeur/my-repo-pins.el") "github.com/NinjaTrappeur/my-repo-pins.el"))
+ (should (equal (my-repo-pins--filepath-from-clone-url "https://github.com/NinjaTrappeur/my-repo-pins.el.git") "github.com/NinjaTrappeur/my-repo-pins.el"))
+ (should (equal (my-repo-pins--filepath-from-clone-url "https://github.com/NinjaTrappeur/my-repo-pins.el") "github.com/NinjaTrappeur/my-repo-pins.el"))
+ (should (equal (my-repo-pins--filepath-from-clone-url "http://git.savannah.gnu.org/cgit/emacs/elpa.git") "git.savannah.gnu.org/cgit/emacs/elpa"))
+ (should (equal (my-repo-pins--filepath-from-clone-url "https://git.savannah.gnu.org/git/emacs.git") "git.savannah.gnu.org/git/emacs"))
+ ;; SSH
+ (should (equal (my-repo-pins--filepath-from-clone-url "ssh://git@github.com:NinjaTrappeur/my-repo-pins.el.git") "github.com/NinjaTrappeur/my-repo-pins.el"))
+ (should (equal (my-repo-pins--filepath-from-clone-url "ssh://git@github.com:NinjaTrappeur/my-repo-pins.el") "github.com/NinjaTrappeur/my-repo-pins.el"))
+ (should (equal (my-repo-pins--filepath-from-clone-url "git@github.com:NinjaTrappeur/my-repo-pins.el.git") "github.com/NinjaTrappeur/my-repo-pins.el"))
+ (should (equal (my-repo-pins--filepath-from-clone-url "git@github.com:NinjaTrappeur/my-repo-pins.el") "github.com/NinjaTrappeur/my-repo-pins.el")))
+
+(ert-deftest my-repo-pins--test-git-clone-in-dir ()
+ "Test the my-repo-pins--git-clone-in-dir function."
+ (my-repo-pins--tests-run-on-testroot-1
+ (lambda (dir)
+ (let*
+ ((tmpdir (make-temp-file "my-repo-pins-test-" t))
+ (git-process (my-repo-pins--git-clone-in-dir
+ (format "file://%s" (concat dir "example1.tld/user1/proj1/"))
+ tmpdir)))
+ (progn
+ (while (accept-process-output git-process))
+ (should (file-exists-p (format "%s/.git" tmpdir)))
+ (delete-directory tmpdir t))))))
+
+;;; State Management tests
+
+(ert-deftest my-repo-pins--test-init-forges-state ()
+ "Test the my-repo-pins--init-forges-state function."
+ (let* ((forge-fetchers
+ '(("GitHub.com" .
+ ((query-user-repo . my-repo-pins--query-github-owner-repo)))
+ ("GitLab.com" .
+ ((query-user-repo . (lambda (owner repo cb) (my-repo-pins--query-gitlab-owner-repo "gitlab.com" owner repo cb)))))
+ ("git.sr.ht" .
+ ((query-user-repo . (lambda (owner repo cb) (my-repo-pins--query-sourcehut-owner-repo "git.sr.ht" owner repo cb)))))
+ ("Codeberg.org" .
+ ((query-user-repo . (lambda (owner repo cb) (my-repo-pins--query-gitea-owner-repo "codeberg.org" owner repo cb)))))))
+ (result (my-repo-pins--init-forges-state forge-fetchers)))
+ (should (equal (alist-get "GitHub.com" result nil nil 'equal) 'loading))
+ (should (equal (alist-get "GitLab.com" result nil nil 'equal) 'loading))
+ (should (equal (alist-get "git.sr.ht" result nil nil 'equal) 'loading))
+ (should (equal (alist-get "Codeberg.org" result nil nil 'equal) 'loading))))
+
+;;; UI-related tests
+
+(ert-deftest my-repo-pins--test-add-keys-to-forge-status ()
+ "Test the my-repo-pins--add-keys-to-forge-status function."
+ (let
+ ((dummy-forge-query-status-one-result
+ '(("GitHub"
+ (ssh . "git@github.com:NinjaTrappeur/my-repo-pins.el.git")
+ (https . "https://github.com/NinjaTrappeur/my-repo-pins.el.git"))
+ ("GitLab" . not-found)))
+ (expected-forge-query-status-with-keys-one-result
+ `(("GitHub"
+ (status
+ (ssh . "git@github.com:NinjaTrappeur/my-repo-pins.el.git")
+ (https . "https://github.com/NinjaTrappeur/my-repo-pins.el.git"))
+ (key . ,?1))
+ ("GitLab" (status . not-found))))
+ (dummy-forge-query-status-two-results
+ '(("GitHub"
+ (ssh . "git@github.com:NinjaTrappeur/my-repo-pins.el.git")
+ (https . "https://github.com/NinjaTrappeur/my-repo-pins.el.git"))
+ ("Codeberg" . not-found)
+ ("GitLab"
+ (ssh . "git@gitlab.com:NinjaTrappeur/my-repo-pins.el.git")
+ (https . "https://gitlab.com/NinjaTrappeur/my-repo-pins.el.git"))))
+ (expected-forge-query-status-with-keys-two-results
+ `(("GitHub"
+ (status
+ (ssh . "git@github.com:NinjaTrappeur/my-repo-pins.el.git")
+ (https . "https://github.com/NinjaTrappeur/my-repo-pins.el.git"))
+ (key . ,'?1))
+ ("Codeberg" (status . not-found))
+ ("GitLab"
+ (status
+ (ssh . "git@gitlab.com:NinjaTrappeur/my-repo-pins.el.git")
+ (https . "https://gitlab.com/NinjaTrappeur/my-repo-pins.el.git"))
+ (key . ,'?2)))))
+
+ (should (equal
+ expected-forge-query-status-with-keys-one-result
+ (my-repo-pins--add-keys-to-forge-status dummy-forge-query-status-one-result)))
+ (should (equal
+ expected-forge-query-status-with-keys-two-results
+ (my-repo-pins--add-keys-to-forge-status dummy-forge-query-status-two-results)))))
+
+(provide 'my-repo-pins-tests)
+;;; my-repo-pins-tests.el ends here
diff --git a/h.el b/my-repo-pins.el
similarity index 74%
rename from h.el
rename to my-repo-pins.el
index 3b39e3a..58458b9 100644
--- a/h.el
+++ b/my-repo-pins.el
@@ -1,10 +1,10 @@
-;;; h.el --- Helps you keep your git repositories organized -*- lexical-binding: t -*-
+;;; my-repo-pins.el --- Helps you keep your git repositories organized -*- lexical-binding: t -*-
;;; Copyright (C) 2022 Félix Baylac Jacqué
;;; Author: Félix Baylac Jacqué
;;; Maintainer: Félix Baylac Jacqué
;;; Version: 0.1
-;;; Homepage: https://github.com/NinjaTrappeur/h.el
+;;; Homepage: https://github.com/NinjaTrappeur/my-repo-pins.el
;;; Package-Requires: ((emacs "26.1"))
;;; License:
;;;
@@ -41,10 +41,10 @@
;;; │ └── mpv
;;; └── NinjaTrappeur
;;; ├── cinny
-;;; └── h.el
+;;; └── my-repo-pins.el
;;;
-;;; The main entry point of this package is the h-jump-to-project
-;;; command. Using it, you can either:
+;;; The main entry point of this package is the my-repo-pins command.
+;;; Using it, you can either:
;;;
;;; - Open dired in a local project you already cloned.
;;; - Query remote forges for a repository, clone it, and finally open
@@ -53,16 +53,16 @@
;;;
;;; The minimal configuration consists in setting the directory in
;;; which you want to clone all your git repositories via the
-;;; h-code-root variable.
+;;; my-repo-pins-code-root variable.
;;;
;;; Let's say you'd like to store all your git repositories in the
;;; ~/code-root directory. You'll want to add the following snippet in
;;; your Emacs configuration file:
;;;
-;;; (require 'h)
-;;; (setq h-code-root "~/code-root")
+;;; (require 'my-repo-pins)
+;;; (setq my-repo-pins-code-root "~/code-root")
;;;
-;;; You can then call the M-x h-jump-to-project command to open a
+;;; You can then call the M-x my-repo-pins command to open a
;;; project living in your ~/code-root directory or clone a new
;;; project in your code root.
;;;
@@ -71,7 +71,7 @@
;;; add the following snippet to your Emacs configuration to set up
;;; this key binding:
;;;
-;;; (global-set-key (kbd "M-h") 'h-jump-to-project)
+;;; (global-set-key (kbd "M-h") 'my-repo-pins)
;;; Code:
@@ -82,40 +82,40 @@
;; loaded by default in interactive emacs, not in batch-mode emacs.
(eval-when-compile (require 'subr-x))
-(defgroup h-group nil
- "Variables used to setup the h.el project manager."
+(defgroup my-repo-pins-group nil
+ "Variables used to setup the my-repo-pins.el project manager."
:group 'Communication)
;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Internal: git primitives
;;;;;;;;;;;;;;;;;;;;;;;;;;;
-(defcustom h-git-bin "git"
+(defcustom my-repo-pins-git-bin "git"
"Path pointing to the git binary.
By default, it'll look for git in the current $PATH."
:type 'file
- :group 'h-group)
+ :group 'my-repo-pins-group)
-(defun h--git-path ()
- "Find the git binary path using ‘h-git-bin’.
+(defun my-repo-pins--git-path ()
+ "Find the git binary path using ‘my-repo-pins-git-bin’.
Errors out if we can't find it."
- (if (file-executable-p h-git-bin)
- h-git-bin
- (let ((git-from-bin-path (locate-file h-git-bin exec-path)))
+ (if (file-executable-p my-repo-pins-git-bin)
+ my-repo-pins-git-bin
+ (let ((git-from-bin-path (locate-file my-repo-pins-git-bin exec-path)))
(if (file-executable-p git-from-bin-path)
git-from-bin-path
- (error "Can't find git. Is h-git-bin correctly set?")))))
+ (error "Can't find git. Is my-repo-pins-git-bin correctly set?")))))
-(defun h--call-git-in-dir (dir &optional callback &rest args)
- "Call the git binary as pointed by ‘h-git-bin’ in DIR with ARGS.
+(defun my-repo-pins--call-git-in-dir (dir &optional callback &rest args)
+ "Call the git binary as pointed by ‘my-repo-pins-git-bin’ in DIR with ARGS.
Once the git subprocess exists, call CALLBACK with a the process exit
code as single argument. If CALLBACK is set to nil, don't call any
callback.
Returns the git PROCESS object."
- (let* ((git-buffer (get-buffer-create "*h git log*"))
+ (let* ((git-buffer (get-buffer-create "*my repo pins git log*"))
(git-window nil)
(current-buffer (current-buffer))
(git-sentinel (lambda
@@ -133,49 +133,49 @@ Returns the git PROCESS object."
(setq git-window (display-buffer git-buffer))
(prog1
(make-process
- :name "h-git-subprocess"
+ :name "my-repo-pins-git-subprocess"
:buffer git-buffer
- :command (seq-concatenate 'list `(,(h--git-path)) args)
+ :command (seq-concatenate 'list `(,(my-repo-pins--git-path)) args)
:sentinel git-sentinel)
(set-buffer current-buffer)))))
-(defun h--git-clone-in-dir (clone-url checkout-filepath &optional callback)
+(defun my-repo-pins--git-clone-in-dir (clone-url checkout-filepath &optional callback)
"Clone the CLONE-URL repo at CHECKOUT-FILEPATH.
Call CALLBACK with no arguments once the git subprocess exists."
- (h--call-git-in-dir "~/" callback "clone" clone-url checkout-filepath))
+ (my-repo-pins--call-git-in-dir "~/" callback "clone" clone-url checkout-filepath))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Internal: builtin fetchers
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Generic fetcher infrastructure
-(defvar h--builtins-forge-fetchers
+(defvar my-repo-pins--builtins-forge-fetchers
'(("GitHub.com" .
- ((query-user-repo . h--query-github-owner-repo)))
+ ((query-user-repo . my-repo-pins--query-github-owner-repo)))
("GitLab.com" .
- ((query-user-repo . (lambda (owner repo cb) (h--query-gitlab-owner-repo "gitlab.com" owner repo cb)))))
+ ((query-user-repo . (lambda (owner repo cb) (my-repo-pins--query-gitlab-owner-repo "gitlab.com" owner repo cb)))))
("git.sr.ht" .
- ((query-user-repo . (lambda (owner repo cb) (h--query-sourcehut-owner-repo "git.sr.ht" owner repo cb)))))
+ ((query-user-repo . (lambda (owner repo cb) (my-repo-pins--query-sourcehut-owner-repo "git.sr.ht" owner repo cb)))))
("Codeberg.org" .
- ((query-user-repo . (lambda (owner repo cb) (h--query-gitea-owner-repo "codeberg.org" owner repo cb))))))
+ ((query-user-repo . (lambda (owner repo cb) (my-repo-pins--query-gitea-owner-repo "codeberg.org" owner repo cb))))))
- "Fetchers meant to be used in conjunction with ‘h-forge-fetchers’.
+ "Fetchers meant to be used in conjunction with ‘my-repo-pins-forge-fetchers’.
This variable contains fetchers for:
- github.com")
-(defcustom h-forge-fetchers
- h--builtins-forge-fetchers
+(defcustom my-repo-pins-forge-fetchers
+ my-repo-pins--builtins-forge-fetchers
"List of forges for which we want to remote fetch projects."
:type '(alist
:key-type symbol
:value-type (alist
:key-type symbol
:value-type (choice function string)))
- :group 'h-group)
+ :group 'my-repo-pins-group)
-(defvar h--forge-fetchers-state '()
+(defvar my-repo-pins--forge-fetchers-state '()
"Internal state where we keep a forge request status.
@@ -192,12 +192,12 @@ A ongoing/failed lookup will also be represented by an entry in this alist:
\(\"FORGE-NAME1\" . 'loading)
\(\"FORGE-NAME1\" . 'not-found)")
-(defvar h--forge-fetchers-state-mutex
- (make-mutex "h-ui-mutex")
+(defvar my-repo-pins--forge-fetchers-state-mutex
+ (make-mutex "my-repo-pins-ui-mutex")
"Mutex in charge of preventing several fetchers to update the state concurently.")
;;; Sourcehut Fetcher
-(defun h--query-sourcehut-owner-repo (instance-url user-name repo-name callback)
+(defun my-repo-pins--query-sourcehut-owner-repo (instance-url user-name repo-name callback)
"Query the INSTANCE-URL Sourcehut instance and retrieve some infos about a repo.
This function will try to determine whether or not the
@@ -228,7 +228,7 @@ using a HEAD request and infer the clone links from there."
(setq url-request-method nil)))
;;; Gitlab Fetcher
-(defun h--query-gitlab-owner-repo (instance-url user-name repo-name callback)
+(defun my-repo-pins--query-gitlab-owner-repo (instance-url user-name repo-name callback)
"Queries the INSTANCE-URL Gitlab instance and retrieve some infos about a repo.
This function will try to determine whether or not the
@@ -260,7 +260,7 @@ only option we have for now."
(setq url-request-method nil)))
;;; Github Fetcher
-(defun h--query-github-owner-repo (user-name repo-name callback)
+(defun my-repo-pins--query-github-owner-repo (user-name repo-name callback)
"Queries the GitHub API to retrieve some infos about a GitHub repo.
This function will first try to determine whether
github.com/USER-NAME/REPO-NAME exists.
@@ -271,10 +271,10 @@ nil as parameter."
(progn
(url-retrieve
(format "https://api.github.com/repos/%s/%s" user-name repo-name)
- (lambda (&rest _rest) (funcall callback (h--fetch-github-parse-response(current-buffer)))))))
+ (lambda (&rest _rest) (funcall callback (my-repo-pins--fetch-github-parse-response(current-buffer)))))))
-(defun h--fetch-github-parse-response (response-buffer)
+(defun my-repo-pins--fetch-github-parse-response (response-buffer)
"Parse the RESPONSE-BUFFER containing a GET response from the GitHub API.
Parsing a response from a GET https://api.github.com/repos/user/repo request.
@@ -302,7 +302,7 @@ Returns nil if the repo does not exists."
nil)))
;;; Gitea Fetcher
-(defun h--query-gitea-owner-repo (instance-url user-name repo-name callback)
+(defun my-repo-pins--query-gitea-owner-repo (instance-url user-name repo-name callback)
"Queries the INSTANCE-URL gitea instance to retrieve a repo informations.
This function will first try to dertermine whether the
USER-NAME/REPO-NAME exists.
@@ -312,9 +312,9 @@ https clone URLs. If the repo does not exists, calls the callback with
nil as parameter."
(url-retrieve
(format "https://%s/api/v1/repos/%s/%s" instance-url user-name repo-name)
- (lambda (&rest _rest) (funcall callback (h--fetch-gitea-parse-response(current-buffer))))))
+ (lambda (&rest _rest) (funcall callback (my-repo-pins--fetch-gitea-parse-response(current-buffer))))))
-(defun h--fetch-gitea-parse-response (response-buffer)
+(defun my-repo-pins--fetch-gitea-parse-response (response-buffer)
"Parse the RESPONSE-BUFFER containing a GET response from the Gitea API.
Parsing a response from a GET https://instance/api/v1/repos/user/repo request.
@@ -345,7 +345,7 @@ Returns nil if the repo does not exists."
;; Internal: repo URI parser
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-(defun h--parse-repo-identifier (query-str)
+(defun my-repo-pins--parse-repo-identifier (query-str)
"Do its best to figure out which repo the user meant by QUERY-STR.
A valid QUERY-STR is in one of the 4 following formats:
@@ -368,9 +368,9 @@ each kind of format, it'll return something along the line of:
\"https://full-url.org/path/to/git/repo/checkout\"))
or
\(('tag . 'owner-repo) ('owner . \"NinjaTrappeur\") ('repo\
-. \"h.el\"))
+. \"my-repo-pins.el\"))
or
-\(('tag . 'repo) ('repo . \"h.el\"))"
+\(('tag . 'repo) ('repo . \"my-repo-pins.el\"))"
(cond
;; Full-url case
((or (string-match "^https?://.*/.*/.*$" query-str)
@@ -386,7 +386,7 @@ or
;; repo case
(t `((tag . repo) (repo . ,query-str)))))
-(defun h--filepath-from-clone-url (clone-url)
+(defun my-repo-pins--filepath-from-clone-url (clone-url)
"Return the relative path relative to the coderoot for CLONE-URL.
CLONE-STR being the git clone URL we want to find the local path for."
@@ -409,29 +409,30 @@ CLONE-STR being the git clone URL we want to find the local path for."
;; Internal: code-root management functions
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-(defcustom h-code-root nil
+(defcustom my-repo-pins-code-root nil
"Root directory containing all your projects.
-h.el organise the git repos you'll checkout in a tree fashion.
+my-repo-pins.el organise the git repos you'll checkout in a tree
+fashion.
-All the code fetched using h.el will end up in this root directory. A
+All the code fetched using my-repo-pins.el will end up in this root directory. A
tree of subdirectories will be created mirroring the remote URI.
For instance, after checking out
https://git.savannah.gnu.org/git/emacs/org-mode.git, the source code
-will live in the h-code-root/git.savannah.gnu.org/git/emacs/org-mode/
+will live in the my-repo-pins-code-root/git.savannah.gnu.org/git/emacs/org-mode/
local directory"
:type 'directory
- :group 'h-group)
+ :group 'my-repo-pins-group)
-(defun h--safe-get-code-root ()
- "Ensure ‘h-code-root’ is correctly set, then canonalize the path.
-Errors out if ‘h-code-root’ has not been set yet."
- (progn (when (not h-code-root)
- (error "h-code-root has not been set. Please point it to your code root"))
- (expand-file-name (file-name-as-directory h-code-root))))
+(defun my-repo-pins--safe-get-code-root ()
+ "Ensure ‘my-repo-pins-code-root’ is correctly set, then canonalize the path.
+Errors out if ‘my-repo-pins-code-root’ has not been set yet."
+ (progn (when (not my-repo-pins-code-root)
+ (error "My-Repo-Pins-code-root has not been set. Please point it to your code root"))
+ (expand-file-name (file-name-as-directory my-repo-pins-code-root))))
-(defun h--find-git-dirs-recursively (dir)
+(defun my-repo-pins--find-git-dirs-recursively (dir)
"Vendored, slightly modified version of ‘directory-files-recursively’.
This library isn't available for Emacs > 25.1. Vendoring it for
@@ -464,21 +465,22 @@ included."
;; It's not a git repo, let's recurse into it.
(setq recur-result
(nconc recur-result
- (h--find-git-dirs-recursively full-file)))))))))
+ (my-repo-pins--find-git-dirs-recursively full-file)))))))))
(nconc recur-result (nreverse projects))))
-(defun h--get-code-root-projects (code-root)
+(defun my-repo-pins--get-code-root-projects (code-root)
"Retrieve the projects contained in the CODE-ROOT directory.
-We're going to make some hard assumptions about how the ‘h-code-root’
-directory should look like. First of all, if a directory seem to be a
-git repository, it'll automatically be considered as a project root.
+We're going to make some hard assumptions about how the
+‘my-repo-pins-code-root’ directory should look like. First of all, if
+a directory seem to be a git repository, it'll automatically be
+considered as a project root.
It means that after encountering a git repository, we won't recurse
any further.
-If the directory pointed by ‘h-code-root’ does not exists yet, returns
-an empty list."
+If the directory pointed by ‘my-repo-pins-code-root’ does not exists
+yet, returns an empty list."
(if (not (file-directory-p code-root))
'()
(let*
@@ -486,7 +488,7 @@ an empty list."
(lambda (path)
(let ((path-without-prefix (string-remove-prefix code-root path)))
(substring path-without-prefix 0 (1- (length path-without-prefix))))))
- (projects-absolute-path (h--find-git-dirs-recursively code-root))
+ (projects-absolute-path (my-repo-pins--find-git-dirs-recursively code-root))
(projects-relative-to-code-root
(mapcar remove-code-root-prefix-and-trailing-slash projects-absolute-path)))
projects-relative-to-code-root)))
@@ -495,7 +497,7 @@ an empty list."
;; Internal: UI
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-(defun h--evil-safe-binding (kbd action)
+(defun my-repo-pins--evil-safe-binding (kbd action)
"Bind ACTION to the KBD keyboard key.
This key binding will be bound to the current buffer. If ‘evil-mode’
@@ -520,7 +522,7 @@ is used, the key binding will be bound to the normal mode as well."
(eval `(evil-local-set-key 'normal ,kbd ',action))))
(local-set-key kbd action))))
-(defun h--draw-ui-buffer (forge-query-status user-query)
+(defun my-repo-pins--draw-ui-buffer (forge-query-status user-query)
"Draws the UI depending on the app state.
FORGE-QUERY-STATUS being a alist in the form of (FORGE-NAME . LOOKUP-STATUS)
@@ -534,34 +536,34 @@ to clone for.
We're going to draw these forge query status results in a buffer and
associate each of them with a key binding.
-, ‘h--draw-forge-status’ is in charge of
-drawing the forge status in the h.el buffer."
+, ‘my-repo-pins--draw-forge-status’ is in charge of
+drawing the forge status in the my-repo-pins.el buffer."
(let* (
- (h-buffer (get-buffer-create "h.el"))
- (h-window nil)
+ (my-repo-pins-buffer (get-buffer-create "my-repo-pins.el"))
+ (my-repo-pins-window nil)
(previous-buffer (current-buffer))
- (forge-status-with-keys (h--add-keys-to-forge-status forge-query-status)))
+ (forge-status-with-keys (my-repo-pins--add-keys-to-forge-status forge-query-status)))
(progn
- (set-buffer h-buffer)
+ (set-buffer my-repo-pins-buffer)
(setq buffer-read-only nil)
(erase-buffer)
(insert (format "Looking up for %s in different forges:\n\n\n" user-query))
(set-text-properties 1 (point) `(face (:foreground "orange" :weight bold)))
(seq-map
- (lambda (e) (h--draw-forge-status e)) forge-status-with-keys)
+ (lambda (e) (my-repo-pins--draw-forge-status e)) forge-status-with-keys)
(insert "\n\nPlease select the forge we should clone the project from.\n")
(insert "Press q to close this window.")
(setq buffer-read-only t)
- (h--evil-safe-binding (kbd "q")
+ (my-repo-pins--evil-safe-binding (kbd "q")
`(lambda () (interactive)
(progn
(delete-window)
- (kill-buffer ,h-buffer))))
+ (kill-buffer ,my-repo-pins-buffer))))
(set-buffer previous-buffer)
- (setq h-window (display-buffer h-buffer))
- (select-window h-window))))
+ (setq my-repo-pins-window (display-buffer my-repo-pins-buffer))
+ (select-window my-repo-pins-window))))
-(defun h--add-keys-to-forge-status (forge-query-status)
+(defun my-repo-pins--add-keys-to-forge-status (forge-query-status)
"Add key bindings to relevant FORGE-QUERY-STATUS entries.
FORGE-QUERY-STATUS is list of alists in the form of ((FORGE-NAME .
@@ -570,7 +572,7 @@ lookup results or the 'not-found atom when no results could be found.
This function adds a key binding alist to the LOOKUP-STATUS list when
results have been found, nothing if the repo couldn't be found.
-‘h--find-next-available-key-binding’ is in charge of generating the
+‘my-repo-pins--find-next-available-key-binding’ is in charge of generating the
key bindings."
(reverse
(cdr
@@ -583,7 +585,7 @@ key bindings."
(key (car acc))
(isFound (listp status))
(nextKeybinding
- (if isFound (h--find-next-available-key-binding (car acc)) (car acc)))
+ (if isFound (my-repo-pins--find-next-available-key-binding (car acc)) (car acc)))
(forge-status-with-key
(if isFound
`((status . ,status)
@@ -595,7 +597,7 @@ key bindings."
forge-query-status
:initial-value '(?1 . ())))))
-(defun h--draw-forge-status (forge-result)
+(defun my-repo-pins--draw-forge-status (forge-result)
"Draws FORGE-RESULT status to the current buffer.
FORGE-STATUS being a alist in the form of (FORGE-NAME . LOOKUP-STATUS).
@@ -611,7 +613,7 @@ https-checkout-url)) ('key . \"1\"))."
((eq status 'loading) (format "[?] %s (loading...)" forge-name))
((eq status 'not-found) (format "[X] %s" forge-name))
((listp status) (format "[✓] %s" forge-name))
- (t (error (format "h--draw-forge-status: Invalid forge status %s" status)))))
+ (t (error (format "my-repo-pins--draw-forge-status: Invalid forge status %s" status)))))
(text (if (null key)
(format "%s\n" status-text)
(format "%s [%s]\n" status-text (char-to-string key))))
@@ -619,18 +621,18 @@ https-checkout-url)) ('key . \"1\"))."
((eq status 'loading) "orange")
((eq status 'not-found) "red")
((listp status) "green")
- (t (error (format "h--draw-forge-status: Invalid forge status %s" status)))))
- (h-buffer (current-buffer))
+ (t (error (format "my-repo-pins--draw-forge-status: Invalid forge status %s" status)))))
+ (my-repo-pins-buffer (current-buffer))
(original-point (point)))
(progn
(if (not (null key))
- (h--evil-safe-binding (kbd (format "%s" (char-to-string key)))
+ (my-repo-pins--evil-safe-binding (kbd (format "%s" (char-to-string key)))
`(lambda ()
(interactive)
(progn
(delete-window)
- (kill-buffer ,h-buffer)
- (h--clone-from-forge-result ',forge-result)))))
+ (kill-buffer ,my-repo-pins-buffer)
+ (my-repo-pins--clone-from-forge-result ',forge-result)))))
(insert text)
;; Set color for status indicator
(set-text-properties original-point
@@ -641,8 +643,8 @@ https-checkout-url)) ('key . \"1\"))."
(set-text-properties (- (point) 4) (point)
'(face (:foreground "orange" :weight bold)))))))
-(defun h--find-next-available-key-binding (cur-key-binding)
- "Find a key binding starting CUR-KEY-BINDING for the h buffer.
+(defun my-repo-pins--find-next-available-key-binding (cur-key-binding)
+ "Find a key binding starting CUR-KEY-BINDING for the my-repo-pins buffer.
We're using the 1-9 numbers, then, once all the numbers are already in
use, we start allocating the a-Z letters."
@@ -650,7 +652,7 @@ use, we start allocating the a-Z letters."
((= cur-key-binding ?z) (error "Keys exhausted, can't bind any more"))
(t (+ cur-key-binding 1))))
-(defun h--clone-from-forge-result (forge-result)
+(defun my-repo-pins--clone-from-forge-result (forge-result)
"Clone a repository using the FORGE-RESULT alist.
The FORGE-RESULT alist is in the form of (status . (https .
@@ -663,14 +665,14 @@ url."
((forge-result-status (alist-get 'status (cdr forge-result)))
(ssh-url (alist-get 'ssh forge-result-status))
(http-url (alist-get 'https forge-result-status))
- (code-root (h--safe-get-code-root))
- (dest-dir (concat code-root (h--filepath-from-clone-url http-url))))
+ (code-root (my-repo-pins--safe-get-code-root))
+ (dest-dir (concat code-root (my-repo-pins--filepath-from-clone-url http-url))))
(progn
(message (format "Cloning %s to %s" ssh-url dest-dir))
(cl-flet*
((clone-http
()
- (h--git-clone-in-dir
+ (my-repo-pins--git-clone-in-dir
http-url
dest-dir
(lambda (exit-code)
@@ -681,7 +683,7 @@ url."
(find-file dest-dir))))))
(clone-ssh
()
- (h--git-clone-in-dir
+ (my-repo-pins--git-clone-in-dir
ssh-url
dest-dir
(lambda (exit-code)
@@ -700,7 +702,7 @@ url."
;; Internal: improving builtin autocomplete
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-(defun h--completing-read-or-custom (prompt collection)
+(defun my-repo-pins--completing-read-or-custom (prompt collection)
"Behaves similarly to ‘complete-read’.
See the ‘complete-read’ documentation for more details about PROMPT
@@ -721,31 +723,31 @@ READ-RESULT)"
;; Internal: Internal state management
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-(defun h--init-forges-state (forge-fetchers)
- "Initialize ‘h--forge-fetchers-state’.
+(defun my-repo-pins--init-forges-state (forge-fetchers)
+ "Initialize ‘my-repo-pins--forge-fetchers-state’.
We iterate through the forges set in FORGE-FETCHERS and associate
each of them with a pending status. We then return this new state
alist."
(seq-map (lambda (e) `(,(car e) . loading)) forge-fetchers))
-(defun h--update-forges-state (forge-name new-state user-query)
- "Update ‘h--forge-fetchers-state’ for FORGE-NAME with NEW-STATE.
+(defun my-repo-pins--update-forges-state (forge-name new-state user-query)
+ "Update ‘my-repo-pins--forge-fetchers-state’ for FORGE-NAME with NEW-STATE.
USER-QUERY was the original query for this state update."
(progn
- (mutex-lock h--forge-fetchers-state-mutex)
- (setq h--forge-fetchers-state (assq-delete-all forge-name h--forge-fetchers-state))
- (setq h--forge-fetchers-state (cons `(,forge-name . ,new-state) h--forge-fetchers-state))
- (h--draw-ui-buffer h--forge-fetchers-state user-query)
- (mutex-unlock h--forge-fetchers-state-mutex)))
+ (mutex-lock my-repo-pins--forge-fetchers-state-mutex)
+ (setq my-repo-pins--forge-fetchers-state (assq-delete-all forge-name my-repo-pins--forge-fetchers-state))
+ (setq my-repo-pins--forge-fetchers-state (cons `(,forge-name . ,new-state) my-repo-pins--forge-fetchers-state))
+ (my-repo-pins--draw-ui-buffer my-repo-pins--forge-fetchers-state user-query)
+ (mutex-unlock my-repo-pins--forge-fetchers-state-mutex)))
-(defun h--query-forge-fetchers (repo-query)
+(defun my-repo-pins--query-forge-fetchers (repo-query)
"Find repo matches to the relevant forges for REPO-QUERY then query forge.
TODO: split that mess before release. We shouldn't query here."
- (let* ((parsed-repo-query (h--parse-repo-identifier repo-query))
+ (let* ((parsed-repo-query (my-repo-pins--parse-repo-identifier repo-query))
(repo-query-kind (alist-get 'tag parsed-repo-query)))
(cond
((equal repo-query-kind 'owner-repo)
@@ -762,15 +764,15 @@ TODO: split that mess before release. We shouldn't query here."
(let ((new-state
(if (null result) 'not-found result)))
(progn
- (h--update-forges-state ,forge-str new-state ,repo-query))))))))
- h-forge-fetchers))
+ (my-repo-pins--update-forges-state ,forge-str new-state ,repo-query))))))))
+ my-repo-pins-forge-fetchers))
((equal repo-query-kind 'repo) (error (format "Can't checkout %s (for now), please specify a owner" repo-query)))
((equal repo-query-kind 'full-url)
(let*
- ((code-root (h--safe-get-code-root))
- (dest-dir (concat code-root (h--filepath-from-clone-url repo-query))))
+ ((code-root (my-repo-pins--safe-get-code-root))
+ (dest-dir (concat code-root (my-repo-pins--filepath-from-clone-url repo-query))))
(progn
- (h--git-clone-in-dir
+ (my-repo-pins--git-clone-in-dir
repo-query
dest-dir
(lambda (exit-code)
@@ -783,31 +785,29 @@ TODO: split that mess before release. We shouldn't query here."
;; Interactive Commands
;;;;;;;;;;;;;;;;;;;;;;;;
-;;;###autoload
-(defun h-clone-project (user-query)
- "Clone USER-QUERY in its appropriate directory in ‘h-code-root’."
- (interactive "sGit repository to checkout: ")
+(defun my-repo-pins--clone-project (user-query)
+ "Clone USER-QUERY in its appropriate directory in ‘my-repo-pins-code-root’."
(progn
- (setq h--forge-fetchers-state (h--init-forges-state h-forge-fetchers))
- (h--query-forge-fetchers user-query)))
+ (setq my-repo-pins--forge-fetchers-state (my-repo-pins--init-forges-state my-repo-pins-forge-fetchers))
+ (my-repo-pins--query-forge-fetchers user-query)))
;;;###autoload
-(defun h-jump-to-project ()
- "Open a project contained in the ‘h-code-root’ directory.
-If the project is not in the ‘h-code-root’ yet, check it out from the
+(defun my-repo-pins ()
+ "Open a project contained in the ‘my-repo-pins-code-root’ directory.
+If the project is not in the ‘my-repo-pins-code-root’ yet, check it out from the
available forge sources."
(interactive)
(let ((user-query
- (h--completing-read-or-custom
+ (my-repo-pins--completing-read-or-custom
"Jump to project: "
- (h--get-code-root-projects (h--safe-get-code-root)))))
+ (my-repo-pins--get-code-root-projects (my-repo-pins--safe-get-code-root)))))
(cond
((equal (car user-query) 'in-collection)
- (let ((selected-project-absolute-path (concat (h--safe-get-code-root) (cdr user-query))))
+ (let ((selected-project-absolute-path (concat (my-repo-pins--safe-get-code-root) (cdr user-query))))
(find-file selected-project-absolute-path)))
((equal (car user-query) 'user-provided)
- (h-clone-project (cdr user-query))))))
+ (my-repo-pins--clone-project (cdr user-query))))))
-(provide 'h)
-;;; h.el ends here
+(provide 'my-repo-pins)
+;;; my-repo-pins.el ends here
diff --git a/tests/fixtures/gitea-get-request-ok.txt b/tests/fixtures/gitea-get-request-ok.txt
index df52ff2..49c31c6 100644
--- a/tests/fixtures/gitea-get-request-ok.txt
+++ b/tests/fixtures/gitea-get-request-ok.txt
@@ -8,4 +8,4 @@ Set-Cookie: _csrf=000000000000000000000000000000000000000000000000000000; Path=/
X-Content-Type-Options: nosniff
X-Frame-Options: SAMEORIGIN
-{"id":28,"owner":{"id":1,"login":"NinjaTrappeur","full_name":"","email":"felix@alternativebit.fr","avatar_url":"https://git.alternativebit.fr/avatars/326105984221f71c9e555addc514dae6","language":"","is_admin":false,"last_login":"0001-01-01T00:00:00Z","created":"2018-04-10T16:10:10+02:00","restricted":false,"active":false,"prohibit_login":false,"location":"","website":"","description":"","visibility":"public","followers_count":0,"following_count":0,"starred_repos_count":0,"username":"NinjaTrappeur"},"name":"h.el","full_name":"NinjaTrappeur/h.el","description":"","empty":false,"private":false,"fork":false,"template":false,"parent":null,"mirror":false,"size":107,"html_url":"https://git.alternativebit.fr/NinjaTrappeur/h.el","ssh_url":"gitea@git.alternativebit.fr:NinjaTrappeur/h.el.git","clone_url":"https://git.alternativebit.fr/NinjaTrappeur/h.el.git","original_url":"","website":"","stars_count":0,"forks_count":0,"watchers_count":1,"open_issues_count":0,"open_pr_counter":0,"release_counter":0,"default_branch":"master","archived":false,"created_at":"2020-10-25T15:53:38+01:00","updated_at":"2022-06-16T18:43:05+02:00","permissions":{"admin":false,"push":false,"pull":true},"has_issues":true,"internal_tracker":{"enable_time_tracker":true,"allow_only_contributors_to_track_time":true,"enable_issue_dependencies":true},"has_wiki":true,"has_pull_requests":true,"has_projects":false,"ignore_whitespace_conflicts":false,"allow_merge_commits":true,"allow_rebase":true,"allow_rebase_explicit":true,"allow_squash_merge":true,"default_merge_style":"merge","avatar_url":"","internal":false,"mirror_interval":"","mirror_updated":"0001-01-01T00:00:00Z","repo_transfer":null}
+{"id":28,"owner":{"id":1,"login":"NinjaTrappeur","full_name":"","email":"felix@alternativebit.fr","avatar_url":"https://git.alternativebit.fr/avatars/326105984221f71c9e555addc514dae6","language":"","is_admin":false,"last_login":"0001-01-01T00:00:00Z","created":"2018-04-10T16:10:10+02:00","restricted":false,"active":false,"prohibit_login":false,"location":"","website":"","description":"","visibility":"public","followers_count":0,"following_count":0,"starred_repos_count":0,"username":"NinjaTrappeur"},"name":"my-repo-pins.el","full_name":"NinjaTrappeur/my-repo-pins.el","description":"","empty":false,"private":false,"fork":false,"template":false,"parent":null,"mirror":false,"size":107,"html_url":"https://git.alternativebit.fr/NinjaTrappeur/my-repo-pins.el","ssh_url":"gitea@git.alternativebit.fr:NinjaTrappeur/my-repo-pins.el.git","clone_url":"https://git.alternativebit.fr/NinjaTrappeur/my-repo-pins.el.git","original_url":"","website":"","stars_count":0,"forks_count":0,"watchers_count":1,"open_issues_count":0,"open_pr_counter":0,"release_counter":0,"default_branch":"master","archived":false,"created_at":"2020-10-25T15:53:38+01:00","updated_at":"2022-06-16T18:43:05+02:00","permissions":{"admin":false,"push":false,"pull":true},"has_issues":true,"internal_tracker":{"enable_time_tracker":true,"allow_only_contributors_to_track_time":true,"enable_issue_dependencies":true},"has_wiki":true,"has_pull_requests":true,"has_projects":false,"ignore_whitespace_conflicts":false,"allow_merge_commits":true,"allow_rebase":true,"allow_rebase_explicit":true,"allow_squash_merge":true,"default_merge_style":"merge","avatar_url":"","internal":false,"mirror_interval":"","mirror_updated":"0001-01-01T00:00:00Z","repo_transfer":null}
diff --git a/tests/fixtures/github-get-request-ok.txt b/tests/fixtures/github-get-request-ok.txt
index 8299f18..43578e8 100644
--- a/tests/fixtures/github-get-request-ok.txt
+++ b/tests/fixtures/github-get-request-ok.txt
@@ -24,4 +24,4 @@ Accept-Ranges: bytes
Content-Length: 1288
X-GitHub-Request-Id: EADE:942B:156216:17E519:62503D61
-{"id":476823023,"node_id":"R_kgDOHGu97w","name":"h.el","full_name":"NinjaTrappeur/h.el","private":false,"owner":{"login":"NinjaTrappeur","id":1219785,"node_id":"MDQ6VXNlcjEyMTk3ODU=","avatar_url":"https://avatars.githubusercontent.com/u/1219785?v=4","gravatar_id":"","url":"https://api.github.com/users/NinjaTrappeur","html_url":"https://github.com/NinjaTrappeur","followers_url":"https://api.github.com/users/NinjaTrappeur/followers","following_url":"https://api.github.com/users/NinjaTrappeur/following{/other_user}","gists_url":"https://api.github.com/users/NinjaTrappeur/gists{/gist_id}","starred_url":"https://api.github.com/users/NinjaTrappeur/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/NinjaTrappeur/subscriptions","organizations_url":"https://api.github.com/users/NinjaTrappeur/orgs","repos_url":"https://api.github.com/users/NinjaTrappeur/repos","events_url":"https://api.github.com/users/NinjaTrappeur/events{/privacy}","received_events_url":"https://api.github.com/users/NinjaTrappeur/received_events","type":"User","site_admin":false},"html_url":"https://github.com/NinjaTrappeur/h.el","description":"Emacs project manager and remote git checkout","fork":false,"url":"https://api.github.com/repos/NinjaTrappeur/h.el","forks_url":"https://api.github.com/repos/NinjaTrappeur/h.el/forks","keys_url":"https://api.github.com/repos/NinjaTrappeur/h.el/keys{/key_id}","collaborators_url":"https://api.github.com/repos/NinjaTrappeur/h.el/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/NinjaTrappeur/h.el/teams","hooks_url":"https://api.github.com/repos/NinjaTrappeur/h.el/hooks","issue_events_url":"https://api.github.com/repos/NinjaTrappeur/h.el/issues/events{/number}","events_url":"https://api.github.com/repos/NinjaTrappeur/h.el/events","assignees_url":"https://api.github.com/repos/NinjaTrappeur/h.el/assignees{/user}","branches_url":"https://api.github.com/repos/NinjaTrappeur/h.el/branches{/branch}","tags_url":"https://api.github.com/repos/NinjaTrappeur/h.el/tags","blobs_url":"https://api.github.com/repos/NinjaTrappeur/h.el/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/NinjaTrappeur/h.el/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/NinjaTrappeur/h.el/git/refs{/sha}","trees_url":"https://api.github.com/repos/NinjaTrappeur/h.el/git/trees{/sha}","statuses_url":"https://api.github.com/repos/NinjaTrappeur/h.el/statuses/{sha}","languages_url":"https://api.github.com/repos/NinjaTrappeur/h.el/languages","stargazers_url":"https://api.github.com/repos/NinjaTrappeur/h.el/stargazers","contributors_url":"https://api.github.com/repos/NinjaTrappeur/h.el/contributors","subscribers_url":"https://api.github.com/repos/NinjaTrappeur/h.el/subscribers","subscription_url":"https://api.github.com/repos/NinjaTrappeur/h.el/subscription","commits_url":"https://api.github.com/repos/NinjaTrappeur/h.el/commits{/sha}","git_commits_url":"https://api.github.com/repos/NinjaTrappeur/h.el/git/commits{/sha}","comments_url":"https://api.github.com/repos/NinjaTrappeur/h.el/comments{/number}","issue_comment_url":"https://api.github.com/repos/NinjaTrappeur/h.el/issues/comments{/number}","contents_url":"https://api.github.com/repos/NinjaTrappeur/h.el/contents/{+path}","compare_url":"https://api.github.com/repos/NinjaTrappeur/h.el/compare/{base}...{head}","merges_url":"https://api.github.com/repos/NinjaTrappeur/h.el/merges","archive_url":"https://api.github.com/repos/NinjaTrappeur/h.el/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/NinjaTrappeur/h.el/downloads","issues_url":"https://api.github.com/repos/NinjaTrappeur/h.el/issues{/number}","pulls_url":"https://api.github.com/repos/NinjaTrappeur/h.el/pulls{/number}","milestones_url":"https://api.github.com/repos/NinjaTrappeur/h.el/milestones{/number}","notifications_url":"https://api.github.com/repos/NinjaTrappeur/h.el/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/NinjaTrappeur/h.el/labels{/name}","releases_url":"https://api.github.com/repos/NinjaTrappeur/h.el/releases{/id}","deployments_url":"https://api.github.com/repos/NinjaTrappeur/h.el/deployments","created_at":"2022-04-01T18:06:46Z","updated_at":"2022-04-01T18:08:16Z","pushed_at":"2022-04-07T10:20:28Z","git_url":"git://github.com/NinjaTrappeur/h.el.git","ssh_url":"git@github.com:NinjaTrappeur/h.el.git","clone_url":"https://github.com/NinjaTrappeur/h.el.git","svn_url":"https://github.com/NinjaTrappeur/h.el","homepage":null,"size":54,"stargazers_count":0,"watchers_count":0,"language":"Emacs Lisp","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"archived":false,"disabled":false,"open_issues_count":0,"license":{"key":"gpl-3.0","name":"GNU General Public License v3.0","spdx_id":"GPL-3.0","url":"https://api.github.com/licenses/gpl-3.0","node_id":"MDc6TGljZW5zZTk="},"allow_forking":true,"is_template":false,"topics":[],"visibility":"public","forks":0,"open_issues":0,"watchers":0,"default_branch":"master","temp_clone_token":null,"network_count":0,"subscribers_count":1}
+{"id":476823023,"node_id":"R_kgDOHGu97w","name":"my-repo-pins.el","full_name":"NinjaTrappeur/my-repo-pins.el","private":false,"owner":{"login":"NinjaTrappeur","id":1219785,"node_id":"MDQ6VXNlcjEyMTk3ODU=","avatar_url":"https://avatars.githubusercontent.com/u/1219785?v=4","gravatar_id":"","url":"https://api.github.com/users/NinjaTrappeur","html_url":"https://github.com/NinjaTrappeur","followers_url":"https://api.github.com/users/NinjaTrappeur/followers","following_url":"https://api.github.com/users/NinjaTrappeur/following{/other_user}","gists_url":"https://api.github.com/users/NinjaTrappeur/gists{/gist_id}","starred_url":"https://api.github.com/users/NinjaTrappeur/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/NinjaTrappeur/subscriptions","organizations_url":"https://api.github.com/users/NinjaTrappeur/orgs","repos_url":"https://api.github.com/users/NinjaTrappeur/repos","events_url":"https://api.github.com/users/NinjaTrappeur/events{/privacy}","received_events_url":"https://api.github.com/users/NinjaTrappeur/received_events","type":"User","site_admin":false},"html_url":"https://github.com/NinjaTrappeur/my-repo-pins.el","description":"Emacs project manager and remote git checkout","fork":false,"url":"https://api.github.com/repos/NinjaTrappeur/my-repo-pins.el","forks_url":"https://api.github.com/repos/NinjaTrappeur/my-repo-pins.el/forks","keys_url":"https://api.github.com/repos/NinjaTrappeur/my-repo-pins.el/keys{/key_id}","collaborators_url":"https://api.github.com/repos/NinjaTrappeur/my-repo-pins.el/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/NinjaTrappeur/my-repo-pins.el/teams","hooks_url":"https://api.github.com/repos/NinjaTrappeur/my-repo-pins.el/hooks","issue_events_url":"https://api.github.com/repos/NinjaTrappeur/my-repo-pins.el/issues/events{/number}","events_url":"https://api.github.com/repos/NinjaTrappeur/my-repo-pins.el/events","assignees_url":"https://api.github.com/repos/NinjaTrappeur/my-repo-pins.el/assignees{/user}","branches_url":"https://api.github.com/repos/NinjaTrappeur/my-repo-pins.el/branches{/branch}","tags_url":"https://api.github.com/repos/NinjaTrappeur/my-repo-pins.el/tags","blobs_url":"https://api.github.com/repos/NinjaTrappeur/my-repo-pins.el/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/NinjaTrappeur/my-repo-pins.el/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/NinjaTrappeur/my-repo-pins.el/git/refs{/sha}","trees_url":"https://api.github.com/repos/NinjaTrappeur/my-repo-pins.el/git/trees{/sha}","statuses_url":"https://api.github.com/repos/NinjaTrappeur/my-repo-pins.el/statuses/{sha}","languages_url":"https://api.github.com/repos/NinjaTrappeur/my-repo-pins.el/languages","stargazers_url":"https://api.github.com/repos/NinjaTrappeur/my-repo-pins.el/stargazers","contributors_url":"https://api.github.com/repos/NinjaTrappeur/my-repo-pins.el/contributors","subscribers_url":"https://api.github.com/repos/NinjaTrappeur/my-repo-pins.el/subscribers","subscription_url":"https://api.github.com/repos/NinjaTrappeur/my-repo-pins.el/subscription","commits_url":"https://api.github.com/repos/NinjaTrappeur/my-repo-pins.el/commits{/sha}","git_commits_url":"https://api.github.com/repos/NinjaTrappeur/my-repo-pins.el/git/commits{/sha}","comments_url":"https://api.github.com/repos/NinjaTrappeur/my-repo-pins.el/comments{/number}","issue_comment_url":"https://api.github.com/repos/NinjaTrappeur/my-repo-pins.el/issues/comments{/number}","contents_url":"https://api.github.com/repos/NinjaTrappeur/my-repo-pins.el/contents/{+path}","compare_url":"https://api.github.com/repos/NinjaTrappeur/my-repo-pins.el/compare/{base}...{head}","merges_url":"https://api.github.com/repos/NinjaTrappeur/my-repo-pins.el/merges","archive_url":"https://api.github.com/repos/NinjaTrappeur/my-repo-pins.el/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/NinjaTrappeur/my-repo-pins.el/downloads","issues_url":"https://api.github.com/repos/NinjaTrappeur/my-repo-pins.el/issues{/number}","pulls_url":"https://api.github.com/repos/NinjaTrappeur/my-repo-pins.el/pulls{/number}","milestones_url":"https://api.github.com/repos/NinjaTrappeur/my-repo-pins.el/milestones{/number}","notifications_url":"https://api.github.com/repos/NinjaTrappeur/my-repo-pins.el/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/NinjaTrappeur/my-repo-pins.el/labels{/name}","releases_url":"https://api.github.com/repos/NinjaTrappeur/my-repo-pins.el/releases{/id}","deployments_url":"https://api.github.com/repos/NinjaTrappeur/my-repo-pins.el/deployments","created_at":"2022-04-01T18:06:46Z","updated_at":"2022-04-01T18:08:16Z","pushed_at":"2022-04-07T10:20:28Z","git_url":"git://github.com/NinjaTrappeur/my-repo-pins.el.git","ssh_url":"git@github.com:NinjaTrappeur/my-repo-pins.el.git","clone_url":"https://github.com/NinjaTrappeur/my-repo-pins.el.git","svn_url":"https://github.com/NinjaTrappeur/my-repo-pins.el","homepage":null,"size":54,"stargazers_count":0,"watchers_count":0,"language":"Emacs Lisp","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"archived":false,"disabled":false,"open_issues_count":0,"license":{"key":"gpl-3.0","name":"GNU General Public License v3.0","spdx_id":"GPL-3.0","url":"https://api.github.com/licenses/gpl-3.0","node_id":"MDc6TGljZW5zZTk="},"allow_forking":true,"is_template":false,"topics":[],"visibility":"public","forks":0,"open_issues":0,"watchers":0,"default_branch":"master","temp_clone_token":null,"network_count":0,"subscribers_count":1}