Skip to content
This repository was archived by the owner on Sep 11, 2020. It is now read-only.

Commit 59335b6

Browse files
authored
Merge pull request #297 from alcortesm/issue274
issue #274: new filemode package
2 parents d105e15 + da704cf commit 59335b6

16 files changed

+784
-283
lines changed

plumbing/filemode/filemode.go

+188
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
package filemode
2+
3+
import (
4+
"encoding/binary"
5+
"fmt"
6+
"os"
7+
"strconv"
8+
)
9+
10+
// A FileMode represents the kind of tree entries used by git. It
11+
// resembles regular file systems modes, although FileModes are
12+
// considerably simpler (there are not so many), and there are some,
13+
// like Submodule that has no file system equivalent.
14+
type FileMode uint32
15+
16+
const (
17+
// Empty is used as the FileMode of tree elements when comparing
18+
// trees in the following situations:
19+
//
20+
// - the mode of tree elements before their creation. - the mode of
21+
// tree elements after their deletion. - the mode of unmerged
22+
// elements when checking the index.
23+
//
24+
// Empty has no file system equivalent. As Empty is the zero value
25+
// of FileMode, it is also returned by New and
26+
// NewFromOsNewFromOSFileMode along with an error, when they fail.
27+
Empty FileMode = 0
28+
// Dir represent a Directory.
29+
Dir FileMode = 0040000
30+
// Regular represent non-executable files. Please note this is not
31+
// the same as golang regular files, which include executable files.
32+
Regular FileMode = 0100644
33+
// Deprecated represent non-executable files with the group writable
34+
// bit set. This mode was supported by the first versions of git,
35+
// but it has been deprecatred nowadays. This library uses them
36+
// internally, so you can read old packfiles, but will treat them as
37+
// Regulars when interfacing with the outside world. This is the
38+
// standard git behaviuor.
39+
Deprecated FileMode = 0100664
40+
// Executable represents executable files.
41+
Executable FileMode = 0100755
42+
// Symlink represents symbolic links to files.
43+
Symlink FileMode = 0120000
44+
// Submodule represents git submodules. This mode has no file system
45+
// equivalent.
46+
Submodule FileMode = 0160000
47+
)
48+
49+
// New takes the octal string representation of a FileMode and returns
50+
// the FileMode and a nil error. If the string can not be parsed to a
51+
// 32 bit unsigned octal number, it returns Empty and the parsing error.
52+
//
53+
// Example: "40000" means Dir, "100644" means Regular.
54+
//
55+
// Please note this function does not check if the returned FileMode
56+
// is valid in git or if it is malformed. For instance, "1" will
57+
// return the malformed FileMode(1) and a nil error.
58+
func New(s string) (FileMode, error) {
59+
n, err := strconv.ParseUint(s, 8, 32)
60+
if err != nil {
61+
return Empty, err
62+
}
63+
64+
return FileMode(n), nil
65+
}
66+
67+
// NewFromOSFileMode returns the FileMode used by git to represent
68+
// the provided file system modes and a nil error on success. If the
69+
// file system mode cannot be mapped to any valid git mode (as with
70+
// sockets or named pipes), it will return Empty and an error.
71+
//
72+
// Note that some git modes cannot be generated from os.FileModes, like
73+
// Deprecated and Submodule; while Empty will be returned, along with an
74+
// error, only when the method fails.
75+
func NewFromOSFileMode(m os.FileMode) (FileMode, error) {
76+
if m.IsRegular() {
77+
if isSetTemporary(m) {
78+
return Empty, fmt.Errorf("no equivalent git mode for %s", m)
79+
}
80+
if isSetCharDevice(m) {
81+
return Empty, fmt.Errorf("no equivalent git mode for %s", m)
82+
}
83+
if isSetUserExecutable(m) {
84+
return Executable, nil
85+
}
86+
return Regular, nil
87+
}
88+
89+
if m.IsDir() {
90+
return Dir, nil
91+
}
92+
93+
if isSetSymLink(m) {
94+
return Symlink, nil
95+
}
96+
97+
return Empty, fmt.Errorf("no equivalent git mode for %s", m)
98+
}
99+
100+
func isSetCharDevice(m os.FileMode) bool {
101+
return m&os.ModeCharDevice != 0
102+
}
103+
104+
func isSetTemporary(m os.FileMode) bool {
105+
return m&os.ModeTemporary != 0
106+
}
107+
108+
func isSetUserExecutable(m os.FileMode) bool {
109+
return m&0100 != 0
110+
}
111+
112+
func isSetSymLink(m os.FileMode) bool {
113+
return m&os.ModeSymlink != 0
114+
}
115+
116+
// Bytes return a slice of 4 bytes with the mode in little endian
117+
// encoding.
118+
func (m FileMode) Bytes() []byte {
119+
ret := make([]byte, 4)
120+
binary.LittleEndian.PutUint32(ret, uint32(m))
121+
return ret[:]
122+
}
123+
124+
// IsMalformed returns if the FileMode should not appear in a git packfile,
125+
// this is: Empty and any other mode not mentioned as a constant in this
126+
// package.
127+
func (m FileMode) IsMalformed() bool {
128+
return m != Dir &&
129+
m != Regular &&
130+
m != Deprecated &&
131+
m != Executable &&
132+
m != Symlink &&
133+
m != Submodule
134+
}
135+
136+
// String returns the FileMode as a string in the standatd git format,
137+
// this is, an octal number padded with ceros to 7 digits. Malformed
138+
// modes are printed in that same format, for easier debugging.
139+
//
140+
// Example: Regular is "0100644", Empty is "0000000".
141+
func (m FileMode) String() string {
142+
return fmt.Sprintf("%07o", uint32(m))
143+
}
144+
145+
// IsRegular returns if the FileMode represents that of a regular file,
146+
// this is, either Regular or Deprecated. Please note that Executable
147+
// are not regular even though in the UNIX tradition, they usually are:
148+
// See the IsFile method.
149+
func (m FileMode) IsRegular() bool {
150+
return m == Regular ||
151+
m == Deprecated
152+
}
153+
154+
// IsFile returns if the FileMode represents that of a file, this is,
155+
// Regular, Deprecated, Excutable or Link.
156+
func (m FileMode) IsFile() bool {
157+
return m == Regular ||
158+
m == Deprecated ||
159+
m == Executable ||
160+
m == Symlink
161+
}
162+
163+
// ToOSFileMode returns the os.FileMode to be used when creating file
164+
// system elements with the given git mode and a nil error on success.
165+
//
166+
// When the provided mode cannot be mapped to a valid file system mode
167+
// (e.g. Submodule) it returns os.FileMode(0) and an error.
168+
//
169+
// The returned file mode does not take into account the umask.
170+
func (m FileMode) ToOSFileMode() (os.FileMode, error) {
171+
switch m {
172+
case Dir:
173+
return os.ModePerm | os.ModeDir, nil
174+
case Submodule:
175+
return os.ModePerm | os.ModeDir, nil
176+
case Regular:
177+
return os.FileMode(0644), nil
178+
// Deprecated is no longer allowed: treated as a Regular instead
179+
case Deprecated:
180+
return os.FileMode(0644), nil
181+
case Executable:
182+
return os.FileMode(0755), nil
183+
case Symlink:
184+
return os.ModePerm | os.ModeSymlink, nil
185+
}
186+
187+
return os.FileMode(0), fmt.Errorf("malformed mode (%s)", m)
188+
}

0 commit comments

Comments
 (0)