Skip to content
This repository was archived by the owner on Jan 16, 2025. It is now read-only.

Commit 85eb73c

Browse files
urdhMikeMcQuaid
authored andcommitted
brew-pin: prevent selected formulae from upgrade.
* Added `pin` et. al. to manpage. * Added `brew pin` to `brew.1` * Added `brew unpin` to `brew.1` * Added `brew list --pinned` to `brew.1` * Added information about frozen formulae to `brew upgrade` in `brew.1` * Added `pin` et.al. to completion scripts. * Unpin formulae when uninstalling them * Unpin and re-pin formulae when upgrading (avoids stale symlink) References #18386. Closes #18515. Signed-off-by: Mike McQuaid <mike@mikemcquaid.com>
1 parent ee7224c commit 85eb73c

13 files changed

Lines changed: 168 additions & 11 deletions

File tree

‎.gitignore‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,6 @@
99
!/share/man/man1/brew.1
1010
.DS_Store
1111
/Library/LinkedKegs
12+
/Library/PinnedKegs
1213
/Library/Taps
1314
/Library/Formula/.gitignore

‎Library/Contributions/brew_bash_completion.sh‎

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -238,10 +238,12 @@ _brew_list ()
238238
return
239239
elif __brewcomp_words_include "--verbose"; then
240240
return
241+
elif __brewcomp_words_include "--pinned"; then
242+
return
241243
elif __brewcomp_words_include "--versions"; then
242244
return
243245
else
244-
__brewcomp "--unbrewed --verbose --versions"
246+
__brewcomp "--unbrewed --verbose --pinned --versions"
245247
return
246248
fi
247249
;;
@@ -400,11 +402,13 @@ _brew ()
400402
options
401403
outdated
402404
prune
405+
pin
403406
search
404407
tap
405408
test
406409
uninstall remove rm
407410
unlink
411+
unpin
408412
untap
409413
update
410414
upgrade
@@ -435,9 +439,11 @@ _brew ()
435439
missing) __brew_complete_formulae ;;
436440
options) _brew_options ;;
437441
outdated) _brew_outdated ;;
442+
pin) _brew_complete_formulae ;;
438443
search|-S) _brew_search ;;
439444
tap) __brew_complete_taps ;;
440445
uninstall|remove|rm) _brew_uninstall ;;
446+
unpin) __brew_complete_formulae ;;
441447
untap) __brew_complete_tapped ;;
442448
update) _brew_update ;;
443449
uses) _brew_uses ;;

‎Library/Contributions/brew_fish_completion.fish‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ function __fish_complete_brew_argument
3232
return 0
3333
end
3434

35-
if contains -- $cmd cleanup link ln missing rm remove test unlink uninstall upgrade
35+
if contains -- $cmd cleanup link ln missing rm remove test unlink uninstall upgrade pin unpin
3636
ls (brew --prefix)/Cellar
3737
return 0
3838
end
@@ -148,6 +148,7 @@ complete -c brew -s f -l force -n '__fish_complete_brew_command link ln' -d "Ove
148148
complete -c brew -s n -l dry-run -n '__fish_complete_brew_command link ln' -d "Show which files would be deleted"
149149

150150
complete -c brew -l unbrewed -n '__fish_complete_brew_command list ls' -d "List files in Homebrew prefix not installed by Homebrew"
151+
complete -c brew -l pinned -n '__fish_complete_brew_command list ls' -d "Show the version number for (specified) pinned formulae"
151152
complete -c brew -l versions -n '__fish_complete_brew_command list ls' -d "Show the version number for specified formulae"
152153

153154
complete -c brew -n '__fish_complete_brew_command log' -u

‎Library/Contributions/brew_zsh_completion.zsh‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,13 @@ _1st_arguments=(
3434
'log:git commit log for a formula'
3535
'missing:check all installed formuale for missing dependencies.'
3636
'outdated:list formulae for which a newer version is available'
37+
'pin:pin specified formulae'
3738
'prune:remove dead links'
3839
'remove:remove a formula'
3940
'search:search for a formula (/regex/ or string)'
4041
'server:start a local web app that lets you browse formulae (requires Sinatra)'
4142
'unlink:unlink a formula'
43+
'unpin:unpin specified formulae'
4244
'update:freshen up links'
4345
'upgrade:upgrade outdated formulae'
4446
'uses:show formulae which depend on a formula'
@@ -71,6 +73,7 @@ case "$words[1]" in
7173
list|ls)
7274
_arguments \
7375
'(--unbrewed)--unbrewed[files in brew --prefix not controlled by brew]' \
76+
'(--pinned)--pinned[list all versions of pinned formulae]' \
7477
'(--versions)--versions[list all installed versions of a formula]' \
7578
'1: :->forms' && return 0
7679

