Skip to content

Simplify client structure initializations#6254

Merged
chrisd8088 merged 6 commits into
git-lfs:mainfrom
chrisd8088:simplify-client-inits
Apr 30, 2026
Merged

Simplify client structure initializations#6254
chrisd8088 merged 6 commits into
git-lfs:mainfrom
chrisd8088:simplify-client-inits

Conversation

@chrisd8088

Copy link
Copy Markdown
Member

This PR simplifies all of the factory functions we use to construct new API, HTTP, and locking clients by removing their unused error return values.

For historical reasons, the NewClient() functions in our lfsapi, lfshttp, and locking packages all return a value with the error type, but at present none of the functions could ever actually encounter an error condition and return a non-nil error value. We can therefore just eliminate these unused return values.

This PR will be most easily reviewed on a commit-by-commit basis.

Avoiding Future Deadlocks

One specific advantage of this change is that the getAPIClient() function in our commands package will no longer need to include a conditional block which checks the error return value from the lfsapi.NewClient() constructor function. This block handles the (hypothetical) case of a non-nil error return value by calling the ExitWithError() function, which causes the Git LFS client to exit immediately.

In a future PR, we may revise the ExitWithError() function so that it performs various cleanup operations prior to calling os.Exit() function, but only after first acquiring the same global mutex that the getAPIClient() function also acquires. With this goal in mind, we need to ensure that the getAPIClient() function does not invoke the ExitWithError() function, since that would result in a deadlock condition.

In commit d101bdb of PR git-lfs#3244 we
extracted the portions of our "lfsapi" package specifically related
to HTTP request handling into a new package named "lfshttp".

As part of that process, we moved some of the initialization steps
formerly performed by the NewClient() function of our "lfsapi" package
into a separate NewClient() function in the "lfshttp" package.

Both NewClient() functions return an "error" type as their second
return value, and at the time of PR git-lfs#3244, the NewClient() function
in the "lfsapi" package could encounter an error condition and
return a non-nil "error" value.

However, none of the initialization steps that we moved to the
NewClient() function of the "lfshttp" package could encounter an
error condition, so there was no specific need for the function
to continue to return an "error" value.

As this remains the case, we can simplify the NewClient() function
in our "lfshttp" package by just eliminating its "error" return value.

We then update all the callers of this function, including those in
our Go test functions, as they no longer need to check for an "error"
return value from the function.
In PR git-lfs#1839 we first developed our "lfsapi" package, and in commit
31f180b of that PR we introduced
the package's NewClient() function to initialize and return a Client
structure.

At the time, the NewClient() function invoked the ParseNetrc() function,
which was located in the same package.  As this function parsed a user's
"netrc" file, it could encounter error conditions, and so it might
return a non-nil "error" value.  The NewClient() function checked for
this possibility, and in the case of an error, would itself return a
non-nil "error" value.

However, in commit 1026ff0 of PR git-lfs#3307
we moved the ParseNetrc() function into our "creds" package, and added
the netrcCredentialHelper structure in that package.  In the same
commit we also refactored our handling of "netrc" files so that we
now parsed them when we retrieve credentials for an HTTP request,
rather than when we initialized a new Client structure in our "lfsapi"
package.

As a result, none of the remaining initialization steps in the
NewClient() function of the "lfsapi" package could encounter an error
condition, and so there was no specific need for the function to
continue to return an "error" value.

As this remains the case, we can simplify the NewClient() function
in our "lfsapi" package by just eliminating its "error" return value.

We then update all the callers of this function, including those in
our Go test functions, as they no longer need to check for an "error"
return value from the function.
In PR git-lfs#1769 we first developed our "locking" package, and in commit
4910706 of that PR we introduced
the package's NewClient() function to initialize and return a Client
structure.

At the conclusion of PR git-lfs#1769, the NewClient() function invoked both
the NewLockCache() function, which was also located in the "locking"
package, and the MkdirAll() function from the "os" package of the
Go standard library.  The NewClient() function called these functions
in order to create the "lockcache.db" file we store in the ".git/lfs"
directory.

