-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathpatch_file_operations.go
More file actions
133 lines (117 loc) · 3.75 KB
/
patch_file_operations.go
File metadata and controls
133 lines (117 loc) · 3.75 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
package git_diff_parser
import "fmt"
type PatchOperationType string
const (
PatchOperationTypeModify PatchOperationType = "modify"
PatchOperationTypeCreate PatchOperationType = "create"
PatchOperationTypeDelete PatchOperationType = "delete"
PatchOperationTypeRename PatchOperationType = "rename"
PatchOperationTypeCopy PatchOperationType = "copy"
PatchOperationTypeModeChange PatchOperationType = "mode_change"
PatchOperationTypeBinary PatchOperationType = "binary"
)
// PatchOperation describes one file-level operation in a git patchset.
//
// Values returned by ParsePatchOperations can be passed to ApplyPatchOperations.
type PatchOperation struct {
Type PatchOperationType
SourcePath string
TargetPath string
OldMode string
NewMode string
IndexMode string
IsBinary bool
Patch []byte
file *patchsetFile
}
// MutatesFileSet reports whether this operation adds, removes, or moves a file.
func (op *PatchOperation) MutatesFileSet() bool {
switch op.Type {
case PatchOperationTypeCreate, PatchOperationTypeDelete, PatchOperationTypeRename, PatchOperationTypeCopy:
return true
default:
return false
}
}
// ParsePatchOperations parses patchData into ordered file-level operations.
func ParsePatchOperations(patchData []byte) ([]PatchOperation, error) {
patchset, errs := parsePatchset(patchData)
if len(errs) > 0 {
return nil, fmt.Errorf("unsupported patch syntax: %w", errs[0])
}
operations := make([]PatchOperation, 0, len(patchset.Files))
for i := range patchset.Files {
operation, err := patchOperationFromFile(&patchset.Files[i])
if err != nil {
return nil, err
}
operations = append(operations, operation)
}
return operations, nil
}
// ApplyPatchOperations applies ordered patch operations to a copy of tree.
func ApplyPatchOperations(tree map[string][]byte, operations []PatchOperation) (map[string][]byte, error) {
files, err := patchsetFilesFromOperations(operations)
if err != nil {
return nil, err
}
return applyPatchsetFiles(tree, files)
}
func patchsetFilesFromOperations(operations []PatchOperation) ([]*patchsetFile, error) {
files := make([]*patchsetFile, 0, len(operations))
for i := range operations {
file, err := operations[i].patchsetFile()
if err != nil {
return nil, err
}
files = append(files, file)
}
return files, nil
}
func patchOperationFromFile(file *patchsetFile) (PatchOperation, error) {
return PatchOperation{
Type: publicPatchOperationType(file.Operation),
SourcePath: file.SourcePath,
TargetPath: file.TargetPath,
OldMode: file.Diff.OldMode,
NewMode: file.Diff.NewMode,
IndexMode: file.Diff.IndexMode,
IsBinary: file.Diff.IsBinary,
Patch: append([]byte(nil), file.Patch...),
file: file,
}, nil
}
func publicPatchOperationType(op patchsetOperation) PatchOperationType {
switch op {
case patchsetOperationCreate:
return PatchOperationTypeCreate
case patchsetOperationDelete:
return PatchOperationTypeDelete
case patchsetOperationRename:
return PatchOperationTypeRename
case patchsetOperationCopy:
return PatchOperationTypeCopy
case patchsetOperationModeChange:
return PatchOperationTypeModeChange
case patchsetOperationBinary:
return PatchOperationTypeBinary
default:
return PatchOperationTypeModify
}
}
func (op *PatchOperation) patchsetFile() (*patchsetFile, error) {
if op.file != nil {
return op.file, nil
}
if len(op.Patch) > 0 {
patchset, errs := parsePatchset(op.Patch)
if len(errs) > 0 {
return nil, fmt.Errorf("unsupported patch syntax: %w", errs[0])
}
if len(patchset.Files) != 1 {
return nil, fmt.Errorf("patch operation contains %d file diffs, expected 1", len(patchset.Files))
}
return &patchset.Files[0], nil
}
return nil, fmt.Errorf("patch operation has no patch data")
}