github.com/keybase/client/go@v0.0.0-20241007131713-f10651d043c8/kbfs/libgit/git_config_without_remotes_storer.go (about) 1 // Copyright 2017 Keybase Inc. All rights reserved. 2 // Use of this source code is governed by a BSD 3 // license that can be found in the LICENSE file. 4 5 package libgit 6 7 import ( 8 "github.com/keybase/client/go/kbfs/libfs" 9 gogitcfg "gopkg.in/src-d/go-git.v4/config" 10 format "gopkg.in/src-d/go-git.v4/plumbing/format/config" 11 "gopkg.in/src-d/go-git.v4/plumbing/storer" 12 "gopkg.in/src-d/go-git.v4/storage" 13 "gopkg.in/src-d/go-git.v4/storage/filesystem" 14 ) 15 16 // GitConfigWithoutRemotesStorer strips remotes from the config before 17 // writing them to disk, to work around a gcfg bug (used by go-git 18 // when reading configs from disk) that causes a freakout when it sees 19 // backslashes in git file URLs. 20 type GitConfigWithoutRemotesStorer struct { 21 *filesystem.Storage 22 cfg *gogitcfg.Config 23 stored bool 24 } 25 26 // NewGitConfigWithoutRemotesStorer creates a new git config 27 // implementation that strips remotes from the config before writing 28 // them to disk. 29 func NewGitConfigWithoutRemotesStorer(fs *libfs.FS) ( 30 *GitConfigWithoutRemotesStorer, error) { 31 fsStorer, err := filesystem.NewStorage(fs) 32 if err != nil { 33 return nil, err 34 } 35 cfg, err := fsStorer.Config() 36 if err != nil { 37 return nil, err 38 } 39 // To figure out if this config has been written already, check if 40 // it differs from the zero Core value (probably because the 41 // IsBare bit is flipped). 42 return &GitConfigWithoutRemotesStorer{ 43 fsStorer, 44 cfg, 45 cfg.Core != gogitcfg.Config{}.Core, 46 }, nil 47 } 48 49 // Init implements the `storer.Initializer` interface. 50 func (cwrs *GitConfigWithoutRemotesStorer) Init() error { 51 return cwrs.Storage.Init() 52 } 53 54 // Config implements the `storer.Storer` interface. 55 func (cwrs *GitConfigWithoutRemotesStorer) Config() (*gogitcfg.Config, error) { 56 return cwrs.cfg, nil 57 } 58 59 // SetConfig implements the `storer.Storer` interface. 60 func (cwrs *GitConfigWithoutRemotesStorer) SetConfig(c *gogitcfg.Config) ( 61 err error) { 62 if cwrs.stored && c.Core == cwrs.cfg.Core { 63 // Ignore any change that doesn't change the core we know 64 // about, to avoid attempting to write config files with 65 // read-only access. 66 return nil 67 } 68 69 defer func() { 70 if err != nil { 71 cwrs.stored = true 72 } 73 }() 74 75 cwrs.cfg = c 76 if len(c.Remotes) != 0 { 77 // If there are remotes, we need to strip them out before 78 // writing them out to disk. Do that by making a copy of 79 // everything but the remotes. (Note that we can't just 80 // Marshal+Unmarshal for a deep-copy, since Unmarshal is where 81 // the gcfg bug is.) 82 cCopy := gogitcfg.NewConfig() 83 cCopy.Core = c.Core 84 for k, v := range c.Submodules { 85 v2 := *v 86 cCopy.Submodules[k] = &v2 87 } 88 89 // Get the raw config so we don't lose any unsupported fields 90 // from c, but clear out the remotes. 91 _, err := c.Marshal() 92 if err != nil { 93 return err 94 } 95 s := c.Raw.Section("remote") 96 s.Subsections = make(format.Subsections, 0) 97 cCopy.Raw = c.Raw 98 99 c = cCopy 100 } 101 return cwrs.Storage.SetConfig(c) 102 } 103 104 var _ storage.Storer = (*GitConfigWithoutRemotesStorer)(nil) 105 var _ storer.Initializer = (*GitConfigWithoutRemotesStorer)(nil)