fork of go-git with some jj specific features
1package dotgit
2
3import (
4 "fmt"
5 "os"
6
7 "github.com/go-git/go-git/v5/plumbing"
8 "github.com/go-git/go-git/v5/utils/ioutil"
9
10 "github.com/go-git/go-billy/v5"
11)
12
13func (d *DotGit) setRef(fileName, content string, old *plumbing.Reference) (err error) {
14 if billy.CapabilityCheck(d.fs, billy.ReadAndWriteCapability) {
15 return d.setRefRwfs(fileName, content, old)
16 }
17
18 return d.setRefNorwfs(fileName, content, old)
19}
20
21func (d *DotGit) setRefRwfs(fileName, content string, old *plumbing.Reference) (err error) {
22 // If we are not checking an old ref, just truncate the file.
23 mode := os.O_RDWR | os.O_CREATE
24 if old == nil {
25 mode |= os.O_TRUNC
26 }
27
28 f, err := d.fs.OpenFile(fileName, mode, 0666)
29 if err != nil {
30 return err
31 }
32
33 defer ioutil.CheckClose(f, &err)
34
35 // Lock is unlocked by the deferred Close above. This is because Unlock
36 // does not imply a fsync and thus there would be a race between
37 // Unlock+Close and other concurrent writers. Adding Sync to go-billy
38 // could work, but this is better (and avoids superfluous syncs).
39 err = f.Lock()
40 if err != nil {
41 return err
42 }
43
44 // this is a no-op to call even when old is nil.
45 err = d.checkReferenceAndTruncate(f, old)
46 if err != nil {
47 return err
48 }
49
50 _, err = f.Write([]byte(content))
51 return err
52}
53
54// There are some filesystems that don't support opening files in RDWD mode.
55// In these filesystems the standard SetRef function can not be used as it
56// reads the reference file to check that it's not modified before updating it.
57//
58// This version of the function writes the reference without extra checks
59// making it compatible with these simple filesystems. This is usually not
60// a problem as they should be accessed by only one process at a time.
61func (d *DotGit) setRefNorwfs(fileName, content string, old *plumbing.Reference) error {
62 _, err := d.fs.Stat(fileName)
63 if err == nil && old != nil {
64 fRead, err := d.fs.Open(fileName)
65 if err != nil {
66 return err
67 }
68
69 ref, err := d.readReferenceFrom(fRead, old.Name().String())
70 fRead.Close()
71
72 if err != nil {
73 return err
74 }
75
76 if ref.Hash() != old.Hash() {
77 return fmt.Errorf("reference has changed concurrently")
78 }
79 }
80
81 f, err := d.fs.Create(fileName)
82 if err != nil {
83 return err
84 }
85
86 defer f.Close()
87
88 _, err = f.Write([]byte(content))
89 return err
90}