fork of go-git with some jj specific features
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge pull request #896 from aymanbagabas/update-server-info

Implement upload-server-info

authored by

Paulo Gomes and committed by
GitHub
8c1e3e2e 22585738

+332 -4
+4 -4
COMPATIBILITY.md
··· 109 109 110 110 ## Server admin 111 111 112 - | Feature | Sub-feature | Status | Notes | Examples | 113 - | -------------------- | ----------- | ------ | ----- | -------- | 114 - | `daemon` | | ❌ | | | 115 - | `update-server-info` | | ❌ | | | 112 + | Feature | Sub-feature | Status | Notes | Examples | 113 + | -------------------- | ----------- | ------ | ----- | ----------------------------------------- | 114 + | `daemon` | | ❌ | | | 115 + | `update-server-info` | | ✅ | | [cli](./cli/go-git/update_server_info.go) | 116 116 117 117 ## Advanced 118 118
+1
cli/go-git/main.go
··· 22 22 } 23 23 24 24 parser := flags.NewNamedParser(bin, flags.Default) 25 + parser.AddCommand("update-server-info", "", "", &CmdUpdateServerInfo{}) 25 26 parser.AddCommand("receive-pack", "", "", &CmdReceivePack{}) 26 27 parser.AddCommand("upload-pack", "", "", &CmdUploadPack{}) 27 28 parser.AddCommand("version", "Show the version information.", "", &CmdVersion{})
+34
cli/go-git/update_server_info.go
··· 1 + package main 2 + 3 + import ( 4 + "fmt" 5 + "os" 6 + 7 + "github.com/go-git/go-git/v5" 8 + "github.com/go-git/go-git/v5/plumbing/serverinfo" 9 + "github.com/go-git/go-git/v5/storage/filesystem" 10 + ) 11 + 12 + // CmdUpdateServerInfo command updates the server info files in the repository. 13 + // This is used by git http transport (dumb) to generate a list of available 14 + // refs for the repository. See: 15 + // https://git-scm.com/docs/git-update-server-info 16 + type CmdUpdateServerInfo struct { 17 + cmd 18 + } 19 + 20 + // Usage returns the usage of the command. 21 + func (CmdUpdateServerInfo) Usage() string { 22 + return fmt.Sprintf("within a git repository run: %s", os.Args[0]) 23 + } 24 + 25 + // Execute runs the command. 26 + func (c *CmdUpdateServerInfo) Execute(args []string) error { 27 + r, err := git.PlainOpen(".") 28 + if err != nil { 29 + return err 30 + } 31 + 32 + fs := r.Storer.(*filesystem.Storage).Filesystem() 33 + return serverinfo.UpdateServerInfo(r.Storer, fs) 34 + }
+14
internal/reference/sort.go
··· 1 + package reference 2 + 3 + import ( 4 + "sort" 5 + 6 + "github.com/go-git/go-git/v5/plumbing" 7 + ) 8 + 9 + // Sort sorts the references by name to ensure a consistent order. 10 + func Sort(refs []*plumbing.Reference) { 11 + sort.Slice(refs, func(i, j int) bool { 12 + return refs[i].Name() < refs[j].Name() 13 + }) 14 + }
+94
plumbing/serverinfo/serverinfo.go
··· 1 + package serverinfo 2 + 3 + import ( 4 + "fmt" 5 + 6 + "github.com/go-git/go-billy/v5" 7 + "github.com/go-git/go-git/v5" 8 + "github.com/go-git/go-git/v5/internal/reference" 9 + "github.com/go-git/go-git/v5/plumbing" 10 + "github.com/go-git/go-git/v5/plumbing/object" 11 + "github.com/go-git/go-git/v5/plumbing/storer" 12 + "github.com/go-git/go-git/v5/storage" 13 + ) 14 + 15 + // UpdateServerInfo updates the server info files in the repository. 16 + // 17 + // It generates a list of available refs for the repository. 18 + // Used by git http transport (dumb), for more information refer to: 19 + // https://git-scm.com/book/id/v2/Git-Internals-Transfer-Protocols#_the_dumb_protocol 20 + func UpdateServerInfo(s storage.Storer, fs billy.Filesystem) error { 21 + pos, ok := s.(storer.PackedObjectStorer) 22 + if !ok { 23 + return git.ErrPackedObjectsNotSupported 24 + } 25 + 26 + infoRefs, err := fs.Create("info/refs") 27 + if err != nil { 28 + return err 29 + } 30 + 31 + defer infoRefs.Close() 32 + 33 + refsIter, err := s.IterReferences() 34 + if err != nil { 35 + return err 36 + } 37 + 38 + defer refsIter.Close() 39 + 40 + var refs []*plumbing.Reference 41 + if err := refsIter.ForEach(func(ref *plumbing.Reference) error { 42 + refs = append(refs, ref) 43 + return nil 44 + }); err != nil { 45 + return err 46 + } 47 + 48 + reference.Sort(refs) 49 + for _, ref := range refs { 50 + name := ref.Name() 51 + hash := ref.Hash() 52 + switch ref.Type() { 53 + case plumbing.SymbolicReference: 54 + if name == plumbing.HEAD { 55 + continue 56 + } 57 + ref, err := s.Reference(ref.Target()) 58 + if err != nil { 59 + return err 60 + } 61 + 62 + hash = ref.Hash() 63 + fallthrough 64 + case plumbing.HashReference: 65 + fmt.Fprintf(infoRefs, "%s\t%s\n", hash, name) 66 + if name.IsTag() { 67 + tag, err := object.GetTag(s, hash) 68 + if err == nil { 69 + fmt.Fprintf(infoRefs, "%s\t%s^{}\n", tag.Target, name) 70 + } 71 + } 72 + } 73 + } 74 + 75 + infoPacks, err := fs.Create("objects/info/packs") 76 + if err != nil { 77 + return err 78 + } 79 + 80 + defer infoPacks.Close() 81 + 82 + packs, err := pos.ObjectPacks() 83 + if err != nil { 84 + return err 85 + } 86 + 87 + for _, p := range packs { 88 + fmt.Fprintf(infoPacks, "P pack-%s.pack\n", p) 89 + } 90 + 91 + fmt.Fprintln(infoPacks) 92 + 93 + return nil 94 + }
+185
plumbing/serverinfo/serverinfo_test.go
··· 1 + package serverinfo 2 + 3 + import ( 4 + "io" 5 + "strings" 6 + "testing" 7 + 8 + "github.com/go-git/go-billy/v5" 9 + "github.com/go-git/go-billy/v5/memfs" 10 + fixtures "github.com/go-git/go-git-fixtures/v4" 11 + "github.com/go-git/go-git/v5" 12 + "github.com/go-git/go-git/v5/plumbing" 13 + "github.com/go-git/go-git/v5/plumbing/object" 14 + "github.com/go-git/go-git/v5/plumbing/storer" 15 + "github.com/go-git/go-git/v5/storage" 16 + "github.com/go-git/go-git/v5/storage/memory" 17 + . "gopkg.in/check.v1" 18 + ) 19 + 20 + type ServerInfoSuite struct{} 21 + 22 + var _ = Suite(&ServerInfoSuite{}) 23 + 24 + func Test(t *testing.T) { TestingT(t) } 25 + 26 + func (s *ServerInfoSuite) TestUpdateServerInfoInit(c *C) { 27 + fs := memfs.New() 28 + st := memory.NewStorage() 29 + r, err := git.Init(st, fs) 30 + c.Assert(err, IsNil) 31 + c.Assert(r, NotNil) 32 + 33 + err = UpdateServerInfo(st, fs) 34 + c.Assert(err, IsNil) 35 + } 36 + 37 + func assertInfoRefs(c *C, st storage.Storer, fs billy.Filesystem) { 38 + refsFile, err := fs.Open("info/refs") 39 + c.Assert(err, IsNil) 40 + 41 + defer refsFile.Close() 42 + bts, err := io.ReadAll(refsFile) 43 + c.Assert(err, IsNil) 44 + 45 + localRefs := make(map[plumbing.ReferenceName]plumbing.Hash) 46 + for _, line := range strings.Split(string(bts), "\n") { 47 + if line == "" { 48 + continue 49 + } 50 + parts := strings.Split(line, "\t") 51 + c.Assert(parts, HasLen, 2) 52 + hash := plumbing.NewHash(parts[0]) 53 + name := plumbing.ReferenceName(parts[1]) 54 + localRefs[name] = hash 55 + } 56 + 57 + refs, err := st.IterReferences() 58 + c.Assert(err, IsNil) 59 + 60 + err = refs.ForEach(func(ref *plumbing.Reference) error { 61 + name := ref.Name() 62 + hash := ref.Hash() 63 + switch ref.Type() { 64 + case plumbing.SymbolicReference: 65 + if name == plumbing.HEAD { 66 + return nil 67 + } 68 + ref, err := st.Reference(ref.Target()) 69 + c.Assert(err, IsNil) 70 + hash = ref.Hash() 71 + fallthrough 72 + case plumbing.HashReference: 73 + h, ok := localRefs[name] 74 + c.Assert(ok, Equals, true) 75 + c.Assert(h, Equals, hash) 76 + if name.IsTag() { 77 + tag, err := object.GetTag(st, hash) 78 + if err == nil { 79 + t, ok := localRefs[name+"^{}"] 80 + c.Assert(ok, Equals, true) 81 + c.Assert(t, Equals, tag.Target) 82 + } 83 + } 84 + } 85 + return nil 86 + }) 87 + 88 + c.Assert(err, IsNil) 89 + } 90 + 91 + func assertObjectPacks(c *C, st storage.Storer, fs billy.Filesystem) { 92 + infoPacks, err := fs.Open("objects/info/packs") 93 + c.Assert(err, IsNil) 94 + 95 + defer infoPacks.Close() 96 + bts, err := io.ReadAll(infoPacks) 97 + c.Assert(err, IsNil) 98 + 99 + pos, ok := st.(storer.PackedObjectStorer) 100 + c.Assert(ok, Equals, true) 101 + localPacks := make(map[string]struct{}) 102 + packs, err := pos.ObjectPacks() 103 + c.Assert(err, IsNil) 104 + 105 + for _, line := range strings.Split(string(bts), "\n") { 106 + if line == "" { 107 + continue 108 + } 109 + parts := strings.Split(line, " ") 110 + c.Assert(parts, HasLen, 2) 111 + pack := strings.TrimPrefix(parts[1], "pack-") 112 + pack = strings.TrimSuffix(pack, ".pack") 113 + localPacks[pack] = struct{}{} 114 + } 115 + 116 + for _, p := range packs { 117 + _, ok := localPacks[p.String()] 118 + c.Assert(ok, Equals, true) 119 + } 120 + } 121 + 122 + func (s *ServerInfoSuite) TestUpdateServerInfoTags(c *C) { 123 + fs := memfs.New() 124 + st := memory.NewStorage() 125 + r, err := git.Clone(st, fs, &git.CloneOptions{ 126 + URL: fixtures.ByURL("https://github.com/git-fixtures/tags.git").One().URL, 127 + }) 128 + c.Assert(err, IsNil) 129 + c.Assert(r, NotNil) 130 + 131 + err = UpdateServerInfo(st, fs) 132 + c.Assert(err, IsNil) 133 + 134 + assertInfoRefs(c, st, fs) 135 + assertObjectPacks(c, st, fs) 136 + } 137 + 138 + func (s *ServerInfoSuite) TestUpdateServerInfoBasic(c *C) { 139 + fs := memfs.New() 140 + st := memory.NewStorage() 141 + r, err := git.Clone(st, fs, &git.CloneOptions{ 142 + URL: fixtures.Basic().One().URL, 143 + }) 144 + c.Assert(err, IsNil) 145 + c.Assert(r, NotNil) 146 + 147 + err = UpdateServerInfo(st, fs) 148 + c.Assert(err, IsNil) 149 + 150 + assertInfoRefs(c, st, fs) 151 + assertObjectPacks(c, st, fs) 152 + } 153 + 154 + func (s *ServerInfoSuite) TestUpdateServerInfoBasicChange(c *C) { 155 + fs := memfs.New() 156 + st := memory.NewStorage() 157 + r, err := git.Clone(st, fs, &git.CloneOptions{ 158 + URL: fixtures.Basic().One().URL, 159 + }) 160 + c.Assert(err, IsNil) 161 + c.Assert(r, NotNil) 162 + 163 + err = UpdateServerInfo(st, fs) 164 + c.Assert(err, IsNil) 165 + 166 + assertInfoRefs(c, st, fs) 167 + assertObjectPacks(c, st, fs) 168 + 169 + head, err := r.Head() 170 + c.Assert(err, IsNil) 171 + 172 + ref := plumbing.NewHashReference("refs/heads/my-branch", head.Hash()) 173 + err = r.Storer.SetReference(ref) 174 + c.Assert(err, IsNil) 175 + 176 + _, err = r.CreateTag("test-tag", head.Hash(), &git.CreateTagOptions{ 177 + Message: "test-tag", 178 + }) 179 + c.Assert(err, IsNil) 180 + 181 + err = UpdateServerInfo(st, fs) 182 + 183 + assertInfoRefs(c, st, fs) 184 + assertObjectPacks(c, st, fs) 185 + }