@@ -5,13 +5,24 @@ import (
5
5
"io"
6
6
"os"
7
7
"path"
8
+ "regexp"
9
+ "strconv"
10
+ "strings"
8
11
9
12
"github.com/git-lfs/gitobj/pack"
10
13
"github.com/git-lfs/gitobj/storage"
11
14
)
12
15
13
16
// NewFilesystemBackend initializes a new filesystem-based backend.
14
17
func NewFilesystemBackend (root , tmp string ) (storage.Backend , error ) {
18
+ return NewFilesystemBackendWithAlternates (root , tmp , "" )
19
+ }
20
+
21
+ // NewFilesystemBackendWithAlternates initializes a new filesystem-based
22
+ // backend, optionally with additional alternates as specified in the
23
+ // `alternates` variable. The syntax is that of the Git environment variable
24
+ // GIT_ALTERNATE_OBJECT_DIRECTORIES.
25
+ func NewFilesystemBackendWithAlternates (root , tmp , alternates string ) (storage.Backend , error ) {
15
26
fsobj := newFileStorer (root , tmp )
16
27
packs , err := pack .NewStorage (root )
17
28
if err != nil {
@@ -23,6 +34,11 @@ func NewFilesystemBackend(root, tmp string) (storage.Backend, error) {
23
34
return nil , err
24
35
}
25
36
37
+ storage , err = addAlternatesFromEnvironment (storage , alternates )
38
+ if err != nil {
39
+ return nil , err
40
+ }
41
+
26
42
return & filesystemBackend {
27
43
fs : fsobj ,
28
44
backends : storage ,
@@ -68,6 +84,66 @@ func addAlternateDirectory(s []storage.Storage, dir string) ([]storage.Storage,
68
84
return s , nil
69
85
}
70
86
87
+ func addAlternatesFromEnvironment (s []storage.Storage , env string ) ([]storage.Storage , error ) {
88
+ if len (env ) == 0 {
89
+ return s , nil
90
+ }
91
+
92
+ for _ , dir := range splitAlternateString (env , alternatesSeparator ) {
93
+ var err error
94
+ s , err = addAlternateDirectory (s , dir )
95
+ if err != nil {
96
+ return nil , err
97
+ }
98
+ }
99
+ return s , nil
100
+ }
101
+
102
+ var (
103
+ octalEscape = regexp .MustCompile ("\\ \\ [0-7]{1,3}" )
104
+ hexEscape = regexp .MustCompile ("\\ \\ x[0-9a-fA-F]{2}" )
105
+ replacements = []struct {
106
+ olds string
107
+ news string
108
+ }{
109
+ {`\a` , "\a " },
110
+ {`\b` , "\b " },
111
+ {`\t` , "\t " },
112
+ {`\n` , "\n " },
113
+ {`\v` , "\v " },
114
+ {`\f` , "\f " },
115
+ {`\r` , "\r " },
116
+ {`\\` , "\\ " },
117
+ {`\"` , "\" " },
118
+ {`\'` , "'" },
119
+ }
120
+ )
121
+
122
+ func splitAlternateString (env string , separator string ) []string {
123
+ dirs := strings .Split (env , separator )
124
+ for i , s := range dirs {
125
+ if ! strings .HasPrefix (s , `"` ) || ! strings .HasSuffix (s , `"` ) {
126
+ continue
127
+ }
128
+
129
+ // Strip leading and trailing quotation marks
130
+ s = s [1 : len (s )- 1 ]
131
+ for _ , repl := range replacements {
132
+ s = strings .Replace (s , repl .olds , repl .news , - 1 )
133
+ }
134
+ s = octalEscape .ReplaceAllStringFunc (s , func (inp string ) string {
135
+ val , _ := strconv .ParseUint (inp [1 :], 8 , 64 )
136
+ return string ([]byte {byte (val )})
137
+ })
138
+ s = hexEscape .ReplaceAllStringFunc (s , func (inp string ) string {
139
+ val , _ := strconv .ParseUint (inp [2 :], 16 , 64 )
140
+ return string ([]byte {byte (val )})
141
+ })
142
+ dirs [i ] = s
143
+ }
144
+ return dirs
145
+ }
146
+
71
147
// NewMemoryBackend initializes a new memory-based backend.
72
148
//
73
149
// A value of "nil" is acceptable and indicates that no entries should be added
0 commit comments