From 743b5e6feb05c92cfea49f89bc946ea1420b80fe Mon Sep 17 00:00:00 2001
From: Misty De Meo <mistydemeo@gmail.com>
Date: Sun, 17 Jun 2012 16:54:20 -0500
Subject: [PATCH] link: add --force and --dry-run options

`brew link` can now be made to delete any conflicting files using
the --force argument. It also has a --dry-run option, similar to
git clean -n, which will list any files which would be deleted
without touching the filesystem.

Closes Homebrew/homebrew#11811.

Signed-off-by: Misty De Meo <mistydemeo@gmail.com>
---
 Library/Contributions/manpages/brew.1.md |  9 +++++++-
 Library/Homebrew/cmd/link.rb             | 18 ++++++++++++++-
 Library/Homebrew/extend/pathname.rb      |  5 ++++
 Library/Homebrew/keg.rb                  | 29 ++++++++++++++----------
 share/man/man1/brew.1                    | 10 ++++++--
 5 files changed, 55 insertions(+), 16 deletions(-)

diff --git a/Library/Contributions/manpages/brew.1.md b/Library/Contributions/manpages/brew.1.md
index 5bd259de6b..20df31c2d1 100644
--- a/Library/Contributions/manpages/brew.1.md
+++ b/Library/Contributions/manpages/brew.1.md
@@ -186,11 +186,18 @@ For the full command list, see the COMMANDS section.
     If `--git` is passed, Homebrew will create a Git repository, useful for
     creating patches to the software.
 
-  * `ln`, `link` <formula>:
+  * `ln`, `link [--force] [--dry-run]` <formula>:
     Symlink all of <formula>'s installed files into the Homebrew prefix. This
     is done automatically when you install formula, but can be useful for DIY
     installations.
 
+    If `--force` is passed, Homebrew will delete files which already exist in
+    the prefix while linking.
+
+    If `--dry-run` or `-n` is passed, Homebrew will list all files which would
+    be deleted by `brew link --force`, but will not actually link or delete
+    any files.
+
   * `ls, list [--unbrewed] [--versions]` [<formulae>]:
     Without any arguments, list all installed formulae.
 
diff --git a/Library/Homebrew/cmd/link.rb b/Library/Homebrew/cmd/link.rb
index 8ac280c0dc..c5e34fe2c0 100644
--- a/Library/Homebrew/cmd/link.rb
+++ b/Library/Homebrew/cmd/link.rb
@@ -9,14 +9,30 @@ module Homebrew extend self
       abort "Cowardly refusing to `sudo brew link'"
     end
 
+    if ARGV.force?
+      mode = :force
+    elsif ARGV.include?("--dry-run") || ARGV.include?("-n")
+      mode = :dryrun
+    else
+      mode = nil
+    end
+
     ARGV.kegs.each do |keg|
       if keg.linked_keg_record.directory? and keg.linked_keg_record.realpath == keg
         opoo "Already linked: #{keg}"
         next
       end
 
+      if mode == :dryrun
+        print "Would remove:\n" do
+          keg.link(mode)
+        end
+
+        next
+      end
+
       print "Linking #{keg}... " do
-        puts "#{keg.link} symlinks created"
+        puts "#{keg.link(mode)} symlinks created"
       end
     end
   end
diff --git a/Library/Homebrew/extend/pathname.rb b/Library/Homebrew/extend/pathname.rb
index 600e114a6d..946ea0d49f 100644
--- a/Library/Homebrew/extend/pathname.rb
+++ b/Library/Homebrew/extend/pathname.rb
@@ -347,6 +347,11 @@ class Pathname
           raise <<-EOS.undent
             Could not symlink file: #{src.expand_path}
             Target #{self} already exists. You may need to delete it.
+            To force the link and delete this file, do:
+              brew link -f formula_name
+
+            To list all files that would be deleted:
+              brew link -n formula_name
             EOS
         elsif !dirname.writable?
           raise <<-EOS.undent
diff --git a/Library/Homebrew/keg.rb b/Library/Homebrew/keg.rb
index f9aba2e0ee..1769a5fea4 100644
--- a/Library/Homebrew/keg.rb
+++ b/Library/Homebrew/keg.rb
@@ -58,7 +58,7 @@ class Keg < Pathname
     linked_keg_record.directory? and self == linked_keg_record.realpath
   end
 
-  def link
+  def link mode=nil
     raise "Cannot link #{fname}\nAnother version is already linked: #{linked_keg_record.realpath}" if linked_keg_record.directory?
 
     $n=0