@@ -81,7 +84,7 @@ case "$words[1]" in
8184
install|home|homepage|log|info|abv|uses|cat|deps|edit|options)
8285
_brew_all_formulae
8386
_wanted formulae expl 'all formulae' compadd -a formulae ;;
84-
remove|rm|uninstall|unlink|cleanup|link|ln)
87+
remove|rm|uninstall|unlink|cleanup|link|ln|pin|unpin)
8588
_brew_installed_formulae
8689
_wanted installed_formulae expl 'installed formulae' compadd -a installed_formulae ;;
8790
upgrade)

‎Library/Contributions/manpages/brew.1.md‎

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,7 @@ Note that these flags should only appear after a command.
195195
be linked or which would be deleted by `brew link --overwrite`, but will not
196196
actually link or delete any files.
197197

198-
* `ls, list [--unbrewed] [--versions]` [<formulae>]:
198+
* `ls, list [--unbrewed] [--versions] [--pinned]` [<formulae>]:
199199
Without any arguments, list all installed formulae.
200200

201201
If <formulae> are given, list the installed files for <formulae>.
@@ -208,6 +208,10 @@ Note that these flags should only appear after a command.
208208
If `--versions` is passed, show the version number for installed formulae,
209209
or only the specified formulae if <formulae> are given.
210210

211+
If `--pinned` is passed, show the versions of pinned formulae, or only the
212+
specified (pinned) formulae if <formulae> are given.
213+
See also [`pin`][], [`unpin`][].
214+
211215
* `log [git-log-options]` <formula> ...:
212216
Show the git log for the given formulae. Options that `git-log`(1)
213217
recognizes can be passed before the formula list.
@@ -233,6 +237,10 @@ Note that these flags should only appear after a command.
233237
If `--quiet` is passed, list only the names of outdated brews. Otherwise,
234238
the versions are printed as well.
235239

240+
* `pin` <formulae>:
241+
Pin the specified <formulae>, preventing them from being upgraded when
242+
issuing the `brew upgrade` command without arguments. See also [`unpin`][].
243+
236244
* `prune`:
237245
Remove dead symlinks from the Homebrew prefix. This is generally not
238246
needed, but can be useful when doing DIY installations.
@@ -273,6 +281,10 @@ Note that these flags should only appear after a command.
273281
Unsymlink <formula> from the Homebrew prefix. This can be useful for
274282
temporarily disabling a formula: `brew unlink foo && commands && brew link foo`.
275283

284+
* `unpin` <formulae>:
285+
Unpin <formulae>, allowing them to be upgraded by `brew upgrade`. See also
286+
[`pin`][].
287+
276288
* `untap` <tap>:
277289
Remove a tapped repository.
278290

@@ -283,9 +295,10 @@ Note that these flags should only appear after a command.
283295
If `--rebase` is specified then `git pull --rebase` is used.
284296

285297
* `upgrade` [<formulae>]:
286-
Upgrade outdated brews.
298+
Upgrade outdated, non-pinned brews.
287299

288-
If <formulae> are given, upgrade only the specified brews.
300+
If <formulae> are given, upgrade only the specified brews (but do so even
301+
if they are pinned; see [`pin`][], [`unpin`][]).
289302

290303
* `uses [--installed] [--recursive]` <formula>:
291304
Show the formulae that specify <formula> as a dependency.

‎Library/Homebrew/cmd/list.rb‎

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,10 @@ def list
88
# Things below use the CELLAR, which doesn't until the first formula is installed.
99
return unless HOMEBREW_CELLAR.exist?
1010

11-
if ARGV.include? '--versions'
11+
if ARGV.include? '--pinned'
12+
require 'formula'
13+
list_pinned
14+
elsif ARGV.include? '--versions'
1215
list_versions
1316
elsif ARGV.named.empty?
1417
ENV['CLICOLOR'] = nil
@@ -48,6 +51,18 @@ def list_versions
4851
puts "#{d.basename} #{versions*' '}"
4952
end
5053
end
54+
55+
def list_pinned
56+
if ARGV.named.empty?
57+
HOMEBREW_CELLAR.children.select{ |pn| pn.directory? }
58+
else
59+
ARGV.named.map{ |n| HOMEBREW_CELLAR+n }.select{ |pn| pn.exist? }
60+
end.select do |d|
61+
Formula.factory(d.basename.to_s).pinned?
62+
end.each do |d|
63+
puts "#{d.basename}"
64+
end
65+
end
5166
end
5267

