From 263b434e9777abd7e7d7fdadb83f414d9c1e7693 Mon Sep 17 00:00:00 2001 From: "brian m. carlson" Date: Tue, 28 Jul 2020 19:58:15 +0000 Subject: [PATCH] config: optionally read .lfsconfig from the repository Currently we only read .lfsconfig from the working tree. This is better than nothing, but it means that if there's an LFS file that starts with a name earlier than .lfsconfig, we won't read the proper config file, and we won't use any LFS remote URLs that are located in that file. If the file is missing, let's additionally read from the index, if there's a working tree, and then from HEAD. If the repository is bare, let's just read from HEAD. This is very similar to what Git does for .gitmodules. It does, however, differ in one significant way: Git will read from the index first if it's doing a checkout, since that might have newer information. We don't do that here, since it's not totally clear that we can articulate all the cases where that occurs in Git LFS, but we explicitly allow for the possibility of changing the behavior in the future and document the feature accordingly. We no longer read the file .lfsconfig from a bare repository, but that we did this in the first place was very questionable and pretty clearly a bug, so it's intentional that we no longer do. --- config/config.go | 2 +- docs/man/git-lfs-config.5.ronn | 5 +++ git/config.go | 31 ++++++++++++-- t/t-config.sh | 74 ++++++++++++++++++++++++++++++++++ 4 files changed, 108 insertions(+), 4 deletions(-) diff --git a/config/config.go b/config/config.go index 52a8052c..fa064003 100644 --- a/config/config.go +++ b/config/config.go @@ -76,7 +76,7 @@ func NewIn(workdir, gitdir string) *Configuration { c.Git = &delayedEnvironment{ callback: func() Environment { - sources, err := gitConf.Sources(filepath.Join(c.LocalWorkingDir(), ".lfsconfig")) + sources, err := gitConf.Sources(c.LocalWorkingDir(), ".lfsconfig") if err != nil { fmt.Fprintf(os.Stderr, "Error reading git config: %s\n", err) } diff --git a/docs/man/git-lfs-config.5.ronn b/docs/man/git-lfs-config.5.ronn index 889382e6..742df999 100644 --- a/docs/man/git-lfs-config.5.ronn +++ b/docs/man/git-lfs-config.5.ronn @@ -12,6 +12,11 @@ details. This configuration file is useful for setting options such as the LFS URL or access type for all users of a repository, especially when these differ from the default. The `.lfsconfig` file uses the same format as `.gitconfig`. +If the `.lfsconfig` file is missing, the index is checked for a version of the +file, and that is used instead. If both are missing, `HEAD` is checked for the +file. If the repository is bare, only `HEAD` is checked. This order may change +for checkouts in the future to better match Git's behavior. + Settings from Git configuration files override the `.lfsconfig` file. This allows you to override settings like `lfs.url` in your local environment without having to modify the `.lfsconfig` file. diff --git a/git/config.go b/git/config.go index 262e11ad..43d33a5f 100644 --- a/git/config.go +++ b/git/config.go @@ -2,6 +2,7 @@ package git import ( "errors" + "fmt" "os" "path/filepath" "strings" @@ -134,17 +135,33 @@ func (c *Configuration) UnsetLocalKey(key string) (string, error) { return c.gitConfigWrite("--unset", key) } -func (c *Configuration) Sources(optionalFilename string) ([]*ConfigurationSource, error) { +func (c *Configuration) Sources(dir string, optionalFilename string) ([]*ConfigurationSource, error) { gitconfig, err := c.Source() if err != nil { return nil, err } - fileconfig, err := c.FileSource(optionalFilename) - if err != nil && !os.IsNotExist(err) { + bare, err := IsBare() + if err != nil { return nil, err } + // First try to read from the working directory and then the index if + // the file is missing from the working directory. + var fileconfig *ConfigurationSource + if !bare { + fileconfig, err = c.FileSource(filepath.Join(dir, optionalFilename)) + if err != nil { + if !os.IsNotExist(err) { + return nil, err + } + fileconfig, _ = c.RevisionSource(fmt.Sprintf(":%s", optionalFilename)) + } + } + if fileconfig == nil { + fileconfig, _ = c.RevisionSource(fmt.Sprintf("HEAD:%s", optionalFilename)) + } + configs := make([]*ConfigurationSource, 0, 2) if fileconfig != nil { configs = append(configs, fileconfig) @@ -165,6 +182,14 @@ func (c *Configuration) FileSource(filename string) (*ConfigurationSource, error return ParseConfigLines(out, true), nil } +func (c *Configuration) RevisionSource(revision string) (*ConfigurationSource, error) { + out, err := c.gitConfig("-l", "--blob", revision) + if err != nil { + return nil, err + } + return ParseConfigLines(out, true), nil +} + func (c *Configuration) Source() (*ConfigurationSource, error) { out, err := c.gitConfig("-l") if err != nil { diff --git a/t/t-config.sh b/t/t-config.sh index e78b02f0..ec92fa7a 100755 --- a/t/t-config.sh +++ b/t/t-config.sh @@ -42,6 +42,80 @@ begin_test "default config" ) end_test +begin_test "config reads from repository" +( + set -e + reponame="repository-config" + setup_remote_repo "$reponame" + mkdir $reponame + cd $reponame + git init + git remote add origin "$GITSERVER/$reponame" + git lfs env | tee env.log + grep "Endpoint=$GITSERVER/$reponame.git/info/lfs (auth=none)" env.log + + git config --file=.lfsconfig lfs.url http://lfsconfig-file + git config --file=.lfsconfig lfs.http://lfsconfig-file.access lfsconfig + git add .lfsconfig + git commit -m 'Add file' + git push origin HEAD + + git checkout -b side + git config --file=.lfsconfig lfs.url http://lfsconfig-file-side + git config --file=.lfsconfig lfs.http://lfsconfig-file-side.access lfsconfig + git add .lfsconfig + git commit -m 'Add file for side' + git push origin HEAD + + mkdir "../$reponame-2" + cd "../$reponame-2" + git init + git remote add origin "$GITSERVER/$reponame" + + git lfs env | tee env.log + grep "Endpoint=$GITSERVER/$reponame.git/info/lfs (auth=none)" env.log + + git fetch origin + git symbolic-ref HEAD refs/remotes/origin/side + git show "HEAD:.lfsconfig" + git lfs env | tee env.log + grep "Endpoint=http://lfsconfig-file-side (auth=lfsconfig)" env.log + + git read-tree refs/remotes/origin/main + git lfs env | tee env.log + grep "Endpoint=http://lfsconfig-file (auth=lfsconfig)" env.log +) +end_test + +begin_test "can read LFS file with name before .lfsconfig" +( + set -e + reponame="early-file-config" + setup_remote_repo "$reponame" + mkdir $reponame + cd $reponame + git init + git remote add origin "$GITSERVER/$reponame" + + git lfs track "*.bin" + git config --file=.lfsconfig lfs.url "$GITSERVER/$reponame.git/info/lfs" + + echo "abc" > .bin + echo "def" > a.bin + + git add . + git commit -m "Add files" + git push origin HEAD + rm -fr .git/lfs/objects + + cd .. + git clone "$reponame" "$reponame-2" + cd "$reponame-2" + grep abc .bin + grep def a.bin +) +end_test + begin_test "extension config" ( set -e