GNU bug report logs - #37978
[PATCH] guix: new command "guix time-machine"

Previous Next

Package: guix-patches;

Reported by: Konrad Hinsen <konrad.hinsen <at> fastmail.net>

Date: Tue, 29 Oct 2019 14:12:01 UTC

Severity: normal

Tags: patch

Done: Ludovic Courtès <ludo <at> gnu.org>

Bug is archived. No further changes may be made.

To add a comment to this bug, you must first unarchive it, by sending
a message to control AT debbugs.gnu.org, with unarchive 37978 in the body.
You can then email your comments to 37978 AT debbugs.gnu.org in the normal way.

Toggle the display of automated, internal messages from the tracker.

View this report as an mbox folder, status mbox, maintainer mbox


Report forwarded to guix-patches <at> gnu.org:
bug#37978; Package guix-patches. (Tue, 29 Oct 2019 14:12:02 GMT) Full text and rfc822 format available.

Acknowledgement sent to Konrad Hinsen <konrad.hinsen <at> fastmail.net>:
New bug report received and forwarded. Copy sent to guix-patches <at> gnu.org. (Tue, 29 Oct 2019 14:12:02 GMT) Full text and rfc822 format available.

Message #5 received at submit <at> debbugs.gnu.org (full text, mbox):

From: Konrad Hinsen <konrad.hinsen <at> fastmail.net>
To: guix-patches <at> gnu.org
Subject: [PATCH] guix: new command "guix time-machine"
Date: Fri, 25 Oct 2019 17:42:21 +0200
* guix/scripts/time-machine.scm: New file.
* guix/scripts/pull.scm: Export function channel-list.
* guix/inferior.scm: New function cached-guix-filetree-for-channels.
* doc/guix.texi: Document "git time-machine"
---
 doc/guix.texi                 |  47 +++++++++++++++-
 guix/inferior.scm             |  38 +++++++++----
 guix/scripts/pull.scm         |   1 +
 guix/scripts/time-machine.scm | 101 ++++++++++++++++++++++++++++++++++
 4 files changed, 174 insertions(+), 13 deletions(-)
 create mode 100644 guix/scripts/time-machine.scm

diff --git a/doc/guix.texi b/doc/guix.texi
index 7cc33c6e22..a147f16088 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -247,6 +247,7 @@ Utilities
 * Invoking guix container::     Process isolation.
 * Invoking guix weather::       Assessing substitute availability.
 * Invoking guix processes::     Listing client processes.
+* Invoking guix time-machine::  Running an older version of Guix.
 
 Invoking @command{guix build}
 
@@ -4142,7 +4143,10 @@ say, on another machine, by providing a channel specification in
 @end lisp
 
 The @command{guix describe --format=channels} command can even generate this
-list of channels directly (@pxref{Invoking guix describe}).
+list of channels directly (@pxref{Invoking guix describe}). The resulting
+file can be used with the -C options of @command{guix pull}
+(@pxref{Invoking guix pull}) or @command{guix time-machine}
+(@pxref{Invoking guix time-machine}).
 
 At this point the two machines run the @emph{exact same Guix}, with access to
 the @emph{exact same packages}.  The output of @command{guix build gimp} on
@@ -7894,6 +7898,7 @@ the Scheme programming interface of Guix in a convenient way.
 * Invoking guix container::     Process isolation.
 * Invoking guix weather::       Assessing substitute availability.
 * Invoking guix processes::     Listing client processes.
+* Invoking guix time-machine::  Running an older version of Guix.
 @end menu
 
 @node Invoking guix build
@@ -10563,6 +10568,46 @@ ClientPID: 19419
 ClientCommand: cuirass --cache-directory /var/cache/cuirass @dots{}
 @end example
 
+@node Invoking guix time-machine
+@section Invoking @command{guix time-machine}
+
+@cindex @command{guix time-machine}
+@cindex pinning, channels
+@cindex replicating Guix
+@cindex reproducibility, of Guix
+
+The @command{guix time-machine} command provides access to older
+versions of Guix, for example to install older versions of packages,
+or to reproduce a computation in an identical environment. The version
+of Guix to be used is defined by a commit or by a channel
+description file created by @command{guix describe}
+(@pxref{Invoking guix describe}).
+
+The general syntax is:
+
+@example
+guix time-machine @var{channels} -- @var{command} @var {arg}@dots{}
+@end example
+
+where @var{command} and @var{arg}@dots{} are passed unmodified to the
+@command{guix} command in its old version.  The @var{channels} that define
+this version can be specified using the following options:
+
+@table @code
+@item --url=@var{url}
+@itemx --commit=@var{commit}
+@itemx --branch=@var{branch}
+Use the @code{guix} channel from the specified @var{url}, at the
+given @var{commit} (a valid Git commit ID represented as a hexadecimal
+string), or @var{branch}.
+
+@item --channels=@var{file}
+@itemx -C @var{file}
+Read the list of channels from @var{file}.  @var{file} must contain
+Scheme code that evaluates to a list of channel objects.
+@xref{Channels} for more information.
+@end table
+
 
 @node System Configuration
 @chapter System Configuration
diff --git a/guix/inferior.scm b/guix/inferior.scm
index b8e2f21f42..cb80bb43d5 100644
--- a/guix/inferior.scm
+++ b/guix/inferior.scm
@@ -89,6 +89,7 @@
             gexp->derivation-in-inferior
 
             %inferior-cache-directory
+            cached-guix-filetree-for-channels
             inferior-for-channels))
 
 ;;; Commentary:
@@ -635,16 +636,13 @@ failing when GUIX is too old and lacks the 'guix repl' command."
   (make-parameter (string-append (cache-directory #:ensure? #f)
                                  "/inferiors")))
 
-(define* (inferior-for-channels channels
-                                #:key
-                                (cache-directory (%inferior-cache-directory))
-                                (ttl (* 3600 24 30)))
-  "Return an inferior for CHANNELS, a list of channels.  Use the cache at
-CACHE-DIRECTORY, where entries can be reclaimed after TTL seconds.  This
-procedure opens a new connection to the build daemon.
-
-This is a convenience procedure that people may use in manifests passed to
-'guix package -m', for instance."
+(define* (cached-guix-filetree-for-channels channels
+                                            #:key
+                                            (cache-directory (%inferior-cache-directory))
+                                            (ttl (* 3600 24 30)))
+  "Return a directory containing a guix filetree defined by CHANNELS, a list of channels.
+The directory is a subdirectory of CACHE-DIRECTORY, where entries can be reclaimed after TTL seconds.
+This procedure opens a new connection to the build daemon."
   (with-store store
     (let ()
       (define instances
@@ -680,7 +678,7 @@ This is a convenience procedure that people may use in manifests passed to
                                           (file-expiration-time ttl))
 
       (if (file-exists? cached)
-          (open-inferior cached)
+          cached
           (run-with-store store
             (mlet %store-monad ((profile
                                  (channel-instances->derivation instances)))
@@ -689,4 +687,20 @@ This is a convenience procedure that people may use in manifests passed to
                 (built-derivations (list profile))
                 (symlink* (derivation->output-path profile) cached)
                 (add-indirect-root* cached)
-                (return (open-inferior cached)))))))))
+                (return cached))))))))
+
+(define* (inferior-for-channels channels
+                                #:key
+                                (cache-directory (%inferior-cache-directory))
+                                (ttl (* 3600 24 30)))
+  "Return an inferior for CHANNELS, a list of channels.  Use the cache at
+CACHE-DIRECTORY, where entries can be reclaimed after TTL seconds.  This
+procedure opens a new connection to the build daemon.
+
+This is a convenience procedure that people may use in manifests passed to
+'guix package -m', for instance."
+  (define cached
+    (cached-guix-filetree-for-channels channels
+                                       #:cache-directory cache-directory
+                                       #:ttl ttl))
+  (open-inferior cached))
diff --git a/guix/scripts/pull.scm b/guix/scripts/pull.scm
index 80d070652b..a508e817b2 100644
--- a/guix/scripts/pull.scm
+++ b/guix/scripts/pull.scm
@@ -56,6 +56,7 @@
   #:use-module (ice-9 vlist)
   #:use-module (ice-9 format)
   #:export (display-profile-content
+            channel-list
             guix-pull))
 
 
