Code Monkey home page Code Monkey logo

gerrit.el's Introduction

Build Status MELPA

gerrit.el

Gerrit is a great code review tool and a great git hosting service. This package provides an emacs interface for

  • uploading changes (gerrit-upload)
  • downloading changes (gerrit-download and gerrit-download-transient)
  • creating a dashboard (gerrit-dashboard)
  • creating buffers that contain details about gerrit topics and gerrit changes (gerrit-section-topic-info and gerrit-section-change-info).

The function gerrit-upload uses the transient package and provides the following features in addition to uploading new changes (and new patchsets)

  • specify reviewers
  • set an assignee
  • set a topic name
  • set WIP flag
  • set a ready-for-review flag

Furthermore, a minimalistic open-reviews status-section (gerrit-magit-insert-status) for magit status buffers is available.

This code is tested using gerrit=3.8 and the gerrit version used on review.gerrithub.org.

News

March 2024:

  • Renamed gerrit-get-changeid-from-current-commit to gerrit-get-changeid-from-commit with optional revision parameter.
  • Renamed gerrit-get-unique-changeid-from-current-commit to gerrit-get-unique-changeid-from-commit with optional revision parameter.

February 2024:

  • Add gerrit-rest-change-get-description and gerrit-rest-change-set-description.
  • gerrit-upload: Do nothing if remote SHA1 matches SHA1 of HEAD.

Dezember 2023:

  • Try to fetch as much gerrit account-info objects as possible at startup.
  • Fix invalid json in request problems with gerrit-rest-change-delete-cr-vote, gerrit-rest-change-delete-reviewer and gerrit-rest-change-delete-verified-vote.

Use DELETE method for deleting verified votes + Improve rest-sync-v2

September 2022:

  • Add "--no-verify" option to gerrit-upload-transient, which allows skipping the pre-push git hooks.

August 2022:

  • [internal] Introduce new gerrit-rest-sync-v2.
  • Update gerrit-dashboard-query-alist (Include the "Your turn" in the query, which requires a new-ish gerrit server release >= 3.3).
  • Update storage format of gerrit--accounts-alist. This allows displaying the real names of the users instead usernames in the dashboards and the query frontends.
  • Display the reviewers instead of the assignee in the default dashboard. It is still possible to display the assignee by adding an assignee entry to gerrit-dashboard-columns.
  • Add support for displaying the CC list in the dashboard. To add the CC list to the dashboard add "CC" to gerrit-dashboard-columns.

April 2022:

  • Add a new interactive gerrit-query function, which display the results of a user defined gerrit query.
  • Display clickable links in the gerrit-dashboard.
  • Add support for sorting the columns in the dashboard buffers: To sort by column either click on the column that you want to sort or use tabulated-list-sort (S). [Requested by Gerald]

March 2022:

  • Add support for voting to gerrit-dashboard.
    • V set a "Verified" vote for the entire topic under point.
    • C set a "Code-Review" vote for the entire topic under point.

Februrary 2022:

  • Fix formatting issue when gerrit-dashboard-columns is customized.
  • Allow toggling between HTTPS and HTTP by changing gerrit-use-ssl.

Jannuary 2022:

  • Legacy git-review functions were dropped. The gerrit-use-gitreview-interface variable is no longer used.
  • Support displaying the owner-name in the displayed changes in gerrit-download.

October 2021:

  • Add a new transient called gerrit-download-transient, which will replace the gerrit-download function in the future.
  • Add support for downloading changes in arbitrary git workspaces using gerrit-download-transient.
  • Add new d keybinding to gerrit-dashboard.

Installation

This emacs package is available on MELPA.

Example use-package config

