New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Refactor GitRepositoryExtension to avoid monkey-patching #15032
Conversation
Review period will end on 2023-03-23 at 04:08:15 UTC. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice work @dduugg! Just thinking if you're going to refactor all these call-sites: I think some of the naming/API could be tweaked at the same time to make it a little nicer to use.
55c63b3
to
62abe0d
Compare
PTAL @MikeMcQuaid @RandomDSdevel |
Don't mind me, I just had the same thought as the one @MikeMcQuaid did in that review comment of his that I gave a thumbs-up reaction to. Some other thoughts:
|
Solid thought, |
Library/Homebrew/extend/git_path.rb
Outdated
@@ -7,12 +7,14 @@ | |||
# Extensions to {Pathname} for querying Git repository information. | |||
# @see Utils::Git | |||
# @api private | |||
module GitRepositoryExtension | |||
class GitPath < SimpleDelegator |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
class GitPath < SimpleDelegator | |
class GitRepository < SimpleDelegator |
Maybe? GitRepoPath
is fine too.
Review period ended. |
I disagree. Makes sense to do all this refactoring at once.
Not in homebrew/core. It's not marked as a public API so if it goes 💥 beyond that: that's not on us. |
9195ad7
to
e8108d6
Compare
sig { returns(T::Boolean) } | ||
def git? | ||
join(".git").exist? | ||
def git_repo? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
def git_repo? | |
def exist? |
or something feels like it makes more sense with this naming. GitRepoPath.new(repo).git_repo?
feels pretty repetitive?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
GitRepoPath.new(repo).git_repo?
is repetitive, yes, but the problem is in Library/Homebrew/tap.rb
. I think some minor redundancy elsewhere is preferable to the below, wdyt?
diff --git a/Library/Homebrew/tap.rb b/Library/Homebrew/tap.rb
index d1e9c4850..4c17062f6 100644
--- a/Library/Homebrew/tap.rb
+++ b/Library/Homebrew/tap.rb
@@ -135,7 +135,7 @@ class Tap
def remote
return default_remote unless installed?
- @remote ||= path.git_origin
+ @remote ||= path.origin
end
# The remote repository name of this {Tap}.
@@ -163,28 +163,28 @@ class Tap
# True if this {Tap} is a Git repository.
def git?
- path.git_repo?
+ path.repo?
end
# git branch for this {Tap}.
def git_branch
raise TapUnavailableError, name unless installed?
- path.git_branch
+ path.branch
end
# git HEAD for this {Tap}.
def git_head
raise TapUnavailableError, name unless installed?
- @git_head ||= path.git_head
+ @git_head ||= path.head
end
# Time since last git commit for this {Tap}.
def git_last_commit
raise TapUnavailableError, name unless installed?
- path.git_last_commit
+ path.last_commit
end
# The issues URL of this {Tap}.
@@ -385,20 +385,20 @@ class Tap
$stderr.ohai "#{name}: changed remote from #{remote} to #{requested_remote}" unless quiet
end
- current_upstream_head = path.git_origin_branch
- return if requested_remote.blank? && path.git_origin_has_branch?(current_upstream_head)
+ current_upstream_head = path.origin_branch
+ return if requested_remote.blank? && path.origin_has_branch?(current_upstream_head)
args = %w[fetch]
args << "--quiet" if quiet
args << "origin"
safe_system "git", "-C", path, *args
- path.git_origin_set_head_auto
+ path.origin_set_head_auto
- new_upstream_head = path.git_origin_branch
+ new_upstream_head = path.origin_branch
return if new_upstream_head == current_upstream_head
- path.git_rename_branch old: current_upstream_head, new: new_upstream_head
- path.git_branch_set_upstream local: new_upstream_head, origin: new_upstream_head
+ path.rename_branch old: current_upstream_head, new: new_upstream_head
+ path.branch_set_upstream local: new_upstream_head, origin: new_upstream_head
return if quiet
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I dunno, I kinda like the abbreviated version. If we wanted to improve it: I wonder if aliasing path
to git_path
or similar would be nicer?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Re-reading the above: yeh, I agree moreso with this that it's confusing that path
doesn't return a Pathname
. Feels like this method should have git_
somewhere in it as it's now returning a GitRepoPath
Library/Homebrew/dev-cmd/extract.rb
Outdated
@@ -113,7 +113,7 @@ def self.extract | |||
end | |||
destination_tap.install unless destination_tap.installed? | |||
|
|||
repo = source_tap.path | |||
repo = source_tap.path.pathname |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm sorry this is taking so much back and forth!
This now looks pretty weird?
I'd expect that source_tap.path
returns a Pathname
. If not, maybe something like
repo = source_tap.path.pathname | |
repo = source_tap.git_repo.path |
or
repo = source_tap.path.pathname | |
repo = source_tap.git_repo_path.pathname |
would make more sense?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, we can do that. This is just being explicit for the type checker though, since .path
delegates methods to .pathname
anyay. We could equivalently update relevant sig
s to allow GitRepoPath
along with Pathname
, or use T.cast
to overrule sorbet where necessary. (I'm happy to do the suggest refactor, just providing some context.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm fine with either my suggestion or the sig one? I think T.cast
is better avoided when we can (it's a bit ugly).
sig { returns(T::Boolean) } | ||
def git? | ||
join(".git").exist? | ||
def git_repo? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Re-reading the above: yeh, I agree moreso with this that it's confusing that path
doesn't return a Pathname
. Feels like this method should have git_
somewhere in it as it's now returning a GitRepoPath
Going to park this one for a bit in favor of closing out some easier-to-type files |
@dduugg Let's leave this open if that's ok! I can tone down the bike-shedding on the naming 😅, just would be good to land this in some form given how much review there has been. |
@@ -156,21 +156,21 @@ def self.determine_bump_subject(old_contents, new_contents, subject_path, reason | |||
|
|||
# Cherry picks a single commit that modifies a single file. | |||
# Potentially rewords this commit using {determine_bump_subject}. | |||
def self.reword_package_commit(commit, file, reason: "", verbose: false, resolve: false, path: ".") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've renamed path:
to git_repo:
and removed the default value (it was never valid bc it was a String
). Same for squash_package_commits
below.
PTAL @MikeMcQuaid |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks great, thanks for this @dduugg!
brew style
with your changes locally?brew typecheck
with your changes locally?brew tests
with your changes locally?This refactors
GitRepositoryExtension
as a static module, rather than one that was dynamically extended by aPathname
. This approach allows us to enable typing, remove an.rbi
file, and make the code more comprehensible (it wasn't clear whichPathname
objects had this add'l behavior).Note that
Library/Homebrew/extend/git_repository.rb
andLibrary/Homebrew/utils/git_repository.rb
have considerable overlap, but i haven't attempted to unify them in this PR. (Theextend
version lacks test, theutils
version is part of the public API, and they diverge in subtle ways. For instance, both have agit_commit_message
but with opposite defaults for thesafe
kwarg.)