0

I would like to have my git timestamps in UTC. Currently attempting to commit using magit gives

Wrong type argument: sequencep, my/magit-timestamp-utc

Here's the code:

(defun timestamp-now()
  (format-time-string "%Y-%m-%dT%H:%M:%S%z" (current-time)))

(defun timestamp-utc ()
  (let* ((original-tz (getenv "TZ")))
    (setenv "TZ" "UTC0")
    (let ((timestamp (timestamp-now)))
      (setenv "TZ" original-tz)
      timestamp)))

(defun my/magit-timestamp-utc (&optional prompt default)
  (concat "--date=" (timestamp-utc)))

(setq magit-commit-popup
  '(:variable magit-commit-arguments
    :man-page "git-commit"
    :switches ((?a "Stage all modified and deleted files"   "--all")
               (?e "Allow empty commit"                     "--allow-empty")
               (?v "Show diff of changes to be committed"   "--verbose")
               (?h "Disable hooks"                          "--no-verify")
               (?s "Add Signed-off-by line"                 "--signoff")
               (?R "Claim authorship and reset author date" "--reset-author")
               (?u "UTC" my/magit-timestamp-utc))
    :options  ((?A "Override the author"  "--author=")
               (?S "Sign using gpg"       "--gpg-sign=" magit-read-gpg-secret-key)
               (?C "Reuse commit message" "--reuse-message="
                   magit-read-reuse-message))
    :actions  ((?c "Commit"         magit-commit)
               (?e "Extend"         magit-commit-extend)
               (?f "Fixup"          magit-commit-fixup)
               (?F "Instant Fixup"  magit-commit-instant-fixup) nil
               (?w "Reword"         magit-commit-reword)
               (?s "Squash"         magit-commit-squash)
               (?S "Instant Squash" magit-commit-instant-squash) nil
               (?a "Amend"          magit-commit-amend)
               (?A "Augment"        magit-commit-augment))
    :max-action-columns 4
    :default-action magit-commit))
  • 1
    By the way, a better way to temporarily modify TZ would be: (let ((process-environment (cons "TZ=UTC0" process-environment))) (timestamp-now)). – npostavs Apr 20 at 12:10
2

I would like to have my git timestamps in UTC.

To just do this for all magit calls, put this in your init file:

(eval-when-compile (require 'cl-lib))
(with-eval-after-load 'magit
  (cl-callf setenv-internal magit-git-environment "TZ" "UTC0" nil))

Below is some code to make this toggleable from the commit transient command, which I wrote as an exercise to learn about transient:

(eval-when-compile
  (require 'subr-x))

(require 'magit-git)
(require 'magit-transient)

(defclass magit--git-environment-variable (transient-variable)
  ()) ;; No slots needed

(defclass magit--git-environment-variable:toggle (magit--git-environment-variable)
  ((toggle-value :initarg :toggle-value)))

(defun magit--git-environment-variable-get-value (transient-variable)
  (getenv-internal (oref transient-variable variable) magit-git-environment))

(cl-defmethod transient-infix-set ((obj magit--git-environment-variable) value)
  (cl-callf setenv-internal magit-git-environment (oref obj variable) value nil)
  (unless (or value transient--prefix)
    (message "Unset %s" variable)))

(cl-defmethod transient-infix-set ((obj magit--git-environment-variable:toggle) _value)
  (cl-call-next-method obj (if (magit--git-environment-variable-get-value obj)
                               nil
                             (oref obj toggle-value))))

(cl-defmethod transient-format-value ((obj magit--git-environment-variable))
  (if-let ((value (magit--git-environment-variable-get-value obj)))
      (concat "("
              (propertize (format "%s=%s"
                                  (oref obj variable)
                                  (car (split-string value "\n")))
                          'face 'transient-value)
              ")")
    (propertize "unset" 'face 'transient-inactive-value)))

(cl-defmethod transient-infix-read ((obj magit--git-environment-variable:toggle))
  (if-let ((value (magit--git-environment-variable-get-value obj)))
      (oref obj toggle-value)
    nil))

(define-infix-command magit-commit-env:TZ ()
  :class 'magit--git-environment-variable
  :description "Timezone"
  :variable "TZ")

(define-infix-command magit-commit-env-toggle:TZ=UTC0 ()
  :class 'magit--git-environment-variable:toggle
  :description "Timezone"
  :variable "TZ"
  :toggle-value "UTC0")

(transient-append-suffix 'magit-commit "-s"
  '("-u" "Timezone" magit-commit-env-toggle:TZ=UTC0))
0

You can of course just redefine the complete popup but a better way would be to just add a new switch using magit-define-popup-switch as documented in the manual. Edit: Magit now uses transient instead of magit-popup, the relevant documentation now is Modifying Existing Transients.

Regardless of how you define it, the "name" of the argument has to be a constant string. In some rare cases a switch has the form --arg=val like you are trying to do here, but it still has to be a constant string, not (the name of) a function that returns such a string.

An function that reads a value from the user can be added to an option. However that function is only called when the user invoke the option and is supposed to then read a value in the minibuffer. Automatically generating a value for an option every time the popup is invoked is not supported.

To achieve what you want you have to advice magit-commit-create and likely a few other magit-commit-* commands.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.