(use-package gerrit
  :ensure t
  :custom
  (gerrit-host "gerrit.my.domain")  ;; is needed for REST API calls
  :config
  (progn
    (add-hook 'magit-status-sections-hook #'gerrit-magit-insert-status t)
    (global-set-key (kbd "C-x i") 'gerrit-upload-transient)
    (global-set-key (kbd "C-x o") 'gerrit-download)))

Authentication

.authinfo, .authinfo.gpg and .netrc

By default emacs searches in files called ~/.authinfo, ~/.authinfo.gpg and ~/.netrc in the specified order for credentials. Take a look at the auth-sources variable and its documentation if you want to change this.

You can add an entry with the following format to any of above files

machine gerrithostname.org
    login my-gerrit-username
    password xxxx

Note: Depending on your auth configuration, Gerrit may expect a generated HTTP password (ex. if you have git_basic_auth_policy="HTTP_LDAP"). If there is an HTTP credentials section in your user's account settings page, then an HTTP password needs to be generated and supplied in your auth-source file.

Pre-commit hook

As you know, there is a gerrit pre-commit hook that must be installed for every gerrit repo s.t. the Change-ID is added to the bottom of your git commit messages. This pre-commit hook can be installed using the gerrit--ensure-commit-msg-hook-exists function or e.g. by calling git review -s provided that the git-review CLI tool is installed.

Screenshots

gerrit-dashboard

gerrit-download

gerrit-upload

gerrit-section-change-info

Usage notes for the gerrit-upload transient

All settings entered in the gerrit-upload transient are saved to a file, whose filename is in the transient-history-file variable. This file is updated in the kill-emacs-hook, which is run when the emacs process/daemon is stopped using (kill-emacs).

If you are using systemd for starting emacs as a daemon, make sure that your unit files contains

ExecStop=/usr/bin/emacsclient --eval "(kill-emacs)"

You can cycle through the history by using M-p and M-n.

The reviewers have to be added as a comma-separated string. Completion of the individual reviewers using the account information from the gerrit servers should work with TAB.

Dashboards

gerrit-dashboard displays a dashboard similar to the one in the gerrit web-interface. The currently supported keybindings in a dashboard buffer are

  • a Assign the change under point
  • A Assign the change under point to me
  • g Refresh
  • o Open change under point in browser using browse-url
  • d Download change under point in a local clone of the repository
  • t Switch to a buffer containing a topic-overview of the change under point
  • e Edit the query and open a new buffer with query results
  • RET Download patch of change under point and display it in new buffer

It is possible to click on the links in the dashboard buffer to open new query buffers like in the gerrit webfrontend.

If you want to create multiple dashboards you can create a dashboard using

(defun gerrit-dashboard-standup ()
  (interactive)
  (let ((gerrit-dashboard-query-alist
         '(
           ("Waiting for +1" . "is:open assignee:groupX label:Code-Review=0")
           ("Waiting for +2" . "is:open assignee:groupX label:Code-Review=1")
           )
         )
        (gerrit-dashboard-buffer-name "*gerrit-groupX-standup*")
        )
    (gerrit-dashboard)))

Similar elisp packages

gerrit.el's People

Contributors

accraze avatar snogge avatar tarsius avatar twmr avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

gerrit.el's Issues

Output "no-new-changes" message in echo-area

If you want to upload a change/commit that is already up2date, gerrit returns the following message in the git push call

 ! [remote rejected]   HEAD -> refs/for/main (no new changes)
error: failed to push some refs to 'gerrit.example.org:software/demo'

This is currently only visible in the magit-process buffer. It would be nice if this message were also output in the echo area.

Stash changes before calling git-review

If git-review is called with unstaged changes, I get the following output:

Errors running git rebase -p -i remotes/origin/version10
error: cannot rebase: You have unstaged changes.
error: Please commit or stash them.
It is likely that your change has a merge conflict. You may resolve it
in the working tree now as described above and then run 'git review'
again, or if you do not want to resolve it yet (note that the change
can not merge until the conflict is resolved) you may run 'git rebase
--abort' then 'git review -R' to upload the change without rebasing.

gerrit.el should temporarily stash changes before calling git-review.

I'll try to fix this issue in git-review.

Support for reviewing gerrit changes

What is still missing in gerrit.el is support for reviewing gerrit changes from within emacs. The reason why this hasn't been done yet, is that it was not clear how an emacs UI for this could look like. I've just stumbled upon an emacs pkg that adds support for reviews on github, which is https://github.com/charignon/github-review. The UI that is used there could also be used for gerrit.el.
If you are aware of similar (emacs) projects, please let me know.

Perform additional checks at the beginning of gerrit-upload

Sometimes it happens that I'm not sure if the checked-out branch is in sync with gerrit, i.e., that all not-yet-submitted changes are on gerrit and are up2date.

It would be nice if e.g. magit-status showed in the Unmerged into section the following info for each commit

  • was the change uploaded to gerrit
  • has the change on gerrit already been submitted
  • is the local version or the version on the server is newer
  • gerrit-upload could skip the git push call if the version on the server is the same as the local version of the current branch/commit.

Function that downloads a review (independent of CWD) and opens an (editable) overview of the change

A new function would be nice that:

  • finds the workspace of the project on the host (we need to introduce a mapping from (projectname + branch) to the directory of the workspace on the host)
  • switches the working directory to this workspace and downloads the change
  • opens a overview of the change in which
    ** modified files can easily be opened in a new buffer
    ** the displayed diff hunks can easily be edited inline (requires a fix for magit/magit#2938)

Functions which can be used for implementing this feature:

  • (magit-show-commit "HEAD")
  • magit-diff-visit-worktree-file
  • (gerrit-download)

Support autostashing in `gerrit-download`

I think it makes sense to check if there are uncommitted changes in the workspace at the beginning of gerrit-download and if yes, ask the user if he/she wants to stash/unstash(=autostash) them.

gerrit-upload dryrun: output diff for already uploaded changes

If you upload a change that is already on gerrit it should be possible to somehow display the local changes (if there are any) between the local HEAD and the latest patchset of the change on gerrit.

If your local commit/change is in sync with the latest PS of the change on gerrit, gerrit-upload should output an error/warning.

To be performed steps:

  • find the change with the changeid of the current change on the gerrit server and fetch (only fetch!) it
  • if HEAD and FETCH_HEAD are identical, output a warning, otherwise
  • diff HEAD against the FETCH_HEAD and display it in new buffer

Support cookie authentification

The google gerrit server (gerrit.googlesource.com) requires authentification using cookies, which is currently not supported by gerrit.el. Unfortunately the underlying url.el AFAIK does not have support for this, but curl does:

curl --cookie ~/.gitcookies https://gerrit-review.googlesource.com/a/config/server/info 

Add dashboard

A command that displays a dashboard similar to the one shown in the web interface would be useful.
Using tabulated-list-mode seems to be quite easy to do this, but it does not have support for the sections ("Assigned Reviews", "Work in Progress", ...)

Automatically install commit-msg hook

It happened to me already several times that I wanted to upload a commit (or commits) to gerrit in a repo that doesn't have the gerrit commit-msg hook, which is responsible for adding the ChangeId line to the commit-msg.

In a recent commit I've added support for checking if the commit contains a change-id line before the gerrit-upload transient is shown. This avoids that you have to re-enter the gerrit-upload transient settings (assignee, topic, ..) after realizing that gerrit didn't accept the new commit due to a missing change-id line.

It would be nice if we could instead add a hook to git-commit-mode that checks if the upstream is a repo on a gerrit server downloads the commit-msg hook if it does not yet exist for the current repo.

Discuss possible collaboration with egerrit

Hi Thomas,

just wanted to drop by and say hello and check if you have had any time to try out the egerrit package. I wanted to let you know that I have moved the development to sourcehut, so you will find the package here https://git.sr.ht/~niklaseklund/egerrit.

Have you given it any more thoughts to possibilities for collaboration? :). I have focused on some other development recently but picked up the package the other week and made some more improvements to it. So then I came to think about your package :)

/Niklas

Replace usage of `git-review` cli tool by gerrit REST api

Since it doesn't seem that #2 will soon be fixed in gerrit and in git-review and since not everyone is using git-review, we should remove the usage of git-review from gerrit.el and use REST calls instead.

The user-interface of gerrit-download and gerrit-upload should be kept and only slightly adapted.

DONE which features of git-review do we want to implement in gerrit.el?
DONE how do we determine the target branch for a to-be-uploaded gerrit-change? git-review reads .gitreview file, which may contain a branch name that is used for gerrit changes.
DONE who sets-up the pre-commit hooks that ensure that a "Change-ID" line is part of the commit message?

gerrit-download

This implementation could be used instead of the current gerrit-download function. It uses the REST API for querying the open changes in the current project and uses magit for downloading the change.

(defun gerrit-format-change (change)
  (concat
   (propertize (number-to-string (alist-get '_number change)) 'face 'magit-hash)
   " "
   (propertize (alist-get 'branch change) 'face 'magit-branch-remote)
   " "
   (propertize (alist-get 'subject change) 'face 'magit-section-highlight)
   ))

(defun gerrit--get-refspec(change-metadata)
  ;; this is important for determining the refspec needed for
  ;; git-fetch
  ;; change-ref is e.g. "refs/changes/16/35216/2"
  (let* ((revisions (alist-get 'revisions change-metadata))
         (revision (alist-get 'current_revision change-metadata)))
    (gerrit--alist-get-recursive (intern revision) 'ref revisions)))

(defun gerrit--download-change (change-metadata)
  (let* ((change-nr (alist-get '_number change-metadata))
         (change-branch (alist-get 'branch change-metadata))
         (change-topic (or (alist-get 'topic change-metadata)
                           (number-as-string change-nr)))
         ;; TODO ensure that gerrit--acounts-alist is initialized
         (change-owner (alist-get (gerrit--alist-get-recursive
                                   'owner '_account_id change-metadata)
                                  gerrit--accounts-alist))
         (local-branch (format "reviews/%s/%s" change-owner change-topic)))

    ;; TODO log messages?

    ;; this runs async, which is not what I want, because then I can't
    ;; run other git commands afterwards
    ;; (magit-fetch-refspec (gerrit-get-remote) (gerrit--get-refspec change-metadata) nil)
    (magit-call-git "fetch" (gerrit-get-remote) (gerrit--get-refspec change-metadata))
    ;; TODO handle errors of magit-branch-and-checkout:
    ;; check if a local branch with this name already exists
    ;; if yes, determine the remote and remote-branch of this local-branch
    ;;         and reuse it only if they are equal with the current-remote
    ;;         (gerrit-get-remote)
    ;;              and `change-branch`
    (magit-branch-and-checkout local-branch "FETCH_HEAD")))


(defun gerrit-download-new ()
  "Download change from the gerrit server."
  (interactive)
  (let* ((open-changes
          (seq-map #'gerrit-format-change (gerrit-rest-change-query
                                    (concat "status:open project:"
                                            (gerrit-get-current-project)))))
         (selected-line (completing-read
                         "Download Change: " open-changes nil nil))
         (changenr (car (s-split " " (s-trim selected-line))))

         ;; the return value of `gerrit-rest-change-query` contains the
         ;; current revision, but not the one of `gerrit-rest-change-get`.
         (change-metadata (car (gerrit-rest-change-query changenr))))

    (gerrit--download-change change-metadata)))

feature request: allow new arguments to be added to upload transient

https://github.com/thisch/gerrit.el/blob/4de561d1295d4c86ca9b159ab0c746bedc2d0380/gerrit.el#L433-L434

the above line in the condition of gerrit-upload:--action causes the function to error out if there are unknown transient args rather than just ignoring them. I tried adding an argument to the upload transient that would enable an env var before calling the function and then disable it afterwards, but it ended up not working because the function would error out at the above lines when it got around to parsing the new argument I injected into the transient. See my snippets of code here

;;
;; add advice that enables skipping the pre push hook by setting an env var before
;; calling the upload function and then unsetting it after
;;
(defadvice gerrit-upload:--action (around advice-gerrit-upload:--action activate)
  (interactive)
  (if (called-interactively-p 'any)
      (progn
        (cl-loop for arg in (transient-args 'gerrit-upload-transient) do
                 (cond ((string= "skip-checks" arg)
                        (setenv "GIT_SKIP_PRE_PUSH_HOOK" "1"))))
        (call-interactively (ad-get-orig-definition 'gerrit-upload:--action))
        (setenv "GIT_SKIP_PRE_PUSH_HOOK" nil))
    ad-do-it))

(transient-insert-suffix 'gerrit-upload-transient (list 0 -1)
  '("s" "Skip checks" "skip-checks"))

I ended up overriding gerrit-upload:--action and deleting the t condition but it would be nice if that change were upstreamed into this repo so that I could implement this only using advice.

Don't override locally modified by a checkout of a gerrit change

  1 integrationtests/ git … checkout review/thomas_hisch/58720
error: Your local changes to the following files would be overwritten by checkout:
	integrationtests/test_data.py
	pytest.ini
Please commit your changes or stash them before you switch branches.
Aborting

  0 integrationtests/ git … reset --hard FETCH_HEAD
HEAD is now at f778b6a WIP add testcase

We have to check the return value of the git checkout command before we start the git reset command.

The exit code is returned by magit-call-git, but currently we are ignoring the return value. The respective code that causes a problem looks like

(progn
(magit-call-git "foobar1")
(magit-call-git "foobar2")) ;; foobar2 is run even though foobar1 failed

Number of gerrit accounts limit (=500)

The GET /accounts/ endpoint returns a max number of 500 accounts. If the gerrit server has more than 500 accounts registered, then the client needs to call the /accounts endpoints multiple times.

See
https://gerrit-review.googlesource.com/Documentation/rest-api-accounts.html

gerrit.el is currently limited to 500 accounts, but the following patch should fix that. The problem is that querying a couple of thousand accounts is very slow.

(defun gerrit-rest--get-gerrit-accounts ()
  "Return an alist of all active gerrit users."
  (interactive)
  (condition-case nil
      (let ((continue t)
            (start-idx 0)
            (accounts '()))
        (while continue
          (let ((response
                 ;; see https://gerrit-review.googlesource.com/Documentation/rest-api-accounts.html
                 ;; and https://gerrit-review.googlesource.com/Documentation/user-search-accounts.html#_search_operators
                 (gerrit-rest-sync "GET" nil (format "/accounts/?q=is:active&o=DETAILS&S=%s" start-idx))))
            (setq accounts (append
                           accounts
                           (mapcar (lambda (account-info)
                                     (cons (alist-get '_account_id account-info)
                                           (alist-get 'username account-info)))
                                   response)))
            (setq start-idx (+ start-idx (length response)))
            ;; (message "start: %s" start-idx)
            (setq continue (alist-get '_more_accounts (car (last response))))))
            accounts)
    (error '())))

For tests the review.gerrithub.org gerrit instance can be used, which has between 10000 and 20000 accounts.

For comparison here is a simple shell script that uses curl to download the accounts.
TODO output some numbers

#!/bin/bash
for i in `seq 0 500 10000`; do
  echo $i;
  curl --silent -o out/$i.out -n https://review.gerrithub.io/a/accounts/?q=is:active\&o=DETAILS\&S=$i
done

dashboard buttons only work if they have not been opened previously

steps to reproduce:

  1. open gerrit dashboard
  2. click on one of the repo buttons, ie abc ( opens buffer called gerrit:project:abc)
  3. click on one of the owner buttons, ie xyz (open buffer called gerrit:owner:xyz)
  4. click on a button pointing to repo abc

expected behavior:
gerrit:project:abc buffer opens and the contents reflect the abc repo

actual behavior:
gerrit:project:abc buffer opens but the contents never update, they still reflect the contest of gerrit:owner:xyz

the only way to get the proper contents of gerrit:project:abc to show is to delete the buffer that already exists before clicking on the button again.

its not a huge issue but its kind of inconvenient to have to be aware of which gerrit dashboards are open. itd be nice to be able to click the buttons and have them work as expected regardless of which buffers are currently open.

thanks for the project btw! upload & such work perfectly and have been very helpful : )

Fix setting of assignees in gerrit-upload

git-review does not support setting the assignee via a command line option. The reason for this is that gerrit (server) doesn't provide a push_option for setting assignees.

See

As long as git-review doesn't have a means to set the assignee, we have to parse the change number(s) from the output of git-review and set the assignee via REST API calls.

only the first page of gerrit account results get fetched

by default the gerrit API implements pagination. the instance of gerrit returns a maximum of 500 users per page, even tho there are over 2000 users. this plugin will only fetch the first page.

fixed in my config with this brittle hack that assumes the page length will be 500. works for now : )

 ;; override function in gerrit-rest.el
 (defun gerrit-rest--get-gerrit-accounts ()
   "Return an alist of all active gerrit users."
   (interactive)
   (condition-case nil
       (let ((accounts (list))
             (batch (list))
             (n 0)
             (stop nil))
         (while (not stop)
           (progn
             (setq batch
                   (mapcar (lambda (account-info) (cons (cdr (assoc '_account_id account-info))
                                                        (cdr (assoc 'username account-info))))
                           ;; see https://gerrit-review.googlesource.com/Documentation/rest-api-accounts.html
                           ;; and https://gerrit-review.googlesource.com/Documentation/user-search-accounts.html#_search_operators
                           (gerrit-rest-sync "GET" nil (concat "/accounts/?q=is:active&o=DETAILS&S="
                                                               (number-to-string n)))))
             (if (not (eq (length batch) 500))
                 (setq stop t))
             (setq n (+ n (length batch)))
             (setq accounts (append accounts batch))
             ))
         accounts)
     (error '())))

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.