@@ -70,12 +70,12 @@ class Keg < Pathname
 
     # yeah indeed, you have to force anything you need in the main tree into
     # these dirs REMEMBER that *NOT* everything needs to be in the main tree
-    link_dir('etc') {:mkpath}
-    link_dir('bin') {:skip_dir}
-    link_dir('sbin') {:skip_dir}
-    link_dir('include') {:link}
+    link_dir('etc', mode) {:mkpath}
+    link_dir('bin', mode) {:skip_dir}
+    link_dir('sbin', mode) {:skip_dir}
+    link_dir('include', mode) {:link}
 
-    link_dir('share') do |path|
+    link_dir('share', mode) do |path|
       case path.to_s
       when 'locale/locale.alias' then :skip_file
       when INFOFILE_RX then ENV['HOMEBREW_KEEP_INFO'] ? :info : :skip_file
@@ -86,7 +86,7 @@ class Keg < Pathname
       end
     end
 
-    link_dir('lib') do |path|
+    link_dir('lib', mode) do |path|
       case path.to_s
       when 'charset.alias' then :skip_file
       # pkg-config database gets explicitly created
@@ -106,7 +106,7 @@ class Keg < Pathname
       end
     end
 
-    linked_keg_record.make_relative_symlink(self)
+    linked_keg_record.make_relative_symlink(self) unless mode == :dryrun
 
     return $n + $d
   end
@@ -127,16 +127,21 @@ protected
     puts "Won't resolve conflicts for symlink #{dst} as it doesn't resolve into the Cellar" if ARGV.verbose?
   end
 
-  def make_relative_symlink dst, src
+  def make_relative_symlink dst, src, mode=nil
     if dst.exist? and dst.realpath == src.realpath
       puts "Skipping; already exists: #{dst}" if ARGV.verbose?
+    # cf. git-clean -n: list files to delete, don't really link or delete
+    elsif mode == :dryrun
+      puts dst if dst.exist?
+      return
     else
+      dst.delete if mode == :force && dst.exist?
       dst.make_relative_symlink src
     end
   end
 
   # symlinks the contents of self+foo recursively into /usr/local/foo
-  def link_dir foo
+  def link_dir foo, mode=nil
     root = self+foo
     return unless root.exist?
 
@@ -154,10 +159,10 @@ protected
           Find.prune
         when :info
           next if File.basename(src) == 'dir' # skip historical local 'dir' files
-          make_relative_symlink dst, src
+          make_relative_symlink dst, src, mode
           dst.install_info
         else
-          make_relative_symlink dst, src
+          make_relative_symlink dst, src, mode
         end
       elsif src.directory?
         # if the dst dir already exists, then great! walk the rest of the tree tho
diff --git a/share/man/man1/brew.1 b/share/man/man1/brew.1
index e1c546cdcf..4c3d591e9b 100644
--- a/share/man/man1/brew.1
+++ b/share/man/man1/brew.1
@@ -1,7 +1,7 @@
 .\" generated with Ronn/v0.7.3
 .\" http://github.com/rtomayko/ronn/tree/0.7.3
 .
-.TH "BREW" "1" "March 2012" "Homebrew" "brew"
+.TH "BREW" "1" "June 2012" "Homebrew" "brew"
 .
 .SH "NAME"
 \fBbrew\fR \- The missing package manager for OS X
@@ -208,9 +208,15 @@ Download and patch \fIformula\fR, then open a shell\. This allows the user to ru
 If \fB\-\-git\fR is passed, Homebrew will create a Git repository, useful for creating patches to the software\.
 .
 .TP
-\fBln\fR, \fBlink\fR \fIformula\fR
+\fBln\fR, \fBlink [\-\-force] [\-\-dry\-run]\fR \fIformula\fR
 Symlink all of \fIformula\fR\'s installed files into the Homebrew prefix\. This is done automatically when you install formula, but can be useful for DIY installations\.
 .
+.IP
+If \fB\-\-force\fR is passed, Homebrew will delete files which already exist in the prefix while linking\.
+.
+.IP
+If \fB\-\-dry\-run\fR or \fB\-n\fR is passed, Homebrew will list all files which would be deleted by \fBbrew link \-\-force\fR, but will not actually link or delete any files\.
+.
 .TP
 \fBls, list [\-\-unbrewed] [\-\-versions]\fR [\fIformulae\fR]
 Without any arguments, list all installed formulae\.
-- 
GitLab