33

Sometimes, package updates from MELPA can break some part of emacs and when that happens I'd like to be able to revert back to using an older version of the package.

Right now, I can do it in two ways:

  • I've set emacs to delete files by moving to trash and when I update a package, the older version is trashed. I can retrieve the older version and replace the one in ~/.emacs.d/elpa.

  • Go to the github repo of the package that broke functionality, fetch an older version of the package, replace the one in ~/.emacs.d/elpa with the one from gitub, byte-recompile the file(s).

Both these ways involve a lot of manual work moving things around. Is there an easier(preferably automatic) way to downgrade packages installed from MELPA?

Chakravarthy Raghunandan
  • 3,132
  • 2
  • 18
  • 42
  • 5
    You can only install latest version through ELPA/package.el, Melpa and Melpa-stable don't even keep old versions of packages. To install a package manually, 1) download source code 2) `M-x package-install-file`. There are also other ways to manage packages besides ELPA/package.el, such as [el-get](https://github.com/dimitri/el-get) and [quelpa](https://github.com/quelpa/quelpa), they seem to be able to used without ELPA, maybe they can do what you want. – xuchunyang Nov 18 '16 at 06:45
  • 2
    If you do not get a *simple*, *workable* answer to this question, please consider sending an enhancement request to Emacs: `M-x report-emacs-bug` (it is also for enhancement requests). – Drew Nov 18 '16 at 16:18

5 Answers5

11

When you update your packages through the M-x list-packages interface, after the successful installation of the package, you'll get asked if you want to remove the old package. Don't delete them so they stay in place and you could then later remove the newer package through this interface.

My current package list shows 4 versions of magit installed in my ~/.emacs.d/elpa/ directory tree.

  magit              20160827.1549 obsolete              A Git porcelain inside Emacs
  magit              20160907.945  obsolete              A Git porcelain inside Emacs
  magit              20161001.1454 obsolete              A Git porcelain inside Emacs
  magit              20161123.617  installed             A Git porcelain inside Emacs

You can clean-up old versions later with the key ~ (package-menu-mark-obsolete-for-deletion) to mark all obsolete packages. To delete a certain old version move to its line and press d to mark them for deletion. After you marked the packages you'd use x to execute the actions as usual.

In Emacs 25 the mark all packages for Upgrade functionality automatically sets all old packages for deletion, and doesn't prompt for confirmation after installing. You have to look for lines that start with a capital "D", which you can just unmark (best with the following macro)

Type the key or chord on the left of the dash from the following lines.

<F3>  - start macro recording
C-s   - isearch-forward
C-q   - quoted-insert
C-j   - linefeed character
D     - the mark at the start of the line
<Ret> - stops the isearch on the line with the "D"
u     - unmark the package for deletion
<F4>  - stops macro recording - the first package is now unmarked
<F4>  - executes the macro for the next upgraded package

If there are no further matches for the search the macro will ring the bell and stop, so you could C-u 0 <F4> to unmark all packages marked for deletion. After this you can execute the installations.

The function I declared to be changed in my comment has to be changed in a way I cannot grasp yet, as it's important that the last (cond) block has to be successful in order to not loop endlessly.

p_wiersig
  • 1,051
  • 9
  • 15
  • Oh hey, It does not ask for confirmation to delete in my emacs. It just deletes the old package :/ i think it used to ask in emacs 24? i don't remember – Chakravarthy Raghunandan Nov 24 '16 at 14:25
  • indeed. Emacs 24 prompts, 25 seems to mark the old for deletion and that will trigger on executing the marks. You could mark the packages for installation by hand or unmark the deletions or change the `(t (package-menu-mark-delete))` lines in `package-menu--mark-upgrades-1` so that the first line is a test for a configuration variable or something that returns `nil` when you don't want to delete old packages immediately – p_wiersig Nov 27 '16 at 15:12
  • Can you please add the relevant lines to edit in `package.el` in the original answer, so that I can accept the answer? Thanks – Chakravarthy Raghunandan Nov 27 '16 at 16:03
  • Sure. I noticed that I can't make the function to do what I want it to, so I explained how to unmark all via a macro. – p_wiersig Nov 28 '16 at 12:31
11

The "nuclear option", as it were, would be to ditch package.el entirely and instead use the package manager that I wrote, straight.el. The advantage would be that straight.el installs packages by cloning their Git repositories, thereby making it trivial to use whichever version you would like. Also, straight.el provides functionality for dealing with revision lockfiles, which allow you to record the exact state of your package management configuration down to the smallest detail. Then, in the case of an emergency, you can simply revert all packages to their known-good versions.

These kinds of operations are, in general, impossible with package.el, and they will always be impossible, due to the overall design.

In response to your desire to avoid making a commit every time you update your packages, this is not necessary with straight.el. I would recommend writing a version lockfile and committing that every time you update your packages, since that makes it impossible to ever get into a state where your Emacs configuration is broken after an upgrade and you don't know how to revert. But you don't have to do this, if you like living life on the edge.

Resigned June 2023
  • 1,502
  • 15
  • 20
5

I find an easy way to downgrade: managing your own melpa archive.

  1. Clone melpa repo.
  2. Follow melpa's custom melpa archive wiki page to remove all recipes.
  3. Find the commit of the package you want to down grade to, and create a new recipe with that commit for the package. (see melpa's readme about how to specify the commit)
  4. Run make to build the downgraded package
  5. Add your own melpa archive, which can be a local directory, to the package-archives list.
  6. Install the downgraded package using the package menu. Or you can use package-pinned-packages to restrict the archive where the package should be downloaded from.
akaihola
  • 138
  • 5
chunfengd
  • 51
  • 1
  • 2
4

Lots of people elect not to commit ELPA packages to version control, but this is an example of why I believe you should do so.

Reverting anything is trivial if it's all committed.

Depending on the state of the upstream ELPA packages is a risk.

phils
  • 48,657
  • 3
  • 76
  • 115
  • But that would mean I'd have to remember to commit every time I update packages. That's not what I want. OR is there a way I can make emacs automatically stage and commit the updated packages whenever I do a package update? – Chakravarthy Raghunandan Nov 18 '16 at 16:14
  • 1
    I don't understand why you wouldn't want changes to your configuration to be intentional decisions. Regarding automatic commits, I'm sure that *could* be automated, but I don't have a solution for that. Searching for such solutions might provide productive, however. – phils Nov 19 '16 at 00:41
2

I download the package from github(the package home page) and build it, and recover files to .emacs.d/elpa.

for example: If I want to downgrade package - hl-todo to version 3bba4591, I can download the specific version package, then build it (with the command make), and it will generate .elc files and others. Copy and Cover them to ~/.emacs.d/elpa/hl-todo-20200807.1546 (replace the timestamp with yours)

lagagain
  • 21
  • 1