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?

2
  • 6
    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 and quelpa, they seem to be able to used without ELPA, maybe they can do what you want.
    – xuchunyang
    Nov 18, 2016 at 6: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, 2016 at 16:18

5 Answers 5

12

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.

4
  • 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 Nov 24, 2016 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, 2016 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 Nov 27, 2016 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, 2016 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.

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.
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.

2
  • 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? Nov 18, 2016 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, 2016 at 0: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)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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