From fad235d8e82ae57b2004429a0297f313aafa3186 Mon Sep 17 00:00:00 2001
From: ilovezfs
Date: Mon, 4 Apr 2016 03:18:55 -0700
Subject: [PATCH] tap: support --full even if installed
Makes `tap` re-runnable and unshallows when requested with `--full`.
Tapping with a different URL raises an exception.
The homebrew/core tap cannot be untapped with `untap` so running
`brew tap --full homebrew/core` is now a built-in way to get a full
clone of this tap without resorting to workarounds.
Closes #17.
Signed-off-by: ilovezfs
---
Library/Homebrew/cmd/tap.rb | 6 ++++--
Library/Homebrew/exceptions.rb | 29 +++++++++++++++++++++++++++
Library/Homebrew/manpages/brew.1.md | 7 ++++++-
Library/Homebrew/tap.rb | 31 ++++++++++++++++++++++++-----
Library/Homebrew/test/test_tap.rb | 27 ++++++++++++++++++++++++-
share/doc/homebrew/brew.1.html | 7 ++++++-
share/man/man1/brew.1 | 5 ++++-
7 files changed, 101 insertions(+), 11 deletions(-)
diff --git a/Library/Homebrew/cmd/tap.rb b/Library/Homebrew/cmd/tap.rb
index 0793653366..1bd378d69f 100644
--- a/Library/Homebrew/cmd/tap.rb
+++ b/Library/Homebrew/cmd/tap.rb
@@ -17,8 +17,10 @@ module Homebrew
tap.install :clone_target => ARGV.named[1],
:full_clone => ARGV.include?("--full"),
:quiet => ARGV.quieter?
- rescue TapAlreadyTappedError => e
- opoo e
+ rescue TapRemoteMismatchError => e
+ odie e
+ rescue TapAlreadyTappedError, TapAlreadyUnshallowError
+ # Do nothing.
end
end
end
diff --git a/Library/Homebrew/exceptions.rb b/Library/Homebrew/exceptions.rb
index 79cfac012d..dfd60549d5 100644
--- a/Library/Homebrew/exceptions.rb
+++ b/Library/Homebrew/exceptions.rb
@@ -119,6 +119,23 @@ class TapUnavailableError < RuntimeError
end
end
+class TapRemoteMismatchError < RuntimeError
+ attr_reader :name
+ attr_reader :expected_remote
+ attr_reader :actual_remote
+
+ def initialize(name, expected_remote, actual_remote)
+ @name = name
+ @expected_remote = expected_remote
+ @actual_remote = actual_remote
+
+ super <<-EOS.undent
+ Tap #{name} remote mismatch.
+ #{expected_remote} != #{actual_remote}
+ EOS
+ end
+end
+
class TapAlreadyTappedError < RuntimeError
attr_reader :name
@@ -131,6 +148,18 @@ class TapAlreadyTappedError < RuntimeError
end
end
+class TapAlreadyUnshallowError < RuntimeError
+ attr_reader :name
+
+ def initialize(name)
+ @name = name
+
+ super <<-EOS.undent
+ Tap #{name} already a full clone.
+ EOS
+ end
+end
+
class TapPinStatusError < RuntimeError
attr_reader :name, :pinned
diff --git a/Library/Homebrew/manpages/brew.1.md b/Library/Homebrew/manpages/brew.1.md
index 4863bcd5db..47b0517ef3 100644
--- a/Library/Homebrew/manpages/brew.1.md
+++ b/Library/Homebrew/manpages/brew.1.md
@@ -394,7 +394,12 @@ With `--verbose` or `-v`, many commands print extra debugging information. Note
using protocols other than HTTPS, e.g., SSH, GIT, HTTP, FTP(S), RSYNC.
By default, the repository is cloned as a shallow copy (`--depth=1`), but
- if `--full` is passed, a full clone will be used.
+ if `--full` is passed, a full clone will be used. To convert a shallow copy
+ to a full copy, you can retap passing `--full` without first untapping.
+
+ `tap` is re-runnable and exits successfully if there's nothing to do.
+ However, retapping with a different will cause an exception, so first
+ `untap` if you need to modify the .
* `tap` `--repair`:
Migrate tapped formulae from symlink-based to directory-based structure.
diff --git a/Library/Homebrew/tap.rb b/Library/Homebrew/tap.rb
index 8f8e99e67c..a2e21ca0a5 100644
--- a/Library/Homebrew/tap.rb
+++ b/Library/Homebrew/tap.rb
@@ -158,6 +158,11 @@ class Tap
path.directory?
end
+ # True if this {Tap} is not a full clone.
+ def shallow?
+ (path/".git/shallow").exist?
+ end
+
# @private
def core_tap?
false
@@ -171,17 +176,33 @@ class Tap
# @option options [Boolean] :quiet If set, suppress all output.
def install(options = {})
require "descriptions"
- raise TapAlreadyTappedError, name if installed?
- clear_cache
+ full_clone = options.fetch(:full_clone, false)
quiet = options.fetch(:quiet, false)
+ requested_remote = options[:clone_target] || "https://github.com/#{user}/homebrew-#{repo}"
+
+ if installed?
+ raise TapRemoteMismatchError.new(name, @remote, requested_remote) unless remote == requested_remote
+ raise TapAlreadyTappedError, name unless full_clone
+ raise TapAlreadyUnshallowError, name unless shallow?
+ end
# ensure git is installed
Utils.ensure_git_installed!
+
+ if installed?
+ ohai "Unshallowing #{name}" unless quiet
+ args = %W[fetch --unshallow]
+ args << "-q" if quiet
+ path.cd { safe_system "git", *args }
+ return
+ end
+
+ clear_cache
+
ohai "Tapping #{name}" unless quiet
- remote = options[:clone_target] || "https://github.com/#{user}/homebrew-#{repo}"
- args = %W[clone #{remote} #{path} --config core.autocrlf=false]
- args << "--depth=1" unless options.fetch(:full_clone, false)
+ args = %W[clone #{requested_remote} #{path} --config core.autocrlf=false]
+ args << "--depth=1" unless full_clone
args << "-q" if quiet
begin
diff --git a/Library/Homebrew/test/test_tap.rb b/Library/Homebrew/test/test_tap.rb
index fa0602e940..0b26716986 100644
--- a/Library/Homebrew/test/test_tap.rb
+++ b/Library/Homebrew/test/test_tap.rb
@@ -164,7 +164,32 @@ class TapTest < Homebrew::TestCase
end
def test_install_tap_already_tapped_error
- assert_raises(TapAlreadyTappedError) { @tap.install }
+ setup_git_repo
+ already_tapped_tap = Tap.new("Homebrew", "foo")
+ assert_equal true, already_tapped_tap.installed?
+ assert_raises(TapAlreadyTappedError) { already_tapped_tap.install }
+ end
+
+ def test_install_tap_remote_match_already_tapped_error
+ setup_git_repo
+ already_tapped_tap = Tap.new("Homebrew", "foo")
+ assert_equal true, already_tapped_tap.installed?
+ right_remote = "#{@tap.remote}"
+ assert_raises(TapAlreadyTappedError) { already_tapped_tap.install :clone_target => right_remote }
+ end
+
+ def test_install_tap_remote_mismatch_error
+ setup_git_repo
+ already_tapped_tap = Tap.new("Homebrew", "foo")
+ assert_equal true, already_tapped_tap.installed?
+ wrong_remote = "#{@tap.remote}-oops"
+ assert_raises(TapRemoteMismatchError) { already_tapped_tap.install :clone_target => wrong_remote }
+ end
+
+ def test_install_tap_already_unshallow_error
+ setup_git_repo
+ already_tapped_tap = Tap.new("Homebrew", "foo")
+ assert_raises(TapAlreadyUnshallowError) { already_tapped_tap.install :full_clone => true }
end
def test_uninstall_tap_unavailable_error
diff --git a/share/doc/homebrew/brew.1.html b/share/doc/homebrew/brew.1.html
index 2997bcb034..37519d67e5 100644
--- a/share/doc/homebrew/brew.1.html
+++ b/share/doc/homebrew/brew.1.html
@@ -303,7 +303,12 @@ assumptions, so taps can be cloned from places other than GitHub and
using protocols other than HTTPS, e.g., SSH, GIT, HTTP, FTP(S), RSYNC.
By default, the repository is cloned as a shallow copy (--depth=1
), but
-if --full
is passed, a full clone will be used.
+if --full
is passed, a full clone will be used. To convert a shallow copy
+to a full copy, you can retap passing --full
without first untapping.
+
+tap
is re-runnable and exits successfully if there's nothing to do.
+However, retapping with a different URL will cause an exception, so first
+untap
if you need to modify the URL.
tap
--repair
Migrate tapped formulae from symlink-based to directory-based structure.
tap
--list-official
List all official taps.
tap
--list-pinned
List all pinned taps.
diff --git a/share/man/man1/brew.1 b/share/man/man1/brew.1
index 8f1167f317..c9adf6bea4 100644
--- a/share/man/man1/brew.1
+++ b/share/man/man1/brew.1
@@ -416,7 +416,10 @@ With \fIURL\fR unspecified, taps a formula repository from GitHub using HTTPS\.
With \fIURL\fR specified, taps a formula repository from anywhere, using any transport protocol that \fBgit\fR handles\. The one\-argument form of \fBtap\fR simplifies but also limits\. This two\-argument command makes no assumptions, so taps can be cloned from places other than GitHub and using protocols other than HTTPS, e\.g\., SSH, GIT, HTTP, FTP(S), RSYNC\.
.
.IP
-By default, the repository is cloned as a shallow copy (\fB\-\-depth=1\fR), but if \fB\-\-full\fR is passed, a full clone will be used\.
+By default, the repository is cloned as a shallow copy (\fB\-\-depth=1\fR), but if \fB\-\-full\fR is passed, a full clone will be used\. To convert a shallow copy to a full copy, you can retap passing \fB\-\-full\fR without first untapping\.
+.
+.IP
+\fBtap\fR is re\-runnable and exits successfully if there\'s nothing to do\. However, retapping with a different \fIURL\fR will cause an exception, so first \fBuntap\fR if you need to modify the \fIURL\fR\.
.
.TP
\fBtap\fR \fB\-\-repair\fR