diff --git a/guix/scripts/time-machine.scm b/guix/scripts/time-machine.scm
new file mode 100644
index 0000000000..8e954d51e1
--- /dev/null
+++ b/guix/scripts/time-machine.scm
@@ -0,0 +1,101 @@
+;;; GNU Guix --- Functional package management for GNU
+;;; Copyright © 2019 Konrad Hinsen <konrad.hinsen <at> fastmail.net>
+;;;
+;;; This file is part of GNU Guix.
+;;;
+;;; GNU Guix 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.
+;;;
+;;; GNU Guix 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 GNU Guix.  If not, see <http://www.gnu.org/licenses/>.
+
+(define-module (guix scripts time-machine)
+  #:use-module (guix ui)
+  #:use-module (guix scripts)
+  #:use-module (guix inferior)
+  #:use-module (guix channels)
+  #:use-module ((guix scripts pull) #:select (channel-list))
+  #:use-module (ice-9 match)
+  #:use-module (srfi srfi-1)
+  #:use-module (srfi srfi-11)
+  #:use-module (srfi srfi-26)
+  #:use-module (srfi srfi-37)
+  #:export (guix-time-machine))
+
+
+;;;
+;;; Command-line options.
+;;;
+
+(define (show-help)
+  (display (G_ "Usage: guix time-machine [OPTION] -- COMMAND ARGS...
+Execute COMMAND ARGS... in an older version of Guix.\n"))
+  (display (G_ "
+  -C, --channels=FILE    deploy the channels defined in FILE"))
+  (display (G_ "
+      --url=URL          use the Git repository at URL"))
+  (display (G_ "
+      --commit=COMMIT    use the specified COMMIT"))
+  (display (G_ "
+      --branch=BRANCH    use the tip of the specified BRANCH"))
+  (display (G_ "
+  -h, --help             display this help and exit"))
+  (display (G_ "
+  -V, --version          display version information and exit"))
+  (newline)
+  (show-bug-report-information))
+
+(define %options
+  ;; Specifications of the command-line options.
+  (list (option '(#\C "channels") #t #f
+                (lambda (opt name arg result)
+                  (alist-cons 'channel-file arg result)))
+         (option '("url") #t #f
+                 (lambda (opt name arg result)
+                   (alist-cons 'repository-url arg
+                               (alist-delete 'repository-url result))))
+         (option '("commit") #t #f
+                 (lambda (opt name arg result)
+                   (alist-cons 'ref `(commit . ,arg) result)))
+         (option '("branch") #t #f
+                 (lambda (opt name arg result)
+                   (alist-cons 'ref `(branch . ,arg) result)))
+        (option '(#\h "help") #f #f
+                (lambda args
+                  (show-help)
+                  (exit 0)))
+        (option '(#\V "version") #f #f
+                (lambda args
+                  (show-version-and-exit "guix time-machine")))))
+
+(define (parse-args args)
+  "Parse the list of command line arguments ARGS."
+  ;; The '--' token is used to separate the command to run from the rest of
+  ;; the operands.
+  (let-values (((args command) (break (cut string=? "--" <>) args)))
+    (let ((opts (parse-command-line args %options '(()) #:build-options? #f)))
+      (match command
+        (() opts)
+        (("--") opts)
+        (("--" command ...) (alist-cons 'exec command opts))))))
+
+
+;;;
+;;; Entry point.
+;;;
+
+(define (guix-time-machine . args)
+  (with-error-handling
+    (let* ((opts         (parse-args args))
+           (channels     (channel-list opts))
+           (command-line (assoc-ref opts 'exec))
+           (directory    (cached-guix-filetree-for-channels channels))
+           (executable   (string-append directory "/bin/guix")))
+      (apply system* (cons executable command-line)))))
-- 
2.23.0





Information forwarded to guix-patches <at> gnu.org:
bug#37978; Package guix-patches. (Wed, 06 Nov 2019 13:54:01 GMT) Full text and rfc822 format available.

Message #8 received at 37978 <at> debbugs.gnu.org (full text, mbox):

From: Ludovic Courtès <ludo <at> gnu.org>
To: Konrad Hinsen <konrad.hinsen <at> fastmail.net>
Cc: 37978 <at> debbugs.gnu.org
Subject: Re: [bug#37978] [PATCH] guix: new command "guix time-machine"
Date: Wed, 06 Nov 2019 14:53:40 +0100
Hello Konrad!

Konrad Hinsen <konrad.hinsen <at> fastmail.net> skribis:

> * guix/scripts/time-machine.scm: New file.
> * guix/scripts/pull.scm: Export function channel-list.
> * guix/inferior.scm: New function cached-guix-filetree-for-channels.
> * doc/guix.texi: Document "git time-machine"

Awesome.  :-)

Please also add time-machine.scm to Makefile.am.  In the commit log,
you’ll get bonus points if you mention the modified entities in
parentheses (see ‘git log’ for examples.)  :-)

> @@ -247,6 +247,7 @@ Utilities
>  * Invoking guix container::     Process isolation.
>  * Invoking guix weather::       Assessing substitute availability.
>  * Invoking guix processes::     Listing client processes.
> +* Invoking guix time-machine::  Running an older version of Guix.

How about moving this section a bit higher, because it’s more widely
useful than ‘guix processes’ for instance?  Actually, it could go under
“Package Management” right before “Inferiors”, WDYT?

>  The @command{guix describe --format=channels} command can even generate this
> -list of channels directly (@pxref{Invoking guix describe}).
> +list of channels directly (@pxref{Invoking guix describe}). The resulting
> +file can be used with the -C options of @command{guix pull}
> +(@pxref{Invoking guix pull}) or @command{guix time-machine}
> +(@pxref{Invoking guix time-machine}).

Nitpick: Please write two spaces after an end-of-sentence period.

> +The general syntax is:
> +
> +@example
> +guix time-machine @var{channels} -- @var{command} @var {arg}@dots{}
> +@end example

I think it should be “guix time-machine @var{options}@dots{} -- …”,
right?

IIUC, if you run:

  guix time-machine -- build hello

you build “hello” with the latest master, right?

Perhaps it would be good to add an example like this one actually, WDYT?

> +where @var{command} and @var{arg}@dots{} are passed unmodified to the
> +@command{guix} command in its old version.  The @var{channels} that define
> +this version can be specified using the following options:

Perhaps add “like for @command{guix pull}”.

> -(define* (inferior-for-channels channels
> -                                #:key
> -                                (cache-directory (%inferior-cache-directory))
> -                                (ttl (* 3600 24 30)))
> -  "Return an inferior for CHANNELS, a list of channels.  Use the cache at
> -CACHE-DIRECTORY, where entries can be reclaimed after TTL seconds.  This
> -procedure opens a new connection to the build daemon.
> -
> -This is a convenience procedure that people may use in manifests passed to
> -'guix package -m', for instance."
> +(define* (cached-guix-filetree-for-channels channels
> +                                            #:key
> +                                            (cache-directory (%inferior-cache-directory))
> +                                            (ttl (* 3600 24 30)))
> +  "Return a directory containing a guix filetree defined by CHANNELS, a list of channels.
> +The directory is a subdirectory of CACHE-DIRECTORY, where entries can be reclaimed after TTL seconds.
> +This procedure opens a new connection to the build daemon."

How about (1) calling it ‘cached-channel-instance’ (or similar), and (2)
not opening a connection to the daemon?

Regarding (2), it means that procedure would be a monadic procedure and
it’s up to the user to do with-store + run-with-store or whatever.  The
general convention is to not open new connections on behalf of the user
(‘inferior-for-channels’ is one of the only exceptions to the rule
because it’s a convenience function for use in manifests.)

Perhaps this change should be a separate patch.

> +(define (guix-time-machine . args)
> +  (with-error-handling
> +    (let* ((opts         (parse-args args))
> +           (channels     (channel-list opts))
> +           (command-line (assoc-ref opts 'exec))
> +           (directory    (cached-guix-filetree-for-channels channels))
> +           (executable   (string-append directory "/bin/guix")))
> +      (apply system* (cons executable command-line)))))

I think this should be:

  (apply execl executable command-line)

so that we don’t create an extra process and actually get the exit code
for that sub-process.

Thanks,
Ludo’.




Information forwarded to guix-patches <at> gnu.org:
bug#37978; Package guix-patches. (Wed, 06 Nov 2019 14:28:01 GMT) Full text and rfc822 format available.

Message #11 received at submit <at> debbugs.gnu.org (full text, mbox):

From: Tobias Geerinckx-Rice <me <at> tobias.gr>
To: guix-patches <at> gnu.org
Cc: Konrad Hinsen <konrad.hinsen <at> fastmail.net>, 37978 <at> debbugs.gnu.org
Subject: Re: [bug#37978] [PATCH] guix: new command "guix time-machine"
Date: Wed, 06 Nov 2019 15:27:27 +0100
[Message part 1 (text/plain, inline)]
Konrad,

Thank you!  This is neat!

Konrad Hinsen 写道:
> * doc/guix.texi: Document "git time-machine"
                            ^^^
Kind regards,

T G-R
[signature.asc (application/pgp-signature, inline)]

Information forwarded to guix-patches <at> gnu.org:
bug#37978; Package guix-patches. (Wed, 06 Nov 2019 14:28:02 GMT) Full text and rfc822 format available.

Information forwarded to guix-patches <at> gnu.org:
bug#37978; Package guix-patches. (Wed, 06 Nov 2019 14:31:02 GMT) Full text and rfc822 format available.

Message #17 received at 37978 <at> debbugs.gnu.org (full text, mbox):

From: Ludovic Courtès <ludo <at> gnu.org>
To: Konrad Hinsen <konrad.hinsen <at> fastmail.net>
Cc: 37978 <at> debbugs.gnu.org
Subject: Re: [bug#37978] [PATCH] guix: new command "guix time-machine"
Date: Wed, 06 Nov 2019 15:30:16 +0100
Another thing that comes to mind: it’d be nice to add an entry in
‘etc/news.scm’ to let users know about the new command!

Ludo’.




Information forwarded to guix-patches <at> gnu.org:
bug#37978; Package guix-patches. (Thu, 07 Nov 2019 13:12:01 GMT) Full text and rfc822 format available.

Message #20 received at 37978 <at> debbugs.gnu.org (full text, mbox):

From: Konrad Hinsen <konrad.hinsen <at> fastmail.net>
To: Tobias Geerinckx-Rice <me <at> tobias.gr>
Cc: 37978 <at> debbugs.gnu.org
Subject: Re: [bug#37978] [PATCH] guix: new command "guix time-machine"
Date: Thu, 07 Nov 2019 14:11:46 +0100
Tobias Geerinckx-Rice <me <at> tobias.gr> writes:

> Konrad Hinsen 写道:
>> * doc/guix.texi: Document "git time-machine"
>                              ^^^

Ouch. That's me. I always confuse "git" and "guix" on the command line.
It's worst for "pull", which works with both.

Is command-line dyslexia a thing already ? ;-)

Thanks for spotting the typo,
  Konrad.




Information forwarded to guix-patches <at> gnu.org:
bug#37978; Package guix-patches. (Thu, 07 Nov 2019 19:41:01 GMT) Full text and rfc822 format available.

Message #23 received at 37978 <at> debbugs.gnu.org (full text, mbox):

From: Konrad Hinsen <konrad.hinsen <at> fastmail.net>
To: Ludovic Courtès <ludo <at> gnu.org>
Cc: 37978 <at> debbugs.gnu.org
Subject: Re: [bug#37978] [PATCH] guix: new command "guix time-machine"
Date: Thu, 07 Nov 2019 20:40:35 +0100
Ludovic Courtès <ludo <at> gnu.org> writes:

> Another thing that comes to mind: it’d be nice to add an entry in
> ‘etc/news.scm’ to let users know about the new command!

If I understand the format correctly, I can't write this entry before
the command is merged into master, because I need the commit that
implements it.

Konrad.




Information forwarded to guix-patches <at> gnu.org:
bug#37978; Package guix-patches. (Thu, 07 Nov 2019 21:12:02 GMT) Full text and rfc822 format available.

Message #26 received at 37978 <at> debbugs.gnu.org (full text, mbox):

From: Ludovic Courtès <ludo <at> gnu.org>
To: Konrad Hinsen <konrad.hinsen <at> fastmail.net>
Cc: 37978 <at> debbugs.gnu.org
Subject: Re: [bug#37978] [PATCH] guix: new command "guix time-machine"
Date: Thu, 07 Nov 2019 22:10:52 +0100
Konrad Hinsen <konrad.hinsen <at> fastmail.net> skribis:

> Ludovic Courtès <ludo <at> gnu.org> writes:
>
>> Another thing that comes to mind: it’d be nice to add an entry in
>> ‘etc/news.scm’ to let users know about the new command!
>
> If I understand the format correctly, I can't write this entry before
> the command is merged into master, because I need the commit that
> implements it.

Exactly, so whoever pushes it must adjust the commit ID in the news
entry right before pushing.  You can just write (commit "XXX") in the
meantime.

Ludo’.




Information forwarded to guix-patches <at> gnu.org:
bug#37978; Package guix-patches. (Fri, 08 Nov 2019 07:15:02 GMT) Full text and rfc822 format available.

Message #29 received at 37978 <at> debbugs.gnu.org (full text, mbox):

From: Konrad Hinsen <konrad.hinsen <at> fastmail.net>
To: Ludovic Courtès <ludo <at> gnu.org>
Cc: 37978 <at> debbugs.gnu.org
Subject: Re: [bug#37978] [PATCH] guix: new command "guix time-machine"
Date: Fri, 08 Nov 2019 08:14:03 +0100
Hi Ludo,

Thanks for your detailed comments, most of which I agree with, and I
will make the required changes. There is one issue I see:

> How about (1) calling it ‘cached-channel-instance’ (or similar), and (2)
> not opening a connection to the daemon?
>
> Regarding (2), it means that procedure would be a monadic procedure and
> it’s up to the user to do with-store + run-with-store or whatever.  The

That would imply that the user always opens a daemon, even if it is not
necessary because the required instance is already in the cache. Is that
a price worth paying for a more conventional interface, in your opinion?

Cheers,
  Konrad.




Information forwarded to guix-patches <at> gnu.org:
bug#37978; Package guix-patches. (Fri, 08 Nov 2019 14:14:02 GMT) Full text and rfc822 format available.

Message #32 received at 37978 <at> debbugs.gnu.org (full text, mbox):

From: Konrad Hinsen <konrad.hinsen <at> fastmail.net>
To: 37978 <at> debbugs.gnu.org
Subject: [PATCH 1/2] guix: new command "guix time-machine"
Date: Fri, 08 Nov 2019 15:13:48 +0100
* guix/scripts/time-machine.scm: New file.
* Makefile.am: (MODULES): Add it.
* guix/scripts/pull.scm: Export function channel-list.
* guix/inferior.scm: New function cached-guix-filetree-for-channels.
* doc/guix.texi: Document "guix time-machine"
---
 Makefile.am                   |   1 +
 doc/guix.texi                 |  59 +++++++++++++++++++-
 guix/inferior.scm             |  38 +++++++++----
 guix/scripts/pull.scm         |   1 +
 guix/scripts/time-machine.scm | 102 ++++++++++++++++++++++++++++++++++
 5 files changed, 187 insertions(+), 14 deletions(-)
 create mode 100644 guix/scripts/time-machine.scm

diff --git a/Makefile.am b/Makefile.am
index b1f33946c5..b3f03d44c8 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -278,6 +278,7 @@ MODULES =					\
   guix/scripts/container.scm			\
   guix/scripts/container/exec.scm		\
   guix/scripts/deploy.scm			\
+  guix/scripts/time-machine.scm			\
   guix.scm					\
   $(GNU_SYSTEM_MODULES)
 
diff --git a/doc/guix.texi b/doc/guix.texi
index 3b8e5935bb..3d4192cab2 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -197,6 +197,7 @@ Package Management
 * Invoking guix gc::            Running the garbage collector.
 * Invoking guix pull::          Fetching the latest Guix and distribution.
 * Channels::                    Customizing the package collection.
+* Invoking guix time-machine::  Running an older revision of Guix.
 * Inferiors::                   Interacting with another revision of Guix.
 * Invoking guix describe::      Display information about your Guix revision.
 * Invoking guix archive::       Exporting and importing store files.
@@ -2548,6 +2549,7 @@ guix install emacs-guix
 * Invoking guix gc::            Running the garbage collector.
 * Invoking guix pull::          Fetching the latest Guix and distribution.
 * Channels::                    Customizing the package collection.
+* Invoking guix time-machine::  Running an older revision of Guix.
 * Inferiors::                   Interacting with another revision of Guix.
 * Invoking guix describe::      Display information about your Guix revision.
 * Invoking guix archive::       Exporting and importing store files.
@@ -4150,7 +4152,10 @@ say, on another machine, by providing a channel specification in
 @end lisp
 
 The @command{guix describe --format=channels} command can even generate this
-list of channels directly (@pxref{Invoking guix describe}).
+list of channels directly (@pxref{Invoking guix describe}).  The resulting
+file can be used with the -C options of @command{guix pull}
+(@pxref{Invoking guix pull}) or @command{guix time-machine}
+(@pxref{Invoking guix time-machine}).
 
 At this point the two machines run the @emph{exact same Guix}, with access to
 the @emph{exact same packages}.  The output of @command{guix build gimp} on
@@ -4164,6 +4169,57 @@ artifacts with very fine grain, and to reproduce software environments at
 will---some sort of ``meta reproducibility'' capabilities, if you will.
 @xref{Inferiors}, for another way to take advantage of these super powers.
 
+@node Invoking guix time-machine
+@section Invoking @command{guix time-machine}
+
+@cindex @command{guix time-machine}
+@cindex pinning, channels
+@cindex replicating Guix
+@cindex reproducibility, of Guix
+
+The @command{guix time-machine} command provides access to other
+revisions of Guix, for example to install older versions of packages,
+or to reproduce a computation in an identical environment.  The revision
+of Guix to be used is defined by a commit or by a channel
+description file created by @command{guix describe}
+(@pxref{Invoking guix describe}).
+
+The general syntax is:
+
+@example
+guix time-machine @var{options}@dots{} -- @var{command} @var {arg}@dots{}
+@end example
+
+where @var{command} and @var{arg}@dots{} are passed unmodified to the
+@command{guix} command if the specified revision.  The @var{options} that define
+this revision are the same as for @command{guix pull} (@pxref{Invoking guix pull}):
+
+@table @code
+@item --url=@var{url}
+@itemx --commit=@var{commit}
+@itemx --branch=@var{branch}
+Use the @code{guix} channel from the specified @var{url}, at the
+given @var{commit} (a valid Git commit ID represented as a hexadecimal
+string), or @var{branch}.
+
+@item --channels=@var{file}
+@itemx -C @var{file}
+Read the list of channels from @var{file}.  @var{file} must contain
+Scheme code that evaluates to a list of channel objects.
+@xref{Channels} for more information.
+@end table
+
+As for @command{guix pull}, the absence of any options means that the
+the latest commit on the master branch will be used. The command
+
+@example
+guix time-machine -- build hello
+@end example
+
+will thus build the package @code{hello} as defined in the master branch,
+which is in general a newer revison of Guix than you have installed.
+Time travel works in both directions!
+
 @node Inferiors
 @section Inferiors
 
@@ -10582,7 +10638,6 @@ ClientPID: 19419
 ClientCommand: cuirass --cache-directory /var/cache/cuirass @dots{}
 @end example
 
-
 @node System Configuration
 @chapter System Configuration
 
diff --git a/guix/inferior.scm b/guix/inferior.scm
index b8e2f21f42..cb80bb43d5 100644
--- a/guix/inferior.scm
+++ b/guix/inferior.scm
@@ -89,6 +89,7 @@
             gexp->derivation-in-inferior
 
             %inferior-cache-directory
+            cached-guix-filetree-for-channels
             inferior-for-channels))
 
 ;;; Commentary:
@@ -635,16 +636,13 @@ failing when GUIX is too old and lacks the 'guix repl' command."
   (make-parameter (string-append (cache-directory #:ensure? #f)
                                  "/inferiors")))
 
-(define* (inferior-for-channels channels
-                                #:key
-                                (cache-directory (%inferior-cache-directory))
-                                (ttl (* 3600 24 30)))
-  "Return an inferior for CHANNELS, a list of channels.  Use the cache at
-CACHE-DIRECTORY, where entries can be reclaimed after TTL seconds.  This
-procedure opens a new connection to the build daemon.
-
-This is a convenience procedure that people may use in manifests passed to
-'guix package -m', for instance."
+(define* (cached-guix-filetree-for-channels channels
+                                            #:key
+                                            (cache-directory (%inferior-cache-directory))
+                                            (ttl (* 3600 24 30)))
+  "Return a directory containing a guix filetree defined by CHANNELS, a list of channels.
+The directory is a subdirectory of CACHE-DIRECTORY, where entries can be reclaimed after TTL seconds.
+This procedure opens a new connection to the build daemon."
   (with-store store
     (let ()
       (define instances
@@ -680,7 +678,7 @@ This is a convenience procedure that people may use in manifests passed to
                                           (file-expiration-time ttl))
 
       (if (file-exists? cached)
-          (open-inferior cached)
+          cached
           (run-with-store store
             (mlet %store-monad ((profile
                                  (channel-instances->derivation instances)))
@@ -689,4 +687,20 @@ This is a convenience procedure that people may use in manifests passed to
                 (built-derivations (list profile))
                 (symlink* (derivation->output-path profile) cached)
                 (add-indirect-root* cached)
-                (return (open-inferior cached)))))))))
+                (return cached))))))))
+
+(define* (inferior-for-channels channels
+                                #:key
+                                (cache-directory (%inferior-cache-directory))
+                                (ttl (* 3600 24 30)))
+  "Return an inferior for CHANNELS, a list of channels.  Use the cache at
+CACHE-DIRECTORY, where entries can be reclaimed after TTL seconds.  This
+procedure opens a new connection to the build daemon.
+
+This is a convenience procedure that people may use in manifests passed to
+'guix package -m', for instance."
+  (define cached
+    (cached-guix-filetree-for-channels channels
+                                       #:cache-directory cache-directory
+                                       #:ttl ttl))
+  (open-inferior cached))
diff --git a/guix/scripts/pull.scm b/guix/scripts/pull.scm
index 92aac6066e..d6173a6acb 100644
--- a/guix/scripts/pull.scm
+++ b/guix/scripts/pull.scm
@@ -56,6 +56,7 @@
   #:use-module (ice-9 vlist)
   #:use-module (ice-9 format)
   #:export (display-profile-content
+            channel-list
             guix-pull))
 
 
diff --git a/guix/scripts/time-machine.scm b/guix/scripts/time-machine.scm
new file mode 100644
index 0000000000..bc7ae573a3
--- /dev/null
+++ b/guix/scripts/time-machine.scm
@@ -0,0 +1,102 @@
+;;; GNU Guix --- Functional package management for GNU
+;;; Copyright © 2019 Konrad Hinsen <konrad.hinsen <at> fastmail.net>
+;;;
+;;; This file is part of GNU Guix.
+;;;
+;;; GNU Guix 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.
+;;;
+;;; GNU Guix 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 GNU Guix.  If not, see <http://www.gnu.org/licenses/>.
+
+(define-module (guix scripts time-machine)
+  #:use-module (guix ui)
+  #:use-module (guix scripts)
+  #:use-module (guix inferior)
+  #:use-module (guix channels)
+  #:use-module ((guix scripts pull) #:select (channel-list))
+  #:use-module (ice-9 match)
+  #:use-module (srfi srfi-1)
+  #:use-module (srfi srfi-11)
+  #:use-module (srfi srfi-26)
+  #:use-module (srfi srfi-37)
+  #:export (guix-time-machine))
+
+
+;;;
+;;; Command-line options.
+;;;
+
+(define (show-help)
+  (display (G_ "Usage: guix time-machine [OPTION] -- COMMAND ARGS...
+Execute COMMAND ARGS... in an older version of Guix.\n"))
+  (display (G_ "
+  -C, --channels=FILE    deploy the channels defined in FILE"))
+  (display (G_ "
+      --url=URL          use the Git repository at URL"))
+  (display (G_ "
+      --commit=COMMIT    use the specified COMMIT"))
+  (display (G_ "
+      --branch=BRANCH    use the tip of the specified BRANCH"))
+  (display (G_ "
+  -h, --help             display this help and exit"))
+  (display (G_ "
+  -V, --version          display version information and exit"))
+  (newline)
+  (show-bug-report-information))
+
+(define %options
+  ;; Specifications of the command-line options.
+  (list (option '(#\C "channels") #t #f
+                (lambda (opt name arg result)
+                  (alist-cons 'channel-file arg result)))
+         (option '("url") #t #f
+                 (lambda (opt name arg result)
+                   (alist-cons 'repository-url arg
+                               (alist-delete 'repository-url result))))
+         (option '("commit") #t #f
+                 (lambda (opt name arg result)
+                   (alist-cons 'ref `(commit . ,arg) result)))
+         (option '("branch") #t #f
+                 (lambda (opt name arg result)
+                   (alist-cons 'ref `(branch . ,arg) result)))
+        (option '(#\h "help") #f #f
+                (lambda args
+                  (show-help)
+                  (exit 0)))
+        (option '(#\V "version") #f #f
+                (lambda args
+                  (show-version-and-exit "guix time-machine")))))
+
+(define (parse-args args)
+  "Parse the list of command line arguments ARGS."
+  ;; The '--' token is used to separate the command to run from the rest of
+  ;; the operands.
+  (let-values (((args command) (break (cut string=? "--" <>) args)))
+    (let ((opts (parse-command-line args %options '(()) #:build-options? #f)))
+      (match command
+        (() opts)
+        (("--") opts)
+        (("--" command ...) (alist-cons 'exec command opts))))))
+
+
+;;;
+;;; Entry point.
+;;;
+
+(define (guix-time-machine . args)
+  (with-error-handling
+    (let* ((opts         (parse-args args))
+           (channels     (channel-list opts))
+           (command-line (assoc-ref opts 'exec)))
+      (when command-line
+        (let* ((directory  (cached-guix-filetree-for-channels channels))
+               (executable (string-append directory "/bin/guix")))
+          (apply execl (cons* executable executable command-line)))))))
-- 
2.23.0





Information forwarded to guix-patches <at> gnu.org:
bug#37978; Package guix-patches. (Fri, 08 Nov 2019 14:16:01 GMT) Full text and rfc822 format available.

Message #35 received at 37978 <at> debbugs.gnu.org (full text, mbox):

From: Konrad Hinsen <konrad.hinsen <at> fastmail.net>
To: 37978 <at> debbugs.gnu.org
Subject: [PATCH 2/2] news: Add entry for "guix time-machine".
Date: Fri, 08 Nov 2019 15:15:16 +0100
* etc/news.scm: Add entry for "guix time-machine".
---
 etc/news.scm | 18 ++++++++++++++++++
 1 file changed, 18 insertions(+)

diff --git a/etc/news.scm b/etc/news.scm
index 08e06351e9..b6a7f46ee9 100644
--- a/etc/news.scm
+++ b/etc/news.scm
@@ -9,6 +9,24 @@
 (channel-news
  (version 0)
 
+ (entry (commit "XXX")
+        (title (en "New command @command{guix time-machine}")
+               (de "Neuer Befehl @command{guix time-machine}")
+               (fr "Nouvelle commande @command{guix time-machine}"))
+        (body (en "The new command @command{guix time-machine} facilitates
+access to older or newer revisions of Guix than the one that is installed.
+It can be used to install different versions of packages, and to
+re-create computational environments exactly as used in the past.")
+              (de "Der neue Befehl @command{guix time-machine} vereinfacht
+den Zugriff auf ältere oder neuere Guix-Versionen als die installierte.
+Er kann zur Installation bestimmer Paket-Versionen verwendet werden, aber
+auch zur Wiederherstellung von Entwicklungsumgebungen wie sie in der
+Vergagngenheit verwendet wurden.")
+              (fr "La nouvelle commande @command{guix time-machine}
+facilite l'accès à des versions antérieures ou postérieures par rapport
+à la version installée.  Elle sert à installer des versions spécifiques
+de paquets, ainsi à la restauration d'environnements dans un état
+historique.")))
  (entry (commit "3e962e59d849e4300e447d94487684102d9d412e")
         (title (en "@command{guix graph} now supports package
 transformations"))
-- 
2.23.0





Information forwarded to guix-patches <at> gnu.org:
bug#37978; Package guix-patches. (Fri, 08 Nov 2019 14:16:02 GMT) Full text and rfc822 format available.

Message #38 received at 37978 <at> debbugs.gnu.org (full text, mbox):

From: Konrad Hinsen <konrad.hinsen <at> fastmail.net>
To: 37978 <at> debbugs.gnu.org
Subject: [PATCH 2/2] news: Add entry for "guix time-machine".
Date: Fri, 08 Nov 2019 15:15:35 +0100
* etc/news.scm: Add entry for "guix time-machine".
---
 etc/news.scm | 18 ++++++++++++++++++
 1 file changed, 18 insertions(+)

diff --git a/etc/news.scm b/etc/news.scm
index 08e06351e9..b6a7f46ee9 100644
--- a/etc/news.scm
+++ b/etc/news.scm
@@ -9,6 +9,24 @@
 (channel-news
  (version 0)
 
+ (entry (commit "XXX")
+        (title (en "New command @command{guix time-machine}")
+               (de "Neuer Befehl @command{guix time-machine}")
+               (fr "Nouvelle commande @command{guix time-machine}"))
+        (body (en "The new command @command{guix time-machine} facilitates
+access to older or newer revisions of Guix than the one that is installed.
+It can be used to install different versions of packages, and to
+re-create computational environments exactly as used in the past.")
+              (de "Der neue Befehl @command{guix time-machine} vereinfacht
+den Zugriff auf ältere oder neuere Guix-Versionen als die installierte.
+Er kann zur Installation bestimmer Paket-Versionen verwendet werden, aber
+auch zur Wiederherstellung von Entwicklungsumgebungen wie sie in der
+Vergagngenheit verwendet wurden.")
+              (fr "La nouvelle commande @command{guix time-machine}
+facilite l'accès à des versions antérieures ou postérieures par rapport
+à la version installée.  Elle sert à installer des versions spécifiques
+de paquets, ainsi à la restauration d'environnements dans un état
+historique.")))
  (entry (commit "3e962e59d849e4300e447d94487684102d9d412e")
         (title (en "@command{guix graph} now supports package
 transformations"))
-- 
2.23.0





Information forwarded to guix-patches <at> gnu.org:
bug#37978; Package guix-patches. (Fri, 08 Nov 2019 14:17:02 GMT) Full text and rfc822 format available.

Message #41 received at 37978 <at> debbugs.gnu.org (full text, mbox):

From: Konrad Hinsen <konrad.hinsen <at> fastmail.net>
To: Ludovic Courtès <ludo <at> gnu.org>
Cc: 37978 <at> debbugs.gnu.org
Subject: Re: [bug#37978] [PATCH] guix: new command "guix time-machine"
Date: Fri, 08 Nov 2019 15:16:36 +0100
Konrad Hinsen <konrad.hinsen <at> fastmail.net> writes:

> Thanks for your detailed comments, most of which I agree with, and I
> will make the required changes.

I just submitted two new patches. Looking forward to everyone's feedbck!

Cheers,
  Konrad.




Information forwarded to guix-patches <at> gnu.org:
bug#37978; Package guix-patches. (Fri, 08 Nov 2019 20:10:01 GMT) Full text and rfc822 format available.

Message #44 received at 37978 <at> debbugs.gnu.org (full text, mbox):

From: Ludovic Courtès <ludo <at> gnu.org>
To: Konrad Hinsen <konrad.hinsen <at> fastmail.net>
Cc: 37978 <at> debbugs.gnu.org
Subject: Re: [bug#37978] [PATCH] guix: new command "guix time-machine"
Date: Fri, 08 Nov 2019 21:09:10 +0100
Hello,

Konrad Hinsen <konrad.hinsen <at> fastmail.net> skribis:

>> How about (1) calling it ‘cached-channel-instance’ (or similar), and (2)
>> not opening a connection to the daemon?
>>
>> Regarding (2), it means that procedure would be a monadic procedure and
>> it’s up to the user to do with-store + run-with-store or whatever.  The
>
> That would imply that the user always opens a daemon, even if it is not
> necessary because the required instance is already in the cache. Is that
> a price worth paying for a more conventional interface, in your opinion?

Ah, I hadn’t thought about it.

Hmm opening a connection to the daemon doesn’t cost much, but OTOH it’s
best if we can avoid it here since ‘guix time-machine’ is just a
trampoline.

Dunno, I’ll comment on the new patches and we’ll see.  :-)

Ludo’.




Information forwarded to guix-patches <at> gnu.org:
bug#37978; Package guix-patches. (Fri, 08 Nov 2019 20:44:02 GMT) Full text and rfc822 format available.

Message #47 received at 37978 <at> debbugs.gnu.org (full text, mbox):

From: "pelzflorian (Florian Pelz)" <pelzflorian <at> pelzflorian.de>
To: Konrad Hinsen <konrad.hinsen <at> fastmail.net>
Cc: 37978 <at> debbugs.gnu.org
Subject: Re: [bug#37978] [PATCH 2/2] news: Add entry for "guix time-machine".
Date: Fri, 8 Nov 2019 21:43:27 +0100
Thank you for not only this useful command but also providing
translations.  It does not really matter, but:

On Fri, Nov 08, 2019 at 03:15:16PM +0100, Konrad Hinsen wrote:
> * etc/news.scm: Add entry for "guix time-machine".
> ---
>  etc/news.scm | 18 ++++++++++++++++++
>  1 file changed, 18 insertions(+)
> […]
> +              (de "Der neue Befehl @command{guix time-machine} vereinfacht
> +den Zugriff auf ältere oder neuere Guix-Versionen als die installierte.
> +Er kann zur Installation bestimmer Paket-Versionen verwendet werden, aber

I would prefer „Paketversionen“ rather than „Paket-Versionen“ because
the hyphen does not make it easier to read.


> +auch zur Wiederherstellung von Entwicklungsumgebungen wie sie in der
> +Vergagngenheit verwendet wurden.")

There needs to be a comma before „wie“ because what follows is a
separate nebensatz.

Regards,
Florian




Information forwarded to guix-patches <at> gnu.org:
bug#37978; Package guix-patches. (Sun, 10 Nov 2019 12:01:02 GMT) Full text and rfc822 format available.

Message #50 received at 37978 <at> debbugs.gnu.org (full text, mbox):

From: Ludovic Courtès <ludo <at> gnu.org>
To: Konrad Hinsen <konrad.hinsen <at> fastmail.net>
Cc: 37978 <at> debbugs.gnu.org
Subject: Re: [bug#37978] [PATCH 1/2] guix: new command "guix time-machine"
Date: Sun, 10 Nov 2019 13:00:33 +0100
Hi Konrad,

Thanks for the updated patch!

Konrad Hinsen <konrad.hinsen <at> fastmail.net> skribis:

> * guix/scripts/time-machine.scm: New file.
> * Makefile.am: (MODULES): Add it.
> * guix/scripts/pull.scm: Export function channel-list.
> * guix/inferior.scm: New function cached-guix-filetree-for-channels.
> * doc/guix.texi: Document "guix time-machine"

[...]

> +(define* (cached-guix-filetree-for-channels channels
> +                                            #:key
> +                                            (cache-directory (%inferior-cache-directory))
> +                                            (ttl (* 3600 24 30)))
> +  "Return a directory containing a guix filetree defined by CHANNELS, a list of channels.
> +The directory is a subdirectory of CACHE-DIRECTORY, where entries can be reclaimed after TTL seconds.
> +This procedure opens a new connection to the build daemon."
>    (with-store store

It’s the same as in v1, right?

How about (1) calling it ‘cached-channel-instance’ (or similar; as a
rule of thumb, I try to avoid “guix” in identifiers as well as
neologisms), and (2) not opening a connection to the daemon?  :-)

As it stands, this procedure opens a connection unconditionally anyway,
so it’s fine IMO to just move that ‘with-store’ to time-machine.scm and
to ‘inferior-for-channels’.

I also think it would be preferable to make it a separate patch
(separate from the one that adds time-machine.scm), if it’s OK for you.

Thoughts?

Thank you!

Ludo’.




Information forwarded to guix-patches <at> gnu.org:
bug#37978; Package guix-patches. (Sun, 10 Nov 2019 12:03:01 GMT) Full text and rfc822 format available.

Message #53 received at 37978 <at> debbugs.gnu.org (full text, mbox):

From: Ludovic Courtès <ludo <at> gnu.org>
To: "pelzflorian \(Florian Pelz\)" <pelzflorian <at> pelzflorian.de>
Cc: Konrad Hinsen <konrad.hinsen <at> fastmail.net>, 37978 <at> debbugs.gnu.org
Subject: Re: [bug#37978] [PATCH 2/2] news: Add entry for "guix time-machine".
Date: Sun, 10 Nov 2019 13:02:04 +0100
"pelzflorian (Florian Pelz)" <pelzflorian <at> pelzflorian.de> skribis:

> Thank you for not only this useful command but also providing
> translations.

Ah yes, I forgot to say it Konrad, but you can Cc: guix-i18n <at> gnu.org to
obtain translations.  It’s convenient to be polyglot though.  ;-)

Ludo’.




Information forwarded to guix-patches <at> gnu.org:
bug#37978; Package guix-patches. (Tue, 12 Nov 2019 15:53:01 GMT) Full text and rfc822 format available.

Message #56 received at 37978 <at> debbugs.gnu.org (full text, mbox):

From: Konrad Hinsen <konrad.hinsen <at> fastmail.net>
To: Ludovic Courtès <ludo <at> gnu.org>
Subject: Re: [bug#37978] [PATCH 1/2] guix: new command "guix time-machine"
Date: Tue, 12 Nov 2019 16:52:03 +0100
Hi Ludo and Florian,

Thanks for your comments and corrections. I have prepared a third
version, now consisting of three patches. I'll send them in a minute...

Cheers,
  Konrad.




Information forwarded to guix-patches <at> gnu.org:
bug#37978; Package guix-patches. (Tue, 12 Nov 2019 15:53:02 GMT) Full text and rfc822 format available.

Message #59 received at 37978 <at> debbugs.gnu.org (full text, mbox):

From: Konrad Hinsen <konrad.hinsen <at> fastmail.net>
To: 37978 <at> debbugs.gnu.org
Subject: [PATCH 1/3] guix: new command "guix time-machine"
Date: Fri, 25 Oct 2019 17:42:21 +0200
* guix/scripts/time-machine.scm: New file.
* Makefile.am: (MODULES): Add it.
* guix/scripts/pull.scm: Export function channel-list.
* guix/inferior.scm: New exported function cached-channel-instance.
* doc/guix.texi: Document "guix time-machine".
---
 Makefile.am                   |   1 +
 doc/guix.texi                 |  59 +++++++++++++++++++-
 guix/inferior.scm             |  38 +++++++++----
 guix/scripts/pull.scm         |   1 +
 guix/scripts/time-machine.scm | 102 ++++++++++++++++++++++++++++++++++
 5 files changed, 187 insertions(+), 14 deletions(-)
 create mode 100644 guix/scripts/time-machine.scm

diff --git a/Makefile.am b/Makefile.am
index b1f33946c5..b3f03d44c8 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -278,6 +278,7 @@ MODULES =					\
   guix/scripts/container.scm			\
   guix/scripts/container/exec.scm		\
   guix/scripts/deploy.scm			\
+  guix/scripts/time-machine.scm			\
   guix.scm					\
   $(GNU_SYSTEM_MODULES)
 
diff --git a/doc/guix.texi b/doc/guix.texi
index 3b8e5935bb..3d4192cab2 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -197,6 +197,7 @@ Package Management
 * Invoking guix gc::            Running the garbage collector.
 * Invoking guix pull::          Fetching the latest Guix and distribution.
 * Channels::                    Customizing the package collection.
+* Invoking guix time-machine::  Running an older revision of Guix.
 * Inferiors::                   Interacting with another revision of Guix.
 * Invoking guix describe::      Display information about your Guix revision.
 * Invoking guix archive::       Exporting and importing store files.
@@ -2548,6 +2549,7 @@ guix install emacs-guix
 * Invoking guix gc::            Running the garbage collector.
 * Invoking guix pull::          Fetching the latest Guix and distribution.
 * Channels::                    Customizing the package collection.
+* Invoking guix time-machine::  Running an older revision of Guix.
 * Inferiors::                   Interacting with another revision of Guix.
 * Invoking guix describe::      Display information about your Guix revision.
 * Invoking guix archive::       Exporting and importing store files.
@@ -4150,7 +4152,10 @@ say, on another machine, by providing a channel specification in
 @end lisp
 
 The @command{guix describe --format=channels} command can even generate this
-list of channels directly (@pxref{Invoking guix describe}).
+list of channels directly (@pxref{Invoking guix describe}).  The resulting
+file can be used with the -C options of @command{guix pull}
+(@pxref{Invoking guix pull}) or @command{guix time-machine}
+(@pxref{Invoking guix time-machine}).
 
 At this point the two machines run the @emph{exact same Guix}, with access to
 the @emph{exact same packages}.  The output of @command{guix build gimp} on
@@ -4164,6 +4169,57 @@ artifacts with very fine grain, and to reproduce software environments at
 will---some sort of ``meta reproducibility'' capabilities, if you will.
 @xref{Inferiors}, for another way to take advantage of these super powers.
 
+@node Invoking guix time-machine
+@section Invoking @command{guix time-machine}
+
+@cindex @command{guix time-machine}
+@cindex pinning, channels
+@cindex replicating Guix
+@cindex reproducibility, of Guix
+
+The @command{guix time-machine} command provides access to other
+revisions of Guix, for example to install older versions of packages,
+or to reproduce a computation in an identical environment.  The revision
+of Guix to be used is defined by a commit or by a channel
+description file created by @command{guix describe}
+(@pxref{Invoking guix describe}).
+
+The general syntax is:
+
+@example
+guix time-machine @var{options}@dots{} -- @var{command} @var {arg}@dots{}
+@end example
+
+where @var{command} and @var{arg}@dots{} are passed unmodified to the
+@command{guix} command if the specified revision.  The @var{options} that define
+this revision are the same as for @command{guix pull} (@pxref{Invoking guix pull}):
+
+@table @code
+@item --url=@var{url}
+@itemx --commit=@var{commit}
+@itemx --branch=@var{branch}
+Use the @code{guix} channel from the specified @var{url}, at the
+given @var{commit} (a valid Git commit ID represented as a hexadecimal
+string), or @var{branch}.
+
+@item --channels=@var{file}
+@itemx -C @var{file}
+Read the list of channels from @var{file}.  @var{file} must contain
+Scheme code that evaluates to a list of channel objects.
+@xref{Channels} for more information.
+@end table
+
+As for @command{guix pull}, the absence of any options means that the
+the latest commit on the master branch will be used. The command
+
+@example
+guix time-machine -- build hello
+@end example
+
+will thus build the package @code{hello} as defined in the master branch,
+which is in general a newer revison of Guix than you have installed.
+Time travel works in both directions!
+
 @node Inferiors
 @section Inferiors
 
@@ -10582,7 +10638,6 @@ ClientPID: 19419
 ClientCommand: cuirass --cache-directory /var/cache/cuirass @dots{}
 @end example
 
-
 @node System Configuration
 @chapter System Configuration
 
diff --git a/guix/inferior.scm b/guix/inferior.scm
index b8e2f21f42..be50e0ec26 100644
--- a/guix/inferior.scm
+++ b/guix/inferior.scm
@@ -89,6 +89,7 @@
             gexp->derivation-in-inferior
 
             %inferior-cache-directory
+            cached-channel-instance
             inferior-for-channels))
 
 ;;; Commentary:
@@ -635,16 +636,13 @@ failing when GUIX is too old and lacks the 'guix repl' command."
   (make-parameter (string-append (cache-directory #:ensure? #f)
                                  "/inferiors")))
 
-(define* (inferior-for-channels channels
-                                #:key
-                                (cache-directory (%inferior-cache-directory))
-                                (ttl (* 3600 24 30)))
-  "Return an inferior for CHANNELS, a list of channels.  Use the cache at
-CACHE-DIRECTORY, where entries can be reclaimed after TTL seconds.  This
-procedure opens a new connection to the build daemon.
-
-This is a convenience procedure that people may use in manifests passed to
-'guix package -m', for instance."
+(define* (cached-channel-instance channels
+                                  #:key
+                                  (cache-directory (%inferior-cache-directory))
+                                  (ttl (* 3600 24 30)))
+  "Return a directory containing a guix filetree defined by CHANNELS, a list of channels.
+The directory is a subdirectory of CACHE-DIRECTORY, where entries can be reclaimed after TTL seconds.
+This procedure opens a new connection to the build daemon."
   (with-store store
     (let ()
       (define instances
@@ -680,7 +678,7 @@ This is a convenience procedure that people may use in manifests passed to
                                           (file-expiration-time ttl))
 
       (if (file-exists? cached)
-          (open-inferior cached)
+          cached
           (run-with-store store
             (mlet %store-monad ((profile
                                  (channel-instances->derivation instances)))
@@ -689,4 +687,20 @@ This is a convenience procedure that people may use in manifests passed to
                 (built-derivations (list profile))
                 (symlink* (derivation->output-path profile) cached)
                 (add-indirect-root* cached)
-                (return (open-inferior cached)))))))))
+                (return cached))))))))
+
+(define* (inferior-for-channels channels
+                                #:key
+                                (cache-directory (%inferior-cache-directory))
+                                (ttl (* 3600 24 30)))
+  "Return an inferior for CHANNELS, a list of channels.  Use the cache at
+CACHE-DIRECTORY, where entries can be reclaimed after TTL seconds.  This
+procedure opens a new connection to the build daemon.
+
+This is a convenience procedure that people may use in manifests passed to
+'guix package -m', for instance."
+  (define cached
+    (cached-channel-instance channels
+                             #:cache-directory cache-directory
+                             #:ttl ttl))
+  (open-inferior cached))
diff --git a/guix/scripts/pull.scm b/guix/scripts/pull.scm
index 92aac6066e..d6173a6acb 100644
--- a/guix/scripts/pull.scm
+++ b/guix/scripts/pull.scm
@@ -56,6 +56,7 @@
   #:use-module (ice-9 vlist)
   #:use-module (ice-9 format)
   #:export (display-profile-content
+            channel-list
             guix-pull))
 
 
diff --git a/guix/scripts/time-machine.scm b/guix/scripts/time-machine.scm
new file mode 100644
index 0000000000..a6598fb0f7
--- /dev/null
+++ b/guix/scripts/time-machine.scm
@@ -0,0 +1,102 @@
+;;; GNU Guix --- Functional package management for GNU
+;;; Copyright © 2019 Konrad Hinsen <konrad.hinsen <at> fastmail.net>
+;;;
+;;; This file is part of GNU Guix.
+;;;
+;;; GNU Guix 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.
+;;;
+;;; GNU Guix 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 GNU Guix.  If not, see <http://www.gnu.org/licenses/>.
+
+(define-module (guix scripts time-machine)
+  #:use-module (guix ui)
+  #:use-module (guix scripts)
+  #:use-module (guix inferior)
+  #:use-module (guix channels)
+  #:use-module ((guix scripts pull) #:select (channel-list))
+  #:use-module (ice-9 match)
+  #:use-module (srfi srfi-1)
+  #:use-module (srfi srfi-11)
+  #:use-module (srfi srfi-26)
+  #:use-module (srfi srfi-37)
+  #:export (guix-time-machine))
+
+
+;;;
+;;; Command-line options.
+;;;
+
+(define (show-help)
+  (display (G_ "Usage: guix time-machine [OPTION] -- COMMAND ARGS...
+Execute COMMAND ARGS... in an older version of Guix.\n"))
+  (display (G_ "
+  -C, --channels=FILE    deploy the channels defined in FILE"))
+  (display (G_ "
+      --url=URL          use the Git repository at URL"))
+  (display (G_ "
+      --commit=COMMIT    use the specified COMMIT"))
+  (display (G_ "
+      --branch=BRANCH    use the tip of the specified BRANCH"))
+  (display (G_ "
+  -h, --help             display this help and exit"))
+  (display (G_ "
+  -V, --version          display version information and exit"))
+  (newline)
+  (show-bug-report-information))
+
+(define %options
+  ;; Specifications of the command-line options.
+  (list (option '(#\C "channels") #t #f
+                (lambda (opt name arg result)
+                  (alist-cons 'channel-file arg result)))
+         (option '("url") #t #f
+                 (lambda (opt name arg result)
+                   (alist-cons 'repository-url arg
+                               (alist-delete 'repository-url result))))
+         (option '("commit") #t #f
+                 (lambda (opt name arg result)
+                   (alist-cons 'ref `(commit . ,arg) result)))
+         (option '("branch") #t #f
+                 (lambda (opt name arg result)
+                   (alist-cons 'ref `(branch . ,arg) result)))
+        (option '(#\h "help") #f #f
+                (lambda args
+                  (show-help)
+                  (exit 0)))
+        (option '(#\V "version") #f #f
+                (lambda args
+                  (show-version-and-exit "guix time-machine")))))
+
+(define (parse-args args)
+  "Parse the list of command line arguments ARGS."
+  ;; The '--' token is used to separate the command to run from the rest of
+  ;; the operands.
+  (let-values (((args command) (break (cut string=? "--" <>) args)))
+    (let ((opts (parse-command-line args %options '(()) #:build-options? #f)))
+      (match command
+        (() opts)
+        (("--") opts)
+        (("--" command ...) (alist-cons 'exec command opts))))))
+
+
+;;;
+;;; Entry point.
+;;;
+
+(define (guix-time-machine . args)
+  (with-error-handling
+    (let* ((opts         (parse-args args))
+           (channels     (channel-list opts))
+           (command-line (assoc-ref opts 'exec)))
+      (when command-line
+        (let* ((directory  (cached-channel-instance channels))
+               (executable (string-append directory "/bin/guix")))
+          (apply execl (cons* executable executable command-line)))))))
-- 
2.24.0





Information forwarded to guix-patches <at> gnu.org:
bug#37978; Package guix-patches. (Tue, 12 Nov 2019 15:54:01 GMT) Full text and rfc822 format available.

Message #62 received at 37978 <at> debbugs.gnu.org (full text, mbox):

From: Konrad Hinsen <konrad.hinsen <at> fastmail.net>
To: 37978 <at> debbugs.gnu.org
Subject: [PATCH 2/3] guix: don't connect to daemon in cached-channel-instance
Date: Tue, 12 Nov 2019 16:39:46 +0100
* guix/inferior.scm (cached-channel-instance): take an explicit store argument
* guix/inferior.scm (inferior-for-channels): wrap call to
  cached-channel-instance in with-store
* guix/time-machine.scm (guix-time-machine): wrap call to
  cached-channel-instance in with-store
---
 guix/inferior.scm             | 99 ++++++++++++++++++-----------------
 guix/scripts/time-machine.scm |  4 +-
 2 files changed, 53 insertions(+), 50 deletions(-)

diff --git a/guix/inferior.scm b/guix/inferior.scm
index be50e0ec26..71dae89e92 100644
--- a/guix/inferior.scm
+++ b/guix/inferior.scm
@@ -636,58 +636,57 @@ failing when GUIX is too old and lacks the 'guix repl' command."
   (make-parameter (string-append (cache-directory #:ensure? #f)
                                  "/inferiors")))
 
-(define* (cached-channel-instance channels
+(define* (cached-channel-instance store
+                                  channels
                                   #:key
                                   (cache-directory (%inferior-cache-directory))
                                   (ttl (* 3600 24 30)))
   "Return a directory containing a guix filetree defined by CHANNELS, a list of channels.
 The directory is a subdirectory of CACHE-DIRECTORY, where entries can be reclaimed after TTL seconds.
 This procedure opens a new connection to the build daemon."
-  (with-store store
-    (let ()
-      (define instances
-        (latest-channel-instances store channels))
-
-      (define key
-        (bytevector->base32-string
-         (sha256
-          (string->utf8
-           (string-concatenate (map channel-instance-commit instances))))))
-
-      (define cached
-        (string-append cache-directory "/" key))
-
-      (define (base32-encoded-sha256? str)
-        (= (string-length str) 52))
-
-      (define (cache-entries directory)
-        (map (lambda (file)
-               (string-append directory "/" file))
-             (scandir directory base32-encoded-sha256?)))
-
-      (define symlink*
-        (lift2 symlink %store-monad))
-
-      (define add-indirect-root*
-        (store-lift add-indirect-root))
-
-      (mkdir-p cache-directory)
-      (maybe-remove-expired-cache-entries cache-directory
-                                          cache-entries
-                                          #:entry-expiration
-                                          (file-expiration-time ttl))
-
-      (if (file-exists? cached)
-          cached
-          (run-with-store store
-            (mlet %store-monad ((profile
-                                 (channel-instances->derivation instances)))
-              (mbegin %store-monad
-                (show-what-to-build* (list profile))
-                (built-derivations (list profile))
-                (symlink* (derivation->output-path profile) cached)
-                (add-indirect-root* cached)
-                (return cached))))))))
+  (define instances
+    (latest-channel-instances store channels))
+
+  (define key
+    (bytevector->base32-string
+     (sha256
+      (string->utf8
+       (string-concatenate (map channel-instance-commit instances))))))
+
+  (define cached
+    (string-append cache-directory "/" key))
+
+  (define (base32-encoded-sha256? str)
+    (= (string-length str) 52))
+
+  (define (cache-entries directory)
+    (map (lambda (file)
+           (string-append directory "/" file))
+         (scandir directory base32-encoded-sha256?)))
+
+  (define symlink*
+    (lift2 symlink %store-monad))
+
+  (define add-indirect-root*
+    (store-lift add-indirect-root))
+
+  (mkdir-p cache-directory)
+  (maybe-remove-expired-cache-entries cache-directory
+                                      cache-entries
+                                      #:entry-expiration
+                                      (file-expiration-time ttl))
+
+  (if (file-exists? cached)
+      cached
+      (run-with-store store
+        (mlet %store-monad ((profile
+                             (channel-instances->derivation instances)))
+          (mbegin %store-monad
+            (show-what-to-build* (list profile))
+            (built-derivations (list profile))
+            (symlink* (derivation->output-path profile) cached)
+            (add-indirect-root* cached)
+            (return cached))))))
 
 (define* (inferior-for-channels channels
                                 #:key
@@ -700,7 +699,9 @@ procedure opens a new connection to the build daemon.
 This is a convenience procedure that people may use in manifests passed to
 'guix package -m', for instance."
   (define cached
-    (cached-channel-instance channels
-                             #:cache-directory cache-directory
-                             #:ttl ttl))
+    (with-store store
+      (cached-channel-instance store
+                               channels
+                               #:cache-directory cache-directory
+                               #:ttl ttl)))
   (open-inferior cached))
diff --git a/guix/scripts/time-machine.scm b/guix/scripts/time-machine.scm
index a6598fb0f7..a64badc27b 100644
--- a/guix/scripts/time-machine.scm
+++ b/guix/scripts/time-machine.scm
@@ -21,6 +21,7 @@
   #:use-module (guix scripts)
   #:use-module (guix inferior)
   #:use-module (guix channels)
+  #:use-module (guix store)
   #:use-module ((guix scripts pull) #:select (channel-list))
   #:use-module (ice-9 match)
   #:use-module (srfi srfi-1)
@@ -97,6 +98,7 @@ Execute COMMAND ARGS... in an older version of Guix.\n"))
            (channels     (channel-list opts))
            (command-line (assoc-ref opts 'exec)))
       (when command-line
-        (let* ((directory  (cached-channel-instance channels))
+        (let* ((directory  (with-store store
+                             (cached-channel-instance store channels)))
                (executable (string-append directory "/bin/guix")))
           (apply execl (cons* executable executable command-line)))))))
-- 
2.24.0





Information forwarded to guix-patches <at> gnu.org:
bug#37978; Package guix-patches. (Tue, 12 Nov 2019 15:55:02 GMT) Full text and rfc822 format available.

Message #65 received at 37978 <at> debbugs.gnu.org (full text, mbox):

From: Konrad Hinsen <konrad.hinsen <at> fastmail.net>
To: 37978 <at> debbugs.gnu.org
Subject: [PATCH 3/3] news: Add entry for "guix time-machine".
Date: Fri, 8 Nov 2019 11:16:50 +0100
---
 etc/news.scm | 18 ++++++++++++++++++
 1 file changed, 18 insertions(+)

diff --git a/etc/news.scm b/etc/news.scm
index 08e06351e9..bc6d173365 100644
--- a/etc/news.scm
+++ b/etc/news.scm
@@ -9,6 +9,24 @@
 (channel-news
  (version 0)
 
+ (entry (commit "XXX")
+        (title (en "New command @command{guix time-machine}")
+               (de "Neuer Befehl @command{guix time-machine}")
+               (fr "Nouvelle commande @command{guix time-machine}"))
+        (body (en "The new command @command{guix time-machine} facilitates
+access to older or newer revisions of Guix than the one that is installed.
+It can be used to install different versions of packages, and to
+re-create computational environments exactly as used in the past.")
+              (de "Der neue Befehl @command{guix time-machine} vereinfacht
+den Zugriff auf ältere oder neuere Guix-Versionen als die installierte.
+Er kann zur Installation bestimmer Paketversionen verwendet werden, aber
+auch zur Wiederherstellung von Entwicklungsumgebungen, wie sie in der
+Vergagngenheit verwendet wurden.")
+              (fr "La nouvelle commande @command{guix time-machine}
+facilite l'accès à des versions antérieures ou postérieures par rapport
+à la version installée.  Elle sert à installer des versions spécifiques
+de paquets, ainsi à la restauration d'environnements dans un état
+historique.")))
  (entry (commit "3e962e59d849e4300e447d94487684102d9d412e")
         (title (en "@command{guix graph} now supports package
 transformations"))
-- 
2.24.0





Reply sent to Ludovic Courtès <ludo <at> gnu.org>:
You have taken responsibility. (Fri, 15 Nov 2019 22:37:02 GMT) Full text and rfc822 format available.

Notification sent to Konrad Hinsen <konrad.hinsen <at> fastmail.net>:
bug acknowledged by developer. (Fri, 15 Nov 2019 22:37:02 GMT) Full text and rfc822 format available.

Message #70 received at 37978-done <at> debbugs.gnu.org (full text, mbox):

From: Ludovic Courtès <ludo <at> gnu.org>
To: Konrad Hinsen <konrad.hinsen <at> fastmail.net>
Cc: 37978-done <at> debbugs.gnu.org
Subject: Re: [bug#37978] [PATCH] guix: new command "guix time-machine"
Date: Fri, 15 Nov 2019 23:35:52 +0100
Hi Konrad,

Konrad Hinsen <konrad.hinsen <at> fastmail.net> skribis:

> Konrad Hinsen <konrad.hinsen <at> fastmail.net> writes:
>
>> Thanks for your detailed comments, most of which I agree with, and I
>> will make the required changes.
>
> I just submitted two new patches. Looking forward to everyone's feedbck!

I’ve just applied them, thank you!

I followed up with one commit to gracefully handle Git errors (e.g.,
when one type ‘--commit=foobar’) and another one to handle build options
like the other commands.  Let me know if anything looks wrong.

Anyway, I can do things like this:

--8<---------------cut here---------------start------------->8---
$ ./pre-inst-env guix time-machine --commit=65956ad3526ba09e1f7a40722c96c6ef7c0936fe -- package -A |wc -l
Updating channel 'guix' from Git repository at 'https://git.savannah.gnu.org/git/guix.git'...
guile: warning: failed to install locale
warning: failed to install locale: Invalid argument
7666
--8<---------------cut here---------------end--------------->8---

… where the commit in question is from June 2018.  Pretty fun!

It would be neat if the “Updating…” message wouldn’t show up when in
fact everything is already available.  Also, it’d be nice if we could
write (and in ‘guix pull’ too) ‘--date=2019-07-07’ or
‘--date=last-month’.  Future work!  :-)

Thanks again for this nifty command!

Ludo’.




Information forwarded to guix-patches <at> gnu.org:
bug#37978; Package guix-patches. (Sat, 16 Nov 2019 09:07:03 GMT) Full text and rfc822 format available.

Message #73 received at 37978-done <at> debbugs.gnu.org (full text, mbox):

From: Konrad Hinsen <konrad.hinsen <at> fastmail.net>
To: Ludovic Courtès <ludo <at> gnu.org>
Cc: 37978-done <at> debbugs.gnu.org
Subject: Re: [bug#37978] [PATCH] guix: new command "guix time-machine"
Date: Sat, 16 Nov 2019 10:06:34 +0100
Hi Ludo,

> I followed up with one commit to gracefully handle Git errors (e.g.,
> when one type ‘--commit=foobar’) and another one to handle build options
> like the other commands.  Let me know if anything looks wrong.

Quite on the contrary, everything looks fine - thanks!

I hadn't realized that "guix pull" takes build options, but obviously
that makes sense.

> It would be neat if the “Updating…” message wouldn’t show up when in
> fact everything is already available.  Also, it’d be nice if we could
> write (and in ‘guix pull’ too) ‘--date=2019-07-07’ or
> ‘--date=last-month’.  Future work!  :-)

Right. Also not pulling anything if the requested channels are identical
to those of the current guix version. There's always more to to!

Cheers,
  Konrad.




bug archived. Request was from Debbugs Internal Request <help-debbugs <at> gnu.org> to internal_control <at> debbugs.gnu.org. (Sat, 14 Dec 2019 12:24:06 GMT) Full text and rfc822 format available.

This bug report was last modified 4 years and 128 days ago.

Previous Next


GNU bug tracking system
Copyright (C) 1999 Darren O. Benham, 1997,2003 nCipher Corporation Ltd, 1994-97 Ian Jackson.