5368
class PrettyListing

‎Library/Homebrew/cmd/pin.rb‎

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
require 'formula'
2+
3+
module Homebrew extend self
4+
def pin
5+
if Process.uid.zero? and not File.stat(HOMEBREW_BREW_FILE).uid.zero?
6+
abort "Cowardly refusing to `sudo pin'"
7+
end
8+
raise FormulaUnspecifiedError if ARGV.named.empty?
9+
ARGV.formulae.each do |fmla|
10+
f = Formula.factory(fmla.to_s)
11+
onoe "Cannot pin uninstalled formula #{f.name}!" unless f.pinable?
12+
opoo "Formula #{f.name} already pinned!" if f.pinable? and f.pinned?
13+
f.pin if f.pinable? and not f.pinned?
14+
end
15+
end
16+
end

‎Library/Homebrew/cmd/uninstall.rb‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ def uninstall
1010
keg.lock do
1111
puts "Uninstalling #{keg}..."
1212
keg.unlink
13+
Formula.factory(keg.fname).unpin
1314
keg.uninstall
1415
rm_opt_link keg.fname
1516
end
@@ -28,6 +29,7 @@ def uninstall
2829
if keg.directory?
2930
keg = Keg.new(keg)
3031
keg.unlink
32+
Formula.factory(keg.fname).unpin
3133
keg.rmtree
3234
end
3335
end

‎Library/Homebrew/cmd/unpin.rb‎

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
require 'formula'
2+
3+
module Homebrew extend self
4+
def unpin
5+
if Process.uid.zero? and not File.stat(HOMEBREW_BREW_FILE).uid.zero?
6+
abort "Cowardly refusing to `sudo unpin'"
7+
end
8+
raise FormulaUnspecifiedError if ARGV.named.empty?
9+
ARGV.formulae.each do |fmla|
10+
f = Formula.factory(fmla.to_s)
11+
onoe "Cannot unpin uninstalled formula #{f.name}!" unless f.pinable?
12+
opoo "Formula #{f.name} already unpinned!" if f.pinable? and not f.pinned?
13+
f.unpin if f.pinable? and f.pinned?
14+
end
15+
end
16+
end

‎Library/Homebrew/cmd/upgrade.rb‎

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,10 @@ def upgrade
1818

1919
if ARGV.named.empty?
2020
require 'cmd/outdated'
21+
upgrade_pinned = false
2122
outdated = Homebrew.outdated_brews
2223
else
24+
upgrade_pinned = true
2325
outdated = ARGV.formulae.select do |f|
2426
if f.installed?
2527
onoe "#{f}-#{f.installed_version} already installed"
@@ -32,10 +34,19 @@ def upgrade
3234
exit 1 if outdated.empty?
3335
end
3436

35-
if outdated.length > 1
37+
unless upgrade_pinned
38+
pinned = outdated.select { |f| f.pinned? }
39+
outdated -= pinned
40+
end
41+
42+
if outdated.length > 0
3643
oh1 "Upgrading #{outdated.length} outdated package#{outdated.length.plural_s}, with result:"
3744
puts outdated.map{ |f| "#{f.name} #{f.version}" } * ", "
3845
end
46+
if not upgrade_pinned and pinned.length > 0
47+
oh1 "Not upgrading #{pinned.length} pinned package#{outdated.length.plural_s}:"
48+
puts pinned.map{ |f| "#{f.name} #{f.version}" } * ", "
49+
end
3950

4051
outdated.each do |f|
4152
upgrade_formula f
@@ -60,6 +71,13 @@ def upgrade_formula f
6071
installer.install
6172
installer.caveats
6273
installer.finish
74+
75+
# If the formula was pinned, and we were force-upgrading it, unpin and
76+
# pin it again to get a symlink pointing to the correct keg.
77+
if f.pinned?
78+
f.unpin
79+
f.pin
80+
end
6381
rescue FormulaInstallationAlreadyAttemptedError
6482
# We already attempted to upgrade f as part of the dependency tree of
6583
# another formula. In that case, don't generate an error, just move on.

0 commit comments

Comments
 (0)