From 4415e974e881113d3d8984045ea7d632784c22ae Mon Sep 17 00:00:00 2001 From: Andreas Gerler Date: Sun, 22 Dec 2024 13:00:02 +0100 Subject: [PATCH] update forge Signed-off-by: Andreas Gerler --- elpa/forge-20240523.1103/forge-pkg.el | 23 - elpa/forge-20240523.1103/forge-topics.el | 488 ---- elpa/forge-20240523.1103/forge.info | 1537 ------------- .../dir | 0 .../forge-autoloads.el | 102 +- .../forge-bitbucket.el | 0 .../forge-commands.el | 400 ++-- .../forge-core.el | 76 +- .../forge-db.el | 3 + .../forge-gitea.el | 0 .../forge-github.el | 96 +- .../forge-gitlab.el | 6 +- .../forge-gogs.el | 0 .../forge-issue.el | 164 +- .../forge-notify.el | 108 +- elpa/forge-20241218.2036/forge-pkg.el | 21 + .../forge-post.el | 6 +- .../forge-pullreq.el | 196 +- .../forge-repo.el | 83 +- .../forge-repos.el | 86 +- .../forge-revnote.el | 0 .../forge-semi.el | 0 .../forge-tablist.el | 4 - .../forge-topic.el | 923 +++++--- elpa/forge-20241218.2036/forge-topics.el | 619 +++++ .../forge.el | 35 +- elpa/forge-20241218.2036/forge.info | 1984 +++++++++++++++++ 27 files changed, 3881 insertions(+), 3079 deletions(-) delete mode 100644 elpa/forge-20240523.1103/forge-pkg.el delete mode 100644 elpa/forge-20240523.1103/forge-topics.el delete mode 100644 elpa/forge-20240523.1103/forge.info rename elpa/{forge-20240523.1103 => forge-20241218.2036}/dir (100%) rename elpa/{forge-20240523.1103 => forge-20241218.2036}/forge-autoloads.el (70%) rename elpa/{forge-20240523.1103 => forge-20241218.2036}/forge-bitbucket.el (100%) rename elpa/{forge-20240523.1103 => forge-20241218.2036}/forge-commands.el (77%) rename elpa/{forge-20240523.1103 => forge-20241218.2036}/forge-core.el (87%) rename elpa/{forge-20240523.1103 => forge-20241218.2036}/forge-db.el (99%) rename elpa/{forge-20240523.1103 => forge-20241218.2036}/forge-gitea.el (100%) rename elpa/{forge-20240523.1103 => forge-20241218.2036}/forge-github.el (90%) rename elpa/{forge-20240523.1103 => forge-20241218.2036}/forge-gitlab.el (99%) rename elpa/{forge-20240523.1103 => forge-20241218.2036}/forge-gogs.el (100%) rename elpa/{forge-20240523.1103 => forge-20241218.2036}/forge-issue.el (58%) rename elpa/{forge-20240523.1103 => forge-20241218.2036}/forge-notify.el (82%) create mode 100644 elpa/forge-20241218.2036/forge-pkg.el rename elpa/{forge-20240523.1103 => forge-20241218.2036}/forge-post.el (98%) rename elpa/{forge-20240523.1103 => forge-20241218.2036}/forge-pullreq.el (61%) rename elpa/{forge-20240523.1103 => forge-20241218.2036}/forge-repo.el (91%) rename elpa/{forge-20240523.1103 => forge-20241218.2036}/forge-repos.el (73%) rename elpa/{forge-20240523.1103 => forge-20241218.2036}/forge-revnote.el (100%) rename elpa/{forge-20240523.1103 => forge-20241218.2036}/forge-semi.el (100%) rename elpa/{forge-20240523.1103 => forge-20241218.2036}/forge-tablist.el (95%) rename elpa/{forge-20240523.1103 => forge-20241218.2036}/forge-topic.el (64%) create mode 100644 elpa/forge-20241218.2036/forge-topics.el rename elpa/{forge-20240523.1103 => forge-20241218.2036}/forge.el (88%) create mode 100644 elpa/forge-20241218.2036/forge.info diff --git a/elpa/forge-20240523.1103/forge-pkg.el b/elpa/forge-20240523.1103/forge-pkg.el deleted file mode 100644 index b12d4495..00000000 --- a/elpa/forge-20240523.1103/forge-pkg.el +++ /dev/null @@ -1,23 +0,0 @@ -(define-package "forge" "20240523.1103" "Access Git forges from Magit." - '((emacs "26.3") - (compat "29.1.4.5") - (closql "20240405") - (dash "2.19.1") - (emacsql "20240124") - (ghub "20240507") - (let-alist "1.0.6") - (magit "20240428") - (markdown-mode "2.6") - (seq "2.24") - (transient "20240421") - (yaml "0.5.5")) - :commit "ceb6f5b8d257fe5e79532793cdd055c3dddd8806" :authors - '(("Jonas Bernoulli" . "emacs.forge@jonas.bernoulli.dev")) - :maintainer - '("Jonas Bernoulli" . "emacs.forge@jonas.bernoulli.dev") - :keywords - '("git" "tools" "vc") - :url "https://github.com/magit/forge") -;; Local Variables: -;; no-byte-compile: t -;; End: diff --git a/elpa/forge-20240523.1103/forge-topics.el b/elpa/forge-20240523.1103/forge-topics.el deleted file mode 100644 index ddf81e4f..00000000 --- a/elpa/forge-20240523.1103/forge-topics.el +++ /dev/null @@ -1,488 +0,0 @@ -;;; forge-topics.el --- List topics -*- lexical-binding:t -*- - -;; Copyright (C) 2018-2024 Jonas Bernoulli - -;; Author: Jonas Bernoulli -;; Maintainer: Jonas Bernoulli - -;; SPDX-License-Identifier: GPL-3.0-or-later - -;; This file 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 file 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 file. If not, see . - -;;; Code: - -(require 'hl-line) - -(require 'forge-topic) -(require 'forge-tablist) - -(defvar x-stretch-cursor) - -;;; Options - -(defcustom forge-topic-list-mode-hook '(hl-line-mode) - "Hook run after entering Forge-Topic-List mode." - :package-version '(forge . "0.1.0") - :group 'forge - :type 'hook - :options '(hl-line-mode)) - -(defcustom forge-topic-list-columns - '(("#" forge--format-topic-slug 5 nil nil) - ("Title" forge--format-topic-title+labels 35 nil nil)) - "List of columns displayed when listing topics for a single repository. - -Each element has the form (HEADER SOURCE WIDTH SORT PROPS). - -HEADER is the string displayed in the header. WIDTH is the width -of the column. SOURCE is used to get the value, it has to be the -name of a slot of `forge-topic' or a function that takes such an -object as argument. SORT is a boolean or a function used to sort -by this column. Supported PROPS include `:right-align' and -`:pad-right'." - :package-version '(forge . "0.4.0") - :group 'forge - :type forge--tablist-columns-type) - -(defcustom forge-global-topic-list-columns - '(("Owner" (repository owner) 15 nil nil) - ("Name" (repository name) 20 nil nil) - ("#" forge--format-topic-slug 5 nil nil) - ("Title" forge--format-topic-title+labels 35 nil nil)) - "List of columns displayed when listing topics for all repositories. - -Each element has the form (HEADER SOURCE WIDTH SORT PROPS). - -HEADER is the string displayed in the header. WIDTH is the width -of the column. SOURCE is used to get the value, it has to be the -name of a slot of `forge-topic' or a function that takes such an -object as argument. SORT is a boolean or a function used to sort -by this column. Supported PROPS include `:right-align' and -`:pad-right'." - :package-version '(forge . "0.4.0") - :group 'forge - :type forge--tablist-columns-type) - -(defcustom forge-owned-accounts nil - "An alist of accounts that are owned by you. -This should include your username as well as any organization -that you own. Used by the commands `forge-list-owned-issues', -`forge-list-owned-pullreqs' and `forge-fork'. - -Each element has the form (ACCOUNT . PLIST). The following -properties are currently being used: - -`remote-name' The default name suggested by `forge-fork' for a - fork created within this account. If unspecified, then the - name of the account is used." - :package-version '(forge . "0.2.0") - :group 'forge - :type '(repeat (cons (string :tag "Account") plist))) - -(defcustom forge-owned-ignored nil - "A list of repositories that are ignored when listing those owned by you. -This is a list of package names. Used by the commands -`forge-list-owned-issues' and `forge-list-owned-pullreqs'." - :package-version '(forge . "0.2.0") - :group 'forge - :type '(repeat (string :tag "Name"))) - -;;; Faces - -(defface forge-suffix-active '((t :inherit transient-value)) - "Face used for suffixes whose effects is currently active." - :group 'forge-faces) - -(defface forge-tablist-hl-line - `((((class color) (background light)) - ,@(and (>= emacs-major-version 27) '(:extend t)) - :box ( :line-width ,(if (>= emacs-major-version 28) (cons -1 -1) -1) - :color "grey25" - :style nil)) - (((class color) (background dark)) - ,@(and (>= emacs-major-version 27) '(:extend t)) - :box ( :line-width ,(if (>= emacs-major-version 28) (cons -1 -1) -1) - :color "grey75" - :style nil))) - "Face uses instead of `hl-line' in Forge's `tabulated-list-mode' buffers. -It is recommended that you stick to using a box for this purpose, -as using the background color would shadow the background colors -used for labels." - :group 'forge-faces) - -(defface forge-tablist-topic-label - `((t :inherit forge-topic-label)) - "Face used for topic labels in Forge's `tabulated-list-mode' buffers. -This face can be used to control whether a box is added to labels -and how that is styled. The background colors used for any given -label, cannot be changed independently of the color used in the -forges web interface." - :group 'forge-faces) - -;;; Mode - -(defvar-keymap forge-topic-list-mode-map - :doc "Local keymap for Forge-Topic-List mode buffers." - :parent tabulated-list-mode-map - "RET" #'forge-visit-this-topic - "" #'forge-visit-this-topic - "o" #'forge-browse-this-topic - "C-c C-m" #'forge-topics-menu - "'" #'forge-dispatch - "?" #'magit-dispatch) - -(defvar forge-topic-list-mode-name - '((:eval (capitalize - (concat (if forge--buffer-list-filter - (format "%s " forge--buffer-list-filter) - "") - (if forge--buffer-list-type - (format "%ss" forge--buffer-list-type) - "topics"))))) - "Information shown in the mode-line for `forge-topic-list-mode'. -Must be set before `forge-list' is loaded.") - -(define-derived-mode forge-topic-list-mode tabulated-list-mode - forge-topic-list-mode-name - "Major mode for browsing a list of topics." - (setq-local hl-line-face 'forge-tablist-hl-line) - (setq-local x-stretch-cursor nil) - (setq tabulated-list-padding 0) - (setq tabulated-list-sort-key (cons "#" nil))) - -(defun forge-topic-get-buffer (&optional repo create) - (let ((name (if repo - (format "*forge-topics: %s*" (oref repo slug)) - "*forge-topics*"))) - (if create - (get-buffer-create name) - (get-buffer name)))) - -(defun forge-topic-list-setup (type filter fn &optional repo global columns) - (let* ((repo (or repo - (and (not global) - (if-let* ((topic (forge-topic-at-point)) - (repo (forge-get-repository topic))) - repo - (forge-get-repository :tracked?))))) - (dir (or (and repo (forge-get-worktree repo)) "/")) - (buffer nil)) - (unless (or repo global) - (error "Cannot determine repository")) - (with-current-buffer (setq buffer (forge-topic-get-buffer repo t)) - (setq default-directory dir) - (setq forge-buffer-repository (and repo (oref repo id))) - (setq forge--tabulated-list-columns (or columns forge-topic-list-columns)) - (setq forge--tabulated-list-query - (cond ((not (functionp fn)) - (lambda () - (cl-sort (mapcan (-cut funcall <> repo) fn) - #'> :key (-cut oref <> number)))) - (repo (apply-partially fn repo)) - (fn))) - (cl-letf (((symbol-function #'tabulated-list-revert) #'ignore)) ; see #229 - (forge-topic-list-mode)) - (setq forge--buffer-list-type type) - (setq forge--buffer-list-filter filter) - (setq forge--buffer-list-global global) - (forge--tablist-refresh) - (add-hook 'tabulated-list-revert-hook #'forge--tablist-refresh nil t) - (tabulated-list-print) - (when hl-line-mode - (hl-line-highlight))) - (switch-to-buffer buffer))) - -;;; Commands -;;;; Menu - -;;;###autoload (autoload 'forge-topics-menu "forge-topics" nil t) -(transient-define-prefix forge-topics-menu () - "Control list of topics and the topic at point." - :transient-suffix t - :transient-non-suffix t - :transient-switch-frame nil - :refresh-suffixes t - :column-widths forge--topic-menus-column-widths - [:hide always - ("q" forge-menu-quit-list) - ("RET" forge-topic-menu) - ("" forge-topic-menu)] - [["Type" - (:info "topics" :face forge-suffix-active) - ("n" "notifications..." forge-notifications-menu :transient replace) - ("r" "repositories..." forge-repositories-menu :transient replace)] - [:description (lambda () - (if forge--buffer-list-global - "Per-repository lists" - "Subtype")) - ("t" "topics" forge-list-topics) - ("i" "issues" forge-list-issues) - ("p" "pull-requests" forge-list-pullreqs) - ""] - ["Filter" - :if (lambda () (and (not forge--buffer-list-global) - (eq forge--buffer-list-type 'topic))) - ("l" "labeled" forge-list-labeled-topics) - ("c" "created" forge-list-authored-topics) - ("a" "assigned" forge-list-assigned-topics)] - ["Filter" - :if (lambda () (and (not forge--buffer-list-global) - (eq forge--buffer-list-type 'issue))) - ("l" "labeled" forge-list-labeled-issues) - ("c" "created" forge-list-authored-issues) - ("a" "assigned" forge-list-assigned-issues)] - ["Filter" - :if (lambda () (and (not forge--buffer-list-global) - (eq forge--buffer-list-type 'pullreq))) - ("l" "labeled" forge-list-labeled-pullreqs) - ("c" "created" forge-list-authored-pullreqs) - ("a" "assigned" forge-list-assigned-pullreqs) - ("w" "awaiting review" forge-list-requested-reviews)]] - [forge--topic-set-state-group - ["Global lists" - ("o t" "owned topics" forge-list-owned-topics) - ("o i" "owned issues" forge-list-owned-issues) - ("o p" "owned pull-requests" forge-list-owned-pullreqs)] - ["Actions" - ("f" "fetch all topics" forge-pull) - ("m" "show more actions" forge-dispatch)]] - [forge--topic-set-status-group] - (interactive) - (catch 'add-instead - (unless (derived-mode-p 'forge-topic-list-mode) - (let ((repo (forge-current-repository))) - (cond - ((or (not repo) - (forge-get-repository repo nil :tracked?))) - ((y-or-n-p (format "Add %s to database, so its topics can be listed?" - (oref repo slug))) - (forge-add-repository (forge-get-url repo)) - (throw 'add-instead t)) - ((setq repo nil))) - (if-let ((buffer (forge-topic-get-buffer repo))) - (switch-to-buffer buffer) - (if repo - (forge-list-topics repo) - (forge-list-owned-topics))))) - (transient-setup 'forge-topics-menu))) - -(defun forge-menu-quit-list () - "From a transient menu, quit the list buffer and the menu. - -If quitting the list buffer causes another topic, repository or -notification list buffer to become current in the selected window, -then display the respective menu, otherwise display no menu." - (interactive) - (when (derived-mode-p 'forge-topic-mode - 'forge-topic-list-mode - 'forge-repository-list-mode - 'forge-notifications-mode) - (quit-window)) - (cond ((derived-mode-p 'forge-topic-mode) - (setq transient--exitp 'replace) - (transient-setup (setq this-command 'forge-topic-menu))) - ((derived-mode-p 'forge-topic-list-mode) - (setq transient--exitp 'replace) - (transient-setup (setq this-command 'forge-topics-menu))) - ((derived-mode-p 'forge-repository-list-mode) - (setq transient--exitp 'replace) - (transient-setup (setq this-command 'forge-repositories-menu))) - ((derived-mode-p 'forge-notifications-mode) - (setq transient--exitp 'replace) - (transient-setup (setq this-command 'forge-notifications-menu))) - (t - (setq transient--exitp t) - (transient--pre-exit) - (transient--stack-zap)))) - -;;;; Suffix Class - -(defclass forge--topic-list-command (transient-suffix) - ((type :initarg :type :initform nil) - (filter :initarg :filter :initform nil) - (global :initarg :global :initform nil) - (inapt-if :initform 'forge--topic-list-inapt) - (inapt-face :initform nil))) - -(defun forge--topic-list-inapt () - (with-slots (type filter global) transient--pending-suffix - (and (eq type forge--buffer-list-type) - (eq filter forge--buffer-list-filter) - (eq global forge--buffer-list-global)))) - -(cl-defmethod transient-format-description ((obj forge--topic-list-command)) - (with-slots (description type filter global) obj - (if (and (eq type forge--buffer-list-type) - (memq filter (list nil forge--buffer-list-filter)) - (eq global forge--buffer-list-global)) - (propertize description 'face 'forge-suffix-active) - description))) - -;;;; Topic - -(defun forge--topic-list-setup (filter fn &optional repo global columns) - (forge-topic-list-setup 'topic filter fn repo global columns)) - -;;;###autoload (autoload 'forge-list-topics "forge-topics" nil t) -(transient-define-suffix forge-list-topics (&optional repository) - "List topics of the current repository. -Non-interactively if optional REPOSITORY is non-nil, then list -topics for that instead." - :class 'forge--topic-list-command :type 'topic - (interactive) - (forge--topic-list-setup nil - (list #'forge--ls-issues - #'forge--ls-pullreqs) - repository)) -(put 'forge-list-topics 'interactive-only nil) - -;;;###autoload (autoload 'forge-list-labeled-topics "forge-topics" nil t) -(transient-define-suffix forge-list-labeled-topics (label) - "List topics of the current repository that have LABEL." - :class 'forge--topic-list-command :type 'topic :filter 'labeled - (interactive (list (forge-read-topic-label))) - (forge--topic-list-setup 'labeled - (list (-cut forge--ls-labeled-issues <> label) - (-cut forge--ls-labeled-pullreqs <> label)))) - -;;;###autoload (autoload 'forge-list-assigned-topics "forge-topics" nil t) -(transient-define-suffix forge-list-assigned-topics () - "List topics of the current repository that are assigned to you." - :class 'forge--topic-list-command :type 'topic :filter 'assigned - (interactive) - (forge--topic-list-setup 'assigned - (list #'forge--ls-assigned-issues - #'forge--ls-assigned-pullreqs))) - -;;;###autoload (autoload 'forge-list-authored-topics "forge-topics" nil t) -(transient-define-suffix forge-list-authored-topics () - "List open topics from the current repository that are authored by you." - :class 'forge--topic-list-command :type 'topic :filter 'authored - (interactive) - (forge--topic-list-setup 'authored - (list #'forge--ls-authored-issues - #'forge--ls-authored-pullreqs))) - -;;;###autoload (autoload 'forge-list-owned-topics "forge-topics" nil t) -(transient-define-suffix forge-list-owned-topics () - "List open pull-requests from all your Github repositories. -Options `forge-owned-accounts' and `forge-owned-ignored' -controls which repositories are considered to be owned by you. -Only Github is supported for now." - :class 'forge--topic-list-command :type 'topic :filter 'owned :global t - (interactive) - (forge--topic-list-setup 'owned - (list (lambda (_) (forge--ls-owned-issues)) - (lambda (_) (forge--ls-owned-pullreqs))) - nil t forge-global-topic-list-columns)) -(put 'forge-list-owned-topics 'interactive-only nil) - -;;;; Issue - -(defun forge--issue-list-setup (filter fn &optional repo global columns) - (forge-topic-list-setup 'issue filter fn repo global columns)) - -;;;###autoload (autoload 'forge-list-issues "forge-topics" nil t) -(transient-define-suffix forge-list-issues () - "List issues of the current repository." - :class 'forge--topic-list-command :type 'issue - (interactive) - (forge--issue-list-setup nil #'forge--ls-issues)) - -;;;###autoload (autoload 'forge-list-labeled-issues "forge-topics" nil t) -(transient-define-suffix forge-list-labeled-issues (label) - "List issues of the current repository that have LABEL." - :class 'forge--topic-list-command :type 'issue :filter 'labeled - (interactive (list (forge-read-topic-label))) - (forge--issue-list-setup 'labeled (-cut forge--ls-labeled-issues <> label))) - -;;;###autoload (autoload 'forge-list-assigned-issues "forge-topics" nil t) -(transient-define-suffix forge-list-assigned-issues () - "List issues of the current repository that are assigned to you." - :class 'forge--topic-list-command :type 'issue :filter 'assigned - (interactive) - (forge--issue-list-setup 'assigned #'forge--ls-assigned-issues)) - -;;;###autoload (autoload 'forge-list-authored-issues "forge-topics" nil t) -(transient-define-suffix forge-list-authored-issues () - "List open issues from the current repository that are authored by you." - :class 'forge--topic-list-command :type 'issue :filter 'authored - (interactive) - (forge--issue-list-setup 'authored #'forge--ls-authored-issues)) - -;;;###autoload (autoload 'forge-list-owned-issues "forge-topics" nil t) -(transient-define-suffix forge-list-owned-issues () - "List open issues from all your Github repositories. -Options `forge-owned-accounts' and `forge-owned-ignored' -controls which repositories are considered to be owned by you. -Only Github is supported for now." - :class 'forge--topic-list-command :type 'issue :filter 'owned :global t - (interactive) - (forge--issue-list-setup 'owned #'forge--ls-owned-issues - nil t forge-global-topic-list-columns)) - -;;;; Pullreq - -(defun forge--pullreq-list-setup (filter fn &optional repo global columns) - (forge-topic-list-setup 'pullreq filter fn repo global columns)) - -;;;###autoload (autoload 'forge-list-pullreqs "forge-topics" nil t) -(transient-define-suffix forge-list-pullreqs () - "List pull-requests of the current repository." - :class 'forge--topic-list-command :type 'pullreq - (interactive) - (forge--pullreq-list-setup nil #'forge--ls-pullreqs)) - -;;;###autoload (autoload 'forge-list-labeled-pullreqs "forge-topics" nil t) -(transient-define-suffix forge-list-labeled-pullreqs (label) - "List pull-requests of the current repository that have LABEL." - :class 'forge--topic-list-command :type 'pullreq :filter 'labeled - (interactive (list (forge-read-topic-label))) - (forge--pullreq-list-setup 'labeled (-cut forge--ls-labeled-pullreqs <> label))) - -;;;###autoload (autoload 'forge-list-assigned-pullreqs "forge-topics" nil t) -(transient-define-suffix forge-list-assigned-pullreqs () - "List pull-requests of the current repository that are assigned to you." - :class 'forge--topic-list-command :type 'pullreq :filter 'assigned - (interactive) - (forge--pullreq-list-setup 'assigned #'forge--ls-assigned-pullreqs)) - -;;;###autoload (autoload 'forge-list-requested-reviews "forge-topics" nil t) -(transient-define-suffix forge-list-requested-reviews () - "List pull-requests of the current repository that are awaiting your review." - :class 'forge--topic-list-command :type 'pullreq :filter 'review - (interactive) - (forge--pullreq-list-setup 'review #'forge--ls-requested-reviews)) - -;;;###autoload (autoload 'forge-list-authored-pullreqs "forge-topics" nil t) -(transient-define-suffix forge-list-authored-pullreqs () - "List open pull-requests from the current repository that are authored by you." - :class 'forge--topic-list-command :type 'pullreq :filter 'authored - (interactive) - (forge--pullreq-list-setup 'authored #'forge--ls-authored-pullreqs)) - -;;;###autoload (autoload 'forge-list-owned-pullreqs "forge-topics" nil t) -(transient-define-suffix forge-list-owned-pullreqs () - "List open pull-requests from all your Github repositories. -Options `forge-owned-accounts' and `forge-owned-ignored' -controls which repositories are considered to be owned by you. -Only Github is supported for now." - :class 'forge--topic-list-command :type 'pullreq :filter 'owned :global t - (interactive) - (forge--pullreq-list-setup 'owned #'forge--ls-owned-pullreqs - nil t forge-global-topic-list-columns)) - -;;; _ -(provide 'forge-topics) -;;; forge-topics.el ends here diff --git a/elpa/forge-20240523.1103/forge.info b/elpa/forge-20240523.1103/forge.info deleted file mode 100644 index bd873922..00000000 --- a/elpa/forge-20240523.1103/forge.info +++ /dev/null @@ -1,1537 +0,0 @@ -This is forge.info, produced by makeinfo version 6.7 from forge.texi. - - Copyright (C) 2018-2024 Jonas Bernoulli - - - You can redistribute this document 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 document 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. - -INFO-DIR-SECTION Emacs -START-INFO-DIR-ENTRY -* Forge: (forge). Access Git Forges from Magit. -END-INFO-DIR-ENTRY - - -File: forge.info, Node: Top, Next: Introduction, Up: (dir) - -Forge User and Developer Manual -******************************* - -Forge allows you to work with Git forges, such as Github and Gitlab, -from the comfort of Magit and the rest of Emacs. - -This manual is for Forge version 0.3.2.50-git. - - Copyright (C) 2018-2024 Jonas Bernoulli - - - You can redistribute this document 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 document 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. - -* Menu: - -* Introduction:: -* Supported Forges and Hosts:: -* Getting Started:: -* Usage:: -* FAQ:: -* Keystroke Index:: -* Function and Command Index:: -* Variable Index:: - -— The Detailed Node Listing — - -Supported Forges and Hosts - -* Supported Forges:: -* Partially Supported Forges:: -* Supported Semi-Forges:: - -Getting Started - -* Loading Forge:: -* Token Creation:: -* Initial Pull:: -* Repository Detection:: -* Caveats:: - -Usage - -* Pulling:: -* Branching:: -* Working with Topics:: -* Miscellaneous:: - -Working with Topics - -* Visiting Topics:: -* Listing Topics and Notifications:: -* Creating Topics:: -* Editing Topics and Posts:: - - -FAQ - -* Is it possible to create a single pull-request without pulling everything?:: -* ‘error in process filter HTTP Error 502, "Bad gateway"’: error in process filter HTTP Error 502 "Bad gateway". - - - -File: forge.info, Node: Introduction, Next: Supported Forges and Hosts, Prev: Top, Up: Top - -1 Introduction -************** - -Forge allows you to work with Git forges, such as Github and Gitlab, -from the comfort of Magit and the rest of Emacs. - - Forge fetches issues, pull-requests and other data using the forge’s -API and stores that in a local database. Additionally it fetches the -pull-request references using Git. Forge implements various features -that use this data but the database and pull-request refs can also be -used by third-party packages. - - -File: forge.info, Node: Supported Forges and Hosts, Next: Getting Started, Prev: Introduction, Up: Top - -2 Supported Forges and Hosts -**************************** - -Currently Forge supports two forges and three more forges partially. -Additionally it supports four semi-forges. Support for more forges and -semi-forges can and will be added. - - Both forges and semi-forges provide web interfaces for Git -repositories. Forges additionally support pull-requests and issues and -make those and other information available using an API. - - When a forge is only partially supported, then that means that only -the functionality that does not require the API is implemented, or in -other words, that the forge is only supported as a semi-forge. - - A host is a particular instance of a forge. For example the hosts - and are both instances -of the Gitlab forge. Forge supports some well known hosts out of the -box and additional hosts can easily be supported by adding entries to -the option ‘forge-alist’ (see *note Repository Detection::). - - For more details about the caveats mentioned below (and some others) -also see *note Getting Started::. - -* Menu: - -* Supported Forges:: -* Partially Supported Forges:: -* Supported Semi-Forges:: - - -File: forge.info, Node: Supported Forges, Next: Partially Supported Forges, Up: Supported Forges and Hosts - -2.1 Supported Forges -==================== - -Github ------- - -Forge’s support for Github can be considered the "reference -implementation". Support for other forges can lag behind a bit. - -Github Caveats -.............. - - • Forge uses the Github GraphQL API when possible but has to fall - back to use the REST API in many cases because the former is still - rather incomplete. - - • The Github GraphQL API has a hard-coded timeout on queries. The - only solution is to reduce the number of entities we query at once, - which can be done by adjusting either the ‘forge.graphqlItemLimit’ - git variable or the field "GQL entity limit" in a status buffer. - - • Forge depends on the ‘updated_at’ field being updated when - appropriate. For Github pull-requests at least, that is not always - done. - -Github Hosts -............ - - • - -Gitlab ------- - -Gitlab Caveats -.............. - - • Forge cannot provide notifications because the Gitlab API does not - expose those. - -Gitlab Hosts -............ - - • - • - • - - -File: forge.info, Node: Partially Supported Forges, Next: Supported Semi-Forges, Prev: Supported Forges, Up: Supported Forges and Hosts - -2.2 Partially Supported Forges -============================== - -Gitea https://gitea.io ----------------------- - -This is the next forge whose API will be supported. - -Gitea Hosts -........... - - • - -Gogs https://gogs.io --------------------- - -Once Gitea is supported it should be fairly simple to support Gogs too, -because the former is a fork of the latter and the APIs seem to still be -very similar. - -Gogs Hosts -.......... - - • - -Bitbucket https://bitbucket.org -------------------------------- - -I don’t plan to support Bitbucket’s API any time soon, and it gets less -likely that I will every do it every time I look at it. - -Bitbucket Caveats -................. - - • The API documentation is poor and initial tests indicated that the - implementation is buggy. - - • Atlassian’s offering contains two very distinct implementations - that are both called "Bitbucket". Forge only supports the - implementation whose only instance is available at - , because I only have access to that. - - • Unlike all other forges, Bitbucket does not expose pull-requests as - references in the upstream repository. For that reason Forge - actually treats it as a semi-forge, not as forge whose API is not - supported yet. This means that you cannot checkout pull-requests - locally. There is little hope that this will ever get fixed; the - respective issue was opened six years ago and there has been no - progress since: . - -Bitbucket Hosts -............... - - • - - -File: forge.info, Node: Supported Semi-Forges, Prev: Partially Supported Forges, Up: Supported Forges and Hosts - -2.3 Supported Semi-Forges -========================= - -Gitweb https://git-scm.com/docs/gitweb --------------------------------------- - -Gitweb Caveats -.............. - - • I could find only one public installation - (), which gives users the choice - between Gitweb and Cgit. The latter seems more popular (not just - on this site). - -Cgit https://git.zx2c4.com/cgit/about -------------------------------------- - -Cgit Caveats -............ - - • Different sites use different URL schemata and some of the bigger - sites use a fork. For this reason Forge has to provide several - classes to support different variations of Cgit and you have to - look at their definitions to figure out which one is the correct - one for a particular installation. - -Cgit Hosts -.......... - - • - • - • - -Stgit https://codemadness.org/git/stagit/file/README.html ---------------------------------------------------------- - -Stgit Caveats -............. - - • Stgit cannot show logs for branches beside "master". For that - reason Forge takes users to a page listing the branches when they - request the log for a particular branch (even for "master" whose - log is just one click away from there). - -Stgit Hosts -........... - - • - -Srht https://meta.sr.ht ------------------------ - -Srht Caveats -............ - - • Srht cannot show logs for branches beside "master". For that - reason Forge takes users to a page listing the branches when they - request the log for a particular branch (even for "master" whose - log is just one click away from there). - -Srht Hosts -.......... - - • - - -File: forge.info, Node: Getting Started, Next: Usage, Prev: Supported Forges and Hosts, Up: Top - -3 Getting Started -***************** - -Initial setup turned out to more challenging for many users than I had -hoped. I am trying to counter that by providing detailed instructions -that cover not just the happy path but also many of the issues users -have ran into in the past. The recommended reading is longer than one -might wish, but skipping it does not necessarily save time. - - Forge uses the Ghub package to access the APIs of supported forges. -How this works and how to create and store a token is documented in -*note (ghub)Getting Started::. Please read that in full before coming -back here and continuing with the subsections listed below. - - lists additional resources; -including a link to a much shorter quick-start guide provided by a user. - -* Menu: - -* Loading Forge:: -* Token Creation:: -* Initial Pull:: -* Repository Detection:: -* Caveats:: - - -File: forge.info, Node: Loading Forge, Next: Token Creation, Up: Getting Started - -3.1 Loading Forge -================= - -Loading Magit doesn’t cause Forge to be loaded automatically. Adding -something like this to your init file takes care of that: - - (with-eval-after-load 'magit - (require 'forge)) - -Or if you use ‘use-package’: - - (use-package forge - :after magit) - - -File: forge.info, Node: Token Creation, Next: Initial Pull, Prev: Loading Forge, Up: Getting Started - -3.2 Token Creation -================== - -Forge uses the Ghub package to access the APIs of supported Git forges. -How this works and how to create and store a token is documented in -*note (ghub)Getting Started::. - - Ghub used to provide a setup wizard, but that had to be removed for -reasons given in the manual just mentioned. Nowadays there is no way -around reading the documentation and doing this manually I am afraid. - - Forge requires the following token scopes. - - • For Github these scopes are required. - - • ‘repo’ grants full read/write access to private and public - repositories. - • ‘user’ grants access to profile information. - • ‘read:org’ grants read-only access to organization membership. - - More information about these and other scopes can be found at - . - - • For Gitlab instances ‘api’ is the only required scope. It gives - read and write access to everything. The Gitlab API provides more - fine-grained scopes for read-only access, but when any write access - at all is required, then it is all or nothing. - - -File: forge.info, Node: Initial Pull, Next: Repository Detection, Prev: Token Creation, Up: Getting Started - -3.3 Initial Pull -================ - -To start using Forge in a certain repository visit the Magit status -buffer for that repository and type ‘f n’ (‘forge-pull’). -Alternatively, you can use ‘M-x forge-add-repository’, which makes it -possible to add a forge repository without pulling all topics and even -without having to clone the respective Git repository. - - You must set up a token *before* you can add the first repository. -See *note Token Creation::. - - The first time ‘forge-pull’ is run in a repository, an entry for that -repository is added to the database and a new value is added to the Git -variable ‘remote..fetch’, which fetches all pull-requests. -(‘+refs/pull/*/head:refs/pullreqs/*’ for Github) - - ‘forge-pull’ then fetches topics and other information using the -forge’s API and pull-request references using Git. - - The initial fetch can take a while but most of that is done -asynchronously. Storing the information in the database is done -synchronously though, so there can be a noticeable hang at the end. -Subsequent fetches are much faster. - - Fetching issues from Github is much faster than fetching from other -forges because making a handful of GraphQL requests is much faster than -making hundreds of REST requests. - - -File: forge.info, Node: Repository Detection, Next: Caveats, Prev: Initial Pull, Up: Getting Started - -3.4 Repository Detection -======================== - -Ghub does *not* associate a given local repository with a repository on -a forge. The Forge package itself takes care of this. In doing so it -ignores the Git variable ‘ghub.host’ and other ‘*.host’ variables used -by Ghub. (But ‘github.user’ and other variables used to specify the -user are honored). - - Forge associates the local repository with a forge repository by -first determining which remote is associated with the upstream -repository and then looking that up in ‘forge-alist’. - - If only one remote exists, then Forge uses that unconditionally. To -reduce the number of support requests, this is even the case if the Git -variable ‘forge.remote’ names another, non-existent, remote. - - If several remotes exist, then a remote may be selected based on its -name. Almost always we want to fetch the data associated with the -upstream repository, so that is what the logic described here tries to -achieve. The convention is to name the upstream remote "origin", and if -that convention were universally followed, then things would be trivial. -However many people name the upstream remote "upstream", which also -makes sense. - - Note, however, that even though a surprising number of people do - just that, it does not make any sense to use the name "origin" to - refer to a fork; not even to your own fork. A fork is a *copy* of - the original, "copy" is an antonym for "original", and the word - "origin" is not only closely related to but even contained in the - word "original". Naming a fork the "origin" is at best extremely - confusing. - - copy - a thing made to be similar or identical to another. - - original - the earliest form of something, from which copies may be made. - - origin - the point or place where something begins, arises, or is - derived. - - If several remotes exist, then the following remote names are tried -in order and the first remote thus named that exists in the repository -is used. - - 1. The value of the Git variable ‘forge.remote’, if set. If the - variable has a value but no remote by the specified name exists, - then a warning is shown, but otherwise this conflict is ignored. - This behavior is arguably odd, but due to historic and pragmatic - reasons it is the least painful path forward. - - 2. The remote named ‘upstream’, if it exists. - - 3. The remote named ‘origin’, if it exists. - - The remote named "upstream" is preferred over the remote named -"origin" because the existence of the former strongly suggests that the -latter is either not used in this repository (in which case the order -does not matter) or else it is abused as the name of a fork (in which -case "upstream" must be preferred). - - -- Variable: forge.remote - The value of this variable specifies the remote from which Forge - fetches data. It is usually best to leave this unspecified and to - rely on the behavior described above. - - If the remote has to be specified explicitly, then this should be - done locally, for a single repository. - - Only ever set this globally, if you consistently use a certain name - to refer to the upstream repository and it isn’t one of "upstream" - or "origin", and you *never* use that name to refer to a repository - that does *not* refer to the upstream repository. - -‘N r’ (‘forge-forge.remote’) - This command changes the value of the ‘forge.remote’ Git variable - in the current repository. - - If this variable is set, then Forge uses the remote by that name, if -it exists, the same way it may have used ‘origin’ if the the variable -were undefined. I.e., it does not fall through to try ‘origin’ if no -remote by your chosen name exists. - - Once the upstream remote has been determined, Forge looks it up in -‘forge-alist’, using the host part of the URL as the key. For example -the key for ‘git@github.com:magit/forge.git’ is ‘github.com’. - - -- User Option: forge-alist - This option defines forge hosts known to Forge. - - Each entry has the form ‘(GITHOST APIHOST WEBHOST CLASS)’. - - • GITHOST is the host used to access repositories on the forge - using Git. - - • APIHOST is the host used to access the forge’s API. For some - forges the isn’t just a host, but a host followed by the path - to the API’s endpoint. - - • WEBHOST is the host used to access repositories on this forge - using a browser. The IDs used to identify repositories from - the forge in the local database also derives from this value. - - • CLASS is the class to be used for repositories from the forge. - - Complications: - - • When connecting to a Github Enterprise edition whose REST - API’s end point is "/v3" and whose GraphQL API’s end - point is "/graphql", then use "/v3" as APIHOST. - This is a historic accident. See issue #174. - - • WEBHOST and CLASS cannot be changed once you have added one or - more repositories from a forge. Changing GITHOST and/or - APIHOST may be possible, but should seldom be necessary. - - -File: forge.info, Node: Caveats, Prev: Repository Detection, Up: Getting Started - -3.5 Caveats -=========== - - • Fetched information is stored in a database. The table schemata of - that database have not been finalized yet. Until that has happened - it will occasionally have to be discarded. That isn’t such a huge - deal because for now the database does not contain any information - that cannot simply be fetched again, see *note Initial Pull::. - - • Fetching is implemented under the assumption that the API can be - asked to list the things that have changed since we last checked. - Unfortunately the APIs are not bug-free, so this is not always the - case. If in doubt, then re-fetch an individual topic to ensure it - is up-to-date using the command ‘forge-pull-topic’. - - • Some other, forge-specific, caveats are mentioned in *note - Supported Forges and Hosts::. - - -File: forge.info, Node: Usage, Next: FAQ, Prev: Getting Started, Up: Top - -4 Usage -******* - -Once information has been pulled from a repository’s forge for the first -time, Forge adds two additional sections, named "Pull requests" and -"Issues" to Magit’s status buffer. It is also possible to add a -repository to the local database without pulling all the data, which is -useful if you want to create a single pull-request. - -‘N a’ (‘forge-add-repository’) - This command adds a repository to the database. - - It offers to either pull topics (now and in the future) or to only - pull individual topics when the user invokes ‘forge-pull-topic’. - - Some of Forge’s commands are only bound when point is within one of -these sections but other commands are also available elsewhere in -Magit’s status buffer and/or from Magit’s transient commands. - -‘N’ (‘forge-dispatch’) - This prefix command is available in any Magit buffer and provides - access to several of the available Forge commands. Most of these - commands are also bound elsewhere, but some are not. See the - following sections for information about the available commands. - - Throughout this manual you will find many bindings that begin with - ‘N’, but if you prefer to continue to use ‘forge-dispatch’’s older - binding you can substitute ‘'’ for that. - -* Menu: - -* Pulling:: -* Branching:: -* Working with Topics:: -* Miscellaneous:: - - -File: forge.info, Node: Pulling, Next: Branching, Up: Usage - -4.1 Pulling -=========== - -The commands that fetch forge data are available from the same transient -prefix command (‘magit-fetch’ on ‘f’) that is used to fetch Git data. -If option ‘magit-pull-or-fetch’ is non-nil, then they are also available -from the ‘magit-pull’ transient (on ‘F’). - -‘f n’ (‘forge-pull’) -‘N f f’ - This command uses a forge’s API to fetch topics and other - information about the current repository and stores the fetched - information in the database. It also fetches notifications for all - repositories from the same forge host. (Currently this is limited - to Github.) Finally it fetches pull-request references using Git. - - After using this command for the first time in a given repository - the status buffer for that repository always lists the - pull-requests and issues. See *note Initial Pull::. - -‘f N’ (‘forge-pull-notifications’) -‘N f n’ - This command uses a forge’s API to fetch all notifications from - that forge including, but not limited to, the notifications for the - current repository. - - Fetching all notifications fetches associated topics even if you - have not started fetching *all* topics for the respective - repositories (using ‘forge-pull’), but it does not cause the topics - to be listed in the status buffer of such "uninitialized" - repositories. - - Note how pulling data from a forge’s API works the same way as -pulling Git data does; you do it explicitly when you want to see the -work done by others. - - This is less disruptive, more reliable, and easier to understand than -if Forge did the pulling by itself at random intervals. It might -however mean that you occasionally invoke a command expecting the most -recent data to be available and then have to abort to pull first. The -same can happen with Git, e.g., you might attempt to merge a branch that -you know exists but haven’t actually pulled yet. - -‘N f t’ (‘forge-pull-topic’) - This command uses a forge’s API to fetch a single pull-request and - stores it in the database. - - Normally you wouldn’t want to pull a single pull-request by itself, - but due to a bug in the Github API you might sometimes have to do - so. - - Fetching is implemented under the assumption that the API can be - asked to list the things that have changed since we last checked. - Unfortunately the APIs are not bug-free, so this is not always the - case. If in doubt, then re-fetch an individual topic to ensure it - is up-to-date using the command ‘forge-pull-topic’. - - -File: forge.info, Node: Branching, Next: Working with Topics, Prev: Pulling, Up: Usage - -4.2 Branching -============= - -Forge provides commands for creating and checking out a new branch or -work tree from a pull-request. These commands are available from the -same transient prefix commands as the suffix commands used to create and -check out branches and work trees in a more generic fashion -(‘magit-branch’ on ‘b’ and ‘magit-worktree’ on ‘%’). - -‘b F’ (‘forge-branch-pullreq’) - This command creates and configures a new branch from a - pull-request, creating and configuring a new remote if necessary. - - The name of the local branch is the same as the name of the remote - branch that you are being asked to merge, unless the contributor - could not be bothered to properly name the branch before opening - the pull-request. The most likely such case is when you are being - asked to merge something like "fork/master" into "origin/master". - In such cases the local branch will be named "pr-N", where ‘N’ is - the pull-request number. - - These variables are always set by this command: - - • ‘branch..pullRequest’ is set to the pull-request number. - • ‘branch..pullRequestRemote’ is set to the remote on - which the pull-request branch is located. - • ‘branch..pushRemote’ is set to the same remote as - ‘branch..pullRequestRemote’ if that is possible, - otherwise it is set to the upstream remote. - • ‘branch..description’ is set to the pull-request title. - • ‘branch..rebase’ is set to ‘true’ because there should - be no merge commits among the commits in a pull-request. - - This command also configures the upstream and the push-remote of - the local branch that it creates. - - The branch against which the pull-request was opened is always used - as the upstream. This makes it easy to see what commits you are - being asked to merge in the section titled something like "Unmerged - into origin/master". - - Like for other commands that create a branch, it depends on the - option ‘magit-branch-prefer-remote-upstream’ whether the remote - branch itself or the respective local branch is used as the - upstream, so this section may also be titled, e.g., "Unmerged into - master". - - When necessary and possible, the remote pull-request branch is - configured to be used as the push-target. This makes it easy to - see what further changes the contributor has made since you last - reviewed their changes in the section titled something like - "Unpulled from origin/new-feature" or "Unpulled from - fork/new-feature". - - • If the pull-request branch is located in the upstream - repository, then you probably have set ‘remote.pushDefault’ to - that repository. However some users like to set that variable - to their personal fork, even if they have push access to the - upstream, so ‘branch..pushRemote’ is set anyway. - - • If the pull-request branch is located inside a fork, then you - are usually able to push to that branch, because Github by - default allows the recipient of a pull-request to push to the - remote pull-request branch even if it is located in a fork. - The contributor has to explicitly disable this. - - • If you are not allowed to push to the pull-request branch - on the fork, then a branch by the same name located in - the upstream repository is configured as the push-target. - - • A—sadly rather common—special case is when the - contributor didn’t bother to use a dedicated branch for - the pull-request. - - The most likely such case is when you are being asked to - merge something like "fork/master" into "origin/master". - The special push permission mentioned above is never - granted for the branch that is the repository’s default - branch, and that would almost certainly be the case in - this scenario. - - To enable you to easily push somewhere anyway, the local - branch is named "pr-N" (where ‘N’ is the pull-request - number) and the upstream repository is used as the - push-remote. - - • Finally, if you are allowed to push to the pull-request - branch and the contributor had the foresight to use a - dedicated branch, then the fork is configured as the - push-remote. - - The push-remote is configured using - ‘branch..pushRemote’, even if the used value is - identical to that of ‘remote.pushDefault’, just in case you - change the value of the latter later on. Additionally the - variable ‘branch..pullRequestRemote’ is set to the - remote on which the pull-request branch is located. - -‘b f’ (‘forge-checkout-pullreq’) - This command creates and configures a new branch from a - pull-request the same way ‘forge-branch-pullreq’ does. - Additionally it checks out the new branch. - -‘Z n’ (‘forge-checkout-worktree’) - This command creates and configures a new branch from a - pull-request the same way ‘forge-branch-pullreq’ does. - Additionally it checks out the new branch using a new working tree. - - -- User Option: forge-checkout-worktree-read-directory-function - This function is used by ‘forge-checkout-worktree’ to read read the - new worktree directory where it checks out to pull-request. It - takes the pull-request as the only argument and must return a - directory. - - When you delete a pull-request branch, which was created using one of -the above three commands, then ‘magit-branch-delete’ usually offers to -also delete the corresponding remote. It does not offer to delete a -remote if (1) the remote is the upstream remote, and/or (2) if other -branches are being fetched from the remote. - - Note that you have to delete the local branch (e.g., "feature") for -this to work. If you delete the tracking branch (e.g., "fork/feature"), -then the remote is never removed. - - -File: forge.info, Node: Working with Topics, Next: Miscellaneous, Prev: Branching, Up: Usage - -4.3 Working with Topics -======================= - -We call both issues and pull-requests "topics". The contributions to -the conversation are called "posts". - -* Menu: - -* Visiting Topics:: -* Listing Topics and Notifications:: -* Creating Topics:: -* Editing Topics and Posts:: - - -File: forge.info, Node: Visiting Topics, Next: Listing Topics and Notifications, Up: Working with Topics - -4.3.1 Visiting Topics ---------------------- - -Magit’s status buffer contains lists of issues and pull-requests. -Topics are ordered by last modification time. All open issues and some -recently edited and closed topics are listed. - - Forge provides some commands that act on the listed topics. These -commands can also be used in other contexts, such as when point is on a -commit or branch section. - -‘C-c C-w’ (‘forge-browse-TYPE’) -‘C-c C-w’ (‘forge-browse-dwim’) -‘N b r’ (‘forge-browse-remote’) -‘N b I’ (‘forge-browse-issues’) -‘N b P’ (‘forge-browse-pullreqs’) -‘N b t’ (‘forge-browse-topic’) -‘N b i’ (‘forge-browse-issue’) -‘N b p’ (‘forge-browse-pullreq’) - These commands visit the topic, issue(s), pull-request(s), post, - branch, commit, or remote at point in a browser. - - This is implemented using various commands named - ‘forge-browse-TYPE’, and the key binding is defined by remapping - ‘magit-browse-thing’ (as defined in ‘magit-mode-map)’. For commit - sections this key is bound to ‘forge-browse-dwim’, which prefers a - topic over a branch and a branch over a commit. - -‘’ (‘forge-visit-TYPE’) -‘C-c C-v’ (‘forge-visit-topic’) -‘N v t’ (‘forge-visit-topic’) -‘N v i’ (‘forge-visit-issue’) -‘N v p’ (‘forge-visit-pullreq’) - These commands visit the pull-request(s), issue(s), or repository - in a separate buffer. - - The ‘RET’ binding is only available when point is on a issue or - pull-request section because that key is already bound to something - else for most of Magit’s own sections. ‘C-c C-v’ however is also - available on regular commit (e.g., in a log) and branch sections. - - This is implemented using various commands named ‘forge-visit-TYPE’ - and the key binding is defined by remapping ‘magit-visit-thing’ (as - defined in ‘magit-mode-map’). - - -File: forge.info, Node: Listing Topics and Notifications, Next: Creating Topics, Prev: Visiting Topics, Up: Working with Topics - -4.3.2 Listing Topics and Notifications --------------------------------------- - -By default Forge lists a subset of topics directly in the Magit status -buffer. It also provides commands to list topics and notifications in -separate buffers. - - Forge adds the following functions to ‘magit-status-sections-hook’: - - -- Function: forge-insert-pullreqs - This function inserts a list of the most recent and/or open - pull-requests. - - -- Function: forge-insert-issues - This function inserts a list of the most recent and/or open issues. - - -- User Option: forge-topic-list-limit - This option limits the number of topics listed by the above - functions. - - All unread topics are always shown. If the value of this option - has the form ‘(OPEN . CLOSED)’, then the integer ‘OPEN’ specifies - the maximal number of topics and ‘CLOSED’ specifies the maximal - number of closed topics. IF ‘CLOSED’ is negative then show no - closed topics until the command ‘forge-toggle-closed-visibility’ - changes the sign. - - -- Command: forge-toggle-closed-visibility - This command toggles whether the above two functions list recently - closed issues in the current buffer. - - The following functions are also suitable for -‘magit-status-sections-hook’: - - -- Function: forge-insert-assigned-pullreqs - This function inserts a list of open pull-requests that are - assigned to you. - - -- Function: forge-insert-requested-reviews - This function inserts a list of open pull-requests that are - awaiting your review. - - -- Function: forge-insert-authored-pullreqs - This function inserts a list of open pull-requests that are - authored by you. - - -- Function: forge-insert-assigned-issues - This function inserts a list of open issues that are assigned to - you. - - -- Function: forge-insert-authored-issues - This function inserts a list of open issues that are authored by - you. - - The following commands list repositories, notifications and topics in -dedicated buffers: - -‘N l r’ (‘forge-list-repositories’) - This command lists all known repositories in a separate buffer. - -‘N l n’ (‘forge-list-notifications’) - This command lists all notifications for all forges in a separate - buffer. - -‘N l p’ (‘forge-list-pullreqs’) - This command lists the current repository’s pull-requests in a - separate buffer. - -‘N l i’ (‘forge-list-issues’) - This command lists the current repository’s issues in a separate - buffer. - - -- Command: forge-list-labeled-pullreqs - This command lists the current repository’s open pull-requests that - are labeled with a label read from the user. - - -- Command: forge-list-labeled-issues - This command lists the current repository’s open issues that are - labeled with a label read from the user. - - -- Command: forge-list-assigned-pullreqs - This command lists the current repository’s open pull-requests that - are assigned to you in a separate buffer. - - -- Command: forge-list-assigned-issues - This command lists the current repository’s open issues that are - assigned to you in a separate buffer. - - -- Command: forge-list-requested-reviews - This command lists pull-requests of the current repository that are - awaiting your review in a separate buffer. - - -- Command: forge-list-authored-pullreqs - This command lists the current repository’s open pull-requests that - are authored by you in a separate buffer. - - -- Command: forge-list-authored-issues - This command lists the current repository’s open issues that are - authored by you in a separate buffer. - - -- Command: forge-list-owned-pullreqs - This command lists open pull-requests from all the repositories - that you own. Options ‘forge-owned-accounts’ and - ‘forge-owned-ignored’ controls which repositories are considered to - be owned by you. Only Github is supported for now. - - -- Command: forge-list-owned-issues - This command lists open issues from all the repositories that you - own. Options ‘forge-owned-accounts’ and ‘forge-owned-ignored’ - controls which repositories are considered to be owned by you. - Only Github is supported for now. - - -- User Option: forge-owned-accounts - This is an alist of accounts that are owned by you. This should - include your username as well as any organization that you own. - Used by the commands ‘forge-list-owned-issues’, - ‘forge-list-owned-pullreqs’ and ‘forge-fork’. - - Each element has the form ‘(ACCOUNT . PLIST)’. The following - properties are currently being used: - - • ‘remote-name’ The default name suggested by ‘forge-fork’ for a - fork created within this account. If unspecified, then the - name of the account is used. - - Example: ‘(("tarsius") ("emacsmirror" remote-name "mirror"))’. - - -- User Option: forge-owned-ignored - This is a list of repository names that are considered to not be - owned by you even though they would have been considered to be - owned by you based on ‘forge-owned-accounts’. - - -File: forge.info, Node: Creating Topics, Next: Editing Topics and Posts, Prev: Listing Topics and Notifications, Up: Working with Topics - -4.3.3 Creating Topics ---------------------- - -‘N c p’ (‘forge-create-pullreq’) -‘C-c C-n [on "Pull requests" section]’ - This command creates a new pull-request for the current repository. - -‘N c i’ (‘forge-create-issue’) -‘C-c C-n [on "Issues" section]’ - This command creates a new issue for the current repository. - - -File: forge.info, Node: Editing Topics and Posts, Prev: Creating Topics, Up: Working with Topics - -4.3.4 Editing Topics and Posts ------------------------------- - -We call both issues and pull-requests "topics". The contributions to -the conversation are called "posts". The post that initiated the -conversation is also called a post. - - These commands are available only from the topic buffer (i.e., from -the buffer that shows the posts on a topic). Other commands that also -work in other buffers are available here also. For example ‘C-c C-w’ on -a post visits that post in a browser. - -‘C-c C-n’ (‘forge-create-post’) -‘C-c C-r’ - This command allows users to create a new post on an existing - topic. It opens a buffer in which the user can write the post. - When the post is done, then the user has to submit using ‘C-c C-c’. - - If the region is active and marks part of an existing post, then - that part of the post is quoted. Otherwise, or if a prefix - argument is used, then the complete post that point is currently on - is quoted. - -‘C-c C-e [on a post section]’ (‘forge-edit-post’) - This command visits an existing post in a separate buffer. When - the changes to the post are done, then the user has to submit using - ‘C-c C-c’. - -‘C-c C-e [on "Title" section]’ (‘forge-edit-topic-title’) - This command reads a new title for an existing topic in the - minibuffer. - -‘C-c C-e [on "State" section]’ (‘forge-edit-topic-state’) - This command toggles the state of an existing topic between "open" - and "closed". - -‘C-c C-e [on "Draft" section]’ (‘forge-edit-topic-draft’) - This command toggles whether an existing topic is a draft or not. - -‘C-c C-e [on "Labels" section]’ (‘forge-edit-topic-labels’) - This command reads a list of labels for an existing topic in the - minibuffer. - -‘C-c C-e [on "Marks" section]’ (‘forge-edit-topic-marks’) - This command reads a list of marks for an existing topic in the - minibuffer. - - Marks are like labels except that they are not shared with anyone - else. To create a mark that topics can subsequently be marked with - use the command ‘forge-create-mark’. Existing marks can be edited - using the command ‘forge-edit-mark’. - -‘C-c C-e [on "Assignees" section]’ (‘forge-edit-topic-assignees’) - This command reads a list of assignees for an existing topic in the - minibuffer. - -‘C-c C-e [on "Review-Requests" section]’ (‘forge-edit-topic-review-requests’) - This command reads a list of people who you would like to review an - existing topic in the minibuffer. - -‘C-c C-e [on "Note" section]’ -‘M-x forge-edit-topic-note’ - This lets you edit your private note about a topic. - -‘C-c C-k [on a comment section]’ (‘forge-delete-comment’) - This command deletes the comment at point. - -‘m M [if enabled]’ (‘forge-merge’) -‘N M [if enabled]’ - This command merges the current pull-request using the forge’s API. - If there is no current pull-request or with a prefix argument, then - it reads a pull-request to visit instead. - - The "merge method" to be used is read from the user. - - Use of this command is discouraged. Unless the remote repository - is configured to disallow that, you should instead merge locally - and then push the target branch. Forges detect that you have done - that and respond by automatically marking the pull-request as - merged. - - Creating a new post and editing an existing post are similar to -creating a new commit and editing the message of an existing commit. In -both cases the message has to be written in a separate buffer and then -the process has to be finished or canceled using a separate command. - - The following commands are available in buffers used to edit posts: - -‘C-c C-c’ (‘forge-post-submit’) - This command submits the post that is being edited in the current - buffer. - -‘C-c C-k’ (‘forge-post-cancel’) - This command cancels the post that is being edited in the current - buffer. - -‘C-c C-e’ (‘forge-post-dispatch’) - This prefix command features the above two commands as suffixes, - and when creating a pull-request also the following command. More - suffix commands will likely be added in the future. - -‘C-c C-e d’ (‘forge-post-toggle-draft’) - This command toggles whether the pull-request being created is a - draft. - - -File: forge.info, Node: Miscellaneous, Prev: Working with Topics, Up: Usage - -4.4 Miscellaneous -================= - -‘N c f’ (‘forge-fork’) - This command adds an additional remote to the current repository. - The remote can either point at an existing repository or one that - has to be created first by forking it to an account the user has - access to. - - Currently this only supports Github and Gitlab. - -‘N a’ (‘forge-add-repository’) - This command reads a repository from the user and adds it to the - database. The repository can be provided as a URL, a name, or in - the form OWNER/NAME. This is subject to ‘magit-clone-name-alist’. - - This command offers to either pull topics (now and in the future) - or to only pull individual topics when the user invokes - ‘forge-pull-topic’. - -‘N t t’ (‘forge-toggle-display-in-status-buffer’) - This command toggles whether any topics are displayed in the - current Magit status buffer. - -‘N t c’ (‘forge-toggle-closed-visibility’) - This command toggles whether closed topics are shown in the Magit - status buffer. - - -- Command: forge-add-user-repositories - This command reads a host and a username from the user and adds all - of that user’s repositories on that host to the local database. - - This may take a while. Only Github is supported at the moment. - - -- Command: forge-add-organization-repositories - This command reads a host and an organization from the user and - adds all the organization’s repositories on that host to the local - database. - - This may take a while. Only Github is supported at the moment. - - -- Command: forge-remove-repository - This command reads a repository and removes it from the local - database. - - -- Command: forge-remove-topic-locally - This command reads a topic and removes it from the local database. - The topic is not removed from the forge and, if it is later - modified, then it will be added to the database again. - - Due to how the supported APIs work, it would be too expensive to - automatically remove topics from the local database that were - removed from the forge. The only purpose of this command is to - allow you to manually clean up the local database. - - -- Command: forge-reset-database - This command moves the current database file to the trash and - creates a new empty database. - - This is useful after the database’s table schemata have changed, - which will happen a few times while the Forge functionality is - still under heavy development. - - -File: forge.info, Node: FAQ, Next: Keystroke Index, Prev: Usage, Up: Top - -Appendix A FAQ -************** - -This section lists some frequently asked questions. Please also see - for an extended list of common -issues. - -* Menu: - -* Is it possible to create a single pull-request without pulling everything?:: -* ‘error in process filter HTTP Error 502, "Bad gateway"’: error in process filter HTTP Error 502 "Bad gateway". - - -File: forge.info, Node: Is it possible to create a single pull-request without pulling everything?, Next: error in process filter HTTP Error 502 "Bad gateway", Up: FAQ - -A.1 Is it possible to create a single pull-request without pulling everything? -============================================================================== - -Yes. ‘M-x forge-add-repository’ offers to add a repository to the -database without also fetching all pull-requests and issues. - - -File: forge.info, Node: error in process filter HTTP Error 502 "Bad gateway", Prev: Is it possible to create a single pull-request without pulling everything?, Up: FAQ - -A.2 ‘error in process filter: HTTP Error: 502, "Bad gateway"’ -============================================================= - -This is a frequently occurring error. Adding some formatting the full -error is: - - error in process filter: ghub--signal-error: HTTP Error: 502, - "Bad gateway", "/graphql", - ((data . "null") - (errors ((message . "Something went wrong while executing your query. - This may be the result of a timeout, or it could be a GitHub bug. - Please include `CC2C:4FEA:A1771C1:CBF40CE:5C33F7E5` - when reporting this issue.")))) - - This indicates that something went wrong within Github’s network. -See -. -The appended error message also says as much: "This may be the result of -a timeout, or it could be a GitHub bug." - - It appears that this happens more often in big repositories, -especially during the initial pull, but this may be an illusion; it is -known to also happens for smaller, incremental requests. - - I believe that more data just means more requests and thus more -opportunities for things to go wrong. It seems unlikely that this is -due to us requesting too much data at once (in that case we would get a -different error from GraphQL, not HTTP). When fetching lots of data, -then we do not request one large response but make many requests and we -then collect the many responses (pagination forces us to do that). - - The timeout may be due to many requests from other people arriving at -some Github-internal bottleneck at the same time, or it may be due to -cold caches and overly aggressive timeouts. We just don’t know; it’s -their infrastructure. - - The second problem is that we currently simply error out if we get -this error. This could be changed and eventually it will be, but for -the time being your only option is to just try again, possibly -repeatedly and possibly after a delay to give whatever congestion may -exist on the other end a chance to clear or to give their caches a -chance to warm up. - - This was also discussed in -and . - - -File: forge.info, Node: Keystroke Index, Next: Function and Command Index, Prev: FAQ, Up: Top - -Appendix B Keystroke Index -************************** - -[index] -* Menu: - -* b F: Branching. (line 13) -* b f: Branching. (line 102) -* C-c C-c: Editing Topics and Posts. - (line 92) -* C-c C-e: Editing Topics and Posts. - (line 100) -* C-c C-e d: Editing Topics and Posts. - (line 105) -* C-c C-e [on "Assignees" section]: Editing Topics and Posts. - (line 56) -* C-c C-e [on "Draft" section]: Editing Topics and Posts. - (line 40) -* C-c C-e [on "Labels" section]: Editing Topics and Posts. - (line 43) -* C-c C-e [on "Marks" section]: Editing Topics and Posts. - (line 47) -* C-c C-e [on "Note" section]: Editing Topics and Posts. - (line 65) -* C-c C-e [on "Review-Requests" section]: Editing Topics and Posts. - (line 60) -* C-c C-e [on "State" section]: Editing Topics and Posts. - (line 36) -* C-c C-e [on "Title" section]: Editing Topics and Posts. - (line 32) -* C-c C-e [on a post section]: Editing Topics and Posts. - (line 27) -* C-c C-k: Editing Topics and Posts. - (line 96) -* C-c C-k [on a comment section]: Editing Topics and Posts. - (line 68) -* C-c C-n: Editing Topics and Posts. - (line 17) -* C-c C-n [on "Issues" section]: Creating Topics. (line 12) -* C-c C-n [on "Pull requests" section]: Creating Topics. (line 8) -* C-c C-r: Editing Topics and Posts. - (line 17) -* C-c C-v: Visiting Topics. (line 36) -* C-c C-w: Visiting Topics. (line 22) -* C-c C-w <1>: Visiting Topics. (line 22) -* f n: Pulling. (line 13) -* f N: Pulling. (line 25) -* m M [if enabled]: Editing Topics and Posts. - (line 72) -* N: Usage. (line 23) -* N a: Usage. (line 13) -* N a <1>: Miscellaneous. (line 15) -* N b I: Visiting Topics. (line 22) -* N b i: Visiting Topics. (line 22) -* N b P: Visiting Topics. (line 22) -* N b p: Visiting Topics. (line 22) -* N b r: Visiting Topics. (line 22) -* N b t: Visiting Topics. (line 22) -* N c f: Miscellaneous. (line 7) -* N c i: Creating Topics. (line 12) -* N c p: Creating Topics. (line 8) -* N f f: Pulling. (line 13) -* N f n: Pulling. (line 25) -* N f t: Pulling. (line 47) -* N l i: Listing Topics and Notifications. - (line 72) -* N l n: Listing Topics and Notifications. - (line 64) -* N l p: Listing Topics and Notifications. - (line 68) -* N l r: Listing Topics and Notifications. - (line 61) -* N M [if enabled]: Editing Topics and Posts. - (line 72) -* N r: Repository Detection. - (line 80) -* N t c: Miscellaneous. (line 28) -* N t t: Miscellaneous. (line 24) -* N v i: Visiting Topics. (line 36) -* N v p: Visiting Topics. (line 36) -* N v t: Visiting Topics. (line 36) -* RET: Visiting Topics. (line 36) -* Z n: Branching. (line 107) - - -File: forge.info, Node: Function and Command Index, Next: Variable Index, Prev: Keystroke Index, Up: Top - -Appendix C Function and Command Index -************************************* - -[index] -* Menu: - -* forge-add-organization-repositories: Miscellaneous. (line 37) -* forge-add-repository: Usage. (line 13) -* forge-add-repository <1>: Miscellaneous. (line 15) -* forge-add-user-repositories: Miscellaneous. (line 31) -* forge-branch-pullreq: Branching. (line 13) -* forge-browse-dwim: Visiting Topics. (line 22) -* forge-browse-issue: Visiting Topics. (line 22) -* forge-browse-issues: Visiting Topics. (line 22) -* forge-browse-pullreq: Visiting Topics. (line 22) -* forge-browse-pullreqs: Visiting Topics. (line 22) -* forge-browse-remote: Visiting Topics. (line 22) -* forge-browse-topic: Visiting Topics. (line 22) -* forge-browse-TYPE: Visiting Topics. (line 22) -* forge-checkout-pullreq: Branching. (line 102) -* forge-checkout-worktree: Branching. (line 107) -* forge-create-issue: Creating Topics. (line 12) -* forge-create-post: Editing Topics and Posts. - (line 17) -* forge-create-pullreq: Creating Topics. (line 8) -* forge-delete-comment: Editing Topics and Posts. - (line 68) -* forge-dispatch: Usage. (line 23) -* forge-edit-post: Editing Topics and Posts. - (line 27) -* forge-edit-topic-assignees: Editing Topics and Posts. - (line 56) -* forge-edit-topic-draft: Editing Topics and Posts. - (line 40) -* forge-edit-topic-labels: Editing Topics and Posts. - (line 43) -* forge-edit-topic-marks: Editing Topics and Posts. - (line 47) -* forge-edit-topic-note: Editing Topics and Posts. - (line 65) -* forge-edit-topic-review-requests: Editing Topics and Posts. - (line 60) -* forge-edit-topic-state: Editing Topics and Posts. - (line 36) -* forge-edit-topic-title: Editing Topics and Posts. - (line 32) -* forge-forge.remote: Repository Detection. - (line 80) -* forge-fork: Miscellaneous. (line 7) -* forge-insert-assigned-issues: Listing Topics and Notifications. - (line 49) -* forge-insert-assigned-pullreqs: Listing Topics and Notifications. - (line 37) -* forge-insert-authored-issues: Listing Topics and Notifications. - (line 53) -* forge-insert-authored-pullreqs: Listing Topics and Notifications. - (line 45) -* forge-insert-issues: Listing Topics and Notifications. - (line 16) -* forge-insert-pullreqs: Listing Topics and Notifications. - (line 12) -* forge-insert-requested-reviews: Listing Topics and Notifications. - (line 41) -* forge-list-assigned-issues: Listing Topics and Notifications. - (line 87) -* forge-list-assigned-pullreqs: Listing Topics and Notifications. - (line 83) -* forge-list-authored-issues: Listing Topics and Notifications. - (line 99) -* forge-list-authored-pullreqs: Listing Topics and Notifications. - (line 95) -* forge-list-issues: Listing Topics and Notifications. - (line 72) -* forge-list-labeled-issues: Listing Topics and Notifications. - (line 79) -* forge-list-labeled-pullreqs: Listing Topics and Notifications. - (line 75) -* forge-list-notifications: Listing Topics and Notifications. - (line 64) -* forge-list-owned-issues: Listing Topics and Notifications. - (line 109) -* forge-list-owned-pullreqs: Listing Topics and Notifications. - (line 103) -* forge-list-pullreqs: Listing Topics and Notifications. - (line 68) -* forge-list-repositories: Listing Topics and Notifications. - (line 61) -* forge-list-requested-reviews: Listing Topics and Notifications. - (line 91) -* forge-merge: Editing Topics and Posts. - (line 72) -* forge-post-cancel: Editing Topics and Posts. - (line 96) -* forge-post-dispatch: Editing Topics and Posts. - (line 100) -* forge-post-submit: Editing Topics and Posts. - (line 92) -* forge-post-toggle-draft: Editing Topics and Posts. - (line 105) -* forge-pull: Pulling. (line 13) -* forge-pull-notifications: Pulling. (line 25) -* forge-pull-topic: Pulling. (line 47) -* forge-remove-repository: Miscellaneous. (line 44) -* forge-remove-topic-locally: Miscellaneous. (line 48) -* forge-reset-database: Miscellaneous. (line 58) -* forge-toggle-closed-visibility: Listing Topics and Notifications. - (line 30) -* forge-toggle-closed-visibility <1>: Miscellaneous. (line 28) -* forge-toggle-display-in-status-buffer: Miscellaneous. (line 24) -* forge-visit-issue: Visiting Topics. (line 36) -* forge-visit-pullreq: Visiting Topics. (line 36) -* forge-visit-topic: Visiting Topics. (line 36) -* forge-visit-TYPE: Visiting Topics. (line 36) - - -File: forge.info, Node: Variable Index, Prev: Function and Command Index, Up: Top - -Appendix D Variable Index -************************* - -[index] -* Menu: - -* forge-alist: Repository Detection. - (line 92) -* forge-checkout-worktree-read-directory-function: Branching. (line 111) -* forge-owned-accounts: Listing Topics and Notifications. - (line 115) -* forge-owned-ignored: Listing Topics and Notifications. - (line 130) -* forge-topic-list-limit: Listing Topics and Notifications. - (line 19) -* forge.remote: Repository Detection. - (line 66) - - - -Tag Table: -Node: Top771 -Node: Introduction2475 -Node: Supported Forges and Hosts3038 -Node: Supported Forges4323 -Ref: Github4479 -Ref: Github Caveats4622 -Ref: Github Hosts5268 -Ref: Gitlab5324 -Ref: Gitlab Caveats5339 -Ref: Gitlab Hosts5464 -Node: Partially Supported Forges5584 -Ref: Gitea https//giteaio5790 -Ref: Gitea Hosts5890 -Ref: Gogs https//gogsio5946 -Ref: Gogs Hosts6149 -Ref: Bitbucket https//bitbucketorg6207 -Ref: Bitbucket Caveats6405 -Ref: Bitbucket Hosts7302 -Node: Supported Semi-Forges7367 -Ref: Gitweb https//git-scmcom/docs/gitweb7538 -Ref: Gitweb Caveats7617 -Ref: Cgit https//gitzx2c4com/cgit/about7857 -Ref: Cgit Caveats7934 -Ref: Cgit Hosts8283 -Ref: Stgit https//codemadnessorg/git/stagit/file/READMEhtml8410 -Ref: Stgit Caveats8527 -Ref: Stgit Hosts8813 -Ref: Srht https//metasrht8873 -Ref: Srht Caveats8922 -Ref: Srht Hosts9205 -Node: Getting Started9256 -Node: Loading Forge10254 -Node: Token Creation10649 -Node: Initial Pull11928 -Node: Repository Detection13323 -Node: Caveats18690 -Node: Usage19616 -Node: Pulling21088 -Node: Branching23771 -Node: Working with Topics30166 -Node: Visiting Topics30539 -Node: Listing Topics and Notifications32616 -Node: Creating Topics37930 -Node: Editing Topics and Posts38419 -Node: Miscellaneous42946 -Node: FAQ45569 -Node: Is it possible to create a single pull-request without pulling everything?46037 -Node: error in process filter HTTP Error 502 "Bad gateway"46503 -Node: Keystroke Index48874 -Node: Function and Command Index54564 -Node: Variable Index62522 - -End Tag Table - - -Local Variables: -coding: utf-8 -End: diff --git a/elpa/forge-20240523.1103/dir b/elpa/forge-20241218.2036/dir similarity index 100% rename from elpa/forge-20240523.1103/dir rename to elpa/forge-20241218.2036/dir diff --git a/elpa/forge-20240523.1103/forge-autoloads.el b/elpa/forge-20241218.2036/forge-autoloads.el similarity index 70% rename from elpa/forge-20240523.1103/forge-autoloads.el rename to elpa/forge-20241218.2036/forge-autoloads.el index cce3a830..af1141c4 100644 --- a/elpa/forge-20240523.1103/forge-autoloads.el +++ b/elpa/forge-20241218.2036/forge-autoloads.el @@ -29,16 +29,12 @@ is loaded, then `magit-mode-map' ends up being modified anyway.") ;;; Generated autoloads from forge-commands.el - (autoload 'forge-dispatch "forge-commands" nil t) - (autoload 'forge-configure "forge-commands" nil t) -(autoload 'forge-pull "forge-commands" "\ -Pull forge topics for the current repository if it is already tracked. -If the current repository is still untracked locally, or the current -repository cannot be determined, instead invoke `forge-add-repository'." t) -(autoload 'forge-pull-notifications "forge-commands" "\ -Fetch notifications for all repositories from the current forge." t) - (autoload 'forge-pull-topic "forge-commands" nil t) - (autoload 'forge-pull-this-topic "forge-commands" nil t) +(autoload 'forge-dispatch "forge-commands" nil t) +(autoload 'forge-configure "forge-commands" nil t) +(autoload 'forge-pull "forge-commands" nil t) +(autoload 'forge-pull-notifications "forge-commands" nil t) +(autoload 'forge-pull-topic "forge-commands" nil t) +(autoload 'forge-pull-this-topic "forge-commands" nil t) (autoload 'forge-browse-issues "forge-commands" "\ Visit the current repository's issues using a browser." t) (autoload 'forge-browse-pullreqs "forge-commands" "\ @@ -77,7 +73,7 @@ Read a REMOTE and visit it using a browser. Read a REPOSITORY and visit it using a browser. (fn REPOSITORY)" t) - (autoload 'forge-browse-this-topic "forge-commands" nil t) +(autoload 'forge-browse-this-topic "forge-commands" nil t) (autoload 'forge-browse-this-repository "forge-commands" "\ Visit the repository at point using a browser." t) (autoload 'forge-copy-url-at-point-as-kill "forge-commands" "\ @@ -86,24 +82,30 @@ Copy the url of the thing at point." t) Visit the thing at point using a browser." t) (autoload 'forge-visit-topic "forge-commands" "\ Read a TOPIC and visit it. -By default only offer open topics for completion; -with a prefix argument also closed topics. +By default only offer active topics for completion. With a prefix +argument offer all topics. While completion is in progress, \\\\[forge-read-topic-lift-limit] lifts +the limitation to active topics. (fn TOPIC)" t) (autoload 'forge-visit-issue "forge-commands" "\ Read an ISSUE and visit it. -By default only offer open topics for completion; -with a prefix argument also closed topics. +By default only offer active issues for completion. With a prefix +argument offer all topics. While completion is in progress, \\\\[forge-read-topic-lift-limit] lifts +the limitation to active issues. (fn ISSUE)" t) (autoload 'forge-visit-pullreq "forge-commands" "\ Read a PULL-REQUEST and visit it. -By default only offer open topics for completion; -with a prefix argument also closed topics. +By default only offer active pull-requests for completion. With a +prefix argument offer all topics. While completion is in progress, +\\\\[forge-read-topic-lift-limit] lifts the limitation to active pull-requests. (fn PULL-REQUEST)" t) (autoload 'forge-visit-this-topic "forge-commands" "\ -Visit the topic at point." t) +Visit the topic at point. +With prefix argument MENU, also show the topic menu. + +(fn &optional MENU)" t) (autoload 'forge-visit-this-repository "forge-commands" "\ Visit the repository at point." t) (autoload 'forge-branch-pullreq "forge-commands" "\ @@ -116,6 +118,7 @@ Create, configure and checkout a new branch from a pull-request. Please see the manual for more information. (fn PULLREQ)" t) +(autoload 'forge-checkout-this-pullreq "forge-commands" nil t) (autoload 'forge-checkout-worktree "forge-commands" "\ Create, configure and checkout a new worktree from a pull-request. This is like `forge-checkout-pullreq', except that it also @@ -129,25 +132,13 @@ If the fork already exists, then that isn't an error; the remote is added anyway. Currently this only supports Github and Gitlab. (fn FORK REMOTE)" t) -(autoload 'forge-merge "forge-commands" "\ -Merge the current pull-request using METHOD using the forge's API. - -If there is no current pull-request or with a prefix argument, -then read pull-request PULLREQ to visit instead. - -Use of this command is discouraged. Unless the remote repository -is configured to disallow that, you should instead merge locally -and then push the target branch. Forges detect that you have -done that and respond by automatically marking the pull-request -as merged. - -(fn PULLREQ METHOD)" t) +(autoload 'forge-merge "forge-commands" nil t) (autoload 'forge-rename-default-branch "forge-commands" "\ Rename the default branch to NEWNAME. -Change the name on the upstream remote and locally, and update -the upstream remotes of local branches accordingly." t) - (autoload 'forge-add-pullreq-refspec "forge-commands" nil t) - (autoload 'forge-add-repository "forge-commands" nil t) +Change the name on the upstream remotely and locally, and update the +upstream remotes of local branches accordingly." t) +(autoload 'forge-add-pullreq-refspec "forge-commands" nil t) +(autoload 'forge-add-repository "forge-commands" nil t) (autoload 'forge-add-user-repositories "forge-commands" "\ Add all of USER's repositories from HOST to the database. This may take a while. Only Github is supported at the moment. @@ -215,8 +206,7 @@ heavy development." t) ;;; Generated autoloads from forge-notify.el -(autoload 'forge-list-notifications "forge-notify" "\ -List notifications." t) +(autoload 'forge-list-notifications "forge-notify" nil t) (register-definition-prefixes "forge-notify" '("forge-")) @@ -237,9 +227,9 @@ List notifications." t) ;;; Generated autoloads from forge-repos.el - (autoload 'forge-repositories-menu "forge-repos" nil t) - (autoload 'forge-list-repositories "forge-repos" nil t) - (autoload 'forge-list-owned-repositories "forge-repos" nil t) +(autoload 'forge-repositories-menu "forge-repos" nil t) +(autoload 'forge-list-repositories "forge-repos" nil t) +(autoload 'forge-list-owned-repositories "forge-repos" nil t) (register-definition-prefixes "forge-repos" '("forge-")) @@ -255,36 +245,26 @@ List notifications." t) ;;; Generated autoloads from forge-tablist.el -(register-definition-prefixes "forge-tablist" '("forge--")) +(register-definition-prefixes "forge-tablist" '("forge--tab")) ;;; Generated autoloads from forge-topic.el - (autoload 'forge-topic-menu "forge-topic" nil t) - (autoload 'forge-topic-state-menu "forge-topic" nil t) - (autoload 'forge-topic-status-menu "forge-topic" nil t) +(autoload 'forge-topic-menu "forge-topic" nil t) +(autoload 'forge-topic-state-menu "forge-topic" nil t) +(autoload 'forge-topic-status-menu "forge-topic" nil t) (register-definition-prefixes "forge-topic" '("forge-")) ;;; Generated autoloads from forge-topics.el - (autoload 'forge-topics-menu "forge-topics" nil t) - (autoload 'forge-list-topics "forge-topics" nil t) - (autoload 'forge-list-labeled-topics "forge-topics" nil t) - (autoload 'forge-list-assigned-topics "forge-topics" nil t) - (autoload 'forge-list-authored-topics "forge-topics" nil t) - (autoload 'forge-list-owned-topics "forge-topics" nil t) - (autoload 'forge-list-issues "forge-topics" nil t) - (autoload 'forge-list-labeled-issues "forge-topics" nil t) - (autoload 'forge-list-assigned-issues "forge-topics" nil t) - (autoload 'forge-list-authored-issues "forge-topics" nil t) - (autoload 'forge-list-owned-issues "forge-topics" nil t) - (autoload 'forge-list-pullreqs "forge-topics" nil t) - (autoload 'forge-list-labeled-pullreqs "forge-topics" nil t) - (autoload 'forge-list-assigned-pullreqs "forge-topics" nil t) - (autoload 'forge-list-requested-reviews "forge-topics" nil t) - (autoload 'forge-list-authored-pullreqs "forge-topics" nil t) - (autoload 'forge-list-owned-pullreqs "forge-topics" nil t) +(autoload 'forge-topics-menu "forge-topics" nil t) +(autoload 'forge-list-topics "forge-topics" nil t) +(autoload 'forge-list-issues "forge-topics" nil t) +(autoload 'forge-list-pullreqs "forge-topics" nil t) +(autoload 'forge-list-global-topics "forge-topics" nil t) +(autoload 'forge-list-global-issues "forge-topics" nil t) +(autoload 'forge-list-global-pullreqs "forge-topics" nil t) (register-definition-prefixes "forge-topics" '("forge-")) ;;; End of scraped data diff --git a/elpa/forge-20240523.1103/forge-bitbucket.el b/elpa/forge-20241218.2036/forge-bitbucket.el similarity index 100% rename from elpa/forge-20240523.1103/forge-bitbucket.el rename to elpa/forge-20241218.2036/forge-bitbucket.el diff --git a/elpa/forge-20240523.1103/forge-commands.el b/elpa/forge-20241218.2036/forge-commands.el similarity index 77% rename from elpa/forge-20240523.1103/forge-commands.el rename to elpa/forge-20241218.2036/forge-commands.el index 1abd89bd..ecfec1db 100644 --- a/elpa/forge-20240523.1103/forge-commands.el +++ b/elpa/forge-20241218.2036/forge-commands.el @@ -52,76 +52,101 @@ Takes the pull-request as only argument and must return a directory." ;;; Dispatch -;;;###autoload (autoload 'forge-dispatch "forge-commands" nil t) +;;;###autoload(autoload 'forge-dispatch "forge-commands" nil t) (transient-define-prefix forge-dispatch () "Dispatch a forge command." - [:if forge--get-repository:tracked? - ["Create" - ("c i" "issue" forge-create-issue) - ("c p" "pull-request" forge-create-pullreq) - ("c u" "pull-request from issue" - forge-create-pullreq-from-issue - :if forge--get-github-repository) - ("c f" "fork or remote" forge-fork)]] - [:if forge--get-repository:tracked? - ["List" - ("t" "topics... " forge-topics-menu :transient replace) - ("n" "notifications... " forge-notifications-menu :transient replace) - ("r" "repositories... " forge-repositories-menu :transient replace)] + :transient-non-suffix #'transient--do-call + :refresh-suffixes t + :environment #'forge--menu-environment + :column-widths forge--topic-menus-column-widths + [forge--topic-menus-group ["Fetch" - ("f f" "all topics " forge-pull) - ("f t" "one topic " forge-pull-topic) - ("f n" "notifications " forge-pull-notifications)] - ["Do" + ("f f" "all topics" forge-pull) + ("f t" "one topic" forge-pull-topic) + ("f n" "notifications" forge-pull-notifications)] + ["Create" :if forge--get-repository:tracked? - ("C" "configure" forge-configure) - ("M" "merge w/api" forge-merge :level 7)]] - [:if forge--get-repository:tracked? + ("c i" "issue" forge-create-issue) + ("c p" "pull-request" forge-create-pullreq) + ("c u" "pr from issue" forge-create-pullreq-from-issue) + ("c f" "fork or remote" forge-fork)] + [:description (lambda () + (cond + ((forge--get-repository:tracked?) "Actions") + ((or (magit-gitdir) (forge-repository-at-point)) + "Forge does not yet track this repository") + ("Not inside a Git repository"))) + ("/a" forge-add-repository + :description (lambda () (let ((repo (forge-get-repository :stub?))) + (if (or (not repo) + (eq (oref repo condition) :tracked)) + "track some repo" + "track this repository")))) + ("/M" "merge with api" forge-merge + :if forge--get-repository:tracked? :level 7)]] + [forge--lists-group ["Visit" - ("v t" "topic" forge-visit-topic) - ("v i" "issue" forge-visit-issue) - ("v p" "pull-request" forge-visit-pullreq)] - ["Browse" - ("b t" "topic" forge-browse-topic) - ("b i" "issue" forge-browse-issue) - ("b p" "pull-request" forge-browse-pullreq)] + :inapt-if-not forge--get-repository:tracked? + ("v t" "topic" forge-visit-topic) + ("v i" "issue" forge-visit-issue) + ("v p" "pull-request" forge-visit-pullreq)] ["Browse" - ("b r" "remote" forge-browse-remote) - ("b I" "issues" forge-browse-issues) - ("b P" "pull-requests" forge-browse-pullreqs)]] - [[:description (lambda () - (if (magit-gitdir) - "Forge doesn't know about this Git repository yet" - "Not inside a Git repository")) - :if-not forge--get-repository:tracked? - ("a" "add repository to database" forge-add-repository) - ("f" "fetch notifications" forge-pull-notifications) - ("l" "list notifications" forge-list-notifications)]]) - -;;;###autoload (autoload 'forge-configure "forge-commands" nil t) + ("b t" "topic" forge-browse-topic + :inapt-if-not forge--get-repository:tracked?) + ("b i" "issue" forge-browse-issue + :inapt-if-not forge--get-repository:tracked?) + ("b p" "pull-request" forge-browse-pullreq + :inapt-if-not forge--get-repository:tracked?) + ("b r" "remote" forge-browse-remote) + ("b I" "issues" forge-browse-issues) + ("b P" "pull-requests" forge-browse-pullreqs) + ""] + ["Display" + ("-S" forge-toggle-display-in-status-buffer + :inapt-if-not forge--buffer-with-topics-sections-p) + ("-H" forge-toggle-topic-legend)]] + [forge--topic-legend-group]) + +(transient-augment-suffix forge-dispatch + :transient #'transient--do-replace + :inapt-if (lambda () (eq (oref transient--prefix command) 'forge-dispatch)) + :inapt-face 'forge-suffix-active) + +;;;###autoload(autoload 'forge-configure "forge-commands" nil t) (transient-define-prefix forge-configure () "Configure current repository and global settings." - [["Configure" - :if forge--get-repository:tracked? - ("a " "add another repository to database" forge-add-some-repository) + :transient-non-suffix #'transient--do-call + :refresh-suffixes t + :environment #'forge--menu-environment + :column-widths forge--topic-menus-column-widths + [forge--topic-menus-group + ["Configure" ("R " forge-add-pullreq-refspec) ("s r" forge-forge.remote) - ("s l" forge-forge.graphqlItemLimit) - ("s s" forge-toggle-display-in-status-buffer) - ("s c" forge-toggle-closed-visibility)]]) + ("s l" forge-forge.graphqlItemLimit)]]) + +(transient-augment-suffix forge-configure + :transient #'transient--do-replace + :inapt-if (lambda () (eq (oref transient--prefix command) 'forge-configure)) + :inapt-face 'forge-suffix-active) ;;; Pull -;;;###autoload -(defun forge-pull () +;;;###autoload(autoload 'forge-pull "forge-commands" nil t) +(transient-define-suffix forge-pull () "Pull forge topics for the current repository if it is already tracked. If the current repository is still untracked locally, or the current repository cannot be determined, instead invoke `forge-add-repository'." + :description (lambda () + (if (forge-get-repository :tracked?) + "forge topics" + "new forge repository")) + (declare (interactive-only nil)) (interactive) (if-let ((repo (forge-get-repository :tracked?))) (forge--pull repo) (transient-setup 'forge-add-repository nil nil - :scope (forge-get-repository :stub)))) + :scope (forge-add-repository--scope)))) (defun forge-read-date (prompt) (require (quote org) nil) @@ -145,20 +170,22 @@ repository cannot be determined, instead invoke `forge-add-repository'." (magit-git-fetch (oref repo remote) (magit-fetch-arguments))) (defun forge--maybe-git-fetch (repo &optional buffer) - (if (and (buffer-live-p buffer) - (with-current-buffer buffer - (and (derived-mode-p 'magit-mode) - (forge-repository-equal (forge-get-repository :stub?) repo)))) + (if (buffer-live-p buffer) (with-current-buffer buffer - (magit-git-fetch (oref repo remote) (magit-fetch-arguments))) + (if (and (derived-mode-p 'magit-mode) + (forge-repository-equal (forge-get-repository :stub?) repo) + (magit-toplevel)) + (magit-git-fetch (oref repo remote) (magit-fetch-arguments)) + (magit-refresh-buffer))) (when-let ((worktree (forge-get-worktree repo))) (let ((default-directory worktree) (magit-inhibit-refresh t)) (magit-git-fetch (oref repo remote) (magit-fetch-arguments)))))) -;;;###autoload -(defun forge-pull-notifications () +;;;###autoload(autoload 'forge-pull-notifications "forge-commands" nil t) +(transient-define-suffix forge-pull-notifications () "Fetch notifications for all repositories from the current forge." + :description "forge notifications" (interactive) (if-let ((repo (forge-get-repository :stub?))) (let ((class (eieio-object-class repo))) @@ -168,17 +195,18 @@ repository cannot be determined, instead invoke `forge-add-repository'." (oref repo forge)))) (forge--pull-notifications 'forge-github-repository "github.com"))) -;;;###autoload (autoload 'forge-pull-topic "forge-commands" nil t) +;;;###autoload(autoload 'forge-pull-topic "forge-commands" nil t) (transient-define-suffix forge-pull-topic (number) "Read a topic TYPE and NUMBER pull data about it from its forge." - :inapt-if-not #'forge--get-github-repository + :inapt-if-not (lambda () (and (forge--get-repository:tracked?) + (forge--get-github-repository))) (interactive (list (read-number "Pull topic: " (and-let* ((topic (forge-current-topic))) (oref topic number))))) (forge--pull-topic (forge-get-repository :tracked) number)) -;;;###autoload (autoload 'forge-pull-this-topic "forge-commands" nil t) +;;;###autoload(autoload 'forge-pull-this-topic "forge-commands" nil t) (transient-define-suffix forge-pull-this-topic () "Pull data about the topic at point from its forge." :inapt-if-not #'forge--get-github-repository @@ -265,7 +293,7 @@ argument also offer closed pull-requests." (interactive (list (forge-read-repository "Browse repository"))) (browse-url (forge-get-url repository))) -;;;###autoload (autoload 'forge-browse-this-topic "forge-commands" nil t) +;;;###autoload(autoload 'forge-browse-this-topic "forge-commands" nil t) (transient-define-suffix forge-browse-this-topic () "Visit the topic at point using a browser." :description "browse" @@ -313,7 +341,7 @@ argument also offer closed pull-requests." (forge-current-topic) (and magit-buffer-revision (forge-get-url :commit magit-buffer-revision)) - (forge-current-repository))) + (forge-get-repository :stub?))) ;;;; Urls @@ -334,7 +362,7 @@ argument also offer closed pull-requests." (unless (magit-list-containing-branches commit "-r" (concat (oref repo remote) "/*")) (if-let* ((branch (car (magit-list-containing-branches commit "-r"))) - (remote (cdr (magit-split-branch-name branch)))) + (remote (car (magit-split-branch-name branch)))) (setq repo (forge-get-repository :stub remote)) (message "%s does not appear to be available on any remote. %s" commit "You might have to push it first."))) @@ -344,9 +372,7 @@ argument also offer closed pull-requests." (cl-defmethod forge-get-url ((_(eql :branch)) branch) (let (remote) (if (magit-remote-branch-p branch) - (let ((cons (magit-split-branch-name branch))) - (setq remote (car cons)) - (setq branch (cdr cons))) + (pcase-setq `(,remote . ,branch) (magit-split-branch-name branch)) (unless (setq remote (or (magit-get-push-remote branch) (magit-get-upstream-remote branch))) (user-error "Cannot determine remote for %s" branch))) @@ -372,32 +398,46 @@ argument also offer closed pull-requests." ;;;###autoload (defun forge-visit-topic (topic) "Read a TOPIC and visit it. -By default only offer open topics for completion; -with a prefix argument also closed topics." +By default only offer active topics for completion. With a prefix +argument offer all topics. While completion is in progress, \ +\\\\[forge-read-topic-lift-limit] lifts +the limitation to active topics." (interactive (list (forge-read-topic "View topic"))) (forge-topic-setup-buffer (forge-get-topic topic))) ;;;###autoload (defun forge-visit-issue (issue) "Read an ISSUE and visit it. -By default only offer open topics for completion; -with a prefix argument also closed topics." +By default only offer active issues for completion. With a prefix +argument offer all topics. While completion is in progress, \ +\\\\[forge-read-topic-lift-limit] lifts +the limitation to active issues." (interactive (list (forge-read-issue "View issue"))) (forge-topic-setup-buffer (forge-get-issue issue))) ;;;###autoload (defun forge-visit-pullreq (pull-request) "Read a PULL-REQUEST and visit it. -By default only offer open topics for completion; -with a prefix argument also closed topics." +By default only offer active pull-requests for completion. With a +prefix argument offer all topics. While completion is in progress, +\\\\[forge-read-topic-lift-limit] \ +lifts the limitation to active pull-requests." (interactive (list (forge-read-pullreq "View pull-request"))) (forge-topic-setup-buffer (forge-get-pullreq pull-request))) ;;;###autoload -(defun forge-visit-this-topic () - "Visit the topic at point." - (interactive) - (forge-topic-setup-buffer (forge-topic-at-point))) +(defun forge-visit-this-topic (&optional menu) + "Visit the topic at point. +With prefix argument MENU, also show the topic menu." + (interactive (list current-prefix-arg)) + (forge-topic-setup-buffer (forge-topic-at-point)) + (cond + ((eq transient-current-command 'forge-topic-menu) + (setq forge--quit-keep-topic-menu t)) + ((or menu + (memq transient-current-command + '(forge-topics-menu forge-notifications-menu))) + (transient-setup 'forge-topic-menu)))) ;;;###autoload (defun forge-visit-this-repository () @@ -406,14 +446,17 @@ with a prefix argument also closed topics." (let* ((repo (forge-repository-at-point)) (worktree (forge-get-worktree repo))) (cond - ((eq transient-current-command 'forge-repositories-menu) - (if-let ((buffer (forge-topic-get-buffer repo))) - (switch-to-buffer buffer) - (forge-list-topics repo)) - (transient-setup 'forge-topics-menu)) + ((and (eq transient-current-command 'forge-repositories-menu) + (forge-get-repository repo nil :tracked?)) + (if-let ((buffer (get-buffer (forge-topics-buffer-name repo)))) + (progn (switch-to-buffer buffer) + (transient-setup 'forge-topics-menu)) + (forge-list-topics repo))) (worktree (magit-status-setup-buffer worktree)) - ((forge-list-topics repo))))) + ((forge-get-repository repo nil :tracked?) + (forge-list-topics repo)) + ((user-error "Not tracked and location of clone is unknown"))))) ;;; Create @@ -450,9 +493,10 @@ with a prefix argument also closed topics." "Convert an existing ISSUE into a pull-request." :description "convert to pull-request" :if (lambda () - (let ((issue (forge-current-issue))) - (and issue (eq (oref issue state) 'open) - issue))) + (and (forge--get-github-repository) + (let ((issue (forge-current-issue))) + (and issue (eq (oref issue state) 'open) + issue)))) (interactive (cons (forge-read-open-issue "Convert issue") (forge-create-pullreq--read-args))) (setq issue (forge-get-issue issue)) @@ -558,17 +602,21 @@ point is currently on." (propertize "none" 'face 'magit-dimmed))) "note")) (interactive) - (let* ((topic (forge-current-topic t)) - (buf (forge--prepare-post-buffer - (forge--format topic "%i;note") - (forge--format topic "New note on #%i of %p")))) - (with-current-buffer buf - (setq forge--buffer-post-object topic) - (setq forge--submit-post-function #'forge--save-note) - (erase-buffer) - (when-let ((note (oref topic note))) - (save-excursion (insert note ?\n)))) - (forge--display-post-buffer buf))) + (if-let* ((topic (forge-current-topic t)) + (repo (forge-get-repository topic)) + (default-directory (forge-get-worktree repo)) + (buf (forge--prepare-post-buffer + (forge--format topic "%i;note") + (forge--format topic "New note on #%i of %p")))) + (progn + (with-current-buffer buf + (setq forge--buffer-post-object topic) + (setq forge--submit-post-function #'forge--save-note) + (erase-buffer) + (when-let ((note (oref topic note))) + (save-excursion (insert note ?\n)))) + (forge--display-post-buffer buf)) + (message "Cannot determine topic or worktree"))) ;;; Delete @@ -725,6 +773,16 @@ Please see the manual for more information." (magit--checkout (forge--branch-pullreq (forge-get-pullreq pullreq))) (forge-refresh-buffer)) +;;;###autoload(autoload 'forge-checkout-this-pullreq "forge-commands" nil t) +(transient-define-suffix forge-checkout-this-pullreq () + "Checkout the current pull-request. +If the branch for that pull-request does not exist yet, then create and +configure it first." + :description "checkout" + :inapt-if-not #'forge-current-pullreq + (interactive) + (forge-checkout-pullreq (forge-current-topic t))) + ;;;###autoload (defun forge-checkout-worktree (path pullreq) "Create, configure and checkout a new worktree from a pull-request. @@ -827,8 +885,8 @@ is added anyway. Currently this only supports Github and Gitlab." (oref repo name)) (list "--fetch")))) -;;;###autoload -(defun forge-merge (pullreq method) +;;;###autoload(autoload 'forge-merge "forge-commands" nil t) +(transient-define-suffix forge-merge (pullreq method) "Merge the current pull-request using METHOD using the forge's API. If there is no current pull-request or with a prefix argument, @@ -861,8 +919,8 @@ as merged." ;;;###autoload (defun forge-rename-default-branch () "Rename the default branch to NEWNAME. -Change the name on the upstream remote and locally, and update -the upstream remotes of local branches accordingly." +Change the name on the upstream remotely and locally, and update the +upstream remotes of local branches accordingly." (interactive) (let* ((repo (forge-get-repository :tracked?)) (_ (unless (forge-github-repository-p repo) @@ -895,12 +953,21 @@ the upstream remotes of local branches accordingly." ;;; Configuration -(transient-define-infix forge-forge.remote () +(transient-define-suffix forge-forge.remote () "Change the local value of the `forge.remote' Git variable." :class 'magit--git-variable:choices :variable "forge.remote" :choices #'magit-list-remotes - :default (lambda (_) (forge--get-remote t))) + :default (lambda (_) (forge--get-remote t t)) + (interactive) + (let ((obj (transient-suffix-object))) + (transient-infix-set obj (transient-infix-read obj))) + (if (and transient--prefix + (eq (oref transient--prefix command) 'forge-add-repository)) + ;; Improvements to Transient will make this hack unnecessary. + (let ((scope (forge-add-repository--scope))) + (oset (transient-prefix-object) scope scope)) + (transient--show))) (transient-define-infix forge-forge.graphqlItemLimit () "Change the maximum number of GraphQL entities to pull at once." @@ -912,44 +979,29 @@ the upstream remotes of local branches accordingly." (transient-define-suffix forge-toggle-display-in-status-buffer () "Toggle whether to display topics in the current status buffer." - :inapt-if-not (lambda () - (and (eq major-mode 'magit-status-mode) - (forge-get-repository :known?))) + :if-mode 'magit-status-mode + :inapt-if-not #'forge--buffer-with-topics-sections-p :description (lambda () - (if forge-display-in-status-buffer - "hide all topics" + (if (and forge--buffer-topics-spec + (oref forge--buffer-topics-spec type)) + "hide topics" "display topics")) - :transient t (interactive) - (setq forge-display-in-status-buffer (not forge-display-in-status-buffer)) + (oset forge--buffer-topics-spec type + (if (oref forge--buffer-topics-spec type) nil 'topic)) (forge-refresh-buffer)) -(transient-define-suffix forge-toggle-closed-visibility () - "Toggle whether to display recently closed topics. -This only affect the current status buffer." - :inapt-if-not (lambda () - (and forge-display-in-status-buffer - (eq major-mode 'magit-status-mode) - (forge-get-repository :known?))) - :description (lambda () - (if (or (atom forge-topic-list-limit) - (> (cdr forge-topic-list-limit) 0)) - "hide closed topics" - "display recently closed topics")) - :transient t - (interactive) - (make-local-variable 'forge-topic-list-limit) - (if (atom forge-topic-list-limit) - (setq forge-topic-list-limit (cons forge-topic-list-limit 5)) - (setcdr forge-topic-list-limit (* -1 (cdr forge-topic-list-limit)))) - (forge-refresh-buffer)) +(defun forge--buffer-with-topics-sections-p () + (and forge--buffer-topics-spec + (not (eq major-mode 'forge-topics-mode)) + (forge-get-repository :tracked?))) -;;;###autoload (autoload 'forge-add-pullreq-refspec "forge-commands" nil t) +;;;###autoload(autoload 'forge-add-pullreq-refspec "forge-commands" nil t) (transient-define-suffix forge-add-pullreq-refspec () "Configure Git to fetch all pull-requests. This is done by adding \"+refs/pull/*/head:refs/pullreqs/*\" to the value of `remote.REMOTE.fetch', where REMOTE is the -upstream remote. Also fetch from REMOTE." +upstream remote." :if-not 'forge--pullreq-refspec :description "add pull-request refspec" (interactive) @@ -973,37 +1025,77 @@ upstream remote. Also fetch from REMOTE." ;;; Add repositories -;;;###autoload (autoload 'forge-add-repository "forge-commands" nil t) +;;;###autoload(autoload 'forge-add-repository "forge-commands" nil t) (transient-define-prefix forge-add-repository (&optional repo limit) "Add a repository to the database." + :refresh-suffixes t [:class transient-subgroups - [:if (lambda () (forge-get-repository (transient-scope) nil :tracked?)) - (:info + + ;; Already tracked. + [:if (lambda () (forge--scope :tracked)) + (:info* (lambda () (format (propertize "%s is already being tracked" 'face 'transient-heading) - (propertize (forge-get-url (transient-scope)) 'face 'bold))) + (propertize (forge--scope 'url) 'face 'bold))) :format "%d")] - [:if-not (lambda () (forge-get-repository (transient-scope) nil :tracked?)) + + ;; Nothing to tracked. + [:if-not (lambda () (forge--scope 'topdir)) + (:info* + (lambda () + (format + (propertize "%s is not inside a Git repository" 'face 'transient-heading) + (propertize default-directory 'face 'bold))) + :format "%d")] + + ;; Cannot track. + [:if (lambda () (and (not (forge--scope 'repo)) (forge--scope 'topdir))) + :description + (lambda () + (concat + (format (propertize "Cannot determine forge host for %s\n" + 'face 'transient-heading) + (propertize (forge--scope 'topdir) 'face 'bold)) + (if-let* ((remote (forge--get-remote)) + (url (magit-git-string "remote" "get-url" remote))) + (format (propertize "because %s is not on a host known to Forge." + 'face 'transient-heading) + (propertize url 'face 'bold)) + (propertize "because no suitable remote was detected." + 'face 'transient-heading)))) + ("r" forge-forge.remote :format " %k Try another %d %v" :face 'bold) + ("h" "Learn how to configure another Github host" + (lambda () (interactive) (info "(forge)Setup for Another Github Instance"))) + ("l" "Learn how to configure another Gitlab host" + (lambda () (interactive) (info "(forge)Setup for Another Gitlab Instance"))) + ("p" "Learn how to configure partially supported host" + (lambda () (interactive) (info "(forge)Setup a Partially Supported Host")))] + + ;; Track it! + [:if (lambda () (forge--scope :untracked)) :description (lambda () (format (propertize "Adding %s to database," 'face 'transient-heading) - (propertize (forge-get-url (transient-scope)) 'face 'bold))) + (propertize (forge--scope 'url) 'face 'bold))) + ("r" forge-forge.remote :format " %k from %d %v," :face 'bold) ("a" "pulling all topics" (lambda (repo) - (interactive (list (transient-scope))) + (interactive (list (forge--scope 'repo))) (forge-add-repository repo))) ("s" "pulling only topics since " (lambda (repo date) (interactive - (list (transient-scope) + (list (forge--scope 'repo) (forge-read-date "Limit pulling to topics updated since: "))) (forge-add-repository repo date))) ("i" "to allow pulling of individual topics" (lambda (repo) - (interactive (list (transient-scope))) + (interactive (list (forge--scope 'repo))) (forge-add-repository repo :selective)))] + + ;; Pivot. [("o" "Add another repository" forge-add-some-repository) (7 "U" "Add all source repositories belonging to a user" forge-add-user-repositories) @@ -1014,16 +1106,17 @@ upstream remote. Also fetch from REMOTE." (cond ((not repo) (transient-setup 'forge-add-repository nil nil - :scope (forge-get-repository :stub?))) + :scope (forge-add-repository--scope))) ((stringp repo) (transient-setup 'forge-add-repository nil nil - :scope (forge-get-repository repo nil :stub?))) + :scope (forge-add-repository--scope repo))) (t (when-let* (((not (eq limit :selective))) ((magit-git-config-p "forge.autoPull" t)) (remote (oref repo remote)) (refspec (oref repo pullreq-refspec)) + (default-directory (forge-get-worktree repo)) ((and (not (member refspec (magit-get-all "remote" remote "fetch"))) (or (eq forge-add-pullreq-refspec t) (and (eq forge-add-pullreq-refspec 'ask) @@ -1035,7 +1128,30 @@ upstream remote. Also fetch from REMOTE." (when (eq limit :selective) (oset repo selective-p t) (setq limit nil)) - (forge--pull repo nil limit)))) + (forge--pull repo + (and (not (forge-get-worktree repo)) #'ignore) + limit)))) + +(defun forge-add-repository--scope (&optional directory) + (let* ((repo (if directory + (forge-get-repository directory nil :stub?) + (forge-get-repository :stub?))) + (wtree (and repo (forge-get-worktree repo))) + (condition (and repo (oref repo condition))) + (val + `((repo . ,repo) + (wtree . ,wtree) + (condition . ,condition) + (:tracked . ,(eq condition :tracked)) + (:untracked . ,(memq condition '(:known :stub))) + (topdir . ,(or wtree (magit-toplevel))) + (url . ,(and repo (forge-get-url repo)))))) + val)) + +(defun forge--scope (&optional key) + ;; `transient-scope' itself probably offer optional KEY. + (let ((scope (transient-scope))) + (if key (alist-get key scope) scope))) (defun forge-add-some-repository (url) "Read a repository and add it to the database." diff --git a/elpa/forge-20240523.1103/forge-core.el b/elpa/forge-20241218.2036/forge-core.el similarity index 87% rename from elpa/forge-20240523.1103/forge-core.el rename to elpa/forge-20241218.2036/forge-core.el index 47b5b879..3c88494a 100644 --- a/elpa/forge-20240523.1103/forge-core.el +++ b/elpa/forge-20241218.2036/forge-core.el @@ -95,7 +95,7 @@ Each entry has the form (GITHOST APIHOST WEBHOST CLASS). - WEBHOST is the host used to access repositories on this forge using a browser. The IDs used to identify repositories from the forge in the local database also derives from this value. - + - CLASS is the class to be used for repositories from the forge. Complications: @@ -122,8 +122,8 @@ Complications: (defmacro forge--childp (obj type) "Somewhat similar to `cl-typep' but only for (possibly unknown) classes. -TYPE is evaluated at macro-expansion time but unlike with -`cl-typep' the respective class does not have to be defined +TYPE is evaluated at macro-expansion time but, unlike with +`cl-typep', the respective class does not have to be defined at that time." (let ((fn (intern (concat (symbol-name (eval type)) "--eieio-childp")))) `(and (fboundp ',fn) (,fn ,obj)))) @@ -144,7 +144,7 @@ repository. (Forge repositories are also Git repositories, but not the other way around.) A `:known' repository has an entry in the local database. All other -repositories are unknown. `:known' repositories are devided into two +repositories are unknown. `:known' repositories are divided into two subgroups: `:tracked' and \"untracked\" repositories. A `:tracked' repository was previously explicitly added to the database @@ -231,7 +231,7 @@ Entries have the form (GITHOST APIHOST WEBHOST CLASS). - If HOST matches a GITHOST, return the corresponding entry. - Else, if HOST is an ssh alias and the canonical hostname matches a GITHOST, return the corresponding entry. -- Finally, if HOST matches a WEBHOST, return the corresponding entry +- Finally, if HOST matches a WEBHOST, return the corresponding entry. If no entry matches, return nil, or signal an error if optional DEMAND is non-nil." @@ -246,25 +246,39 @@ is non-nil." (and demand (error "No entry for \"%s\" in `forge-alist'" host)))) -(defun forge--split-forge-url (url) +(defun forge--split-forge-url (url &optional relax) (save-match-data - (and (string-match - (concat "\\`" - "\\(?:git://\\|" - "[^/@]+@\\|" - "\\(?:ssh\\|ssh\\+git\\|git\\+ssh\\)://\\(?:[^/@]+@\\)?\\|" - "https?://\\(?:[^/@]+@\\)?\\)?" - (regexp-opt (mapcar #'car forge-alist) t) - "\\(?::[0-9]+\\)?" - "\\(?:/\\|:/?\\)" - "~?\\(.+?\\)/" - "\\([^/]+?\\)" - "\\(?:\\.git\\|/\\)?" - "\\'") - url) - (list (caddr (forge--get-forge-host (match-string 1 url) t)) - (match-string 2 url) - (match-string 3 url))))) + (cond + ((string-match + (concat "\\`" + "\\(?:git://\\|" + "[^/@]+@\\|" + "\\(?:ssh\\|ssh\\+git\\|git\\+ssh\\)://\\(?:[^/@]+@\\)?\\|" + "https?://\\(?:[^/@]+@\\)?\\)?" + (if relax + "\\(?1:[^:/]+\\)" + (regexp-opt (mapcar #'car forge-alist) t)) + "\\(?::[0-9]+\\)?" + "\\(?:/\\|:/?\\)" + "~?\\(?2:.+?\\)/" + "\\(?3:[^/]+?\\)" + "\\(?:\\.git\\|/\\)?" + "\\'") + url) + (and-let* ((elt (forge--get-forge-host (match-string 1 url) (not relax)))) + ;; Return the WEBHOST (not the GITHOST, URLs passed to this + ;; function usually contain a GITHOST) because the IDs used to + ;; identify a repository in the database are based on WEBHOSTs. + (list (caddr elt) + (match-string 2 url) + (match-string 3 url)))) + ((not relax) + ;; The host part didn't match any GITHOST in `forge-alist', but it + ;; might be a ssh host alias. We have to relax strictness; in the + ;; extremely unlikely case that there is a common path between the + ;; HOST and the OWNER for this forge, we would incorrectly end up + ;; making that path part of the owner. + (forge--split-forge-url url t))))) ;;; Identity @@ -344,10 +358,11 @@ parent object (determined using `forge-get-parent')." (defun forge-refresh-buffer (&optional buffer) "Refresh the current buffer, if it is a Magit or Forge buffer. -Refresh the buffer if its major-mode derives from `magit-mode', -`forge-topic-list-mode' or `forge-repository-list-mode'. If -optional BUFFER is non-nil, then refresh that buffer, provided -it is alive and satisfies the mode requirement." +Refresh the buffer if its major-mode derives from `magit-mode' +or `forge-repository-list-mode'. If optional BUFFER is non-nil, +then refresh that buffer, provided it is alive and satisfies +the mode requirement." + (interactive) (cond (buffer (when (buffer-live-p buffer) (with-current-buffer buffer @@ -356,8 +371,11 @@ it is alive and satisfies the mode requirement." (magit-refresh-buffer)) ((derived-mode-p 'magit-mode) (magit-refresh-buffer)) - ((derived-mode-p 'forge-topic-list-mode - 'forge-repository-list-mode) + ((and (derived-mode-p 'forge-topic-mode) + (boundp 'forge--buffer-topics-spec) + (oref forge--buffer-topics-spec global)) + (revert-buffer)) + ((derived-mode-p 'forge-repository-list-mode) (revert-buffer)))) (defun forge--sanitize-string (string) diff --git a/elpa/forge-20240523.1103/forge-db.el b/elpa/forge-20241218.2036/forge-db.el similarity index 99% rename from elpa/forge-20240523.1103/forge-db.el rename to elpa/forge-20241218.2036/forge-db.el index f7df96b4..47d132d7 100644 --- a/elpa/forge-20240523.1103/forge-db.el +++ b/elpa/forge-20241218.2036/forge-db.el @@ -65,6 +65,9 @@ (emacsql (forge-db) (apply #'format sql args)) (apply #'emacsql (forge-db) sql args))) +(defun forge-sql-cdr (sql &rest args) + (mapcar #'cdr (apply #'forge-sql sql args))) + (defun forge-connect-database-once () "Try to connect Forge database on first use of `magit-status' only." (remove-hook 'magit-status-mode-hook #'forge-connect-database-once) diff --git a/elpa/forge-20240523.1103/forge-gitea.el b/elpa/forge-20241218.2036/forge-gitea.el similarity index 100% rename from elpa/forge-20240523.1103/forge-gitea.el rename to elpa/forge-20241218.2036/forge-gitea.el diff --git a/elpa/forge-20240523.1103/forge-github.el b/elpa/forge-20241218.2036/forge-github.el similarity index 90% rename from elpa/forge-20240523.1103/forge-github.el rename to elpa/forge-20241218.2036/forge-github.el index 8b807082..3c673379 100644 --- a/elpa/forge-20240523.1103/forge-github.el +++ b/elpa/forge-20241218.2036/forge-github.el @@ -376,9 +376,8 @@ (cl-defmethod forge--pull-notifications ((_class (subclass forge-github-repository)) githost &optional callback) - ;; The GraphQL API doesn't support notifications and also likes to - ;; timeout for handcrafted requests, forcing us to perform a major - ;; rain dance. + ;; The GraphQL API doesn't support notifications and support in the + ;; REST API is abysmal -- forcing us to perform a major rain dance. (let ((spec (forge--get-forge-host githost t))) (forge--msg nil t nil "Pulling notifications") (pcase-let* @@ -395,6 +394,7 @@ (forge--ghub-get nil "/notifications" `((all . t) ,@(and since `((since . ,since)))) :host apihost :unpaginate t))) + ;; Split into multiple requests to reduce risk of timeouts. (groups (-partition-all 50 notifs)) (pages (length groups)) (page 0) @@ -404,13 +404,40 @@ (when data (setq topics (nconc topics (cdr data)))) (if groups - (progn (cl-incf page) - (forge--msg nil t nil - "Pulling notifications (page %s/%s)" - page pages) - (ghub--graphql-vacuum - (cons 'query (seq-keep #'caddr (pop groups))) - nil #'cb nil :auth 'forge :host apihost)) + (let* ((query (seq-keep #'caddr (pop groups))) + (tries 3) + (errorback nil) + (vacuum (lambda () + (ghub--graphql-vacuum + (cons 'query query) nil #'cb nil + :auth 'forge :host apihost + :errorback errorback)))) + ;; Github also returns notifications for issues + ;; belonging to repositories for which issues + ;; have been disabled. Drop them and try again. + (setq errorback + (lambda (errors _headers _status _req) + (if (zerop tries) + (ghub--signal-error errors) + (cl-decf tries) + (if-let ((notfound + (seq-keep + (lambda (err) + (and (equal (cdr (assq 'type err)) + "NOT_FOUND") + (cadr (assq 'path err)) + (intern (cadr (assq 'path err))))) + (cdr errors)))) + (progn + (setq query (cl-delete-if + (lambda (e) (memq e notfound)) + query :key #'caar)) + (funcall vacuum)) + (ghub--signal-error errors))))) + (cl-incf page) + (forge--msg nil t nil + "Pulling notifications (page %s/%s)" page pages) + (funcall vacuum)) (forge--msg nil t t "Pulling notifications") (forge--msg nil t nil "Storing notifications") (forge--ghub-update-notifications notifs topics (not since)) @@ -462,21 +489,21 @@ (closql-with-transaction (forge-db) (pcase-dolist (`(,alias ,id ,_ ,repo ,type ,data) notifs) (let-alist data - (let* ((topic (funcall (if (eq type 'issue) - #'forge--update-issue - #'forge--update-pullreq) - repo - (cdr (cadr (assq alias topics))) - nil initial-pull)) - (notif (or (forge-get-notification id) - (closql-insert (forge-db) - (forge-notification - :id id - :thread-id .id - :repository (oref repo id) - :type type - :topic (oref topic id) - :url .subject.url))))) + (and-let* + ((topic-data (cdr (cadr (assq alias topics)))) + (topic (funcall (if (eq type 'issue) + #'forge--update-issue + #'forge--update-pullreq) + repo topic-data nil initial-pull)) + (notif (or (forge-get-notification id) + (closql-insert (forge-db) + (forge-notification + :id id + :thread-id .id + :repository (oref repo id) + :type type + :topic (oref topic id) + :url .subject.url))))) (oset notif title .subject.title) (oset notif reason (intern (downcase .reason))) (oset notif last-read .last_read_at) @@ -526,14 +553,13 @@ nil :auth 'forge :host host)) (defun forge--batch-add-callback (host owner names) - (let ((repos (cl-mapcan - (lambda (name) - (let ((repo (forge-get-repository - (list host owner name) - nil :insert!))) - (and (not (forge-get-repository repo nil :tracked?)) - (list repo)))) - names)) + (let ((repos (mapcan (lambda (name) + (let ((repo (forge-get-repository + (list host owner name) + nil :insert!))) + (and (not (forge-get-repository repo nil :tracked?)) + (list repo)))) + names)) (cb nil)) (setq cb (lambda () (when-let ((repo (pop repos))) @@ -683,7 +709,7 @@ (cl-defmethod forge--set-topic-assignees ((_repo forge-github-repository) topic assignees) - (let ((value (mapcar #'car (closql--iref topic 'assignees)))) + (let ((value (mapcar #'cadr (oref topic assignees)))) ;; FIXME Only refresh once. (when-let ((add (cl-set-difference assignees value :test #'equal))) (forge--ghub-post topic "/repos/:owner/:repo/issues/:number/assignees" @@ -696,7 +722,7 @@ (cl-defmethod forge--set-topic-review-requests ((_repo forge-github-repository) topic reviewers) - (let ((value (mapcar #'car (closql--iref topic 'review-requests)))) + (let ((value (mapcar #'cadr (oref topic review-requests)))) ;; FIXME Only refresh once. (when-let ((add (cl-set-difference reviewers value :test #'equal))) (forge--ghub-post topic diff --git a/elpa/forge-20240523.1103/forge-gitlab.el b/elpa/forge-20241218.2036/forge-gitlab.el similarity index 99% rename from elpa/forge-20240523.1103/forge-gitlab.el rename to elpa/forge-20241218.2036/forge-gitlab.el index 55c4b454..92b1d882 100644 --- a/elpa/forge-20240523.1103/forge-gitlab.el +++ b/elpa/forge-20241218.2036/forge-gitlab.el @@ -538,9 +538,7 @@ `(mutation (mergeRequestSetDraft [(input $input MergeRequestSetDraftInput!)] (mergeRequest iid draft))) - `((input (projectPath . ,(format "%s/%s" - (oref repo owner) - (oref repo name))) + `((input (projectPath . ,(oref repo slug)) (iid . ,(number-to-string (oref topic number))) (draft . ,value))) :host (oref (forge-get-repository topic) apihost) @@ -554,7 +552,7 @@ (cl-defmethod forge--set-topic-labels ((repo forge-gitlab-repository) topic labels) (forge--set-topic-field repo topic 'labels - (mapconcat #'identity labels ","))) + (string-join labels ","))) (cl-defmethod forge--set-topic-assignees ((repo forge-gitlab-repository) topic assignees) diff --git a/elpa/forge-20240523.1103/forge-gogs.el b/elpa/forge-20241218.2036/forge-gogs.el similarity index 100% rename from elpa/forge-20240523.1103/forge-gogs.el rename to elpa/forge-20241218.2036/forge-gogs.el diff --git a/elpa/forge-20240523.1103/forge-issue.el b/elpa/forge-20241218.2036/forge-issue.el similarity index 58% rename from elpa/forge-20240523.1103/forge-issue.el rename to elpa/forge-20241218.2036/forge-issue.el index 614d7ca8..e0f69fcc 100644 --- a/elpa/forge-20240523.1103/forge-issue.el +++ b/elpa/forge-20241218.2036/forge-issue.el @@ -47,21 +47,45 @@ (locked-p :initarg :locked-p) (milestone :initarg :milestone) (body :initarg :body) - (assignees :closql-table (issue-assignee assignee)) + (assignees :closql-tables (issue-assignee assignee)) (project-cards) ; projectsCards (edits) ; userContentEdits - (labels :closql-table (issue-label label)) + (labels :closql-tables (issue-label label)) (participants) (posts :closql-class forge-issue-post) (reactions) (timeline) - (marks :closql-table (issue-mark mark)) + (marks :closql-tables (issue-mark mark)) (note :initarg :note :initform nil) (their-id :initarg :their-id) (slug :initarg :slug) (saved-p :initarg :saved-p :initform nil) )) +(cl-defmethod closql-dref ((obj forge-issue) (_(eql assignees))) + (forge-sql-cdr + [:select assignee:* :from assignee + :join issue-assignee :on (= issue-assignee:id assignee:id) + :where (= issue-assignee:issue $s1) + :order-by [(asc login)]] + (closql--oref obj 'id))) + +(cl-defmethod closql-dref ((obj forge-issue) (_(eql labels))) + (forge-sql-cdr + [:select label:* :from label + :join issue-label :on (= issue-label:id label:id) + :where (= issue-label:issue $s1) + :order-by [(asc name)]] + (closql--oref obj 'id))) + +(cl-defmethod closql-dref ((obj forge-issue) (_(eql marks))) + (forge-sql-cdr + [:select mark:* :from mark + :join issue-mark :on (= issue-mark:id mark:id) + :where (= issue-mark:issue $s1) + :order-by [(asc name)]] + (closql--oref obj 'id))) + (defclass forge-issue-post (forge-post) ((closql-table :initform 'issue-post) (closql-primary-key :initform 'id) @@ -97,7 +121,7 @@ 'forge-issue)) (cl-defmethod forge-get-issue ((number integer)) - (and-let* ((repo (forge-get-repository :tracked))) + (and-let* ((repo (forge-get-repository :tracked nil 'notatpt))) (forge-get-issue repo number))) (cl-defmethod forge-get-issue ((id string)) @@ -125,11 +149,6 @@ If there is no such issue and DEMAND is non-nil, then signal an error." (or (thing-at-point 'forge-issue) (magit-section-value-if 'issue) - (and (derived-mode-p 'forge-topic-list-mode) - (and-let* ((id (tabulated-list-get-id)) - (topic (forge-get-topic id))) - (and (forge-issue-p topic) - topic))) (and demand (user-error "No issue at point")))) (put 'forge-issue 'thing-at-point #'forge-thingatpt--issue) @@ -138,90 +157,13 @@ an error." (and (thing-at-point-looking-at "#\\([0-9]+\\)\\_>") (forge-get-issue repo (string-to-number (match-string 1)))))) -;;;; List - -(defun forge--ls-recent-issues (repo) - (forge-ls-recent-topics repo 'issue)) - -(defun forge--ls-issues (repo) - (forge--select-issues repo - [:from issue :where (= issue:repository $s1)])) - -(defun forge--ls-open-issues (repo) - (forge--select-issues repo - [:from issue - :where (and (= issue:repository $s1) - (= issue:state 'open))])) - -(defun forge--ls-active-issues (repo) - (forge--select-issues repo - [:from issue - :where (and (= issue:repository $s1) - (or (= issue:state 'open) - (in issue:status [pending unread])))])) - -(defun forge--ls-assigned-issues (repo) - (forge--select-issues repo - [:from issue - :join issue_assignee :on (= issue_assignee:issue issue:id) - :join assignee :on (= issue_assignee:id assignee:id) - :where (and (= issue:repository $s1) - (= assignee:login $s2) - (isnull issue:closed))] - (ghub--username repo))) - -(defun forge--ls-authored-issues (repo) - (forge--select-issues repo - [:from [issue] - :where (and (= issue:repository $s1) - (= issue:author $s2) - (isnull issue:closed))] - (ghub--username repo))) - -(defun forge--ls-labeled-issues (repo label) - (forge--select-issues repo - [:from issue - :join issue_label :on (= issue_label:issue issue:id) - :join label :on (= issue_label:id label:id) - :where (and (= issue:repository $s1) - (= label:name $s2) - (isnull issue:closed))] - label)) - -(defun forge--ls-owned-issues () - (forge--select-issues nil - [:from [issue repository] - :where (and (= issue:repository repository:id) - (in repository:owner $v1) - (not (in repository:name $v2)) - (isnull issue:closed)) - :order-by [(asc repository:owner) - (asc repository:name) - (desc issue:number)]] - (vconcat (mapcar #'car forge-owned-accounts)) - (vconcat forge-owned-ignored))) - -(defun forge--select-issues (repo query &rest args) - (declare (indent 1)) - (mapcar (let ((db (forge-db))) - (lambda (row) - (closql--remake-instance 'forge-issue db row))) - (apply #'forge-sql - (vconcat [:select *] - query - (and (not (cl-find :order-by query)) - [:order-by [(desc updated)]])) - (if repo - (cons (oref repo id) args) - args)))) - ;;; Read (defun forge-read-issue (prompt) "Read an active issue with completion using PROMPT. Open, unread and pending issues are considered active. -Default to the current issue even if it isn't active. +Default to the current issue, even if it isn't active. \\While completion is in \ progress, \\[forge-read-topic-lift-limit] lifts the limit, extending @@ -231,15 +173,18 @@ If `forge-limit-topic-choices' is nil, then all candidates can be selected from the start." (forge--read-topic prompt #'forge-current-issue - #'forge--ls-active-issues - #'forge--ls-issues)) + (forge--topics-spec :type 'issue :active t) + (forge--topics-spec :type 'issue :active nil :state nil))) (defun forge-read-open-issue (prompt) "Read an open issue with completion using PROMPT." (let* ((current (forge-current-issue)) (repo (forge-get-repository (or current :tracked))) (default (and current (forge--format-topic-line current))) - (alist (forge--topic-collection (forge--ls-open-issues repo))) + (alist (forge--topic-collection + (forge--list-topics + (forge--topics-spec :type 'issue :state 'open) + repo))) (choices (mapcar #'car alist)) (choice (magit-completing-read prompt choices nil t nil nil default))) (cdr (assoc choice alist)))) @@ -247,31 +192,32 @@ can be selected from the start." ;;; Insert (defvar-keymap forge-issues-section-map + :parent forge-common-map " " #'forge-browse-issues " " #'forge-list-issues - "C-c C-m" #'forge-topics-menu + " " #'forge-topics-menu + " " #'forge-topic-menu "C-c C-n" #'forge-create-issue) (defvar-keymap forge-issue-section-map + :parent forge-common-map " " #'forge-visit-this-topic - "C-c C-m" #'forge-topic-menu) - -(defun forge-insert-issues () - "Insert a list of mostly recent and/or open issues. -Also see option `forge-topic-list-limit'." - (forge--insert-issues "Issues" #'forge--ls-recent-issues)) - -(defun forge-insert-assigned-issues () - "Insert a list of open issues that are assigned to you." - (forge--insert-issues "Assigned issues" #'forge--ls-assigned-issues)) - -(defun forge-insert-authored-issues () - "Insert a list of open issues that are authored by you." - (forge--insert-issues "Authored issues" #'forge--ls-authored-issues)) - -(defun forge--insert-issues (heading getter) - (when-let ((repo (forge--assert-insert-topics-get-repository t))) - (forge--insert-topics 'issues heading (funcall getter repo)))) + " " #'forge-topics-menu + " " #'forge-topic-menu) + +(cl-defun forge-insert-issues (&optional (spec nil sspec) heading) + "Insert a list of issues, according to `forge--buffer-topics-spec'. +Optional SPEC can be used to override that filtering specification, +and optional HEADING to change the section heading." + (when-let (((forge-db t)) + (repo (forge-get-repository :tracked?)) + ((oref repo issues-p)) + (spec (if sspec spec (forge--clone-buffer-topics-spec))) + ((memq (oref spec type) '(topic issue)))) + (oset spec type 'issue) + (forge--insert-topics 'issues + (or heading "Issues") + (forge--list-topics spec repo)))) ;;; _ (provide 'forge-issue) diff --git a/elpa/forge-20240523.1103/forge-notify.el b/elpa/forge-20241218.2036/forge-notify.el similarity index 82% rename from elpa/forge-20240523.1103/forge-notify.el rename to elpa/forge-20241218.2036/forge-notify.el index 4415cdf5..8f26cf66 100644 --- a/elpa/forge-20240523.1103/forge-notify.el +++ b/elpa/forge-20241218.2036/forge-notify.el @@ -25,14 +25,6 @@ (require 'forge) (require 'forge-topic) -;;; Options - -(defcustom forge-notifications-repo-slug-width 28 - "Width of repository slugs in `forge-notifications-mode' buffers." - :package-version '(forge . "0.4.0") - :group 'forge - :type (if (>= emacs-major-version 28) 'natnum 'number)) - ;;; Class (defclass forge-notification (forge-object) @@ -123,13 +115,14 @@ signal an error." (defvar-keymap forge-notifications-mode-map :doc "Keymap for `forge-notifications-mode'." - :parent magit-mode-map - " " #'magit-refresh-buffer - "C-c C-m" #'forge-notifications-menu) + :parent (make-composed-keymap forge-common-map magit-mode-map) + " " #'magit-refresh-buffer + " " #'forge-notifications-menu) (define-derived-mode forge-notifications-mode magit-mode "Forge Notifications" - "Mode for looking at forge notifications." - (hack-dir-local-variables-non-file-buffer)) + "Major mode for looking at forge notifications." + :interactive nil + (magit-hack-dir-local-variables)) (defun forge-notifications-setup-buffer () (magit-setup-buffer-internal #'forge-notifications-mode nil @@ -138,8 +131,18 @@ signal an error." (get-buffer-create "*forge-notifications*"))) (defun forge-notifications-refresh-buffer () + (magit-set-header-line-format (forge-notifications-buffer-desc)) (forge-insert-notifications)) +(defun forge-notifications-buffer-desc () + (let ((status forge-notifications-selection)) + (cond + ((not (listp status)) + (format "%s notifications" (capitalize (symbol-name status)))) + ((seq-set-equal-p status '(unread pending)) "Inbox") + ((seq-set-equal-p status '(unread pending done)) "All notifications") + ((format "Notifications %s" status))))) + (defvar forge-notifications-display-style 'flat) (defvar forge-notifications-selection '(unread pending)) @@ -148,39 +151,44 @@ signal an error." (transient-define-prefix forge-notifications-menu () "Control list of notifications and notification at point." :transient-suffix t - :transient-non-suffix t + :transient-non-suffix #'transient--do-call :transient-switch-frame nil :refresh-suffixes t + :environment #'forge--menu-environment :column-widths forge--topic-menus-column-widths - [:hide always - ("q" forge-menu-quit-list) - ("RET" forge-topic-menu) - ("" forge-topic-menu)] - [["Type" - ("t" "topics..." forge-topics-menu :transient replace) - (:info "notifications" :face forge-suffix-active) - ("r" "repositories..." forge-repositories-menu :transient replace) - ""] + [:hide always ("q" forge-menu-quit-list)] + [forge--topic-menus-group ["Selection" ("I" forge-notifications-display-inbox) ("S" forge-notifications-display-saved) ("D" forge-notifications-display-done) ("A" forge-notifications-display-all)]] - [forge--topic-set-state-group - ["Group" - ("f" forge-notifications-style-flat) - ("g" forge-notifications-style-nested)]] - [forge--topic-set-status-group] + [forge--lists-group + ["Display" + ("-F" forge-notifications-style-flat) + ("-G" forge-notifications-style-nested) + ("-H" forge-toggle-topic-legend)]] + [forge--topic-legend-group] (interactive) (unless (derived-mode-p 'forge-notifications-mode) (forge-list-notifications)) (transient-setup 'forge-notifications-menu)) -;;;###autoload -(defun forge-list-notifications () +(transient-augment-suffix forge-notifications-menu + :transient #'transient--do-replace + :if-mode 'forge-notifications-mode + :inapt-if (lambda () (eq (oref transient--prefix command) 'forge-notifications-menu)) + :inapt-face 'forge-suffix-active) + +;;;###autoload(autoload 'forge-list-notifications "forge-notify" nil t) +(transient-define-suffix forge-list-notifications () "List notifications." + :inapt-if-mode 'forge-notifications-mode + :inapt-face 'forge-suffix-active + (declare (interactive-only nil)) (interactive) - (forge-notifications-setup-buffer)) + (forge-notifications-setup-buffer) + (transient-setup 'forge-notifications-menu)) (transient-define-suffix forge-notifications-display-inbox () "List unread and pending notifications." @@ -228,7 +236,7 @@ signal an error." (transient-define-suffix forge-notifications-style-flat () "Show a flat notification list." - :description "flat list" + :description "single list" :inapt-if (lambda () (eq forge-notifications-display-style 'flat)) :inapt-face 'forge-suffix-active (interactive) @@ -239,7 +247,7 @@ signal an error." (transient-define-suffix forge-notifications-style-nested () "Group notifications by repository." - :description "by repository" + :description "group by repo" :inapt-if (lambda () (eq forge-notifications-display-style 'nested)) :inapt-face 'forge-suffix-active (interactive) @@ -262,37 +270,27 @@ signal an error." " " #'forge-visit-this-repository) (defun forge-insert-notifications () - (let* ((status forge-notifications-selection) - (notifs (forge--ls-notifications status))) + (let ((notifs (forge--ls-notifications forge-notifications-selection))) (magit-insert-section (notifications) - (magit-insert-heading - (cond - ((not (listp status)) - (format "%s notifications" (capitalize (symbol-name status)))) - ((seq-set-equal-p status '(unread pending)) "Inbox") - ((seq-set-equal-p status '(unread pending done)) "All notifications") - ((format "Notifications %s" status)))) - (if (eq forge-notifications-display-style 'flat) - (magit-insert-section-body - (if (not notifs) - (insert "(empty)\n") - (dolist (notif notifs) - (forge-insert-notification notif)) - (insert ?\n))) - (pcase-dolist (`(,_ . ,notifs) + (cond + ((not notifs) + (insert "(empty)\n")) + ((eq forge-notifications-display-style 'flat) + (magit-insert-section-body + (dolist (notif notifs) + (forge-insert-notification notif)) + (insert ?\n))) + ((pcase-dolist (`(,_ . ,notifs) (--group-by (oref it repository) notifs)) (let ((repo (forge-get-repository (car notifs)))) (magit-insert-section (forge-repo repo) (magit-insert-heading - (concat (propertize (format "%s/%s" - (oref repo owner) - (oref repo name)) - 'font-lock-face 'bold) + (concat (propertize (oref repo slug) 'font-lock-face 'bold) (format " (%s)" (length notifs)))) (magit-insert-section-body (dolist (notif notifs) (forge-insert-notification notif)) - (insert ?\n))))))))) + (insert ?\n)))))))))) (defun forge-insert-notification (notif) (with-slots (type title url) notif diff --git a/elpa/forge-20241218.2036/forge-pkg.el b/elpa/forge-20241218.2036/forge-pkg.el new file mode 100644 index 00000000..ba6b2b43 --- /dev/null +++ b/elpa/forge-20241218.2036/forge-pkg.el @@ -0,0 +1,21 @@ +;; -*- no-byte-compile: t; lexical-binding: nil -*- +(define-package "forge" "20241218.2036" + "Access Git forges from Magit." + '((emacs "29.1") + (compat "30.0.0.0") + (closql "2.1.0") + (dash "2.19.1") + (emacsql "4.1.0") + (ghub "4.2.0") + (let-alist "1.0.6") + (magit "4.1.3") + (markdown-mode "2.6") + (seq "2.24") + (transient "0.8.0") + (yaml "0.5.5")) + :url "https://github.com/magit/forge" + :commit "3b80ace32c23c030d8c12dd9bf266962718545cf" + :revdesc "3b80ace32c23" + :keywords '("git" "tools" "vc") + :authors '(("Jonas Bernoulli" . "emacs.forge@jonas.bernoulli.dev")) + :maintainers '(("Jonas Bernoulli" . "emacs.forge@jonas.bernoulli.dev"))) diff --git a/elpa/forge-20240523.1103/forge-post.el b/elpa/forge-20241218.2036/forge-post.el similarity index 98% rename from elpa/forge-20240523.1103/forge-post.el rename to elpa/forge-20241218.2036/forge-post.el index 084a9b7b..23526ead 100644 --- a/elpa/forge-20240523.1103/forge-post.el +++ b/elpa/forge-20241218.2036/forge-post.el @@ -41,7 +41,7 @@ (defcustom forge-buffer-draft-p nil "Whether new pull-requests start out as drafts by default. -The buffer-local value is use to keep track of the draft status +The buffer-local value is used to keep track of the draft status of the current pull-request." :package-version '(forge . "0.4.0") :group 'forge @@ -99,7 +99,9 @@ an error." " " #'forge-post-cancel " " #'forge-post-cancel) -(define-derived-mode forge-post-mode gfm-mode "Forge-Post" "") +(define-derived-mode forge-post-mode gfm-mode "Forge-Post" + "Major mode for editing topic posts." + :interactive nil) (defvar-local forge--buffer-base-branch nil) (defvar-local forge--buffer-head-branch nil) diff --git a/elpa/forge-20240523.1103/forge-pullreq.el b/elpa/forge-20241218.2036/forge-pullreq.el similarity index 61% rename from elpa/forge-20240523.1103/forge-pullreq.el rename to elpa/forge-20241218.2036/forge-pullreq.el index 6ff5c4be..0c0c9afd 100644 --- a/elpa/forge-20240523.1103/forge-pullreq.el +++ b/elpa/forge-20241218.2036/forge-pullreq.el @@ -55,18 +55,18 @@ (head-repo :initarg :head-repo) (milestone :initarg :milestone) (body :initarg :body) - (assignees :closql-table (pullreq-assignee assignee)) + (assignees :closql-tables (pullreq-assignee assignee)) (project-cards) ; projectsCards (commits) (edits) ; userContentEdits - (labels :closql-table (pullreq-label label)) + (labels :closql-tables (pullreq-label label)) (participants) (posts :closql-class forge-pullreq-post) (reactions) - (review-requests :closql-table (pullreq-review-request assignee)) + (review-requests :closql-tables (pullreq-review-request assignee)) (reviews) (timeline) - (marks :closql-table (pullreq-mark mark)) + (marks :closql-tables (pullreq-mark mark)) (note :initarg :note :initform nil) (base-rev :initarg :base-rev) (head-rev :initarg :head-rev) @@ -76,6 +76,38 @@ (saved-p :initarg :saved-p :initform nil) )) +(cl-defmethod closql-dref ((obj forge-pullreq) (_(eql assignees))) + (forge-sql-cdr + [:select assignee:* :from assignee + :join pullreq-assignee :on (= pullreq-assignee:id assignee:id) + :where (= pullreq-assignee:pullreq $s1) + :order-by [(asc login)]] + (closql--oref obj 'id))) + +(cl-defmethod closql-dref ((obj forge-pullreq) (_(eql labels))) + (forge-sql-cdr + [:select label:* :from label + :join pullreq-label :on (= pullreq-label:id label:id) + :where (= pullreq-label:pullreq $s1) + :order-by [(asc name)]] + (closql--oref obj 'id))) + +(cl-defmethod closql-dref ((obj forge-pullreq) (_(eql review-requests))) + (forge-sql-cdr + [:select assignee:* :from assignee + :join pullreq-review-request :on (= pullreq-review-request:id assignee:id) + :where (= pullreq-review-request:pullreq $s1) + :order-by [(asc login)]] + (closql--oref obj 'id))) + +(cl-defmethod closql-dref ((obj forge-pullreq) (_(eql marks))) + (forge-sql-cdr + [:select mark:* :from mark + :join pullreq-mark :on (= pullreq-mark:id mark:id) + :where (= pullreq-mark:pullreq $s1) + :order-by [(asc name)]] + (closql--oref obj 'id))) + (defclass forge-pullreq-post (forge-post) ((closql-table :initform 'pullreq-post) (closql-primary-key :initform 'id) @@ -111,7 +143,7 @@ 'forge-pullreq)) (cl-defmethod forge-get-pullreq ((number integer)) - (and-let* ((repo (forge-get-repository :tracked))) + (and-let* ((repo (forge-get-repository :tracked nil 'notatpt))) (forge-get-pullreq repo number))) (cl-defmethod forge-get-pullreq ((id string)) @@ -149,11 +181,6 @@ an error." (or (thing-at-point 'forge-pullreq) (magit-section-value-if 'pullreq) (forge-get-pullreq :branch) - (and (derived-mode-p 'forge-topic-list-mode) - (and-let* ((id (tabulated-list-get-id)) - (topic (forge-get-topic id))) - (and (forge-pullreq-p topic) - topic))) (and demand (user-error "No pull-request at point")))) (put 'forge-pullreq 'thing-at-point #'forge-thingatpt--pullreq) @@ -165,94 +192,13 @@ an error." "#\\([0-9]+\\)\\_>")) (forge-get-pullreq repo (string-to-number (match-string 1)))))) -;;;; List - -(defun forge--ls-recent-pullreqs (repo) - (forge-ls-recent-topics repo 'pullreq)) - -(defun forge--ls-pullreqs (repo) - (forge--select-pullreqs repo - [:from pullreq :where (= pullreq:repository $s1)])) - -(defun forge--ls-active-pullreqs (repo) - (forge--select-pullreqs repo - [:from pullreq - :where (and (= pullreq:repository $s1) - (or (= pullreq:state 'open) - (in pullreq:status [pending unread])))])) - -(defun forge--ls-assigned-pullreqs (repo) - (forge--select-pullreqs repo - [:from pullreq - :join pullreq_assignee :on (= pullreq_assignee:pullreq pullreq:id) - :join assignee :on (= pullreq_assignee:id assignee:id) - :where (and (= pullreq:repository $s1) - (= assignee:login $s2) - (isnull pullreq:closed))] - (ghub--username repo))) - -(defun forge--ls-requested-reviews (repo) - (forge--select-pullreqs repo - [:from pullreq - :join pullreq_review_request :on (= pullreq_review_request:pullreq pullreq:id) - :join assignee :on (= pullreq_review_request:id assignee:id) - :where (and (= pullreq:repository $s1) - (= assignee:login $s2) - (isnull pullreq:closed))] - (ghub--username repo))) - -(defun forge--ls-authored-pullreqs (repo) - (forge--select-pullreqs repo - [:from [pullreq] - :where (and (= pullreq:repository $s1) - (= pullreq:author $s2) - (isnull pullreq:closed))] - (ghub--username repo))) - -(defun forge--ls-labeled-pullreqs (repo label) - (forge--select-pullreqs repo - [:from pullreq - :join pullreq_label :on (= pullreq_label:pullreq pullreq:id) - :join label :on (= pullreq_label:id label:id) - :where (and (= pullreq:repository $s1) - (= label:name $s2) - (isnull pullreq:closed))] - label)) - -(defun forge--ls-owned-pullreqs () - (forge--select-pullreqs nil - [:from [pullreq repository] - :where (and (= pullreq:repository repository:id) - (in repository:owner $v1) - (not (in repository:name $v2)) - (isnull pullreq:closed)) - :order-by [(asc repository:owner) - (asc repository:name) - (desc pullreq:number)]] - (vconcat (mapcar #'car forge-owned-accounts)) - (vconcat forge-owned-ignored))) - -(defun forge--select-pullreqs (repo query &rest args) - (declare (indent 1)) - (mapcar (let ((db (forge-db))) - (lambda (row) - (closql--remake-instance 'forge-pullreq db row))) - (apply #'forge-sql - (vconcat [:select *] - query - (and (not (cl-find :order-by query)) - [:order-by [(desc updated)]])) - (if repo - (cons (oref repo id) args) - args)))) - ;;; Read (defun forge-read-pullreq (prompt) "Read an active pull-request with completion using PROMPT. Open, unread and pending pull-requests are considered active. -Default to the current pull-request even if it isn't active. +Default to the current pull-request, even if it isn't active. \\While completion is in \ progress, \\[forge-read-topic-lift-limit] lifts the limit, extending @@ -262,8 +208,8 @@ If `forge-limit-topic-choices' is nil, then all candidates can be selected from the start." (forge--read-topic prompt #'forge-current-pullreq - #'forge--ls-active-pullreqs - #'forge--ls-pullreqs)) + (forge--topics-spec :type 'pullreq :active t) + (forge--topics-spec :type 'pullreq :active nil :state nil))) ;;; Utilities @@ -299,39 +245,31 @@ can be selected from the start." ;;; Insert (defvar-keymap forge-pullreqs-section-map + :parent forge-common-map " " #'forge-browse-pullreqs " " #'forge-list-pullreqs - "C-c C-m" #'forge-topics-menu + " " #'forge-topics-menu + " " #'forge-topic-menu "C-c C-n" #'forge-create-pullreq) (defvar-keymap forge-pullreq-section-map + :parent forge-common-map " " #'forge-visit-this-topic - "C-c C-m" #'forge-topic-menu) - -(defun forge-insert-pullreqs () - "Insert a list of mostly recent and/or open pull-requests. -Also see option `forge-topic-list-limit'." - (forge--insert-pullreqs "Pull requests" - #'forge--ls-recent-pullreqs)) - -(defun forge-insert-assigned-pullreqs () - "Insert a list of open pull-requests that are assigned to you." - (forge--insert-pullreqs "Assigned pull requests" - #'forge--ls-assigned-pullreqs)) - -(defun forge-insert-requested-reviews () - "Insert a list of pull-requests that are awaiting your review." - (forge--insert-pullreqs "Pull requests awaiting review" - #'forge--ls-requested-reviews)) - -(defun forge-insert-authored-pullreqs () - "Insert a list of open pullreqs that are authored by you." - (forge--insert-pullreqs "Authored pullreqs" - #'forge--ls-authored-pullreqs)) - -(defun forge--insert-pullreqs (heading getter) - (when-let ((repo (forge--assert-insert-topics-get-repository))) - (forge--insert-topics 'pullreqs heading (funcall getter repo)))) + " " #'forge-topics-menu + " " #'forge-topic-menu) + +(cl-defun forge-insert-pullreqs (&optional (spec nil sspec) heading) + "Insert a list of pull-requests, according to `forge--buffer-topics-spec'. +Optional SPEC can be used to override that filtering specification, +and optional HEADING to change the section heading." + (when-let (((forge-db t)) + (repo (forge-get-repository :tracked?)) + (spec (if sspec spec (forge--clone-buffer-topics-spec))) + ((memq (oref spec type) '(topic pullreq)))) + (oset spec type 'pullreq) + (forge--insert-topics 'pullreqs + (or heading "Pull requests") + (forge--list-topics spec repo)))) (defun forge--insert-pullreq-commits (pullreq &optional all) (cl-letf (((symbol-function #'magit-cancel-section) (lambda ()))) @@ -340,14 +278,14 @@ Also see option `forge-topic-list-limit'." ;; pr range may be out of sync. Just show them all. (magit-insert-section-body (magit--insert-log nil - (delq nil (list (concat "^" (or (oref pullreq base-rev) - (concat (forge--get-remote) "/" - (oref pullreq base-ref)))) - (forge--pullreq-ref pullreq) - (forge--pullreq-branch-active pullreq) - (and-let* ((branch (oref pullreq head-ref))) - (and (magit-local-branch-p branch) branch)))) - (seq-uniq (cons "--graph" magit-buffer-log-args)))) + (delq nil (list (concat "^" (or (oref pullreq base-rev) + (concat (forge--get-remote) "/" + (oref pullreq base-ref)))) + (forge--pullreq-ref pullreq) + (forge--pullreq-branch-active pullreq) + (and-let* ((branch (oref pullreq head-ref))) + (and (magit-local-branch-p branch) branch)))) + (seq-uniq (cons "--graph" magit-buffer-log-args)))) (when-let ((range (forge--pullreq-range pullreq))) (magit-insert-section-body (magit--insert-log nil range magit-buffer-log-args) diff --git a/elpa/forge-20240523.1103/forge-repo.el b/elpa/forge-20241218.2036/forge-repo.el similarity index 91% rename from elpa/forge-20240523.1103/forge-repo.el rename to elpa/forge-20241218.2036/forge-repo.el index 998831a0..47791f55 100644 --- a/elpa/forge-20240523.1103/forge-repo.el +++ b/elpa/forge-20241218.2036/forge-repo.el @@ -104,9 +104,10 @@ (defconst forge--signal-no-entry '(:tracked :stub :insert!)) -(defun forge--get-remote (&optional warn) +(defun forge--get-remote (&optional warn ignore-variable) (let* ((remotes (magit-list-remotes)) - (config (magit-get "forge.remote")) + (config (and (not ignore-variable) + (magit-get "forge.remote"))) (remote (if (cdr remotes) (or (car (member config remotes)) (car (member "upstream" remotes)) @@ -114,13 +115,19 @@ (car remotes)))) (when (and warn config remote (not (equal config remote))) (message "Ignored forge.remote=%s; no such remote.\nSee %s." config - "https://magit.vc/manual/forge/Repository-Detection.html")) + "https://magit.vc/manual/forge/How-Forge-Detection-Works.html")) remote)) (cl-defmethod forge-get-repository ((_(eql :id)) id) (closql-get (forge-db) (substring-no-properties id) 'forge-repository)) -(cl-defmethod forge-get-repository ((demand symbol) &optional remote) +(cl-defmethod forge-get-repository ((_(eql :dir)) dir) + (let ((default-directory dir) + (forge-buffer-repository nil) + (forge-buffer-topic nil)) + (forge-get-repository :stub? nil 'notatpt))) + +(cl-defmethod forge-get-repository ((demand symbol) &optional remote notatpt) "Return the current forge repository. First check if `forge-buffer-repository', or if that is nil, then @@ -130,7 +137,10 @@ then return that repository. Otherwise return the repository for `default-directory', if that exists and satisfies DEMAND. If that fails too, then return nil or signal an error, depending on DEMAND." - (or (and-let* ((repo (or (forge-buffer-repository) + (or (and-let* ((repo (and (not notatpt) + (forge-repository-at-point)))) + (forge-get-repository repo 'noerror demand)) + (and-let* ((repo (or (forge-buffer-repository) (and forge-buffer-topic (forge-get-repository forge-buffer-topic))))) (forge-get-repository repo 'noerror demand)) @@ -157,7 +167,8 @@ or signal an error, depending on DEMAND." config "that remote does not exist."))) ((magit-list-remotes) "Cannot decide on remote to use.") (t "No remote configured.")) - "https://magit.vc/manual/forge/Repository-Detection.html"))))))) + "https://magit.vc/manual/forge/How-Forge-Detection-Works.html" + ))))))) (cl-defmethod forge-get-repository ((url string) &optional remote demand) "Return the repository at URL." @@ -282,28 +293,6 @@ See `forge-alist' for valid Git hosts." (defun forge--get-repository:tracked? () (forge-get-repository :tracked?)) -(defun forge-get-worktree (repo) - "Validate and return the worktree recorded for REPO. -If no worktree is recorded, return nil. If a worktree is recorded but -that doesn't exist anymore, then discard the recorded value and return -nil." - (and-let* ((worktree (oref repo worktree))) - (if (file-directory-p worktree) - worktree - (oset repo worktree nil) - nil))) - -;;;; Current - -(defun forge-current-repository (&optional demand) - "Return the repository at point or being visited. -If there is no such repository and DEMAND is non-nil, then signal -an error." - (or (forge-repository-at-point) - (forge-buffer-repository) - (forge-get-repository :known?) - (and demand (user-error "No current repository")))) - (defun forge-repository-at-point (&optional demand) "Return the repository at point. If there is no such repository and DEMAND is non-nil, then signal @@ -322,12 +311,37 @@ an error." (forge-get-repository topic)) (and (not forge-buffer-unassociated-p) (or (forge-buffer-repository) - (forge-get-repository :known?))))) + (forge-get-repository :known? nil 'notatpt))))) (defun forge-buffer-repository () (and-let* ((id forge-buffer-repository)) (forge-get-repository :id id))) +(defun forge-set-buffer-repository () + "Initialize the value of variable `forge-buffer-repository'." + (unless forge-buffer-repository + (and-let* ((repo (forge-get-repository :known?))) + (setq forge-buffer-repository (oref repo id))))) + +(add-hook 'magit-mode-hook #'forge-set-buffer-repository) + +(defun forge-get-worktree (repo) + "Validate, remember and return a worktree for REPO. +If `default-directory' is within one of REPO's worktrees, record that +location in its `worktree' slot and return it. Otherwise, if a worktree +has been recorded before, validate that. If it still is a worktree of +REPO, return it, else set the slot to nil and return nil." + (if-let (((forge-repository-equal + repo (forge-get-repository :dir default-directory))) + (current-tree (magit-toplevel))) + (oset repo worktree current-tree) + (and-let* ((saved-tree (oref repo worktree))) + (and (file-accessible-directory-p saved-tree) + (if (forge-repository-equal + repo (forge-get-repository :dir saved-tree)) + saved-tree + (oset repo worktree nil)))))) + ;;;; List (defun forge--ls-repos () @@ -414,7 +428,7 @@ forges and hosts." (forge-sql [:select [githost owner name] :from repository])) nil t nil nil - (and-let* ((default (forge-current-repository))) + (and-let* ((default (forge-get-repository :stub?))) (format "%s/%s @%s" (oref default owner) (oref default name) @@ -474,11 +488,10 @@ forges and hosts." topic :callback (lambda () (forge-refresh-buffer) - (when (and transient--showp - (memq transient-current-command - '(forge-topic-menu - forge-topics-menu - forge-notifications-menu))) + (when (transient-active-prefix + '(forge-topic-menu + forge-topics-menu + forge-notifications-menu)) (transient--refresh-transient)))))) (defvar forge--mode-line-buffer nil) diff --git a/elpa/forge-20240523.1103/forge-repos.el b/elpa/forge-20241218.2036/forge-repos.el similarity index 73% rename from elpa/forge-20240523.1103/forge-repos.el rename to elpa/forge-20241218.2036/forge-repos.el index 93a6b6b5..90dd572f 100644 --- a/elpa/forge-20240523.1103/forge-repos.el +++ b/elpa/forge-20241218.2036/forge-repos.el @@ -62,13 +62,14 @@ and `:pad-right'." (defvar-keymap forge-repository-list-mode-map :doc "Local keymap for Forge-Repository-List mode buffers." - :parent tabulated-list-mode-map - "RET" #'forge-visit-this-repository - "" #'forge-visit-this-repository - "o" #'forge-browse-this-repository - "C-c C-m" #'forge-repositories-menu - "'" #'forge-dispatch - "?" #'magit-dispatch) + :parent (make-composed-keymap forge-common-map tabulated-list-mode-map) + "n" #'forge-dispatch + "RET" #'forge-visit-this-repository + "" #'forge-visit-this-repository + "o" #'forge-browse-this-repository + " " #'forge-repositories-menu) + +(defvar-local forge--buffer-list-filter nil) (defvar forge-repository-list-buffer-name "*forge-repositories*" "Buffer name to use for displaying lists of repositories.") @@ -85,6 +86,7 @@ Must be set before `forge-list' is loaded.") (define-derived-mode forge-repository-list-mode tabulated-list-mode forge-repository-list-mode-name "Major mode for browsing a list of repositories." + :interactive nil (setq-local x-stretch-cursor nil) (setq tabulated-list-padding 0) (setq tabulated-list-sort-key (cons "Owner" nil))) @@ -97,9 +99,7 @@ Must be set before `forge-list' is loaded.") (setq forge--tabulated-list-query fn) (cl-letf (((symbol-function #'tabulated-list-revert) #'ignore)) ; see #229 (forge-repository-list-mode)) - (setq forge--buffer-list-type 'repo) (setq forge--buffer-list-filter filter) - (setq forge--buffer-list-global t) (forge--tablist-refresh) (add-hook 'tabulated-list-revert-hook #'forge--tablist-refresh nil t) (tabulated-list-print) @@ -120,64 +120,61 @@ Must be set before `forge-list' is loaded.") ('t "*") ('nil " "))) -;;; Menu +;;; Commands +;;;; Menu -;;;###autoload (autoload 'forge-repositories-menu "forge-repos" nil t) +;;;###autoload(autoload 'forge-repositories-menu "forge-repos" nil t) (transient-define-prefix forge-repositories-menu () - "Control list of repositories and repository at point." + "Control list of repositories displayed in the current buffer." :transient-suffix t - :transient-non-suffix 'call + :transient-non-suffix #'transient--do-call :transient-switch-frame nil :refresh-suffixes t + :environment #'forge--menu-environment + :column-widths forge--topic-menus-column-widths [:hide always ("q" forge-menu-quit-list)] - [["Type" - ("t" "topics..." forge-topics-menu :transient replace) - ("n" "notifications..." forge-notifications-menu :transient replace) - ("r" "repositories" forge-list-repositories)] + [forge--topic-menus-group + forge--lists-group ["Filter" - ("o" "owned" forge-list-owned-repositories)]] + ("o" "owned" forge-list-owned-repositories + :if-nil forge--buffer-list-filter) + ("o" "owned" forge-list-repositories + :face forge-suffix-active + :if-non-nil forge--buffer-list-filter + :inapt-if-mode nil)]] (interactive) (unless (derived-mode-p 'forge-repository-list-mode) (if-let ((buffer (get-buffer forge-repository-list-buffer-name))) (switch-to-buffer buffer) - (with-no-warnings ; "interactive use only" - (forge-list-repositories)))) + (forge-list-repositories))) (transient-setup 'forge-repositories-menu)) -;;; Class +(transient-augment-suffix forge-repositories-menu + :transient #'transient--do-replace + :if-mode 'forge-repository-list-mode + :inapt-if (lambda () (eq (oref transient--prefix command) 'forge-repositories-menu)) + :inapt-face 'forge-suffix-active) + +;;;; List (defclass forge--repo-list-command (transient-suffix) ((type :initarg :type :initform nil) (filter :initarg :filter :initform nil) - (global :initarg :global :initform nil) - (inapt-if :initform 'forge--topic-list-inapt) - (inapt-face :initform nil))) - -(defun forge--topic-list-inapt () - (with-slots (type filter global) transient--pending-suffix - (and (eq type forge--buffer-list-type) - (eq filter forge--buffer-list-filter) - (eq global forge--buffer-list-global)))) - -(cl-defmethod transient-format-description ((obj forge--repo-list-command)) - (with-slots (description type filter global) obj - (if (and (eq type forge--buffer-list-type) - (memq filter (list nil forge--buffer-list-filter)) - (eq global forge--buffer-list-global)) - (propertize description 'face 'forge-active-suffix) - description))) + (global :initarg :global :initform nil))) -;;; Commands - -;;;###autoload (autoload 'forge-list-repositories "forge-repos" nil t) +;;;###autoload(autoload 'forge-list-repositories "forge-repos" nil t) (transient-define-suffix forge-list-repositories () "List known repositories in a separate buffer. Here \"known\" means that an entry exists in the local database." :class 'forge--repo-list-command :type 'repo :global t + :inapt-if-mode 'forge-repository-list-mode + :inapt-face 'forge-suffix-active + (declare (interactive-only nil)) (interactive) - (forge-repository-list-setup nil #'forge--ls-repos)) + (forge-repository-list-setup nil #'forge--ls-repos) + (transient-setup 'forge-repositories-menu)) -;;;###autoload (autoload 'forge-list-owned-repositories "forge-repos" nil t) +;;;###autoload(autoload 'forge-list-owned-repositories "forge-repos" nil t) (transient-define-suffix forge-list-owned-repositories () "List your own known repositories in a separate buffer. Here \"known\" means that an entry exists in the local database @@ -186,7 +183,8 @@ controls which repositories are considered to be owned by you. Only Github is supported for now." :class 'forge--repo-list-command :type 'repo :filter 'owned :global t (interactive) - (forge-repository-list-setup 'owned #'forge--ls-owned-repos)) + (forge-repository-list-setup 'owned #'forge--ls-owned-repos) + (transient-setup 'forge-repositories-menu)) ;;; _ (provide 'forge-repos) diff --git a/elpa/forge-20240523.1103/forge-revnote.el b/elpa/forge-20241218.2036/forge-revnote.el similarity index 100% rename from elpa/forge-20240523.1103/forge-revnote.el rename to elpa/forge-20241218.2036/forge-revnote.el diff --git a/elpa/forge-20240523.1103/forge-semi.el b/elpa/forge-20241218.2036/forge-semi.el similarity index 100% rename from elpa/forge-20240523.1103/forge-semi.el rename to elpa/forge-20241218.2036/forge-semi.el diff --git a/elpa/forge-20240523.1103/forge-tablist.el b/elpa/forge-20241218.2036/forge-tablist.el similarity index 95% rename from elpa/forge-20240523.1103/forge-tablist.el rename to elpa/forge-20241218.2036/forge-tablist.el index 31e2561e..7252c624 100644 --- a/elpa/forge-20240523.1103/forge-tablist.el +++ b/elpa/forge-20241218.2036/forge-tablist.el @@ -26,10 +26,6 @@ (require 'forge) -(defvar-local forge--buffer-list-type nil) -(defvar-local forge--buffer-list-filter nil) -(defvar-local forge--buffer-list-global nil) - (defconst forge--tablist-columns-type '(repeat (list :tag "Column" diff --git a/elpa/forge-20240523.1103/forge-topic.el b/elpa/forge-20241218.2036/forge-topic.el similarity index 64% rename from elpa/forge-20240523.1103/forge-topic.el rename to elpa/forge-20241218.2036/forge-topic.el index df2f0c38..dd66c0c7 100644 --- a/elpa/forge-20240523.1103/forge-topic.el +++ b/elpa/forge-20241218.2036/forge-topic.el @@ -23,6 +23,7 @@ ;;; Code: (require 'bug-reference) +(require 'eieio-custom) (require 'markdown-mode) (require 'parse-time) (require 'yaml) @@ -34,36 +35,6 @@ ;;; Options -(defcustom forge-topic-list-order '(updated . string>) - "Order of topics listed in the status buffer. - -The value has the form (SLOT . PREDICATE), where SLOT is a -slot of issue or pullreq objects, and PREDICATE is a function -used to order the topics by that slot. Reasonable values -include (number . >) and (updated . string>)." - :package-version '(forge . "0.1.0") - :group 'forge - :type '(cons (symbol :tag "Slot") - (function :tag "Predicate"))) - -(defcustom forge-topic-list-limit '(60 . 5) - "Limit the number of topics listed in the status buffer. - -All unread topics are always shown. If the value of this option -has the form (OPEN . CLOSED), then the integer OPEN specifies the -maximal number of topics and CLOSED specifies the maximal number -of closed topics. IF CLOSED is negative then show no closed -topics until the command `forge-toggle-closed-visibility' changes -the sign. - -The value can also be an integer, in which case it limits the -number of closed topics only." - :package-version '(forge . "0.1.0") - :group 'forge - :type '(choice (number :tag "Maximal number of closed topics") - (cons (number :tag "Maximal number of open topics") - (number :tag "Maximal number of closed topics")))) - (defcustom forge-limit-topic-choices t "Whether to initially limit completion candidates to active topics." :package-version '(forge . "0.4.0") @@ -88,6 +59,12 @@ The following %-sequences are supported: :group 'forge :type 'boolean) +(defcustom forge-topic-repository-slug-width 28 + "Width of repository slugs (i.e., \"OWNER/NAME\")." + :package-version '(forge . "0.4.0") + :group 'forge + :type 'natnum) + (defcustom forge-bug-reference-hooks '(find-file-hook forge-post-mode-hook @@ -103,10 +80,6 @@ This variable has to be customized before `forge' is loaded." magit-mode-hook) :type '(list :convert-widget custom-hook-convert-widget)) -(defvar-local forge-display-in-status-buffer t - "Whether to display topics in the current Magit status buffer.") -(put 'forge-display-in-status-buffer 'permanent-local t) - (defvar forge-format-avatar-function nil "Function used to insert avatars in certain locations. This is experimental and intended for users who wish to @@ -125,8 +98,7 @@ does not inherit from `magit-dimmed'." :group 'forge-faces) (defface forge-topic-header-line - `((t :inherit magit-header-line - ,@(and (>= emacs-major-version 29) '(:foreground reset)))) + '((t :inherit magit-header-line :foreground reset)) "Face for the `header-line' in `forge-topic-mode' buffers." :group 'forge-faces) @@ -163,32 +135,31 @@ were closed without being merged." ;;;;; Notifications (defface forge-topic-unread - `((t :weight bold - :box ( :line-width ,(if (>= emacs-major-version 28) (cons -1 -1) -1) - :style nil))) + '((t :weight bold + :box (:line-width (-1 . -1) :style nil))) "Face used for summaries of entities with unread notifications. -This face is always used together with, and takes preference -over, a `forge[-fancy]-{issue,pullreq}-STATE' face and should not -specify any attribute that is specified by any of those faces. -Likewise those faces should not set `:weight' or `:slant'." +This face is always used together with, and takes preference over, +a `forge-{issue,pullreq}-STATE' face and should not specify any +attribute that is specified by any of those faces. Likewise those +faces should not set `:weight' or `:slant'." :group 'forge-faces) (defface forge-topic-pending '((t :weight bold)) "Face used for summaries of entities with open notifications. -This face is always used together with, and takes preference -over, a `forge[-fancy]-{issue,pullreq}-STATE' face and should not -specify any attribute that is specified by any of those faces. -Likewise those faces should not set `:weight' or `:slant'." +This face is always used together with, and takes preference over, +a `forge-{issue,pullreq}-STATE' face and should not specify any +attribute that is specified by any of those faces. Likewise those +faces should not set `:weight' or `:slant'." :group 'forge-faces) (defface forge-topic-done - '((t :slant italic)) + '((t)) "Face used for summaries of entities with no unread or open notification. -This face is always used together with, and takes preference -over, a `forge[-fancy]-{issue,pullreq}-STATE' face and should not -specify any attribute that is specified by any of those faces. -Likewise those faces should not set `:weight' or `:slant'." +This face is always used together with, and takes preference over, +a `forge-{issue,pullreq}-STATE' face and should not specify any +attribute that is specified by any of those faces. Likewise those +faces should not set `:weight' or `:slant'." :group 'forge-faces) ;;;;; Issues @@ -225,12 +196,19 @@ Likewise those faces should not set `:weight' or `:slant'." "Face used for summaries of closed pull-requests, that weren't merged." :group 'forge-faces) +(defface forge-pullreq-draft + '((t :slant italic)) + "Face used for summaries of draft pull-requests. +A face attribute should be used that is not already used by any +`forge-topic-STATUS' or `forge-{issue,pullreq}-STATE' face." + :group 'forge-faces) + ;;;; Labels (defface forge-topic-label - `((t :box ( :line-width ,(if (>= emacs-major-version 28) (cons -1 -1) -1) - :style released-button))) - "Face used for topic labels." + '((t :inherit secondary-selection + :box (:line-width (-1 . -1) :style released-button))) + "Face used for topic labels, marks and milestones." :group 'forge-faces) ;;;; Post Details @@ -333,14 +311,10 @@ an error." (defun forge-topic-at-point (&optional demand) "Return the topic at point. If there is no such topic and DEMAND is non-nil, then signal -an error. If NOT-THINGATPT is non-nil, then don't use -`thing-at-point'." +an error." (or (thing-at-point 'forge-topic) (magit-section-value-if '(issue pullreq)) (forge-get-pullreq :branch) - (and (derived-mode-p 'forge-topic-list-mode) - (and-let* ((id (tabulated-list-get-id))) - (forge-get-topic id))) (and demand (user-error "No topic at point")))) (put 'forge-topic 'thing-at-point #'forge-thingatpt--topic) @@ -356,73 +330,237 @@ an error. If NOT-THINGATPT is non-nil, then don't use repo (string-to-number (match-string-no-properties 1)))))) (defun forge-region-topics () - (cond - ((derived-mode-p 'forge-notifications-mode) - (magit-region-values '(issue pullreq))) - ((and (derived-mode-p 'forge-topic-list-mode) - (region-active-p)) - (let ((beg (region-beginning)) - (end (region-end)) - (topics nil)) - (save-excursion - (goto-char beg) - (while (< (point) end) - (when-let* ((id (tabulated-list-get-id))) - (push (forge-get-topic id) topics)) - (forward-line 1)) - (nreverse topics)))))) + (magit-region-values '(issue pullreq))) ;;;; List -(defun forge-ls-recent-topics (repo table) - (let* ((id (oref repo id)) - (limit forge-topic-list-limit) - (open-limit (if (consp limit) (car limit) limit)) - (closed-limit (if (consp limit) (cdr limit) limit)) - (topics (forge-sql [:select * :from $i1 - :where (and (= repository $s2) - (= status 'unread))] - table id))) - (mapc (lambda (row) - (cl-pushnew row topics :test #'equal)) - (if (consp limit) - (forge-sql [:select * :from $i1 - :where (and (= repository $s2) - (isnull closed)) - :order-by [(desc updated)] - :limit $s3] - table id open-limit) - (forge-sql [:select * :from $i1 - :where (and (= repository $s2) - (isnull closed))] - table id))) - (when (> closed-limit 0) - (mapc (lambda (row) - (cl-pushnew row topics :test #'equal)) - (forge-sql [:select * :from $i1 - :where (and (= repository $s2) - (notnull closed)) - :order-by [(desc updated)] - :limit $s3] - table id closed-limit))) - (cl-sort (mapcar (let ((class (if (eq table 'pullreq) - 'forge-pullreq - 'forge-issue))) - (lambda (row) - (closql--remake-instance class (forge-db) row))) - topics) - (cdr forge-topic-list-order) - :key (lambda (it) (eieio-oref it (car forge-topic-list-order)))))) - -(defun forge--ls-topics (repo) - (cl-sort (nconc (forge--ls-issues repo) - (forge--ls-pullreqs repo)) - #'> :key (-cut oref <> number))) - -(defun forge--ls-active-topics (repo) - (cl-sort (nconc (forge--ls-active-issues repo) - (forge--ls-active-pullreqs repo)) - #'> :key (-cut oref <> number))) +(defvar-local forge--buffer-topics-spec nil) +(put 'forge--buffer-topics-spec 'permanent-local t) + +(defun forge--init-buffer-topics-spec () + (unless forge--buffer-topics-spec + (setq forge--buffer-topics-spec + (clone forge-status-buffer-default-topic-filters)))) +(add-hook 'magit-status-mode-hook #'forge--init-buffer-topics-spec) + +(defun forge--clone-buffer-topics-spec () + (forge--init-buffer-topics-spec) + (clone forge--buffer-topics-spec)) + +(defclass forge--topics-spec () + ((type :documentation "\ +Limit list based on topic type." + :initarg :type + :initform 'topic + :type (member topic issue pullreq nil) + :custom (choice + (const topic) + (const issue) + (const pullreq) + (const :tag "disable topic sections (nil)" nil))) + (active :documentation "\ +Limit list to active topics. + +A topic is \"active\" if its state (public condition) is open and/or +its status (private condition) is unread or pending. + +When this is t, then the value of the `state' and `status' slots are +ignored." + :initarg :active + :initform t + :type boolean + :custom boolean) + (state :documentation "\ +Limit list based on topic (public) state. + +State is the \"public condition\". I.e., is the topic still open?" + :initarg :state + :initform 'open + :type (satisfies (lambda (val) + (member val '(open + (completed merged) + (unplanned rejected) + nil)))) + :custom (choice + (const open) + (const (completed merged)) + (const (unplanned rejected)) + (const :tag "all (nil)" nil))) + (status :documentation "\ +Limit list based on topic (private) status. + +Status is the \"private condition\". I.e., have you decided yet +that *you* are done with the topic, and have others made changes, +which *you* have not seen yet? + +`inbox' means \"`unread' or `pending'\"." + :initarg :status + :initform nil + :type (member inbox unread pending done nil) + :custom (choice + (const inbox) + (const unread) + (const pending) + (const done) + (const :tag "all (nil)" nil))) + (updated :documentation "\ +Date when topic was last updated." + :initarg :updated + :initform nil + :type (or string null)) + (milestone :documentation "\ +Limit list to topics assigned to given milestone." + :initarg :milestone + :initform nil + :type (or string null) + :custom string) + (labels :documentation "\ +Limit list to topics with at least one of the given labels." + :initarg :labels + :initform nil + :type (list-of string) + :custom (repeat string)) + (marks :documentation "\ +Limit list to topics with at least one of the given marks. +Marks are like labels, but they are private and local to the +current Forge database." + :initarg :marks + :initform nil + :type (list-of string) + :custom (repeat string)) + (saved :documentation "Limit list to saved topics." + :initarg :saved + :initform nil + :type boolean + :custom boolean) + (author :documentation "\ +Limit list to topics created by given user." + :initarg :author + :initform nil + :label "Author (username)" + :type (or string null) + :custom string) + (assignee :documentation "\ +Limit list to topics assigned to given user." + :initarg :assignee + :initform nil + :label "Assignee (username)" + :type (or string null) + :custom string) + (reviewer :documentation "\ +Limit list to topics for which a review by the given user was requested." + :initarg :reviewer + :initform nil + :label "Reviewer (username)" + :type (or string null) + :custom string) + (global :documentation "Whether to list topics for all repositories." + :initarg :global + :initform nil + :type boolean) + (order :documentation "Order in which topics are listed." + :initarg :order + :initform 'newest + :type (member newest oldest recently-updated anciently-updated) + :custom (choice (const newest) + (const oldest) + (const recently-updated) + (const anciently-updated))) + (limit :documentation "Number of topics to list at most." + :initarg :limit + :initform 200 + :type integer + :custom natnum) + (grouped :documentation "Whether to group topics by repository." + :initarg :grouped + :initform nil + :type boolean + :custom boolean))) + +(cl-defun forge--list-topics + (&optional (spec forge--buffer-topics-spec) + (repo (forge-get-repository :tracked?)) + (type (oref spec type))) + (when (oref spec reviewer) + (setq type 'pullreq)) + (if (eq type 'topic) + (pcase-let ((`(,pred ,slot) (pcase (oref spec order) + ('newest '(> number)) + ('oldest '(< number)) + ('recently-updated '(> updated)) + ('anciently-updated '(< updated))))) + (cl-sort (nconc (forge--list-topics-1 spec repo 'issue) + (forge--list-topics-1 spec repo 'pullreq)) + pred :key (lambda (obj) (eieio-oref obj slot)))) + (forge--list-topics-1 spec repo type))) + +(defun forge--list-topics-1 (spec repo type) + (mapcar (apply-partially #'closql--remake-instance + (if (eq type 'issue) 'forge-issue 'forge-pullreq) + (forge-db)) + (forge-sql (forge--list-topics-2 spec repo type)))) + +(defun forge--list-topics-2 (spec repo type) + (pcase-let (((eieio active state status milestone labels marks + saved author assignee reviewer global order limit) + spec)) + (cond (active + (setq state 'open) + (setq status '(unread pending))) + ((eq status 'inbox) + (setq status '(unread pending)))) + `[:select :distinct topic:* + :from [(as ,type topic)] + ,@(cond + (milestone + `[:join milestone :on (= milestone:title ,milestone)])) + ,@(cond + ((not labels) nil) + ((eq type 'issue) + [:join issue-label :on (= issue-label:issue topic:id) + :join label :on (= label:id issue-label:id)]) + ([:join pullreq-label :on (= pullreq-label:pullreq topic:id) + :join label :on (= label:id pullreq-label:id)])) + ,@(cond + ((not marks) nil) + ((eq type 'issue) + [:join issue-mark :on (= issue-mark:issue topic:id) + :join mark :on (= mark:id issue-mark:id)]) + ([:join pullreq-mark :on (= pullreq-mark:pullreq topic:id) + :join mark :on (= mark:id pullreq-mark:id)])) + ,@(cond + ((not assignee) nil) + ((eq type 'issue) + [:join issue-assignee :on (= issue-assignee:issue topic:id) + :join assignee :on (= assignee:id issue-assignee:id)]) + ([:join pullreq-assignee :on (= pullreq-assignee:pullreq topic:id) + :join assignee :on (= assignee:id pullreq-assignee:id)])) + ,@(cond + (reviewer + [:join (as pullreq-review-request r) :on (= r:pullreq topic:id) + :join assignee :on (= assignee:id r:id)])) + :where + (and + ,@(and (not global) repo `((= topic:repository ,(oref repo id)))) + ,@(cond + ((and active state status) + `((or (in topic:state ,(vconcat (ensure-list state))) + (in topic:status ,(vconcat (ensure-list status)))))) + (`(,@(and state `((in topic:state ,(vconcat (ensure-list state))))) + ,@(and status `((in topic:status ,(vconcat (ensure-list status)))))))) + ,@(and milestone '((= topic:milestone milestone:id))) + ,@(and labels `((or ,@(mapcar (lambda (l) `(= label:name ,l)) labels)))) + ,@(and marks `((or ,@(mapcar (lambda (m) `(= mark:name ,m)) marks)))) + ,@(and saved '((= topic:saved-p 't))) + ,@(and author `((= topic:author ,author))) + ,@(and assignee `((= assignee:login ,assignee))) + ,@(and reviewer `((= assignee:login ,reviewer)))) + :order-by [,(pcase order + ('newest '(desc topic:number)) + ('oldest '(asc topic:number)) + ('recently-updated '(desc topic:updated)) + ('anciently-updated '(asc topic:updated)))] + ,@(and limit `(:limit ,limit))])) ;;; Read @@ -430,7 +568,7 @@ an error. If NOT-THINGATPT is non-nil, then don't use "Read an active topic with completion using PROMPT. Open, unread and pending topics are considered active. -Default to the current topic even if it isn't active. +Default to the current topic, even if it isn't active. \\While completion is in \ progress, \\[forge-read-topic-lift-limit] lifts the limit, extending @@ -440,16 +578,16 @@ If `forge-limit-topic-choices' is nil, then all candidates can be selected from the start." (forge--read-topic prompt #'forge-current-topic - #'forge--ls-active-topics - #'forge--ls-topics)) + (forge--topics-spec :type 'topic :active t) + (forge--topics-spec :type 'topic :active nil :state nil))) (defun forge--read-topic (prompt current active all) (let* ((current (funcall current)) (repo (forge-get-repository (or current :tracked))) (default (and current (forge--format-topic-line current))) (alist (forge--topic-collection - (funcall (if forge-limit-topic-choices active all) - repo))) + (forge--list-topics (if forge-limit-topic-choices active all) + repo))) (choices (mapcar #'car alist)) (choices (cond ((and forge-limit-topic-choices default @@ -477,7 +615,8 @@ can be selected from the start." (forge-limit-topic-choices choices) (t (forge--replace-minibuffer-prompt (concat prompt ": ")) - (setq alist (forge--topic-collection (funcall all repo))) + (setq alist (forge--topic-collection + (forge--list-topics all repo))) (setq all-choices (mapcar #'car alist))))))) nil t nil nil default)) (magit-completing-read prompt choices nil t nil nil default)))) @@ -570,31 +709,25 @@ can be selected from the start." nil t (and topic (forge--format-topic-milestone topic)))) -(defun forge-read-topic-label (&optional prompt repository) - (magit-completing-read (or prompt "Label") - (forge--format-topic-label-choices - (or repository (forge-get-repository :tracked))) - nil t)) - -(defun forge-read-topic-labels (&optional topic) - (let* ((repo (forge-get-repository (or topic :tracked))) - (crm-separator ",")) +(defun forge-read-topic-labels (&optional obj) + (let ((crm-separator ",")) (magit-completing-read-multiple "Labels: " - (mapcar #'cadr (oref repo labels)) + (forge--format-labels (and obj (forge-get-repository obj))) nil t - (and topic (mapconcat #'car (closql--iref topic 'labels) ","))))) + (and (forge-topic-p obj) + (forge--format-labels obj crm-separator))))) -(defun forge-read-topic-marks (&optional topic) - (let ((marks (mapcar #'car (forge-sql [:select name :from mark]))) - (crm-separator ",")) +(defun forge-read-topic-marks (&optional obj) + (let ((crm-separator ",")) (magit-completing-read-multiple - "Marks: " marks nil t - (and topic (mapconcat #'car (closql--iref topic 'marks) ","))))) + "Marks: " (forge--format-marks) nil t + (and (forge-topic-p obj) + (forge--format-marks obj crm-separator))))) (defun forge-read-topic-assignees (&optional topic) (let* ((repo (forge-get-repository (or topic :tracked))) - (value (and topic (closql--iref topic 'assignees))) + (value (and topic (oref topic assignees))) (choices (mapcar #'cadr (oref repo assignees))) (crm-separator ",")) (magit-completing-read-multiple @@ -602,17 +735,17 @@ can be selected from the start." (if (forge--childp repo 'forge-gitlab-repository) t ; Selecting something else would fail later on. 'confirm) - (mapconcat #'car value ",")))) + (mapconcat #'cadr value ",")))) (defun forge-read-topic-review-requests (&optional topic) (let* ((repo (forge-get-repository (or topic :tracked))) - (value (and topic (closql--iref topic 'review-requests))) + (value (and topic (oref topic review-requests))) (choices (mapcar #'cadr (oref repo assignees))) (crm-separator ",")) (magit-completing-read-multiple "Request review from: " choices nil 'confirm - (mapconcat #'car value ",")))) + (mapconcat #'cadr value ",")))) (defun forge--completing-read ( prompt collection &optional predicate require-match initial-input @@ -640,11 +773,14 @@ can be selected from the start." (defun forge--format-topic-line (topic &optional width) (concat - (and (derived-mode-p 'forge-notifications-mode) - (eq forge-notifications-display-style 'flat) + (and (or (and (derived-mode-p 'forge-notifications-mode) + (eq forge-notifications-display-style 'flat)) + (and (derived-mode-p 'forge-topics-mode) + (oref forge--buffer-topics-spec global) + (not (oref forge--buffer-topics-spec grouped)))) (concat (truncate-string-to-width (oref (forge-get-repository topic) slug) - forge-notifications-repo-slug-width + forge-topic-repository-slug-width nil ?\s t) " ")) ;; MAYBE bring this back once we support discussions. @@ -698,7 +834,10 @@ can be selected from the start." nil (magit--propertize-face title - `(,(pcase status + `(,@(and (forge-pullreq-p topic) + (oref topic draft-p) + '(forge-pullreq-draft)) + ,(pcase status ('unread 'forge-topic-unread) ('pending 'forge-topic-pending) ('done 'forge-topic-done)) @@ -710,40 +849,64 @@ can be selected from the start." (`(forge-pullreq merged) 'forge-pullreq-merged) (`(forge-pullreq rejected) 'forge-pullreq-rejected))))))) -(defun forge--format-topic-title+labels (topic) - (concat (forge--format-topic-title topic) " " - (forge--format-topic-labels topic))) - (defun forge--format-topic-milestone (topic) - (and-let* ((id (oref topic milestone))) - (caar (forge-sql [:select [title] :from milestone :where (= id $s1)] id)))) - -(defun forge--format-topic-labels (topic) - (and-let* ((labels (closql--iref topic 'labels))) - (mapconcat (pcase-lambda (`(,name ,color ,_description)) - (let* ((background (forge--sanitize-color color)) - (foreground (forge--contrast-color background))) - (magit--propertize-face - name `(( :background ,background - :foreground ,foreground) - forge-tablist-topic-label)))) - labels " "))) - -(defun forge--format-topic-label-choices (repo) - (mapcar (pcase-lambda (`(,_id ,name ,color ,_description)) - (let* ((background (forge--sanitize-color color)) - (foreground (forge--contrast-color background))) - (magit--propertize-face - name `( :background ,background - :foreground ,foreground)))) - (oref repo labels))) - -(defun forge--format-topic-marks (topic) - (and-let* ((marks (closql--iref topic 'marks))) - (mapconcat (pcase-lambda (`(,name ,face ,_description)) - (magit--propertize-face - name (list 'forge-tablist-topic-label face))) - marks " "))) + (and-let* ((id (oref topic milestone)) + (str (caar (forge-sql [:select [title] + :from milestone + :where (= id $s1)] + id)))) + (magit--propertize-face str 'forge-topic-label))) + +(defun forge--format-labels (&optional arg concat) + (and-let* + ((local t) + (labels (cond + ((eieio-object-p arg) + (oref arg labels)) + ((forge-buffer-repository) + (forge-sql-cdr `[:select label:* :from label :where + ,(if arg + '(and (= repository $s1) + (in name $v2)) + '(= repository $s1)) + :order-by [(asc name)]] + forge-buffer-repository + (vconcat arg))) + (t + (setq local nil) + (forge-sql `[:select :distinct name :from label + ,@(and arg '(:where (in name $v1))) + :order-by [(asc name)]] + (vconcat arg))))) + (format (if local + (pcase-lambda (`(,_id ,name ,color ,_description)) + (let* ((background (forge--sanitize-color color)) + (foreground (readable-foreground-color background))) + (magit--propertize-face + name `(( :background ,background + :foreground ,foreground) + forge-topic-label)))) + (pcase-lambda (`(,name)) + (magit--propertize-face name 'forge-topic-label))))) + (if concat + (mapconcat format labels (if (stringp concat) concat " ")) + (mapcar format labels)))) + +(defun forge--format-marks (&optional arg concat) + (and-let* ((marks (if (forge-topic--eieio-childp arg) + (oref arg marks) + ;; Unlike labels, marks are not repo-specific. + (when (forge-repository-p arg) (setq arg nil)) + (forge-sql-cdr `[:select * :from mark + ,@(and arg '(:where (in name $v1))) + :order-by [(asc name)]] + (vconcat arg)))) + (format (pcase-lambda (`(,_id ,name ,face ,_description)) + (magit--propertize-face + name (list face 'forge-topic-label))))) + (if concat + (mapconcat format marks (if (stringp concat) concat " ")) + (mapcar format marks)))) (defun forge--format-topic-state (topic) (with-slots (state) topic @@ -768,15 +931,15 @@ can be selected from the start." ('done 'forge-topic-done))))) (defun forge--format-topic-assignees (topic) - (and-let* ((assignees (closql--iref topic 'assignees))) + (and-let* ((assignees (oref topic assignees))) (mapconcat #'forge--format-person assignees ", "))) (defun forge--format-topic-review-requests (topic) - (and-let* ((review-requests (closql--iref topic 'review-requests))) + (and-let* ((review-requests (oref topic review-requests))) (mapconcat #'forge--format-person review-requests ", "))) (defun forge--format-person (person) - (pcase-let ((`(,login ,name) person)) + (pcase-let ((`(,_id ,login ,name) person)) (format "%s%s (@%s)" (forge--format-avatar login) name login))) @@ -786,24 +949,52 @@ can be selected from the start." (funcall forge-format-avatar-function person) "")) -(defun forge--format-boolean (slot name) +(defun forge--format-boolean (slot name &optional obj) ;; Booleans are formatted differently in transients and headers. ;; Use this to format the (complete) description of suffix commands. - (let ((topic (forge-current-topic))) - (if (and topic (slot-exists-p topic slot)) + (let ((obj (or obj (forge-current-topic)))) + (if (and obj (slot-exists-p obj slot)) (format (propertize "[%s]" 'face 'transient-delimiter) (propertize name 'face - (if (eieio-oref topic slot) + (if (eieio-oref obj slot) 'transient-value 'transient-inactive-value))) (format "[%s]" name)))) ;;; Insert +(defun forge-insert-topics (type heading prepare) + "Insert a list of topics, according to PREPARE. + +This function is not intended to be added to section hooks directly. +Instead create a function, which calls this function, and add wrapper +to the section hook. + +PREPARE is a function which takes one arguments the repository object, +and must return an filter object of type `forge--topics-spec' or nil. +Insert no topics if PREPARE returns nil, or if the current repository +isn't tracked or Forge hasn't been fully setup yet (in the latter two +cases don't even call PREPARE). + +The filter object can be created either using `forge--topics-spec' or +by `clone'ing the object returned by `forge--init-buffer-topics-spec', +to share some settings with other topic lists in the same buffer. +See `forge--topics-spec' for the valid slots and their values. + +HEADING is used as the heading of the list section and TYPE is used as +its type. TYPE should be a symbol of the form `SUBSET-KIND', where KIND +is one of `topics', `issues' or `pullreqs', and SUBSET should describe +what subset of KIND is being listed." + (declare (indent defun)) + (when-let (((forge-db t)) + (repo (forge-get-repository :tracked?)) + (spec (funcall prepare repo))) + (forge--insert-topics type heading (forge--list-topics spec repo)))) + (defun forge--insert-topics (type heading topics) (when topics (let ((width (apply #'max (--map (length (oref it slug)) topics)))) - (magit-insert-section ((eval type) nil t) + (magit-insert-section ((eval type) heading t) (magit-insert-heading (concat (magit--propertize-face (concat heading " ") 'magit-section-heading) @@ -832,11 +1023,11 @@ can be selected from the start." (forge--insert-pullreq-commits topic))))) (defun forge--insert-topic-labels (topic &optional separate) - (and-let* ((labels (closql--iref topic 'labels))) + (and-let* ((labels (oref topic labels))) (prog1 t - (pcase-dolist (`(,name ,color ,description) labels) + (pcase-dolist (`(,_id ,name ,color ,description) labels) (let* ((background (forge--sanitize-color color)) - (foreground (forge--contrast-color background))) + (foreground (readable-foreground-color background))) (if separate (insert " ") (setq separate t)) (insert name) (let ((o (make-overlay (- (point) (length name)) (point)))) @@ -850,9 +1041,9 @@ can be selected from the start." (overlay-put o 'help-echo description)))))))) (defun forge--insert-topic-marks (topic &optional separate) - (and-let* ((marks (closql--iref topic 'marks))) + (and-let* ((marks (oref topic marks))) (prog1 t - (pcase-dolist (`(,name ,face ,description) marks) + (pcase-dolist (`(,_id ,name ,face ,description) marks) (if separate (insert " ") (setq separate t)) (insert name) (let ((o (make-overlay (- (point) (length name)) (point)))) @@ -862,37 +1053,55 @@ can be selected from the start." (when description (overlay-put o 'help-echo description))))))) -(defun forge--assert-insert-topics-get-repository (&optional issues-p) - (and (forge-db t) - (or forge-display-in-status-buffer - (not (eq major-mode 'magit-status-mode))) - (and-let* ((repo (forge-get-repository :tracked?))) - (and (or (not issues-p) - (oref repo issues-p)) - repo)))) - ;;; Modes +(defvar-keymap forge-common-map + :doc "Parent keymap of many of Forge's keymaps. +Keymaps that use this keymap as the/a parent keymap, remap the +place-holder commands `forge--list-menu' and/or `forge--item-menu' +to the appropriate menu command. To change the keys bound to menu +commands in all Forge keymaps, one only has to change them here." + "C-c C-c" #'forge--list-menu + "C-c RET" #'forge--item-menu + "C-" #'forge--item-menu) + +(defun forge--list-menu () + "Place-holder menu command. See `forge-common-map'." + (interactive) + (message "No list menu available here")) +(put 'forge--list-menu 'completion-predicate #'ignore) + +(defun forge--item-menu () + "Place-holder menu command. See `forge-common-map'." + (interactive) + (message "No item menu available here")) +(put 'forge--item-menu 'completion-predicate #'ignore) + (defvar-keymap forge-post-section-map " " #'forge-edit-post "C-c C-k" #'forge-delete-comment) (defvar-keymap forge-topic-mode-map + :parent (make-composed-keymap forge-common-map magit-mode-map) " " #'markdown-follow-link-at-point "" #'markdown-follow-link-at-point - "C-c C-m" #'forge-topic-menu + " " #'forge-topic-menu + " " #'forge-topic-menu "C-c C-n" #'forge-create-post "C-c C-r" #'forge-create-post) (define-derived-mode forge-topic-mode magit-mode "Topic" - "Parent mode of `forge-{issue,pullreq}-mode'. + "Parent major mode of `forge-{issue,pullreq}-mode'. This mode itself is never used directly." + :interactive nil (face-remap-add-relative 'header-line 'forge-topic-header-line) (setq-local markdown-translate-filename-function #'forge--markdown-translate-filename-function)) +(defvar-keymap forge-issue-mode-map :parent forge-topic-mode-map) (define-derived-mode forge-issue-mode forge-topic-mode "Issue" - "Mode for looking at a Forge issue.") + "Major mode for looking at a Forge issue." + :interactive nil) (defalias 'forge-issue-setup-buffer #'forge-topic-setup-buffer) (defalias 'forge-issue-refresh-buffer #'forge-topic-refresh-buffer) (defvar forge-issue-headers-hook @@ -903,8 +1112,10 @@ This mode itself is never used directly." forge-insert-topic-marks forge-insert-topic-assignees)) +(defvar-keymap forge-pullreq-mode-map :parent forge-topic-mode-map) (define-derived-mode forge-pullreq-mode forge-topic-mode "Pull-request" - "Mode for looking at a Forge pull-request.") + "Major mode for looking at a Forge pull-request." + :interactive nil) (defalias 'forge-pullreq-setup-buffer #'forge-topic-setup-buffer) (defalias 'forge-pullreq-refresh-buffer #'forge-topic-refresh-buffer) (defvar forge-pullreq-headers-hook @@ -924,15 +1135,11 @@ This mode itself is never used directly." (defun forge-topic-setup-buffer (topic) (let* ((repo (forge-get-repository topic)) (name (format "*forge: %s %s*" (oref repo slug) (oref topic slug))) - (magit-generate-buffer-name-function (lambda (_mode _value) name)) - (current-repo (forge-get-repository :known?)) - (default-directory (if (forge-repository-equal current-repo repo) - default-directory - (or (forge-get-worktree repo) - default-directory)))) + (magit-generate-buffer-name-function (lambda (_mode _value) name))) (magit-setup-buffer-internal (if (forge-issue-p topic) #'forge-issue-mode #'forge-pullreq-mode) - t `((forge-buffer-topic ,topic)) name) + t `((forge-buffer-topic ,topic)) + name (or (forge-get-worktree repo) "/")) (forge-topic-mark-read topic))) (defun forge-topic-refresh-buffer () @@ -953,9 +1160,9 @@ This mode itself is never used directly." (insert (forge--fontify-markdown note) "\n\n"))) (dolist (post (cons topic (oref topic posts))) (with-slots (author created body) post - (magit-insert-section section (post post) - (oset section heading-highlight-face - 'magit-diff-hunk-heading-highlight) + (magit-insert-section + ( post post nil + :heading-highlight-face 'magit-diff-hunk-heading-highlight) (let ((heading (format-spec forge-post-heading-format @@ -1045,82 +1252,126 @@ This mode itself is never used directly." ;;; Commands ;;;; Groups -(defconst forge--topic-set-state-group - ["Set state" - ("s o" forge-topic-state-set-open) - ("s c" forge-issue-state-set-completed) - ("s u" forge-issue-state-set-unplanned) - ("s m" forge-pullreq-state-set-merged) - ("s r" forge-pullreq-state-set-rejected) +(defconst forge--lists-group + ["List" + ("l r" "repositories" forge-list-repositories) + ("l n" "notifications" forge-list-notifications) + ("l g" "global topics" forge-list-global-topics) + ("l t" "topics" forge-list-topics) + ""]) + +(defconst forge--topic-menus-group + ["Menu" + ("m s" "edit" forge-topic-menu) + ("m f" "filter" forge-topics-menu) + ("m f" "filter" forge-notifications-menu) + ("m f" "filter" forge-repositories-menu) + ("m d" "dispatch" forge-dispatch) + ("m c" "configure" forge-configure) ""]) +(defconst forge--topic-set-state-group + [:description + (lambda () (if forge--show-topic-legend "Set public state" "Set state")) + ("o" forge-topic-state-set-open) + ("c" forge-issue-state-set-completed) + ("x" forge-issue-state-set-unplanned) + ("c" forge-pullreq-state-set-merged) + ("x" forge-pullreq-state-set-rejected)]) + (defconst forge--topic-set-status-group - ["Set status" - ("s i" forge-topic-status-set-unread) - ("s p" forge-topic-status-set-pending) - ("s d" forge-topic-status-set-done)]) - -(defconst forge--topic-set-slots-group - ["Set" - ("e t" forge-topic-set-title) - ("e m" forge-topic-set-milestone) - ("e l" forge-topic-set-labels) - ("e a" forge-topic-set-assignees) - ("e r" forge-topic-set-review-requests) - ("e d" forge-topic-toggle-draft)]) - -(defconst forge--topic-set-local-group - ["Set local" - ("l s" forge-topic-toggle-saved) - ("l m" forge-topic-set-marks) - ("l n" forge-edit-topic-note)]) + [:description + (lambda () (if forge--show-topic-legend "Set private status" "Set status")) + ("u" forge-topic-status-set-unread) + ("p" forge-topic-status-set-pending) + ("d" forge-topic-status-set-done)]) -;;;; Menus +(defconst forge--topic-legend-group + '(["Legend" :if-non-nil forge--show-topic-legend + (:info* (lambda () (propertize "open issue" 'face 'forge-issue-open))) + (:info* (lambda () (propertize "completed issue" 'face 'forge-issue-completed))) + (:info* (lambda () (propertize "unplanned issue" 'face 'forge-issue-unplanned)))] + ["" :if-non-nil forge--show-topic-legend + (:info* (lambda () (propertize "open pullreq" 'face 'forge-pullreq-open))) + (:info* (lambda () (propertize "merged pullreq" 'face 'forge-pullreq-merged))) + (:info* (lambda () (propertize "rejected pullreq" 'face 'forge-pullreq-rejected)))] + ["" :if-non-nil forge--show-topic-legend + (:info* (lambda () (propertize "unread" 'face 'forge-topic-unread))) + (:info* (lambda () (propertize "pending" 'face 'forge-topic-pending))) + (:info* (lambda () (propertize "done" 'face 'forge-topic-done)))] + ["" :if-non-nil forge--show-topic-legend + (:info* (lambda () (propertize "draft" 'face 'forge-pullreq-draft)))])) + +(defvar forge--show-topic-legend t) + +(transient-define-suffix forge-toggle-topic-legend () + "Toggle whether to show legend for faces used in topic menus and lists." + :description (lambda () (if forge--show-topic-legend "hide legend" "show legend")) + :transient t + (interactive) + (customize-set-variable 'forge--show-topic-legend + (not forge--show-topic-legend))) -(defconst forge--topic-menus-column-widths '(19)) +(defconst forge--topic-menus-column-widths '(21 21 21 21)) -;;;###autoload (autoload 'forge-topic-menu "forge-topic" nil t) +;;;; Menus + +;;;###autoload(autoload 'forge-topic-menu "forge-topic" nil t) (transient-define-prefix forge-topic-menu () "Edit the topic at point." :transient-suffix t - :transient-non-suffix t + :transient-non-suffix #'transient--do-call :transient-switch-frame nil :refresh-suffixes t + :environment #'forge--menu-environment :column-widths forge--topic-menus-column-widths - [:hide always - ("q" forge-menu-quit-list)] - [["Actions" - ("f" forge-pull-this-topic) - ("b" forge-browse-this-topic) - ("k" forge-delete-comment) - ("p" forge-create-pullreq-from-issue) - ("m" "show more actions" forge-dispatch)]] - [forge--topic-set-state-group - forge--topic-set-slots-group] - [forge--topic-set-status-group - forge--topic-set-local-group] - (interactive) - (unless (derived-mode-p 'forge-topic-mode) - (if-let ((topic (forge-topic-at-point))) - (forge-topic-setup-buffer topic) - (user-error "No current topic"))) - (transient-setup 'forge-topic-menu)) - -;;;###autoload (autoload 'forge-topic-state-menu "forge-topic" nil t) + [:hide always ("q" forge-menu-quit-list)] + [forge--topic-menus-group + forge--topic-set-state-group + forge--topic-set-status-group + ["Actions" + ("/f" forge-pull-this-topic) + ("/b" forge-browse-this-topic) + ("/c" forge-checkout-this-pullreq)]] + [forge--lists-group + ["Set " + ("-m" forge-topic-set-milestone) + ("-l" forge-topic-set-labels) + ("-x" forge-topic-set-marks) + ("-a" forge-topic-set-assignees) + ("-r" forge-topic-set-review-requests) + ("-n" forge-edit-topic-note) + ("-t" forge-topic-set-title)] + ["Set" + ("-s" forge-topic-toggle-saved) + ("-d" forge-topic-toggle-draft) + """Display" + ("-H" forge-toggle-topic-legend)]] + [forge--topic-legend-group]) + +(transient-augment-suffix forge-topic-menu + :transient #'transient--do-replace + :inapt-if (lambda () (or (derived-mode-p 'forge-repository-list-mode) + (eq (oref transient--prefix command) 'forge-topic-menu))) + :inapt-face (lambda () (if (derived-mode-p 'forge-repository-list-mode) + 'transient-inapt-suffix + 'forge-suffix-active))) + +;;;###autoload(autoload 'forge-topic-state-menu "forge-topic" nil t) (transient-define-prefix forge-topic-state-menu () "Set state of the current topic." - [("o" forge-topic-state-set-open) - ("c" forge-issue-state-set-completed) - ("u" forge-issue-state-set-unplanned) - ("m" forge-pullreq-state-set-merged) - ("r" forge-pullreq-state-set-rejected)]) + :environment #'forge--menu-environment + [forge--topic-set-state-group]) -;;;###autoload (autoload 'forge-topic-status-menu "forge-topic" nil t) +;;;###autoload(autoload 'forge-topic-status-menu "forge-topic" nil t) (transient-define-prefix forge-topic-status-menu () "Set status of the current topic." - [("i" forge-topic-status-set-unread) - ("p" forge-topic-status-set-pending) - ("d" forge-topic-status-set-done)]) + :environment #'forge--menu-environment + [forge--topic-set-status-group]) + +(defun forge--menu-environment (fn) + (let ((magit--refresh-cache (list (cons 0 0)))) + (funcall fn))) ;;;; State @@ -1270,7 +1521,8 @@ This mode itself is never used directly." "Edit the TITLE of the current topic." :class 'forge--topic-set-slot-command :slot 'title :formatter (lambda (topic) - (propertize (forge--format-topic-title topic) + (propertize (truncate-string-to-width + (forge--format-topic-title topic) 34 nil ?\s t) 'face 'font-lock-string-face))) (transient-define-suffix forge-topic-set-milestone (milestone) @@ -1279,11 +1531,13 @@ This mode itself is never used directly." (transient-define-suffix forge-topic-set-labels (labels) "Edit the LABELS of the current topic." - :class 'forge--topic-set-slot-command :slot 'labels) + :class 'forge--topic-set-slot-command :slot 'labels + :formatter (lambda (topic) (forge--format-labels topic t))) (transient-define-suffix forge-topic-set-marks (marks) "Edit the MARKS of the current topic." - :class 'forge--topic-set-slot-command :slot 'marks) + :class 'forge--topic-set-slot-command :slot 'marks + :formatter (lambda (topic) (forge--format-marks topic t))) (transient-define-suffix forge-topic-set-assignees (assignees) "Edit the ASSIGNEES of the current topic." @@ -1319,26 +1573,6 @@ This mode itself is never used directly." ((string-match-p "\\`#.\\{8\\}\\'" color) (substring color 0 6)) (t "#000000"))) ; Use fallback instead of invalid color. -(defun forge--contrast-color (color) - "Return black or white depending on the luminance of COLOR." - (if (> (forge--x-color-luminance color) 0.5) "black" "white")) - -;; Copy of `rainbow-x-color-luminance'. -(defun forge--x-color-luminance (color) - "Calculate the luminance of a color string (e.g., \"#ffaa00\", \"blue\"). -Return a value between 0 and 1." - (let ((values (color-values color))) - (forge--color-luminance (/ (nth 0 values) 256.0) - (/ (nth 1 values) 256.0) - (/ (nth 2 values) 256.0)))) - -;; Copy of `rainbow-color-luminance'. -;; Also see https://en.wikipedia.org/wiki/Relative_luminance. -(defun forge--color-luminance (red green blue) - "Calculate the luminance of color composed of RED, GREEN and BLUE. -Return a value between 0 and 1." - (/ (+ (* .2126 red) (* .7152 green) (* .0722 blue)) 256)) - ;;; Markdown Utilities (defun forge--fontify-markdown (text) @@ -1479,39 +1713,6 @@ alist, containing just `text' and `position'.") ;;; Bug-Reference -(when (< emacs-major-version 28) - (defun bug-reference-fontify (start end) - "Apply bug reference overlays to region." - (save-excursion - (let ((beg-line (progn (goto-char start) (line-beginning-position))) - (end-line (progn (goto-char end) (line-end-position)))) - ;; Remove old overlays. - (bug-reference-unfontify beg-line end-line) - (goto-char beg-line) - (while (and (< (point) end-line) - (re-search-forward bug-reference-bug-regexp end-line 'move)) - (when (and (or (not bug-reference-prog-mode) - ;; This tests for both comment and string syntax. - (nth 8 (syntax-ppss))) - ;; This is the part where this redefinition differs - ;; from the original defined in "bug-reference.el". - (not (and (derived-mode-p 'magit-status-mode - 'forge-notifications-mode) - (= (match-beginning 0) - (line-beginning-position)))) - ;; End of additions. - ) - (let ((overlay (make-overlay (match-beginning 0) (match-end 0) - nil t nil))) - (overlay-put overlay 'category 'bug-reference) - ;; Don't put a link if format is undefined - (when bug-reference-url-format - (overlay-put overlay 'bug-reference-url - (if (stringp bug-reference-url-format) - (format bug-reference-url-format - (match-string-no-properties 2)) - (funcall bug-reference-url-format))))))))))) - (defun forge-bug-reference-setup () "Setup `bug-reference' in the current buffer. If forge data has been fetched for the current repository, then @@ -1519,38 +1720,24 @@ enable `bug-reference-mode' or `bug-reference-prog-mode' and modify `bug-reference-bug-regexp' if appropriate." (unless (or bug-reference-url-format (not (forge-db t)) - ;; TODO Allow use in this mode again. - (derived-mode-p 'forge-notifications-mode)) + ;; TODO Allow use in these modes again. + (derived-mode-p 'forge-topics-mode 'forge-notifications-mode)) (magit--with-safe-default-directory nil (when-let ((repo (forge-get-repository :tracked?))) - (if (>= emacs-major-version 28) - (when (derived-mode-p 'magit-status-mode - 'forge-notifications-mode) - (setq-local - bug-reference-auto-setup-functions - (let ((hook bug-reference-auto-setup-functions)) - (list (lambda () - ;; HOOK is not allowed to be a lexical var: - ;; (run-hook-with-args-until-success 'hook) - (catch 'success - (dolist (f hook) - (when (funcall f) - (setq bug-reference-bug-regexp - (concat "." bug-reference-bug-regexp)) - (throw 'success t))))))))) - (setq-local bug-reference-url-format - (if (forge--childp repo 'forge-gitlab-repository) - (lambda () - (forge--format repo - (if (equal (match-string 3) "#") - 'issue-url-format - 'pullreq-url-format) - `((?i . ,(match-string 2))))) - (forge--format repo 'issue-url-format '((?i . "%s"))))) - (setq-local bug-reference-bug-regexp - (if (forge--childp repo 'forge-gitlab-repository) - "\\(?3:[!#]\\)\\(?2:[0-9]+\\)" - "#\\(?2:[0-9]+\\)"))) + (when (derived-mode-p 'magit-status-mode + 'forge-notifications-mode) + (setq-local + bug-reference-auto-setup-functions + (let ((hook bug-reference-auto-setup-functions)) + (list (lambda () + ;; HOOK is not allowed to be a lexical var: + ;; (run-hook-with-args-until-success 'hook) + (catch 'success + (dolist (f hook) + (when (funcall f) + (setq bug-reference-bug-regexp + (concat "." bug-reference-bug-regexp)) + (throw 'success t))))))))) (if (derived-mode-p 'prog-mode) (bug-reference-prog-mode 1) (bug-reference-mode 1)) diff --git a/elpa/forge-20241218.2036/forge-topics.el b/elpa/forge-20241218.2036/forge-topics.el new file mode 100644 index 00000000..2b6dedf9 --- /dev/null +++ b/elpa/forge-20241218.2036/forge-topics.el @@ -0,0 +1,619 @@ +;;; forge-topics.el --- List topics -*- lexical-binding:t -*- + +;; Copyright (C) 2018-2024 Jonas Bernoulli + +;; Author: Jonas Bernoulli +;; Maintainer: Jonas Bernoulli + +;; SPDX-License-Identifier: GPL-3.0-or-later + +;; This file 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 file 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 file. If not, see . + +;;; Code: + +(require 'forge-topic) + +;;; Options + +(defcustom forge-list-buffer-default-topic-filters + (forge--topics-spec :type 'topic :active t :state 'open :order 'newest) + "Filters initially used to limit topics listed in list buffers. + +This option controls which topics are listed when initially creating +a `forge-topics-mode' buffer. To temporarily change which topics are +listed in a given buffer, instead use \\`N m' (`forge-topics-menu')." + :package-version '(forge . "0.4.0") + :group 'forge + :type 'object) + +(defcustom forge-status-buffer-default-topic-filters + (forge--topics-spec :type 'topic :active t :state 'open :order 'newest) + "Filters initially used to limit topics listed in status buffers. + +This option controls which topics are listed when initially creating +a `magit-status-mode' buffer. To temporarily change which topics are +listed in a given buffer, instead use \\ \ +\\[forge-topics-menu] (`forge-topics-menu'). + +To initially list no topics, set the `type' slot to nil." + :package-version '(forge . "0.4.0") + :group 'forge + :type 'object) + +(defcustom forge-owned-accounts nil + "An alist of accounts that are owned by you. +This should include your username as well as any organization +that you own. Used by the commands `forge-list-owned-issues', +`forge-list-owned-pullreqs' and `forge-fork'. + +Each element has the form (ACCOUNT . PLIST). The following +properties are currently being used: + +`remote-name' The default name suggested by `forge-fork' for a + fork created within this account. If unspecified, then the + name of the account is used." + :package-version '(forge . "0.2.0") + :group 'forge + :type '(repeat (cons (string :tag "Account") plist))) + +(defcustom forge-owned-ignored nil + "A list of repositories that are ignored when listing those owned by you. +This is a list of package names. Used by the commands +`forge-list-owned-issues' and `forge-list-owned-pullreqs'." + :package-version '(forge . "0.2.0") + :group 'forge + :type '(repeat (string :tag "Name"))) + +;;; Faces + +(defface forge-suffix-active + '((t :inherit transient-value :weight bold)) + "Face used for suffixes whose effects is currently active." + :group 'forge-faces) + +(defface forge-suffix-active-and-implied + '((t :inherit transient-value :weight semibold)) + "Face used for suffixes whose effects is currently active and implied." + :group 'forge-faces) + +(defface forge-suffix-implied + '((t :inherit transient-value :weight normal)) + "Face used for suffixes whose effects is currently implied." + :group 'forge-faces) + +;;; Mode + +(defvar-keymap forge-topics-mode-map + :doc "Local keymap for Forge-Topic-List mode buffers." + :parent (make-composed-keymap forge-common-map magit-mode-map) + "RET" #'forge-visit-this-topic + "" #'forge-visit-this-topic + "o" #'forge-browse-this-topic + " " #'forge-topics-menu + " " #'forge-topic-menu + " " #'forge-refresh-buffer) + +(defvar forge-topics-mode-name '((:eval (forge-topics-buffer-desc))) + "Information shown in the mode-line for `forge-topics-mode'. +Must be set before `forge-topics' is loaded.") + +(define-derived-mode forge-topics-mode magit-mode forge-topics-mode-name + "Major mode for browsing a list of topics." + :interactive nil + (magit-hack-dir-local-variables)) + +(defun forge-topics-setup-buffer (&optional repo spec &rest params) + (let* ((global (or (plist-get params :global) + (and spec (oref spec global)))) + (repo (or repo + (and (not global) + (if-let* ((topic (forge-topic-at-point)) + (repo (forge-get-repository topic))) + repo + (forge-get-repository :tracked?))))) + (dir (or (and repo (forge-get-worktree repo)) "/")) + (buf (forge-topics-buffer-name repo)) + (buf (or (get-buffer buf) buf)) + (spec (cond (spec (clone spec)) + ((and (bufferp buf) + (buffer-local-value 'forge--buffer-topics-spec buf))) + ((clone forge-list-buffer-default-topic-filters))))) + (while-let ((key (pop params))) + (eieio-oset spec key (pop params))) + (unless (oref spec type) + (oset spec type 'topic)) + (unless (or repo global) + (error "Cannot determine repository")) + (magit-setup-buffer-internal #'forge-topics-mode nil + `((forge-buffer-repository + ,(and repo (oref repo id))) + (forge--buffer-topics-spec ,spec) + (forge-buffer-unassociated-p ,global)) + (get-buffer-create buf) + dir))) + +(defun forge-topics-refresh-buffer () + (magit-set-header-line-format (forge-topics-buffer-desc)) + (let ((topics (forge--list-topics + forge--buffer-topics-spec + (forge-get-repository :tracked?)))) + (magit-insert-section (topicbuf) + (cond + ((not topics) + (insert "No matching topics\n")) + ((not (oref forge--buffer-topics-spec grouped)) + (dolist (topic topics) + (forge--insert-topic topic 5))) + ((pcase-dolist (`(,_ . ,topics) + (--group-by (oref it repository) topics)) + (let ((repo (forge-get-repository (car topics)))) + (magit-insert-section (forge-repo repo) + (magit-insert-heading + (concat (propertize (format "%s/%s" + (oref repo owner) + (oref repo name)) + 'font-lock-face 'bold) + (format " (%s)" (length topics)))) + (dolist (topic topics) + (forge--insert-topic topic 5)))))))))) + +(defun forge-topics-buffer-desc () + (capitalize (concat (symbol-name (oref forge--buffer-topics-spec type)) "s"))) + +(defun forge-topics-buffer-name (&optional repo) + (if repo + (format "*forge-topics: %s*" (oref repo slug)) + "*forge-topics*")) + +;;; Commands +;;;; Menu + +;;;###autoload(autoload 'forge-topics-menu "forge-topics" nil t) +(transient-define-prefix forge-topics-menu () + "Control list of topics displayed in the current buffer." + :transient-suffix t + :transient-non-suffix #'transient--do-call + :transient-switch-frame nil + :refresh-suffixes t + :environment #'forge--menu-environment + :column-widths forge--topic-menus-column-widths + [:hide always ("q" forge-menu-quit-list)] + [forge--topic-menus-group + ["State" + ("a" forge-topics-filter-active) + ("o" forge-topics-filter-state-open) + ("c" forge-topics-filter-state-completed) + ("x" forge-topics-filter-state-unplanned)] + ["Status" + ("i" forge-topics-filter-status-inbox) + ("u" forge-topics-filter-status-unread) + ("p" forge-topics-filter-status-pending) + ("d" forge-topics-filter-status-done)] + ["Type" + ("t t" forge-topics-all-types) + ("t i" forge-topics-filter-issues) + ("t p" forge-topics-filter-pullreqs)]] + [forge--lists-group + ["Filter " + ("-m" forge-topics-filter-milestone) + ("-l" forge-topics-filter-labels) + ("-x" forge-topics-filter-marks) + ("-c" forge-topics-filter-author) + ("-a" forge-topics-filter-assignee) + ("-r" forge-topics-filter-reviewer) + ("-s" forge-topics-filter-saved)] + ["Display" + ("-O" forge-topics-set-order) + ("-L" forge-topics-set-limit) + ("-F" forge-topics-ungroup) + ("-G" forge-topics-group) + ("-S" forge-toggle-display-in-status-buffer) + ("-H" forge-toggle-topic-legend)]] + [forge--topic-legend-group] + (interactive) + (cond ((derived-mode-p 'forge-topics-mode 'magit-status-mode) + (transient-setup 'forge-topics-menu)) + ((derived-mode-p 'forge-notifications-mode) + (setq this-command 'forge-notifications-menu) + (transient-setup 'forge-notifications-menu)) + ((forge-list-topics)))) + +(transient-augment-suffix forge-topics-menu + :transient #'transient--do-replace + :if-not-derived '(forge-notifications-mode forge-repository-list-mode) + :inapt-if (lambda () (eq (oref transient--prefix command) 'forge-topics-menu)) + :inapt-face 'forge-suffix-active) + +(defvar-local forge--quit-keep-topic-menu nil) + +(defun forge-menu-quit-list () + "From a transient menu, quit the list buffer and the menu. + +If quitting the list buffer causes another topic, repository list or +notification list buffer to become current in the selected window, +then display the respective menu, otherwise display no menu." + (interactive) + (let ((keep-topic-menu forge--quit-keep-topic-menu)) + (when (derived-mode-p 'forge-topic-mode + 'forge-topics-mode + 'forge-repository-list-mode + 'forge-notifications-mode) + (kill-local-variable 'forge--quit-keep-topic-menu) + (quit-window)) + (cond ((derived-mode-p 'forge-topic-mode) + (setq transient--exitp 'replace) + (transient-setup (setq this-command 'forge-topic-menu))) + ((derived-mode-p 'forge-topics-mode) + (unless keep-topic-menu + (setq transient--exitp 'replace) + (transient-setup (setq this-command 'forge-topics-menu)))) + ((derived-mode-p 'forge-repository-list-mode) + (setq transient--exitp 'replace) + (transient-setup (setq this-command 'forge-repositories-menu))) + ((derived-mode-p 'forge-notifications-mode) + (setq transient--exitp 'replace) + (transient-setup (setq this-command 'forge-notifications-menu))) + (t + (setq transient--exitp t) + (transient--pre-exit) + (transient--stack-zap))))) + +;;;; List + +;;;###autoload(autoload 'forge-list-topics "forge-topics" nil t) +(transient-define-suffix forge-list-topics (&optional repo) + "List topics of the current repository." + :description "topics" + :inapt-if (lambda () (or (not (forge--get-repository:tracked?)) + (and (eq major-mode 'forge-topics-mode) + (not (oref forge--buffer-topics-spec global))))) + :inapt-face (lambda () (if (not (forge--get-repository:tracked?)) + 'transient-inapt-suffix + 'forge-suffix-active)) + (declare (interactive-only nil)) + (interactive) + (forge-topics-setup-buffer repo) + (transient-setup 'forge-topics-menu)) + +;;;###autoload(autoload 'forge-list-issues "forge-topics" nil t) +(transient-define-suffix forge-list-issues (&optional repo) + "List issues of the current repository." + :description "issues" + (declare (interactive-only nil)) + (interactive) + (forge-topics-setup-buffer repo nil :type 'issue) + (transient-setup 'forge-topics-menu)) + +;;;###autoload(autoload 'forge-list-pullreqs "forge-topics" nil t) +(transient-define-suffix forge-list-pullreqs (&optional repo) + "List pull-requests of the current repository." + :description "pull-requests" + (declare (interactive-only nil)) + (interactive) + (forge-topics-setup-buffer repo nil :type 'pullreq) + (transient-setup 'forge-topics-menu)) + +;;;###autoload(autoload 'forge-list-global-topics "forge-topics" nil t) +(transient-define-suffix forge-list-global-topics (&optional repo) + "List topics across all tracked repository." + :description "topics" + :inapt-if (lambda () (and (eq major-mode 'forge-topics-mode) + (oref forge--buffer-topics-spec global))) + :inapt-face 'forge-suffix-active + (declare (interactive-only nil)) + (interactive) + (forge-topics-setup-buffer repo nil :global t) + (transient-setup 'forge-topics-menu)) + +;;;###autoload(autoload 'forge-list-global-issues "forge-topics" nil t) +(transient-define-suffix forge-list-global-issues (&optional repo) + "List issues across all tracked repository." + :description "issues" + (declare (interactive-only nil)) + (interactive) + (forge-topics-setup-buffer repo nil :global t :type 'issue) + (transient-setup 'forge-topics-menu)) + +;;;###autoload(autoload 'forge-list-global-pullreqs "forge-topics" nil t) +(transient-define-suffix forge-list-global-pullreqs (&optional repo) + "List pull-requests across all tracked repository." + :description "pull-requests" + (declare (interactive-only nil)) + (interactive) + (forge-topics-setup-buffer repo nil :global t :type 'pullreq) + (transient-setup 'forge-topics-menu)) + +;;;; Type + +(defclass forge--topics-filter-type-command (transient-suffix) + ((type :initarg :type) + (definition + :initform (lambda (&optional repo) + (interactive) + (oset forge--buffer-topics-spec type + (oref (transient-suffix-object) type)) + (forge-refresh-buffer))) + (inapt-face :initform 'forge-suffix-active) + (inapt-if + :initform (lambda () + (eq (oref forge--buffer-topics-spec type) + (oref (transient-suffix-object) type)))))) + +(transient-define-suffix forge-topics-all-types () + :class 'forge--topics-filter-type-command :type 'topic + :description "topics") + +(transient-define-suffix forge-topics-filter-issues () + "List issues of the current repository." + :class 'forge--topics-filter-type-command :type 'issue + :description "issues") + +(transient-define-suffix forge-topics-filter-pullreqs () + "List pull-requests of the current repository." + :class 'forge--topics-filter-type-command :type 'pullreq + :description "pull-requests") + +;;;; Active + +(transient-define-suffix forge-topics-filter-active () + "Limit topic list to active topics." + :description "active" + :face (lambda () + (and (oref forge--buffer-topics-spec active) + 'forge-suffix-active)) + (interactive) + (oset forge--buffer-topics-spec active + (not (oref forge--buffer-topics-spec active))) + (forge-refresh-buffer)) + +;;;; State + +(defclass forge--topics-filter-state-command (transient-suffix) + ((state :initarg :state) + (definition + :initform (lambda () + (interactive) + (let ((want (oref (transient-suffix-object) state)) + (spec forge--buffer-topics-spec)) + (cond ((and (eq want 'open) + (oref spec active)) + (oset spec active nil) + (oset spec state want)) + ((equal (oref spec state) want) + (oset spec state nil)) + (t + (oset spec active nil) + (oset spec state want)))) + (forge-refresh-buffer))) + (description + :initform (lambda (suffix) + (let ((want (oref suffix state)) + (type (oref forge--buffer-topics-spec type))) + (pcase type + ((guard (atom want)) + (symbol-name want)) + ('topic (apply #'format "%s/%s" want)) + ('issue (symbol-name (car want))) + ('pullreq (symbol-name (cadr want))))))) + (face + :initform (lambda (suffix) + (let ((want (oref suffix state)) + (have (oref forge--buffer-topics-spec state)) + (active (oref forge--buffer-topics-spec active))) + (cond ((and (not active) + (equal have want)) + 'forge-suffix-active) + ((and (or active + (eq have 'open)) + (eq want 'open)) + (if (eq have want) + 'forge-suffix-active-and-implied + 'forge-suffix-implied)))))))) + +(transient-define-suffix forge-topics-filter-state-open () + "Limit topic list to open topics." + :class 'forge--topics-filter-state-command :state 'open) + +(transient-define-suffix forge-topics-filter-state-completed () + "Limit topic list to completed and merged topics." + :class 'forge--topics-filter-state-command :state '(completed merged)) + +(transient-define-suffix forge-topics-filter-state-unplanned () + "Limit topic list to unplanned and rejected topics." + :class 'forge--topics-filter-state-command :state '(unplanned rejected)) + +;;;; Status + +(defclass forge--topics-filter-status-command (transient-suffix) + ((status :initarg :status) + (definition + :initform (lambda () + (interactive) + (let* ((want (oref (transient-suffix-object) status)) + (spec forge--buffer-topics-spec) + (have (oref spec status)) + (active (oref spec active))) + (cond (active + (oset spec active nil) + (oset spec status want)) + ((eq have want) + (oset spec status nil)) + ((oset spec status want)))) + (forge-refresh-buffer))) + (description + :initform (lambda (suffix) (symbol-name (oref suffix status)))) + (face + :initform (lambda (suffix) + (let ((want (oref suffix status)) + (have (oref forge--buffer-topics-spec status)) + (active (oref forge--buffer-topics-spec active))) + (cond ((and (not active) + (equal have want)) + 'forge-suffix-active) + ((and (or active + (eq have 'inbox)) + (memq want '(inbox unread pending))) + (if (eq have want) + 'forge-suffix-active-and-implied + 'forge-suffix-implied)))))))) + +(transient-define-suffix forge-topics-filter-status-inbox () + "Limit topic list to unread and pending topics." + :class 'forge--topics-filter-status-command :status 'inbox) + +(transient-define-suffix forge-topics-filter-status-unread () + "Limit topic list to unread topics." + :class 'forge--topics-filter-status-command :status 'unread) + +(transient-define-suffix forge-topics-filter-status-pending () + "Limit topic list to pending topics." + :class 'forge--topics-filter-status-command :status 'pending) + +(transient-define-suffix forge-topics-filter-status-done () + "Limit topic list to done topics." + :class 'forge--topics-filter-status-command :status 'done) + +;;;; Filter + +(defclass forge--topics-filter-command (transient-suffix) + ((slot :initarg :slot) + (reader :initarg :reader) + (formatter :initarg :formatter :initform nil) + (definition + :initform (lambda () + (interactive) + (with-slots (slot reader) (transient-suffix-object) + (eieio-oset forge--buffer-topics-spec slot + (if (eieio-oref forge--buffer-topics-spec slot) + nil + (funcall reader))) + (forge-refresh-buffer)))) + (description + :initform (lambda (obj) + (with-slots (slot formatter) obj + (let ((value (eieio-oref forge--buffer-topics-spec slot))) + (if value + (format "%s %s" slot + (if formatter + (funcall formatter value) + (propertize (format "%s" value) + 'face 'forge-suffix-active))) + (format "%s" slot)))))))) + +(cl-defmethod initialize-instance :after + ((obj forge--topics-filter-command) &optional _slots) + (unless (slot-boundp obj 'reader) + (oset obj reader (intern (format "forge-read-topic-%s" (oref obj slot)))))) + +(transient-define-suffix forge-topics-filter-milestone () + "Read a milestone and limit topic list to topics with that milestone." + :class 'forge--topics-filter-command + :slot 'milestone + :formatter (lambda (m) (propertize m 'face 'forge-topic-label))) + +(transient-define-suffix forge-topics-filter-labels () + "Read labels and limit topic list to topics with one of these labels." + :class 'forge--topics-filter-command + :slot 'labels + :formatter (lambda (labels) (and labels (forge--format-labels labels " ")))) + +(transient-define-suffix forge-topics-filter-marks () + "Read marks and limit topic list to topics with one of these marks." + :class 'forge--topics-filter-command + :slot 'marks + :formatter (lambda (marks) (and marks (forge--format-marks marks " ")))) + +(transient-define-suffix forge-topics-filter-saved () + "Toggle whether to limit topic list to saved topics." + :class 'forge--topics-filter-command + :slot 'saved + :reader #'always + :description + (lambda () (forge--format-boolean 'saved "saved" forge--buffer-topics-spec))) + +(transient-define-suffix forge-topics-filter-author () + "Read an author and limit topic list to topics created by that author." + :class 'forge--topics-filter-command + :slot 'author + :reader (lambda () (forge--read-filter-by-user "Author"))) + +(transient-define-suffix forge-topics-filter-assignee () + "Read an assignee and limit topic list to topics assignee to that person." + :class 'forge--topics-filter-command + :slot 'assignee + :reader (lambda () (forge--read-filter-by-user "Assignee"))) + +(transient-define-suffix forge-topics-filter-reviewer () + "Read a reviewer and limit topic list to reviews requested from that person." + :class 'forge--topics-filter-command + :slot 'reviewer + :reader (lambda () (forge--read-filter-by-user "Reviewer"))) + +(defun forge--read-filter-by-user (prompt) + (let* ((repo (forge-get-repository :tracked)) + (choices (mapcar #'cadr (oref repo assignees)))) + (magit-completing-read prompt choices))) + +;;;; Display + +(transient-define-suffix forge-topics-set-order (order) + "Select order used to display topics in topic list." + :description + (lambda () + (format "order by %s" + (propertize (format "%s" (oref forge--buffer-topics-spec order)) + 'face 'bold))) + (interactive + (list (magit-read-char-case "Order by: " t + (?n "[n]ewest" 'newest) + (?o "[o]ldest" 'oldest) + (?u "[r]ecently updated" 'recently-updated) + (?U "[a]nciently updated" 'anciently-updated)))) + (oset forge--buffer-topics-spec order order) + (forge-refresh-buffer)) + +(transient-define-suffix forge-topics-set-limit (limit) + "Read maximal number of topics to be displayed in topic list." + :description + (lambda () + (if-let ((limit (oref forge--buffer-topics-spec limit))) + (format "limit to %s" (propertize (format "%s" limit) 'face 'bold)) + "no limit")) + (interactive (list (read-number "Limit number (0 for no limit): "))) + (oset forge--buffer-topics-spec limit (if (zerop limit) nil limit)) + (forge-refresh-buffer)) + +(transient-define-suffix forge-topics-group () + "Group topics by repository." + :description "group by repo" + :if (lambda () (oref forge--buffer-topics-spec global)) + :inapt-if (lambda () (oref forge--buffer-topics-spec grouped)) + :inapt-face 'forge-suffix-active + (interactive) + (oset forge--buffer-topics-spec grouped t) + (forge-refresh-buffer)) + +(transient-define-suffix forge-topics-ungroup () + "Show a flat topic list." + :description "single list" + :if (lambda () (oref forge--buffer-topics-spec global)) + :inapt-if-not (lambda () (oref forge--buffer-topics-spec grouped)) + :inapt-face 'forge-suffix-active + (interactive) + (oset forge--buffer-topics-spec grouped nil) + (forge-refresh-buffer)) + +;;; _ +(provide 'forge-topics) +;;; forge-topics.el ends here diff --git a/elpa/forge-20240523.1103/forge.el b/elpa/forge-20241218.2036/forge.el similarity index 88% rename from elpa/forge-20240523.1103/forge.el rename to elpa/forge-20241218.2036/forge.el index 5a4f93b3..81bcf9f3 100644 --- a/elpa/forge-20240523.1103/forge.el +++ b/elpa/forge-20241218.2036/forge.el @@ -7,6 +7,22 @@ ;; Homepage: https://github.com/magit/forge ;; Keywords: git tools vc +;; Package-Version: 20241218.2036 +;; Package-Revision: 3b80ace32c23 +;; Package-Requires: ( +;; (emacs "29.1") +;; (compat "30.0.0.0") +;; (closql "2.1.0") +;; (dash "2.19.1") +;; (emacsql "4.1.0") +;; (ghub "4.2.0") +;; (let-alist "1.0.6") +;; (magit "4.1.3") +;; (markdown-mode "2.6") +;; (seq "2.24") +;; (transient "0.8.0") +;; (yaml "0.5.5")) + ;; SPDX-License-Identifier: GPL-3.0-or-later ;; This file is free software: you can redistribute it and/or modify @@ -66,12 +82,7 @@ "Whether to add Forge's sections to `magit-status-sections-hook'. If you want to disable this, then you must set this to nil before -`forge' is loaded. - -If this is nil, then `forge-toggle-display-in-status-buffer' can -no longer do its job. It might be better to set the global value -of `forge-display-in-status-buffer' to nil instead. That way you -can still display topics on demand in the status buffer.") +`forge' is loaded.") (when forge-add-default-sections (magit-add-section-hook 'magit-status-sections-hook #'forge-insert-pullreqs nil t) @@ -109,15 +120,11 @@ is loaded, then `magit-mode-map' ends up being modified anyway.") (transient-insert-suffix 'magit-dispatch "o" '("N" "Forge" forge-dispatch)) - (transient-append-suffix 'magit-fetch "m" - '("n" "forge topics" forge-pull)) - (transient-append-suffix 'magit-fetch "n" - '("N" "forge notifications" forge-pull-notifications)) + (transient-append-suffix 'magit-fetch "m" '("n" forge-pull)) + (transient-append-suffix 'magit-fetch "n" '("N" forge-pull-notifications)) - (transient-append-suffix 'magit-pull "m" - '("n" "forge topics" forge-pull)) - (transient-append-suffix 'magit-pull "n" - '("N" "forge notifications" forge-pull-notifications)) + (transient-append-suffix 'magit-pull "m" '("n" forge-pull)) + (transient-append-suffix 'magit-pull "n" '("N" forge-pull-notifications)) (transient-append-suffix 'magit-branch "w" '("f" "pull-request" forge-checkout-pullreq)) diff --git a/elpa/forge-20241218.2036/forge.info b/elpa/forge-20241218.2036/forge.info new file mode 100644 index 00000000..69d64924 --- /dev/null +++ b/elpa/forge-20241218.2036/forge.info @@ -0,0 +1,1984 @@ +This is forge.info, produced by makeinfo version 6.8 from forge.texi. + + Copyright (C) 2018-2024 Jonas Bernoulli + + + You can redistribute this document 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 document 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. + +INFO-DIR-SECTION Emacs +START-INFO-DIR-ENTRY +* Forge: (forge). Access Git Forges from Magit. +END-INFO-DIR-ENTRY + + +File: forge.info, Node: Top, Next: Introduction, Up: (dir) + +Forge User and Developer Manual +******************************* + +Forge allows you to work with Git forges, currently Github and Gitlab, +from the comfort of Magit and Emacs. + +This manual is for Forge version 0.4.5. + + Copyright (C) 2018-2024 Jonas Bernoulli + + + You can redistribute this document 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 document 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. + +* Menu: + +* Introduction:: +* Initial Setup:: +* Initial Pull:: +* Getting Started:: +* Lists and Menus:: +* Visiting Topics:: +* Creating Topics and Posts:: +* Editing Topics:: +* Pulling:: +* Branching:: +* Miscellaneous Commands:: +* Miscellaneous Options:: +* How Forge Detection Works:: +* Supported Forges and Hosts:: +* FAQ:: +* Keystroke Index:: +* Function and Command Index:: +* Variable Index:: + +— The Detailed Node Listing — + +Initial Setup + +* Setup for Github.com: Setup for Githubcom. +* Setup for Another Github Instance:: +* Setup for Gitlab.com: Setup for Gitlabcom. +* Setup for Another Gitlab Instance:: +* Setup a Partially Supported Host:: + +Supported Forges and Hosts + +* Supported Forges:: +* Partially Supported Forges:: +* Supported Semi-Forges:: + +FAQ + +* ‘error in process filter HTTP Error 502, "Bad gateway"’: error in process filter HTTP Error 502 "Bad gateway". + + + +File: forge.info, Node: Introduction, Next: Initial Setup, Prev: Top, Up: Top + +1 Introduction +************** + +Forge allows you to work with Git forges, currently Github and Gitlab, +from the comfort of Magit and Emacs. + + Forge fetches issues, pull-requests and other data using the forge’s +API and stores the retrieved information in a local database. +Additionally it fetches pull-request references using Git. + + +File: forge.info, Node: Initial Setup, Next: Initial Pull, Prev: Introduction, Up: Top + +2 Initial Setup +*************** + +Please first do the common setup below and then carefully follow the +instructions for your forge instance. Once you have completed the +setup, you can start tracking repositories (see *note Initial Pull::). + + If you run into difficulties during setup or the initial pull, then +please also see *note How Forge Detection Works:: and *note +(ghub)Getting Started::. + +* Menu: + +* Setup for Github.com: Setup for Githubcom. +* Setup for Another Github Instance:: +* Setup for Gitlab.com: Setup for Gitlabcom. +* Setup for Another Gitlab Instance:: +* Setup a Partially Supported Host:: + +Common Setup +============ + +Loading Magit doesn’t cause Forge to be loaded automatically. Adding +something like this to your init file takes care of that: + + (with-eval-after-load 'magit + (require 'forge)) + +Or if you use ‘use-package’: + + (use-package forge + :after magit) + + By default Forge adds some bindings to Magit keymaps and menus, and +some sections to Magit buffers. If you would like to prevent that, you +have to set ‘forge-add-default-bindings’ and/or +‘forge-add-default-sections’ to ‘nil’, before ‘magit’ (not just ‘forge’) +is loaded. + + +File: forge.info, Node: Setup for Githubcom, Next: Setup for Another Github Instance, Up: Initial Setup + +2.1 Setup for Github.com +======================== + +Set your Username +----------------- + +First inform Forge about your username: + + git config --global github.user USERNAME + + If you need to identify as another user in a particular repository, +then you have to set that variable locally: + + cd /path/to/repo + git config --local github.user USERNAME + +Create and Store an Access Token +-------------------------------- + +Visit in a browser to generate a +new "classic" token using the ‘repo’, ‘user’ and ‘read:org’ scopes. Do +not close the browser window just yet, because the token will only be +shown once. + + The built-in Auth-Source (*note (auth)Top::) package is used to store +the token generated in the previous step. The ‘auth-sources’ variable +controls how and where Auth-Source keeps its secrets. The default value +is a list of three files: ‘("~/.authinfo" "~/.authinfo.gpg" +"~/.netrc")’, but that can lead to confusing behavior, so you should +make sure that only one of these files exists, and then you should also +adjust the value of the variable to only ever use that file, for +example: + + (setq auth-sources '("~/.authinfo")) + + In ‘~/.authinfo’ secrets are stored in plain text. If you don’t want +that, then you should use the encrypted ‘~/.authinfo.gpg’ instead: + + (setq auth-sources '("~/.authinfo.gpg")) + + Make sure you put one of these forms in your init file *and* to +evaluate it in the current Emacs instance as well, by placing the cursor +after the final closing parenthesis and typing ‘C-x C-e’ +(‘eval-last-sexp’). + + Next add a line like the following to the chosen file: + + machine api.github.com login USERNAME^forge password TOKEN + + • The value of ‘machine’ must be ‘api.github.com’. Variations of + this won’t work. + + • USERNAME must be the same as the value used for the ‘github.user’ + Git variable above. You *must* append ‘^forge’ to that, without + any space in between. + + • TOKEN is the token you generated earlier. + + Finish by typing ‘M-x auth-source-forget-all-cached RET’. If you +don’t do this, then Auth-Source may fail to look up the token. + + +File: forge.info, Node: Setup for Another Github Instance, Next: Setup for Gitlabcom, Prev: Setup for Githubcom, Up: Initial Setup + +2.2 Setup for Another Github Instance +===================================== + +Before you setup a Github instance that is not , +please set that up first. The setup for is easier +and if that works, but the setup for the other Github instance fails, +then we can tentatively narrow the issue down to the parts that differ +between and other instances. + +Tell Forge about the Instance +----------------------------- + +While Forge knows about , it does not know about +your other Github instances. Forge instances are configured using the +option ‘forge-alist’ (also see its docstring). The entry for + in that variable looks like this: + + ("github.com" ; GITHOST + "api.github.com" ; APIHOST + "github.com" ; WEBHOST and INSTANCE-ID + forge-github-repository) ; CLASS + + You have to add an entry for your instance. For example, assuming +you company uses , this might be correct: + + (push '("example.com" ; GITHOST + "api.example.com" ; APIHOST + "example.com" ; WEBHOST and INSTANCE-ID + forge-github-repository) ; CLASS + forge-alist) + + Your company may use hostnames that follow a different format. You +should be able to easily determine and verify GITHOST and WEBHOST, but +determining APIHOST is more difficult; you might have to ask a coworker. +APIHOST could be something like ‘api.example.com’, but it could also be +something like ‘example.com/api’. + + If the REST API’s end point is ‘/v3’ and the GraphQL API’s end point +is ‘/graphql’, then use something like ‘example.com/v3’ as APIHOST. +This is a historic accident. See +. + + We will use INSTANCE-ID (aka WEBHOST) and APIHOST below. + +Set your Username +----------------- + +Inform Forge about your username for the Github instance in question: + + git config --global github.INSTANCE-ID.user USERNAME + + So if INSTANCE-ID is ‘example.com’ and USERNAME is ‘tarsius’ then +use: + + git config --global github.example.com.user tarsius + +Create and Store an Access Token +-------------------------------- + +Visit your forge in a browser. Follow a link to "Settings", from there +to "Developer settings", from there to "Personal access tokens", and +finally to "Tokens (classic)". On that page generate a new token using +the ‘repo’, ‘user’ and ‘read:org’ scopes. Do not close the browser +window just yet, because the token will only be shown once. + + The built-in Auth-Source (*note (auth)Top::) package is used to store +the token generated in the previous step. The ‘auth-sources’ variable +controls how and where Auth-Source keeps its secrets. The default value +is a list of three files: ‘("~/.authinfo" "~/.authinfo.gpg" +"~/.netrc")’, but that can lead to confusing behavior, so you should +make sure that only one of these files exists, and then you should also +adjust the value of the variable to only ever use that file, for +example: + + (setq auth-sources '("~/.authinfo")) + + In ‘~/.authinfo’ secrets are stored in plain text. If you don’t want +that, then you should use the encrypted ‘~/.authinfo.gpg’ instead: + + (setq auth-sources '("~/.authinfo.gpg")) + + Make sure you put one of these forms in your init file *and* to +evaluate it in the current Emacs instance as well, by placing the cursor +after the final closing parenthesis and typing ‘C-x C-e’ +(‘eval-last-sexp’). + + Next add a line like the following to the chosen file: + + machine APIHOST login USERNAME^forge password TOKEN + + • APIHOST must be the same as the second element of the entry we + added to ‘forge-alist’. In the above example that would be + ‘api.example.com’. Do *not* instead use GITHOST or INSTANCE-ID + (aka WEBHOST). + + • USERNAME must be the same username you used above as the value of + the Git variable. You *must* append ‘^forge’ to that, without any + space in between. + + • TOKEN is the token you generated earlier. + + Finish by typing ‘M-x auth-source-forget-all-cached RET’. If you +don’t do this, then Auth-Source may fail to look up the token. + + +File: forge.info, Node: Setup for Gitlabcom, Next: Setup for Another Gitlab Instance, Prev: Setup for Another Github Instance, Up: Initial Setup + +2.3 Setup for Gitlab.com +======================== + +Set your Username +----------------- + +First inform Forge about your username: + + git config --global gitlab.user USERNAME + + If you need to identify as another user in a particular repository, +then you have to set that variable locally: + + cd /path/to/repo + git config --local gitlab.user USERNAME + +Create and Store an Access Token +-------------------------------- + +Visit in a +browser to generate a new token using the ‘api’, ‘read_api’ and +‘read_user’ scopes. Do not close the browser window just yet, because +the token will only be shown once. + + The built-in Auth-Source (*note (auth)Top::) package is used to store +the token generated in the previous step. The ‘auth-sources’ variable +controls how and where Auth-Source keeps its secrets. The default value +is a list of three files: ‘("~/.authinfo" "~/.authinfo.gpg" +"~/.netrc")’, but that can lead to confusing behavior, so you should +make sure that only one of these files exists, and then you should also +adjust the value of the variable to only ever use that file, for +example: + + (setq auth-sources '("~/.authinfo")) + + In ‘~/.authinfo’ secrets are stored in plain text. If you don’t want +that, then you should use the encrypted ‘~/.authinfo.gpg’ instead: + + (setq auth-sources '("~/.authinfo.gpg")) + + Make sure you put one of these forms in your init file *and* to +evaluate it in the current Emacs instance as well, by placing the cursor +after the final closing parenthesis and typing ‘C-x C-e’ +(‘eval-last-sexp’). + + Next add a line like the following to the chosen file: + + machine gitlab.com/api/v4 login USERNAME^forge password TOKEN + + • The value of ‘machine’ must be ‘gitlab.com/api/v4’. Variations of + this won’t work. + + • USERNAME must be the same as the value used for the ‘gitlab.user’ + Git variable above. You *must* append ‘^forge’ to that, without + any space in between. + + • TOKEN is the token you generated earlier. + + Finish by typing ‘M-x auth-source-forget-all-cached RET’. If you +don’t do this, then Auth-Source may fail to look up the token. + + +File: forge.info, Node: Setup for Another Gitlab Instance, Next: Setup a Partially Supported Host, Prev: Setup for Gitlabcom, Up: Initial Setup + +2.4 Setup for Another Gitlab Instance +===================================== + +Before you setup a Gitlab instance that is not , +please set that up first. The setup for is easier +and if that works, but the setup for the other Gitlab instance fails, +then we can tentatively narrow the issue down to the parts that differ +between and other instances. + +Tell Forge about the Instance +----------------------------- + +While Forge knows about (and a few other well-known +instances, see its value) it has to be taught about other Gitlab +instances. Forge instances are configured using the option +‘forge-alist’ (also see its docstring). The entry for + in that variable looks like this: + + ("gitlab.com" ; GITHOST + "gitlab.com/api/v4" ; APIHOST + "gitlab.com" ; WEBHOST and INSTANCE-ID + forge-gitlab-repository) ; CLASS + + For historic reasons, APIHOST actually has to be a host followed by a +path. + + You have to add an entry for your instance. For example, assuming +you company/organisation uses , this might be +correct: + + (push '("example.com" ; GITHOST + "example.com/api/v4" ; APIHOST + "example.com" ; WEBHOST and INSTANCE-ID + forge-gitlab-repository) ; CLASS + forge-alist) + + Your company may use hostnames that follow a different format. You +should be able to easily determine and verify GITHOST and WEBHOST, but +determining APIHOST is more difficult; you might have to ask a +colleague. + + We will use INSTANCE-ID (aka WEBHOST) and APIHOST below. + +Set your Username +----------------- + +Inform Forge about your username for the Gitlab instance in question: + + git config --global gitlab.INSTANCE-ID.user USERNAME + + So if INSTANCE-ID is ‘example.com’ and USERNAME is ‘tarsius’ then +use: + + git config --global gitlab.example.com.user tarsius + +Create and Store an Access Token +-------------------------------- + +Visit your forge in a browser. Follow a link to "Preferences" and from +there to "Access Tokens". On that page generate a new "Personal access +token" using the ‘api’, ‘read_api’ and ‘read_user’ scopes. Do not close +the browser window just yet, because the token will only be shown once. + + The built-in Auth-Source (*note (auth)Top::) package is used to store +the token generated in the previous step. The ‘auth-sources’ variable +controls how and where Auth-Source keeps its secrets. The default value +is a list of three files: ‘("~/.authinfo" "~/.authinfo.gpg" +"~/.netrc")’, but that can lead to confusing behavior, so you should +make sure that only one of these files exists, and then you should also +adjust the value of the variable to only ever use that file, for +example: + + (setq auth-sources '("~/.authinfo")) + + In ‘~/.authinfo’ secrets are stored in plain text. If you don’t want +that, then you should use the encrypted ‘~/.authinfo.gpg’ instead: + + (setq auth-sources '("~/.authinfo.gpg")) + + Make sure you put one of these forms in your init file *and* to +evaluate it in the current Emacs instance as well, by placing the cursor +after the final closing parenthesis and typing ‘C-x C-e’ +(‘eval-last-sexp’). + + Next add a line like the following to the chosen file: + + machine APIHOST login USERNAME^forge password TOKEN + + • APIHOST must be the same as the second element of the entry we + added to ‘forge-alist’. In the above example that would be + ‘example.com/api/v4’. Do *not* instead use GITHOST or INSTANCE-ID + (aka WEBHOST). + + • USERNAME must be the same username you used above as the value of + the Git variable. You *must* append ‘^forge’ to that, without any + space in between. + + • TOKEN is the token you generated earlier. + + Finish by typing ‘M-x auth-source-forget-all-cached RET’. If you +don’t do this, then Auth-Source may fail to look up the token. + + +File: forge.info, Node: Setup a Partially Supported Host, Prev: Setup for Another Gitlab Instance, Up: Initial Setup + +2.5 Setup a Partially Supported Host +==================================== + +Forge currently only supports the Github and Gitlab APIs. + + It does however partially support a few additional forge types (see +*note Partially Supported Forges::) and other lighter weight software +used to host Git repositories, which also provide a web interfaces (see +*note Supported Semi-Forges::). Forge doesn’t use the APIs of such +forges, but registering the host and adding repositories to the local +database at least enables the use of commands such as ‘forge-browse’. + +Tell Forge about the Instance +----------------------------- + +A few hosts, which use partially supported forge types, are available +out-of-the-box, because they have an entry in the default value of +option ‘forge-alist’ (also see its docstring). For example, the entry +for in that variable looks like this: + + ("codeberg.org" ; GITHOST + "codeberg.org/api/v1" ; APIHOST + "codeberg.org" ; WEBHOST and INSTANCE-ID + forge-gitea-repository) ; CLASS + + To be able to add repositories from a, so far, unknown forge instance +to your local database, you have to add an entry for that instance to +‘forge-alist’. For example, assuming you use another Gitea instance, +hosted at , this might be correct: + + (push '("example.com" ; GITHOST + "example.com/api/v1" ; APIHOST + "example.com" ; WEBHOST and INSTANCE-ID + forge-gitea-repository) ; CLASS + forge-alist) + + Look at ‘forge-alist’ entries of other hosts using the same forge +type as the instance you are configuring, to see what format *might* be +appropriate. You should be able to easily determine and verify GITHOST +and WEBHOST, but determining APIHOST is more difficult; you might have +to ask a colleague. APIHOST could be something like +‘example.com/api/vi’, but it could also be something like +‘api.example.com’. + +Add Support for Additional Forge Types +-------------------------------------- + +For each fully or partially supported forge type, Forge defines at least +a class. The following example is taken from ‘forge-semi.el’: + + (defclass forge-cgit-repository (forge-noapi-repository) + ((commit-url-format :initform "https://%h/%p.git/commit/?id=%r") + (branch-url-format :initform "https://%h/%p.git/log/?h=%r") + (remote-url-format :initform "https://%h/%p.git/about")) + "Cgit from https://git.zx2c4.com/cgit/about. + Different hosts use different url schemata, so we need multiple + classes. See their definitions in \"forge-semi.el\".") + + Once you add a host using that class to ‘forge-alist’ and then a +repository from that host to the local database, you will be able to use +commands such as ‘forge-browse-branch’ (but not much more). + + If you want to add a repository from another host, which happens to +use another software or another URL schemata, then you might have to +define an additional class first. See ‘forge-semi.el’ for simple +examples and grep for ‘defclass forge-.*-repository’ for more complex +ones. + + +File: forge.info, Node: Initial Pull, Next: Getting Started, Prev: Initial Setup, Up: Top + +3 Initial Pull +************** + +To start using Forge in a certain repository, visit the Magit status +buffer for that repository and type ‘N / a’ (‘forge-add-repository’). +You are given a choice to pull all topics, all topics that were updated +after a certain date, or only individual topics. + + Beside adding the repository to the database, this also adds a new +value to the Git variable ‘remote..fetch’, which causes all +pull-request refs (‘+refs/pull/*/head:refs/pullreqs/*’ for Github) to be +fetched by Git. + + Note that it is possible to use the same command to add any +repository from a supported forge to the database, without cloning the +Git repository first. + + The initial fetch can take a while but most of the work is done +asynchronously. Storing the information in the database is done +synchronously though, so there can be a noticeable hang at the end. +Subsequent fetches are much faster. + + Fetching issues from Github is much faster than fetching from other +forges, because making a handful of GraphQL requests, is much faster +than making hundreds of REST requests. + + +File: forge.info, Node: Getting Started, Next: Lists and Menus, Prev: Initial Pull, Up: Top + +4 Getting Started +***************** + +Much like Git stores information in a local repository and does not +require a constant internet connection, Forge retrieves additional +information using a forge’s API and stores that in a local database. + + Forge’s equivalent of ‘git clone’ is ‘forge-add-repository’, which +has to be run, before most of Forges features become available in the +local clone of a Git repository. + +‘N / a’ (‘forge-add-repository’) + This command guides the user through the process of adding a + repository to the local database. + + Note that it is possible to add a repository to the local database, + without pulling all the data, which is useful if you just want to + create a single issue or pull-request in a repository, but are not + interested in existing topics, e.g., because you do not regularly + contribute to that repository. + + Also note that you can add a repository to the local database, even + if no local Git clone exists. + + Like with Git, you have to explicitly pull remote changes, at your +leisure, using ‘forge-pull’. + +‘f n’ (‘forge-pull’) +‘N f f’ + This command uses a forge’s API to fetch topics and other + information about the current repository, and stores the fetched + information in the database. + + If the current repository isn’t being tracked in the local database + yet, then this command pivots to behave like + ‘forge-add-repository’. + + Forge adds two additional sections to Magit’s status buffer, which +list open and/or pending issues and pull-requests. Typing ‘RET’, while +the cursor is on a topic section, shows more information about that +topic in a separate buffer. Typing ‘RET’ on a topic list section, shows +that list in a separate buffer, where you can apply different filters. + + The other main entry point to the functionality provided by Forge is +the ‘forge-dispatch’ menu. + +‘N’ (‘forge-dispatch’) + This prefix command is available in all Magit buffers and provides + access to most of the available Forge commands. See the following + sections for information about the available commands. + + +File: forge.info, Node: Lists and Menus, Next: Visiting Topics, Prev: Getting Started, Up: Top + +5 Lists and Menus +***************** + +Topics are listed in two sections in Magit’s status buffer, but can also +be listed in dedicated buffers. Likewise individual topics can be +visited in separate buffers. In both cases this can be done by placing +the cursor on the respective section in the status buffer and typing +‘RET’, or by invoking the appropriate command from Forge’s main menu, on +‘N’ (‘forge-dispatch’). + + List commands and corresponding menu commands exist for topics, +notifications and repositories, but there isn’t always an exclusive +mapping from menu to buffer. The main menu (‘forge-dispatch’), the +configuration menu (‘forge-configure’), the menu which controls the +current topic or the topic at point (‘forge-topic-menu’), and the menu +which controls the topics listed in the current buffer +(‘forge-topics-menu’), are useful in more than one major mode. + + All of these menus feature bindings to directly switch to the other +appropriate menus. So it is enough to remember that ‘N’ always brings +up the dispatch menu; you can always navigate to another menu from +there. + + ‘C-c C-c’ brings up the most appropriate menu for the current buffer. +In Magit’s status buffer the most appropriate menu is Magit’s own +dispatch menu (‘magit-dispatch’), so here the quickest way to invoke +Forge’s dispatch menu is ‘N’. Even in Magit’s status buffer, when the +cursor is an individual topic or on a topic list section, ‘C-c C-c’ +opens the respective menu (‘forge-topics-menu’ or ‘forge-topic-menu’). + + The following sections describe most of the available menu and list +commands. For ‘forge-topic-menu’, see *note Editing Topics::. + +Dispatch and configuration menus +================================ + +‘N’ (‘forge-dispatch’) + This prefix menu command is available in all Magit buffers and + provides access to most of the available Forge commands. See the + following sections for information about the available commands. + +‘N m c’ (‘forge-configure’) + This command displays a menu used to configure the current + repository and some global settings as well. + +Topic menu and list commands +============================ + +‘N m f’ (‘forge-topics-menu’) +‘C-c C-c [in topics list buffer/section]’ + This command displays a menu used to control the list of topics + displayed in the current buffer. + + Note that this command can not only be used in buffers dedicated to + listing topics, but also in Magit’s status buffer. + +‘N l t’ (‘forge-list-topics’) + This command lists the current repository’s issues in a separate + buffer. If the list buffer already exists, this command only + ensures that all types of topics are listed. If any other filters + are in effect, they are left intact. TODO fix preserving type + +‘ [on "Issues" status section]’ (‘forge-list-issues’) + This command lists the current repository’s issues in a separate + buffer. If the list buffer already exists, this command limits the + list to issues. If any other filters are in effect, they are left + intact. + +‘ [on "Pull requests" status section]’ (‘forge-list-pullreqs’) + This command lists the current repository’s pull-requests in a + separate buffer. If the list buffer already exists, this command + limits the list to pull-requests. If any other filters are in + effect, they are left intact. + +‘N l g’ (‘forge-list-global-topics’) + This command lists topics across all tracked repository. If the + list buffer already exists, filters except for the type filter are + left in effect. + + -- Command: forge-list-global-issues + This command lists issues across all tracked repository. If the + list buffer already exists, filters except for the type filter are + left in effect. + + -- Command: forge-list-global-pullreqs + This command lists pull-requests across all tracked repository. If + the list buffer already exists, filters except for the type filter + are in effect. + +Notification menu and list commands +=================================== + +‘N m n’ (‘forge-notifications-menu’) +‘C-c C-c’ + This command displays a menu used to control the list of + notifications displayed in the current buffer. + +‘N l n’ (‘forge-list-notifications’) + This command lists all notifications for all forges in a separate + buffer. + +Repository menu and list commands +================================= + +‘N m r’ (‘forge-repositories-menu’) +‘C-c C-c’ + This command displays a menu used to control the list of + repositories displayed in the current buffer. + +‘N l r’ (‘forge-list-repositories’) + This command lists all known repositories in a separate buffer. + Here "known" means that an entry exists in the local database. + +‘ [on repository]’ (‘forge-visit-this-repository’) + This commands visits the repository at point in a separate buffer. + +‘o [in forge-repositories-menu]’ (‘forge-list-owned-repositories’) + This command lists all known repositories that belong to the user + in a separate buffer. Here "known" means that an entry exists in + the local database. Only Github is supported for now. + + The below options controls which repositories are considered to be +owned by the user. They are additionally used by ‘forge-fork’. + + -- User Option: forge-owned-accounts + This is an alist of accounts that are owned by you. This should + include your username as well as any organization that you own. + + Each element has the form ‘(ACCOUNT . PLIST)’. The following + properties are currently being used: + + • ‘remote-name’ The default name suggested by ‘forge-fork’ for a + fork created within this account. If unspecified, then the + name of the account is used. + + Example: ‘(("tarsius") ("emacsmirror" remote-name "mirror"))’. + + -- User Option: forge-owned-ignored + This is a list of repository names that are considered to not be + owned by you, even though they would have been considered to be + owned by you based on ‘forge-owned-accounts’. + +Exiting menus and lists +======================= + +To exit a menu, type ‘C-g’. If the menu was invoked from another menu +and that menu is useful in the current buffer, then that menu becomes +active again. If that happens and you actually want to quit all menus, +then just type ‘C-g’ again. You can also directly exit all menus by +using ‘C-q’, instead of ‘C-g’. + + Type ‘q’ to quit not only the menu, but also the list or topic detail +buffer. That binding is also available when no menu is active, in which +case it will simply quit the buffer. When invoked from a menu, then +this binding may return to another list buffer, in which case some menu +may also remain active. + +Default topic filters +===================== + + -- User Option: forge-status-buffer-default-topic-filters + This option specifies the filters initially used to limit topics + listed in topic list buffers. + + -- User Option: forge-status-buffer-default-topic-filters + This option specifies the filters initially used to limit topics + listed in Magit status buffers. + + Also see *note Topic sections in Magit status buffers::. + +Topic sections in Magit status buffers +====================================== + +Forge arranges for certain issues and pull-requests to be list in Magit +status buffers, by adding the following functions to +‘magit-status-sections-hook’. + + Which topics are listed initially is customizable using option +‘forge-status-buffer-default-topic-filters’ and can be changed +temporarily for the current buffer, using ‘N m f’ (‘forge-topics-menu’). + + -- Function: forge-insert-issues + This function inserts a list of issues, by default a list of + "active" issues. + + -- Function: forge-insert-pullreqs + This function inserts a list of pull-requests, by default a list of + "active" pull-requests. + + Forge used to provide additional functions to insert hard-coded topic +subsets, but they were removed in favor of the more flexible approach +described above. If you miss the removed sections, you can use the new +‘forge-insert-topics’ helper function to define your own section +inserter functions. See its docstring for more information. + + If you don’t want any topic list sections to be displayed in Magit +status buffers, set ‘forge-add-default-sections’ to ‘nil’ before ‘magit’ +is loaded. + + +File: forge.info, Node: Visiting Topics, Next: Creating Topics and Posts, Prev: Lists and Menus, Up: Top + +6 Visiting Topics +***************** + +The commands, accessible from ‘forge-topic-menu’ (on ‘C-return’), act on +the topic at point; so this menu is useful in buffers dedicated to +listing topics and notifications (which correspond to topics), but also +in the status buffer (which also lists topics). In buffers dedicated to +showing details about a single topic, these commands act on that topic; +so this menu can be used there too. + + To switch to this menu from another menu use ‘m s’. If the cursor is +on a topic or the current buffer visits a topic. + + To display details about a topic in a separate buffer and at the same +time display the topic menu, invoke ‘forge-topic-menu’ with a prefix +argument, i.e., ‘C-u RET’. + +‘ [on topic]’ (‘forge-visit-this-topic’) + This commands visits the topic at point in a separate buffer. When + invoked with a prefix argument then it not only visits the topic in + a separate buffer, it at the same time displays + +‘N v t’ (‘forge-visit-topic’) +‘N v i’ (‘forge-visit-issue’) +‘N v p’ (‘forge-visit-pullreq’) + These commands read a topic, issue or pull-request and visit it in + a separate buffer. + +‘C-c C-o’ (‘forge-browse’) +‘o [on topic in topic list]’ (‘forge-browse-this-topic’) +‘o [on repository in repository list]’ (‘forge-browse-this-repository’) + These commands visit the topic, issue(s), pull-request(s), post, + branch, commit, remote or repository at point in a browser. + + -- Command: forge-browse-commit + -- Command: forge-browse-branch + -- Command: forge-browse-repository +‘N b t’ (‘forge-browse-topic’) +‘N b i’ (‘forge-browse-issue’) +‘N b p’ (‘forge-browse-pullreq’) +‘N b r’ (‘forge-browse-remote’) +‘N b I’ (‘forge-browse-issues’) +‘N b P’ (‘forge-browse-pullreqs’) + These commands read a topic, issue(s), pull-request(s), branch, + commit, remote or repository, and open it in a browser. + + +File: forge.info, Node: Creating Topics and Posts, Next: Editing Topics, Prev: Visiting Topics, Up: Top + +7 Creating Topics and Posts +*************************** + +We call both issues and pull-requests "topics". The contributions to +the conversation are called "posts". The initial topic description is +also called a post. + + Creating a new topic or post and editing an existing post work +similarly to now creating a new commit or editing the message of an +existing commit works in Magit. In both cases the message has to be +written in a separate buffer and then the process has to be finished or +canceled using a separate command. The following commands drop you into +such a buffer. + +‘N c p’ (‘forge-create-pullreq’) +‘C-c C-n [on "Pull requests" section]’ + This command creates a new pull-request for the current repository. + +‘N c i’ (‘forge-create-issue’) +‘C-c C-n [on "Issues" section]’ + This command creates a new issue for the current repository. + +‘C-c C-n’ (‘forge-create-post’) +‘C-c C-r’ + This command creates a new post on an existing topic. It is only + available in buffers that visit an existing topic. + + If the region is active and marks part of an existing post, then + that part of the post is quoted. When a prefix argument is used, + then the complete post, which point is currently on, is quoted. + + The following commands are available in buffers used to edit posts: + +‘C-c C-c’ (‘forge-post-submit’) + This command submits the post that is being edited in the current + buffer. + +‘C-c C-k’ (‘forge-post-cancel’) + This command cancels the post that is being edited in the current + buffer. + +‘C-c C-e’ (‘forge-post-dispatch’) + This prefix command features the above two commands as suffixes, + and when creating a pull-request also the following command. More + suffix commands will likely be added in the future. + +‘C-c C-e d’ (‘forge-post-toggle-draft’) + This command toggles whether the pull-request being created is a + draft. + + +File: forge.info, Node: Editing Topics, Next: Pulling, Prev: Creating Topics and Posts, Up: Top + +8 Editing Topics +**************** + +Many details about a topic can be changed from the buffer that visits +that topic, but also from topic lists, if the cursor is placed on the +topic to be edited. However, to edit the posts on a topic, the topic +has to be visited in its own buffer. + +‘C-c C-e [on a post section]’ (‘forge-edit-post’) + This command visits an existing post in a separate buffer, it can + only be invoked from a topic buffer, when the cursor is on the post + to be edited. + + Editing an existing post is similar to creating a new post, as + described in the previous section. + +‘C-c C-k [on a post section]’ (‘forge-delete-comment’) + This command deletes the post the cursor is on. The initial + message that was written when the topic was created, cannot be + deleted, only replies to that. + +‘N m s’ (‘forge-topic-menu’) +‘C- [on a topic section]’ + This command displays a menu used to edit details about the topic + the cursor is on or that is being visited in the current buffer. + E.g., it can be used to change the status of the topic or to apply + labels to it. Additionally it features a few commands that act on + that topic. + + Details about a topic, such as its status and labels, can +alternatively be edited by visiting the topic in its own buffer, +navigating to the header that displays the detail and then typing ‘C-c +C-e’. This older approach is still available, but it is usually much +faster to use the menu. + + +File: forge.info, Node: Pulling, Next: Branching, Prev: Editing Topics, Up: Top + +9 Pulling +********* + +The commands that fetch forge data are available the Forge’s main menu +(‘forge-dispatch’ on ‘N’) and from the same menu (‘magit-fetch’ on ‘f’) +that is used to fetch Git data. If ‘magit-pull-or-fetch’ is non-nil, +then they are also available from the ‘magit-pull’ menu (on ‘F’). + + With Git you have to explicitly pull Git data to make it available in +the local repository. Forge works the same; you have to explicitly pull +to pull data using the forge’s API and storing in the local database. +This is less disruptive, more reliable, familiar and easier to +understand than if Forge pulled by itself at random intervals. It might +however mean that you occasionally invoke a command expecting the most +recent data to be available and then have to abort and pull first. The +same can happen with Git, e.g., you might attempt to merge a branch that +you know exists but haven’t actually pulled yet. + +‘f n’ (‘forge-pull’) +‘N f f’ + This command uses a forge’s API to fetch topics and other + information about the current repository and stores the fetched + information in the database. + + If the current repository is still untracked locally, or the + current repository cannot be determined, this command instead + behaves like ‘forge-add-repository’, i.e., it adds the repository + to the database and then performs the initial pull. + +‘f N’ (‘forge-pull-notifications’) +‘N f n’ + This command uses a forge’s API to fetch all notifications from + that forge, including, but not limited to, the notifications for + the current repository. + + Fetching notifications fetches associated topics even for + repositories that you have not yet explicitly added to the local + database. + +‘N f t’ (‘forge-pull-topic’) + This command uses a forge’s API to fetch a single pull-request and + stores it in the database. This is useful if you chose to not + fetch all topics when you added the repository using + ‘forge-add-repository’. + + +File: forge.info, Node: Branching, Next: Miscellaneous Commands, Prev: Pulling, Up: Top + +10 Branching +************ + +Forge provides commands for creating and checking out a new branch or +work tree from a pull-request. These commands are available from the +same transient prefix commands as the suffix commands, used to create +and check out branches and work trees in a more generic fashion +(‘magit-branch’ on ‘b’ and ‘magit-worktree’ on ‘%’). + +‘b F’ (‘forge-branch-pullreq’) + This command creates and configures a new branch from a + pull-request, creating and configuring a new remote if necessary. + + The name of the local branch is the same as the name of the remote + branch that you are being asked to merge, unless the contributor + could not be bothered to properly name the branch before opening + the pull-request. The most likely such case is when you are being + asked to merge something like "fork/master" into "origin/master". + In such cases the local branch will be named "pr-N", where ‘N’ is + the pull-request number. + + These variables are always set by this command: + + • ‘branch..pullRequest’ is set to the pull-request number. + • ‘branch..pullRequestRemote’ is set to the remote on + which the pull-request branch is located. + • ‘branch..pushRemote’ is set to the same remote as + ‘branch..pullRequestRemote’ if that is possible, + otherwise it is set to the upstream remote. + • ‘branch..description’ is set to the pull-request title. + • ‘branch..rebase’ is set to ‘true’ because there should + be no merge commits among the commits in a pull-request. + + This command also configures the upstream and the push-remote of + the local branch that it creates. + + The branch against which the pull-request was opened is always used + as the upstream. This makes it easy to see what commits you are + being asked to merge in the section titled something like "Unmerged + into origin/master". + + Like for other commands that create a branch, it depends on the + option ‘magit-branch-prefer-remote-upstream’ whether the remote + branch itself or the respective local branch is used as the + upstream, so this section may also be titled, e.g., "Unmerged into + master". + + When necessary and possible, the remote pull-request branch is + configured to be used as the push-target. This makes it easy to + see what further changes the contributor has made since you last + reviewed their changes in the section titled something like + "Unpulled from origin/new-feature" or "Unpulled from + fork/new-feature". + + • If the pull-request branch is located in the upstream + repository, then you probably have set ‘remote.pushDefault’ to + that repository. However some users like to set that variable + to their personal fork, even if they have push access to the + upstream, so ‘branch..pushRemote’ is set anyway. + + • If the pull-request branch is located inside a fork, then you + are usually able to push to that branch, because Github by + default allows the recipient of a pull-request to push to the + remote pull-request branch even if it is located in a fork. + The contributor has to explicitly disable this. + + • If you are not allowed to push to the pull-request branch + on the fork, then a branch by the same name located in + the upstream repository is configured as the push-target. + + • A—sadly rather common—special case is when the + contributor didn’t bother to use a dedicated branch for + the pull-request. + + The most likely such case is when you are being asked to + merge something like "fork/master" into "origin/master". + The special push permission mentioned above is never + granted for the branch that is the repository’s default + branch, and that would almost certainly be the case in + this scenario. + + To enable you to easily push somewhere anyway, the local + branch is named "pr-N" (where ‘N’ is the pull-request + number) and the upstream repository is used as the + push-remote. + + • Finally, if you are allowed to push to the pull-request + branch and the contributor had the foresight to use a + dedicated branch, then the fork is configured as the + push-remote. + + The push-remote is configured using + ‘branch..pushRemote’, even if the used value is + identical to that of ‘remote.pushDefault’, just in case you + change the value of the latter later on. Additionally the + variable ‘branch..pullRequestRemote’ is set to the + remote on which the pull-request branch is located. + +‘b f’ (‘forge-checkout-pullreq’) + This command creates and configures a new branch from a + pull-request the same way ‘forge-branch-pullreq’ does. + Additionally it checks out the new branch. + +‘Z n’ (‘forge-checkout-worktree’) + This command creates and configures a new branch from a + pull-request the same way ‘forge-branch-pullreq’ does. + Additionally it checks out the new branch, using a new working + tree. + + -- User Option: forge-checkout-worktree-read-directory-function + This function is used by ‘forge-checkout-worktree’, to read the new + worktree directory where it checks out the pull-request. It takes + the pull-request as the only argument and must return a directory. + + When you delete a pull-request branch, which was created using one of +the above three commands, then ‘magit-branch-delete’ usually offers to +also delete the corresponding remote. It does not offer to delete a +remote if (1) the remote is the upstream remote, and/or (2) if other +branches are being fetched from the remote. + + Note that you have to delete the local branch (e.g., "feature") for +this to work. If you delete the tracking branch (e.g., "fork/feature"), +then the remote is never removed. + + +File: forge.info, Node: Miscellaneous Commands, Next: Miscellaneous Options, Prev: Branching, Up: Top + +11 Miscellaneous Commands +************************* + +‘N M’ (‘forge-merge’) +‘m M [if enabled]’ + This command merges the current pull-request using the forge’s API. + If there is no current pull-request or with a prefix argument, then + it reads a pull-request to visit instead. + + The "merge method" to be used is read from the user. + + Use of this command is discouraged. Unless the remote repository + is configured to disallow that, you should instead merge locally + and then push the target branch. Forges detect that you have done + that and respond by automatically marking the pull-request as + merged. + +‘N c f’ (‘forge-fork’) + This command adds an additional remote to the current repository. + The remote can either point at an existing repository or one that + has to be created first by forking it to an account the user has + access to. + + Currently this only supports Github and Gitlab. + +‘N - H’ (‘forge-toggle-topic-legend’) + This command toggle whether to show a legend for faces used in + topic menus and lists. + +‘N - S’ (‘forge-toggle-display-in-status-buffer’) + This command toggles whether any topics are displayed in the + current Magit status buffer. + +‘C-c C-w’ (‘forge-copy-url-at-point-as-kill’) + This command copies the url for the topic, issue(s), + pull-request(s), post, branch, commit, remote or repository to the + kill-ring. + + This determines the url the same way as ‘forge-browse’ does, but + then adds it to the kill-ring, instead of visiting it in a browser. + +‘M b r’ (‘forge-rename-default-branch’) + This command rename the default branch to a new name read from the + user. + + This changes the name on the upstream remotely and locally, and + update the upstream remotes of local branches accordingly. + + -- Command: forge-add-pullreq-refspec + This command configures Git to fetch all pull-requests. + + This is done by adding ‘+refs/pull/*/head:refs/pullreqs/*’ to the + value of ‘remote.REMOTE.fetch’, where REMOTE is the upstream + remote. + + -- Command: forge-add-user-repositories + This command reads a host and a username from the user and adds all + of that user’s repositories on that host to the local database. + + This may take a while. Only Github is supported at the moment. + + -- Command: forge-add-organization-repositories + This command reads a host and an organization from the user and + adds all the organization’s repositories on that host to the local + database. + + This may take a while. Only Github is supported at the moment. + + -- Command: forge-remove-repository + This command reads a repository and removes it from the local + database. + + -- Command: forge-remove-topic-locally + This command reads a topic and removes it from the local database. + The topic is not removed from the forge and, if it is later + modified, then it will be added to the database again. + + Due to how the supported APIs work, it would be too expensive to + automatically remove topics from the local database that were + removed from the forge. The only purpose of this command is to + allow you to manually clean up the local database. + + -- Command: forge-reset-database + This command moves the current database file to the trash and + creates a new empty database. + + This is useful after the database’s table schemata have changed, + which will happen a few times while the Forge functionality is + still under heavy development. + + +File: forge.info, Node: Miscellaneous Options, Next: How Forge Detection Works, Prev: Miscellaneous Commands, Up: Top + +12 Miscellaneous Options +************************ + + -- User Option: forge-database-file + This option specifies the file used to store the forge database. + + -- User Option: forge-topic-repository-slug-width + This option specifies the width of repository slugs (i.e., + "OWNER/NAME"). + + -- User Option: forge-buffer-draft-p + This option controls whether new pull-requests start out as drafts + by default. + + The buffer-local value of this variable is used to keep track of + the draft status of the current pull-request. + + -- User Option: forge-repository-list-columns + This option specifies the list of columns displayed when listing + repositories. + + Each element has the form ‘(HEADER SOURCE WIDTH SORT PROPS)’. + + HEADER is the string displayed in the header. WIDTH is the width + of the column. SOURCE is used to get the value, it has to be the + name of a slot of ‘forge-repository’ or a function that takes such + an object as argument. SORT is a boolean or a function used to + sort by this column. Supported PROPS include ‘:right-align’ and + ‘:pad-right’. + + -- User Option: forge-limit-topic-choices + This option controls whether to initially limit completion + candidates to active topics. + + -- User Option: forge-post-heading-format + This option specifies the format for post headings in topic view. + + The following ‘%’-sequences are supported: + + • ‘%a’ The forge nickname of the author. + • ‘%c’ The absolute creation date. + • ‘%C’ The relative creation date. + + -- User Option: forge-post-fill-region + This option controls whether to call ‘fill-region’ before + displaying forge posts. + + -- User Option: forge-bug-reference-hooks + This option lists the hooks to which ‘forge-bug-reference-setup’ is + added. It has to be customized before ‘forge’ is loaded, or it + won’t take effect. + + +File: forge.info, Node: How Forge Detection Works, Next: Supported Forges and Hosts, Prev: Miscellaneous Options, Up: Top + +Appendix A How Forge Detection Works +************************************ + +Forge uses the Ghub package to communicate with forge APIs. For more +information about Ghub, see *note (ghub)Top::. + + Ghub does *not* associate a given local repository with a repository +on a forge. The Forge package itself takes care of this. In doing so +it ignores the Git variable ‘ghub.host’ and other ‘*.host’ variables +used by Ghub. (But ‘github.user’, and other variables used to specify +the user, are honored). + + Forge associates the local repository with a forge repository, by +first determining which remote is associated with the upstream +repository, and then looking that up in ‘forge-alist’. + + If only one remote exists, then Forge uses that unconditionally. To +reduce the number of support requests, this is even the case if the Git +variable ‘forge.remote’ names another, non-existent, remote. + + If several remotes exist, then a remote may be selected based on its +name. Almost always we want to fetch the data associated with the +upstream repository, so that is what the logic described here tries to +achieve. The convention is to name the upstream remote "origin", and if +that convention were universally followed, then things would be trivial. +However many people name the upstream remote "upstream", which also +makes sense. + + Note, however, that even though a surprising number of people do + just that, it does not make any sense to use the name "origin" to + refer to a fork; not even to your own fork. A fork is a *copy* of + the original, "copy" is an antonym for "original", and the word + "origin" is not only closely related to but is even contained in + the word "original". Naming a fork the "origin" is at best + extremely confusing. + + copy + a thing made to be similar or identical to another. + + original + the earliest form of something, from which copies may be made. + + origin + the point or place where something begins, arises, or is + derived. + + If several remotes exist, then the following remote names are tried +in order and the first remote thus named that exists in the repository +is used. + + 1. The value of the Git variable ‘forge.remote’, if set. If the + variable has a value but no remote by the specified name exists, + then a warning is shown, but otherwise this conflict is ignored. + This behavior is arguably odd, but due to historic and pragmatic + reasons it is the least painful path forward. + + 2. The remote named ‘upstream’, if it exists. + + 3. The remote named ‘origin’, if it exists. + + The remote named "upstream" is preferred over the remote named +"origin" because the existence of the former strongly suggests that the +latter is either not used in this repository (in which case the order +does not matter) or else it is abused as the name of a fork (in which +case "upstream" must be preferred). + + -- Variable: forge.remote + The value of this variable specifies the remote from which Forge + fetches data. It is usually best to leave this unspecified and to + rely on the behavior described above. + + If the remote has to be specified explicitly, then this should be + done locally, for a single repository. + + Only ever set this globally, if you consistently use a certain name + to refer to the upstream repository and it isn’t one of "upstream" + or "origin", and you *never* use that name to refer to a repository + that does *not* refer to the upstream repository. + +‘N r’ (‘forge-forge.remote’) + This command changes the value of the ‘forge.remote’ Git variable + in the current repository. + + If this variable is set, then Forge uses the remote by that name, if +it exists, the same way it may have used ‘origin’ if the variable were +undefined. I.e., it does not fall through to try ‘origin’ if no remote +by your chosen name exists. + + Once the upstream remote has been determined, Forge looks it up in +‘forge-alist’, using the host part of the URL as the key. For example, +the key for ‘git@github.com:magit/forge.git’ is ‘github.com’. + + -- User Option: forge-alist + This option defines forge hosts known to Forge. + + Each entry has the form ‘(GITHOST APIHOST WEBHOST CLASS)’. + + • GITHOST is the host used to access repositories on the forge + using Git. + + • APIHOST is the host used to access the forge’s API. For some + forges the isn’t just a host, but a host followed by the path + to the API’s endpoint. + + • WEBHOST is the host used to access repositories on this forge + using a browser. The IDs used to identify repositories from + the forge in the local database also derives from this value. + + • CLASS is the class to be used for repositories from the forge. + + Complications: + + • When connecting to a Github Enterprise edition whose REST + API’s end point is "/v3" and whose GraphQL API’s end + point is "/graphql", then use "/v3" as APIHOST. + This is a historic accident. See issue #174. + + • WEBHOST and CLASS cannot be changed once you have added one or + more repositories from a forge. Changing GITHOST and/or + APIHOST may be possible, but should seldom be necessary. + + +File: forge.info, Node: Supported Forges and Hosts, Next: FAQ, Prev: How Forge Detection Works, Up: Top + +Appendix B Supported Forges and Hosts +************************************* + +Currently Forge supports two forges and three more forges partially. +Additionally it supports four semi-forges. Support for more forges and +semi-forges can and will be added. + + Both forges and semi-forges provide web interfaces for Git +repositories. Forges additionally support pull-requests and issues and +make those and other information available using an API. + + When a forge is only partially supported, then that means that only +the functionality that does not require the API is implemented, or in +other words, that the forge is only supported as a semi-forge. + + A host is a particular instance of a forge. For example the hosts + and are both instances +of the Gitlab forge. Forge supports some well known hosts out of the +box and additional hosts can easily be supported by adding entries to +the option ‘forge-alist’ (see *note How Forge Detection Works::). + + For more details about the caveats mentioned below (and some others) +see also *note Getting Started::. + +* Menu: + +* Supported Forges:: +* Partially Supported Forges:: +* Supported Semi-Forges:: + + +File: forge.info, Node: Supported Forges, Next: Partially Supported Forges, Up: Supported Forges and Hosts + +B.1 Supported Forges +==================== + +B.1.1 Github +------------ + +Forge’s support for Github can be considered the "reference +implementation". Support for other forges can lag behind a bit. + +B.1.1.1 Github Caveats +...................... + + • Forge uses the Github GraphQL API when possible but has to fall + back to use the REST API in many cases because the former is still + rather incomplete. + + • The Github GraphQL API has a hard-coded timeout on queries. The + only solution is to reduce the number of entities we query at once, + which can be done by adjusting either the ‘forge.graphqlItemLimit’ + git variable or the field "GQL entity limit" in a status buffer. + + • Forge depends on the ‘updated_at’ field being updated when + appropriate. For Github pull-requests at least, that is not always + done. + +B.1.1.2 Github Hosts +.................... + + • + +B.1.2 Gitlab +------------ + +B.1.2.1 Gitlab Caveats +...................... + + • Forge cannot provide notifications because the Gitlab API does not + expose those. + +B.1.2.2 Gitlab Hosts +.................... + + • + • + • + + +File: forge.info, Node: Partially Supported Forges, Next: Supported Semi-Forges, Prev: Supported Forges, Up: Supported Forges and Hosts + +B.2 Partially Supported Forges +============================== + +B.2.1 Gitea +------------------------------ + +This is the next forge whose API will be supported. + +B.2.1.1 Gitea Hosts +................... + + • + +B.2.2 Gogs +---------------------------- + +Once Gitea is supported it should be fairly simple to support Gogs too, +because the former is a fork of the latter and the APIs seem to still be +very similar. + +B.2.2.1 Gogs Hosts +.................. + + • + +B.2.3 Bitbucket +--------------------------------------- + +I don’t plan to support Bitbucket’s API any time soon, and it gets less +likely that I will every do it every time I look at it. + +B.2.3.1 Bitbucket Caveats +......................... + + • The API documentation is poor and initial tests indicated that the + implementation is buggy. + + • Atlassian’s offering contains two very distinct implementations + that are both called "Bitbucket". Forge only supports the + implementation whose only instance is available at + , because I only have access to that. + + • Unlike all other forges, Bitbucket does not expose pull-requests as + references in the upstream repository. For that reason Forge + actually treats it as a semi-forge, not as forge whose API is not + supported yet. This means that you cannot checkout pull-requests + locally. There is little hope that this will ever get fixed; the + respective issue was opened six years ago and there has been no + progress since: . + +B.2.3.2 Bitbucket Hosts +....................... + + • + + +File: forge.info, Node: Supported Semi-Forges, Prev: Partially Supported Forges, Up: Supported Forges and Hosts + +B.3 Supported Semi-Forges +========================= + +B.3.1 Gitweb +---------------------------------------------- + +B.3.1.1 Gitweb Caveats +...................... + + • I could find only one public installation + (), which gives users the choice + between Gitweb and Cgit. The latter seems more popular (not just + on this site). + +B.3.2 Cgit +--------------------------------------------- + +B.3.2.1 Cgit Caveats +.................... + + • Different sites use different URL schemata and some of the bigger + sites use a fork. For this reason Forge has to provide several + classes to support different variations of Cgit and you have to + look at their definitions to figure out which one is the correct + one for a particular installation. + +B.3.2.2 Cgit Hosts +.................. + + • + • + • + +B.3.3 Stgit +----------------------------------------------------------------- + +B.3.3.1 Stgit Caveats +..................... + + • Stgit cannot show logs for branches beside "master". For that + reason Forge takes users to a page listing the branches when they + request the log for a particular branch (even for "master" whose + log is just one click away from there). + +B.3.3.2 Stgit Hosts +................... + + • + +B.3.4 Srht +------------------------------- + +B.3.4.1 Srht Caveats +.................... + + • Srht cannot show logs for branches beside "master". For that + reason Forge takes users to a page listing the branches when they + request the log for a particular branch (even for "master" whose + log is just one click away from there). + +B.3.4.2 Srht Hosts +.................. + + • + + +File: forge.info, Node: FAQ, Next: Keystroke Index, Prev: Supported Forges and Hosts, Up: Top + +Appendix C FAQ +************** + +This section lists some frequently asked questions. Please see also + for an extended list of common +issues. + +* Menu: + +* ‘error in process filter HTTP Error 502, "Bad gateway"’: error in process filter HTTP Error 502 "Bad gateway". + + +File: forge.info, Node: error in process filter HTTP Error 502 "Bad gateway", Up: FAQ + +C.1 ‘error in process filter: HTTP Error: 502, "Bad gateway"’ +============================================================= + +This is a frequently occurring error. Adding some formatting, the full +error is: + + error in process filter: ghub--signal-error: HTTP Error: 502, + "Bad gateway", "/graphql", + ((data . "null") + (errors ((message . "Something went wrong while executing your query. + This may be the result of a timeout, or it could be a GitHub bug. + Please include `CC2C:4FEA:A1771C1:CBF40CE:5C33F7E5` + when reporting this issue.")))) + + This indicates that something went wrong within Github’s network. +Unfortunately the reason given is rather vague, but I believe this +usually happens when there are topics with one or two magnitudes more +posts than usual, which can cause GraphQL responses to become huge. + + This can be countered in the affected repository by setting the Git +variable ‘forge.graphqlItemLimit’: + + git config --local forge.graphqlItemLimit 20 + + The default is specified using the ‘ghub-graphql-items-per-request’, +which defaults to 50 (down from Github’s default and maximum of 100). + + Fetching less items per request results in more requests, which slows +down the process, which is why the default should not be too small, but +for some repositories a more aggressive limit is needed. + + +File: forge.info, Node: Keystroke Index, Next: Function and Command Index, Prev: FAQ, Up: Top + +Appendix D Keystroke Index +************************** + +[index] +* Menu: + +* b F: Branching. (line 13) +* b f: Branching. (line 102) +* C- [on a topic section]: Editing Topics. (line 26) +* C-c C-c: Lists and Menus. (line 97) +* C-c C-c <1>: Lists and Menus. (line 109) +* C-c C-c <2>: Creating Topics and Posts. + (line 37) +* C-c C-c [in topics list buffer/section]: Lists and Menus. (line 53) +* C-c C-e: Creating Topics and Posts. + (line 45) +* C-c C-e d: Creating Topics and Posts. + (line 50) +* C-c C-e [on a post section]: Editing Topics. (line 12) +* C-c C-k: Creating Topics and Posts. + (line 41) +* C-c C-k [on a post section]: Editing Topics. (line 20) +* C-c C-n: Creating Topics and Posts. + (line 27) +* C-c C-n [on "Issues" section]: Creating Topics and Posts. + (line 23) +* C-c C-n [on "Pull requests" section]: Creating Topics and Posts. + (line 19) +* C-c C-o: Visiting Topics. (line 34) +* C-c C-r: Creating Topics and Posts. + (line 27) +* C-c C-w: Miscellaneous Commands. + (line 37) +* f n: Getting Started. (line 32) +* f n <1>: Pulling. (line 23) +* f N: Pulling. (line 34) +* M b r: Miscellaneous Commands. + (line 45) +* m M [if enabled]: Miscellaneous Commands. + (line 8) +* N: Getting Started. (line 50) +* N <1>: Lists and Menus. (line 40) +* N - H: Miscellaneous Commands. + (line 29) +* N - S: Miscellaneous Commands. + (line 33) +* N / a: Getting Started. (line 15) +* N b i: Visiting Topics. (line 46) +* N b I: Visiting Topics. (line 46) +* N b p: Visiting Topics. (line 46) +* N b P: Visiting Topics. (line 46) +* N b r: Visiting Topics. (line 46) +* N b t: Visiting Topics. (line 46) +* N c f: Miscellaneous Commands. + (line 21) +* N c i: Creating Topics and Posts. + (line 23) +* N c p: Creating Topics and Posts. + (line 19) +* N f f: Getting Started. (line 32) +* N f f <1>: Pulling. (line 23) +* N f n: Pulling. (line 34) +* N f t: Pulling. (line 43) +* N l g: Lists and Menus. (line 78) +* N l n: Lists and Menus. (line 101) +* N l r: Lists and Menus. (line 113) +* N l t: Lists and Menus. (line 60) +* N M: Miscellaneous Commands. + (line 8) +* N m c: Lists and Menus. (line 45) +* N m f: Lists and Menus. (line 53) +* N m n: Lists and Menus. (line 97) +* N m r: Lists and Menus. (line 109) +* N m s: Editing Topics. (line 26) +* N r: How Forge Detection Works. + (line 83) +* N v i: Visiting Topics. (line 28) +* N v p: Visiting Topics. (line 28) +* N v t: Visiting Topics. (line 28) +* o [in forge-repositories-menu]: Lists and Menus. (line 120) +* o [on repository in repository list]: Visiting Topics. (line 34) +* o [on topic in topic list]: Visiting Topics. (line 34) +* RET [on "Issues" status section]: Lists and Menus. (line 66) +* RET [on "Pull requests" status section]: Lists and Menus. (line 72) +* RET [on repository]: Lists and Menus. (line 117) +* RET [on topic]: Visiting Topics. (line 21) +* Z n: Branching. (line 107) + + +File: forge.info, Node: Function and Command Index, Next: Variable Index, Prev: Keystroke Index, Up: Top + +Appendix E Function and Command Index +************************************* + +[index] +* Menu: + +* forge-add-organization-repositories: Miscellaneous Commands. + (line 64) +* forge-add-pullreq-refspec: Miscellaneous Commands. + (line 51) +* forge-add-repository: Getting Started. (line 15) +* forge-add-user-repositories: Miscellaneous Commands. + (line 58) +* forge-branch-pullreq: Branching. (line 13) +* forge-browse: Visiting Topics. (line 34) +* forge-browse-branch: Visiting Topics. (line 38) +* forge-browse-commit: Visiting Topics. (line 37) +* forge-browse-issue: Visiting Topics. (line 46) +* forge-browse-issues: Visiting Topics. (line 46) +* forge-browse-pullreq: Visiting Topics. (line 46) +* forge-browse-pullreqs: Visiting Topics. (line 46) +* forge-browse-remote: Visiting Topics. (line 46) +* forge-browse-repository: Visiting Topics. (line 39) +* forge-browse-this-repository: Visiting Topics. (line 34) +* forge-browse-this-topic: Visiting Topics. (line 34) +* forge-browse-topic: Visiting Topics. (line 46) +* forge-checkout-pullreq: Branching. (line 102) +* forge-checkout-worktree: Branching. (line 107) +* forge-configure: Lists and Menus. (line 45) +* forge-copy-url-at-point-as-kill: Miscellaneous Commands. + (line 37) +* forge-create-issue: Creating Topics and Posts. + (line 23) +* forge-create-post: Creating Topics and Posts. + (line 27) +* forge-create-pullreq: Creating Topics and Posts. + (line 19) +* forge-delete-comment: Editing Topics. (line 20) +* forge-dispatch: Getting Started. (line 50) +* forge-dispatch <1>: Lists and Menus. (line 40) +* forge-edit-post: Editing Topics. (line 12) +* forge-forge.remote: How Forge Detection Works. + (line 83) +* forge-fork: Miscellaneous Commands. + (line 21) +* forge-insert-issues: Lists and Menus. (line 184) +* forge-insert-pullreqs: Lists and Menus. (line 188) +* forge-list-global-issues: Lists and Menus. (line 82) +* forge-list-global-pullreqs: Lists and Menus. (line 87) +* forge-list-global-topics: Lists and Menus. (line 78) +* forge-list-issues: Lists and Menus. (line 66) +* forge-list-notifications: Lists and Menus. (line 101) +* forge-list-owned-repositories: Lists and Menus. (line 120) +* forge-list-pullreqs: Lists and Menus. (line 72) +* forge-list-repositories: Lists and Menus. (line 113) +* forge-list-topics: Lists and Menus. (line 60) +* forge-merge: Miscellaneous Commands. + (line 8) +* forge-notifications-menu: Lists and Menus. (line 97) +* forge-post-cancel: Creating Topics and Posts. + (line 41) +* forge-post-dispatch: Creating Topics and Posts. + (line 45) +* forge-post-submit: Creating Topics and Posts. + (line 37) +* forge-post-toggle-draft: Creating Topics and Posts. + (line 50) +* forge-pull: Getting Started. (line 32) +* forge-pull <1>: Pulling. (line 23) +* forge-pull-notifications: Pulling. (line 34) +* forge-pull-topic: Pulling. (line 43) +* forge-remove-repository: Miscellaneous Commands. + (line 71) +* forge-remove-topic-locally: Miscellaneous Commands. + (line 75) +* forge-rename-default-branch: Miscellaneous Commands. + (line 45) +* forge-repositories-menu: Lists and Menus. (line 109) +* forge-reset-database: Miscellaneous Commands. + (line 85) +* forge-toggle-display-in-status-buffer: Miscellaneous Commands. + (line 33) +* forge-toggle-topic-legend: Miscellaneous Commands. + (line 29) +* forge-topic-menu: Editing Topics. (line 26) +* forge-topics-menu: Lists and Menus. (line 53) +* forge-visit-issue: Visiting Topics. (line 28) +* forge-visit-pullreq: Visiting Topics. (line 28) +* forge-visit-this-repository: Lists and Menus. (line 117) +* forge-visit-this-topic: Visiting Topics. (line 21) +* forge-visit-topic: Visiting Topics. (line 28) + + +File: forge.info, Node: Variable Index, Prev: Function and Command Index, Up: Top + +Appendix F Variable Index +************************* + +[index] +* Menu: + +* forge-alist: How Forge Detection Works. + (line 95) +* forge-buffer-draft-p: Miscellaneous Options. + (line 13) +* forge-bug-reference-hooks: Miscellaneous Options. + (line 50) +* forge-checkout-worktree-read-directory-function: Branching. (line 112) +* forge-database-file: Miscellaneous Options. + (line 6) +* forge-limit-topic-choices: Miscellaneous Options. + (line 33) +* forge-owned-accounts: Lists and Menus. (line 127) +* forge-owned-ignored: Lists and Menus. (line 140) +* forge-post-fill-region: Miscellaneous Options. + (line 46) +* forge-post-heading-format: Miscellaneous Options. + (line 37) +* forge-repository-list-columns: Miscellaneous Options. + (line 20) +* forge-status-buffer-default-topic-filters: Lists and Menus. (line 163) +* forge-status-buffer-default-topic-filters <1>: Lists and Menus. + (line 167) +* forge-topic-repository-slug-width: Miscellaneous Options. + (line 9) +* forge.remote: How Forge Detection Works. + (line 69) + + + +Tag Table: +Node: Top771 +Node: Introduction2507 +Node: Initial Setup2928 +Ref: Common Setup3633 +Node: Setup for Githubcom4227 +Ref: Set your Username4388 +Ref: Create and Store an Access Token4718 +Node: Setup for Another Github Instance6586 +Ref: Tell Forge about the Instance7134 +Ref: Set your Username (1)8680 +Ref: Create and Store an Access Token (1)8988 +Node: Setup for Gitlabcom11089 +Ref: Set your Username (2)11292 +Ref: Create and Store an Access Token (2)11622 +Node: Setup for Another Gitlab Instance13513 +Ref: Tell Forge about the Instance (1)14074 +Ref: Set your Username (3)15415 +Ref: Create and Store an Access Token (3)15723 +Node: Setup a Partially Supported Host17775 +Ref: Tell Forge about the Instance (2)18460 +Ref: Add Support for Additional Forge Types19956 +Node: Initial Pull21127 +Node: Getting Started22334 +Node: Lists and Menus24620 +Ref: Dispatch and configuration menus26444 +Ref: Topic menu and list commands26903 +Ref: Notification menu and list commands28840 +Ref: Repository menu and list commands29217 +Ref: Exiting menus and lists30984 +Ref: Default topic filters31679 +Ref: Topic sections in Magit status buffers32119 +Node: Visiting Topics33350 +Node: Creating Topics and Posts35464 +Node: Editing Topics37546 +Node: Pulling39171 +Node: Branching41333 +Node: Miscellaneous Commands47726 +Node: Miscellaneous Options51450 +Node: How Forge Detection Works53535 +Node: Supported Forges and Hosts59072 +Node: Supported Forges60381 +Ref: Github60537 +Ref: Github Caveats60692 +Ref: Github Hosts61354 +Ref: Gitlab61426 +Ref: Gitlab Caveats61453 +Ref: Gitlab Hosts61594 +Node: Partially Supported Forges61730 +Ref: Gitea https//giteaio61936 +Ref: Gitea Hosts62052 +Ref: Gogs https//gogsio62124 +Ref: Gogs Hosts62343 +Ref: Bitbucket https//bitbucketorg62417 +Ref: Bitbucket Caveats62631 +Ref: Bitbucket Hosts63544 +Node: Supported Semi-Forges63625 +Ref: Gitweb https//git-scmcom/docs/gitweb63796 +Ref: Gitweb Caveats63891 +Ref: Cgit https//gitzx2c4com/cgit/about64147 +Ref: Cgit Caveats64240 +Ref: Cgit Hosts64605 +Ref: Stgit https//codemadnessorg/git/stagit/file/READMEhtml64748 +Ref: Stgit Caveats64881 +Ref: Stgit Hosts65183 +Ref: Srht https//metasrht65259 +Ref: Srht Caveats65324 +Ref: Srht Hosts65623 +Node: FAQ65690 +Node: error in process filter HTTP Error 502 "Bad gateway"66100 +Node: Keystroke Index67581 +Node: Function and Command Index73561 +Node: Variable Index79841 + +End Tag Table + + +Local Variables: +coding: utf-8 +End: -- 2.47.3