How do you migrate a Homebrew installation to a new location?

I have a Homebrew installation in $HOME/brew, and historically it has worked well. Unfortunately, over time Homebrew has become less and less tolerant of installations outside of /usr/local. Various formulae make hard assumptions about the installation prefix, and do not work properly (i.e., were not tested) with a non-standard prefix. The brew doctor command even goes so far as to warn about this now:

Warning: Your Homebrew is not installed to /usr/local
You can install Homebrew anywhere you want, but some brews may only build
correctly if you install in /usr/local. Sorry!

As such, I would now like to migrate my Homebrew installation over to /usr/local. However, I am loath to simply mv all the files, as I suspect this will cause problems. I could not find any instructions on the Homebrew site or here on migrating an existing installation to a new prefix. Of course, I could uninstall Homebrew and then reinstall it, but I would prefer not to rebuild all my kegs.

Is there any existing script or documented practice for performing such a migration?

Or is this impossible due to hardcoded absolute paths in linked binaries?

Answers


The modern way to do this is with homebrew-bundle.

brew tap Homebrew/bundle
brew bundle dump # Creates 'Brewfile' in the current directory
# later ...
brew bundle # Installs packages listed in 'Brewfile'

I just wrote a script to achieve the goal to migrate homebrew packages to a new system, which also applies for your case (named backup-homebrew.sh):

#!/bin/bash

echo '#!/bin/bash'
echo ''
echo 'failed_items=""'
echo 'function install_package() {'
echo 'echo EXECUTING: brew install $1 $2'
echo 'brew install $1 $2'
echo '[ $? -ne 0 ] && $failed_items="$failed_items $1"  # package failed to install.'
echo '}'

brew tap | while read tap; do echo "brew tap $tap"; done

brew list | while read item;
do
  echo "install_package $item '$(brew info $item | /usr/bin/grep 'Built from source with:' | /usr/bin/sed 's/^[ \t]*Built from source with:/ /g; s/\,/ /g')'"
done

echo '[ ! -z $failed_items ] && echo The following items were failed to install: && echo $failed_items'

You should first run this script on your original system to generate a restore script:

./backup-homebrew.sh >restore-homebrew.sh && chmod +x restore-homebrew.sh

Then, after installing Homebrew on your new system (in your case the same system), simply run restore-homebrew.sh to install all those packages you have on your original system.

The benefits of this script over the one given by @ctrueden is that this script also tries to back up the installation options you used when you installed the packages.

A more detailed description is in my blog.


As suggested by Peter Eisentraut, I indeed ended up migrating my Homebrew installation by reinstalling it. You can script things a bit to retap all your extra taps, and reinstall all your previously installed kegs, without too much manual work:

#!/bin/sh

# save list of kegs for later reinstallation
brew list > kegs.txt

# back up old Homebrew installation
mv $HOME/brew $HOME/old-brew

# install Homebrew into /usr/local
ruby -e "$(curl -fsSL https://raw.github.com/mxcl/homebrew/go)"

# retap all the taps
# NB: It is not enough to move the tap repos to their new location,
# because Homebrew will not automatically recognize the new formulae.
# There might be a configuration file we could edit, but rather than
# risk an incomplete data structure, let's just retap everything.
for tapDir in $HOME/old-brew/Library/Taps/*
do (
  cd $tapDir
  tap=$(git remote -v | \
    grep '(fetch)' | \
    sed 's/.*github.com\///' | \
    sed 's/ (fetch)//')
  /usr/local/bin/brew tap $tap
) done

# reinstall all the kegs
/usr/local/bin/brew install $(cat kegs.txt)

# much later... ;-)
rm -rf kegs.txt $HOME/old-brew

Of course, customized Homebrew installations will have additional wrinkles. For example, if you have committed changes to any of your Homebrew-related Git repos, you may want to import that work before reinstalling your kegs or blowing away your old installation:

cd /usr/local
for f in $(find . -name '.git')
do (
  repoDir=$(dirname $f)
  cd $f/..
  git remote add old-brew-$f $(dirname $HOME/old-brew/$f/..)
  git fetch old-brew-$f
) done

Note that I only tested the second snippet above very lightly, as I personally have not customized my Homebrew in such a way.

Another aspect of Homebrew not addressed by this approach is custom flags using during your original installation. For example, to install wine you need to install various dependencies with the --universal flag, and the script above will not reinstall them with such flags enabled. See @xuhdev's answer for a solution that does so.


Or is this impossible due to hardcoded absolute paths in linked binaries?

Indeed. You'll need to reinstall everything from scratch.


Need Your Help

<system_error> categories and standard/system error codes

c++ c++11 winapi posix system-error

C++11 introduced the &lt;system_error&gt; header containing a generic system to handle error codes. An std::error_code is a tuple containing an int, the error code, and a reference to an std::

Django: Best way to unit-test an abstract model

django unit-testing django-models abstract

I need to write some unit tests for an abstract base model, that provides some basic functionality that should be used by other apps. It it would be necessary to define a model that inherits from i...