Both the NewLockCache() and MkdirAll() functions could return
error conditions, and so might return non-nil "error" values.
The NewClient() function checked for these possibilities, and in
the case of an error, would itself return a non-nil "error" value.

However, in commit a998543 of PR git-lfs#1839
we revised the NewClient() function so that it did not create a
lock cache file.  Instead, this became the responsibility of a new
Client structure method named SetupFileCache().  We also added a
function to our "commands" package which first calls the "locking"
package's NewClient() function, and then invokes the SetupFileCache()
method on the newly initialized Client structure.

As a result, none of the remaining initialization steps in the
NewClient() function of the "locking" package could encounter an error
condition, and so there was no specific need for the function to
continue to return an "error" value.

As this remains the case, we can simplify the NewClient() function
in our "locking" package by just eliminating its "error" return value.

We then update all the callers of this function, including those in
our Go test functions, as they no longer need to check for an "error"
return value from the function.
@chrisd8088 chrisd8088 requested a review from a team as a code owner April 26, 2026 05:48
@knightitcomp-sketch

This comment was marked as off-topic.

@larsxschneider larsxschneider left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A nice cleanup! Thank you 🙇

(Unless we use it shortly, I would remove the unused Shutdown() method before merging)

Comment thread tq/manifest.go Outdated
Comment thread lfshttp/client.go
chrisd8088 and others added 3 commits April 30, 2026 07:46
As pointed out by larsxschneider on PR review, we inadvertently
included a Shutdown() method for the concreteManifest structure
in our "tq" package before adding any related functionality.

We expect to make use of such a method in a future PR to address
the issue reported in git-lfs#6118, but in the context of the current PR
this method is unnecessary and so we just remove it for the present.
As suggested by larsxschneider on PR review, we clarify the purpose
of the sshAuthClient structure in our "lfshttp" package by adding
a comment to describe its use, which is to execute the
"git-lfs-authenticate" command over SSH in order to fetch HTTP
credentials for the Git LFS Batch API.

Co-authored-by: Lars Schneider <larsxschneider@github.com>
@chrisd8088 chrisd8088 merged commit 14ae170 into git-lfs:main Apr 30, 2026
19 of 20 checks passed
@chrisd8088 chrisd8088 deleted the simplify-client-inits branch April 30, 2026 16:05
chrisd8088 added a commit that referenced this pull request May 21, 2026
In previous commits in this PR, we revised the Git LFS client such
that it now always performs a consistent set of cleanup activities
when exiting, regardless of the circumstances.  In particular, the
Cleanup() function in our "commands" package should always be executed
before the client halts, and one of the steps this function now takes
is to invoke the closeAPIClient() function in the "commands" package.

The closeAPIClient() function checks if the "apiClient" global
variable has been instantiated.  If it has, then it points to a Client
structure from our "lfsapi" package, and the closeAPIClient() function
calls that structure's Close() method to release any resources
associated with the structure.

In addition, we updated several other packages in our main Git LFS
client program and its Go test functions so that after a Client
structure from the "lfsapi" package is instantiated, its Close()
method will always be invoked when the structure is no longer in use.

For consistency, we now update the three other programs in our
project which instantiate Client structures from the "lfsapi" package
so that they also always call the Close() method on these structures
at the end of their lifetimes.

Note that in the case of the "t/git-lfs-test-server-api" program,
we also revise the buildManifest() function as it no longer needs
to return an "error" value, but does now need to return the Client
structure it creates.

In commit d40d1b7 of PR #6254 we
altered the NewClient() function in the "lfsapi" package so that
it does not return an "error" value.  At that time we could also
have simplified the buildManifest() function in the
"t/git-lfs-test-server-api" program to not return an "error" value,
as it previously did so only to report any error condition returned
by the NewClient() function of the "lfsapi" package.  However, we
overlooked this potential simplification in PR #6254, so we make
it now instead.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants