-
Notifications
You must be signed in to change notification settings - Fork 1
/
LCRepository.c
197 lines (172 loc) · 6.72 KB
/
LCRepository.c
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
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
#include "LCRepository.h"
typedef struct repositoryData* repositoryDataRef;
static void* repositoryInitData();
struct repositoryData {
LCCommitRef head;
};
void repositoryDealloc(LCObjectRef object);
void storeDataWithSHAs(LCRepositoryRef store, LCKeyValueRef addPaths[], size_t length, LCKeyValueRef pathSHABuffer[]);
void repositorySetHead(LCRepositoryRef store, LCCommitRef newHead);
struct LCType typeRepository = {
.name = "LCRepository",
.immutable = false,
.initData = repositoryInitData,
.dealloc = repositoryDealloc
};
LCTypeRef LCTypeRepository = &typeRepository;
static void* repositoryInitData() {
repositoryDataRef data = malloc(sizeof(struct repositoryData));
if (data) {
data->head = NULL;
}
return data;
}
LCRepositoryRef LCRepositoryCreate(LCCommitRef head) {
repositoryDataRef data = repositoryInitData();
if (head) {
data->head = objectRetain(head);
}
return objectCreate(LCTypeRepository, data);
};
void LCRepositoryCommit(LCRepositoryRef store, LCStageRef stage) {
LCCommitRef head = LCRepositoryHead(store);
LCTreeRef oldTree = NULL;
if (head) {
oldTree = LCCommitTree(head);
}
LCTreeRef newTree = LCTreeCreateTreeUpdatingData(oldTree, LCStageUpdates(stage), LCStageUpdatesLength(stage));
LCCommitRef newHead;
if (head) {
newHead = LCCommitCreate(newTree, &head, 1);
} else {
newHead = LCCommitCreate(newTree, NULL, 0);
}
repositorySetHead(store, newHead);
objectRelease(newHead);
}
LCCommitRef LCRepositoryHead(LCRepositoryRef store) {
repositoryDataRef data = objectData(store);
return data->head;
}
LCObjectRef LCRepositoryData(LCRepositoryRef store, LCCommitRef commit, char* path) {
if (!commit) {
commit = LCRepositoryHead(store);
}
LCStringRef pathObj = LCStringCreate(path);
LCArrayRef pathArray = createPathArray(pathObj);
LCObjectRef data = LCTreeChildAtPath(LCCommitTree(commit), pathArray);
objectRelease(pathArray);
objectRelease(pathObj);
return data;
}
void LCRepositoryPersist(LCRepositoryRef repo, LCContextRef context) {
objectStore(LCRepositoryHead(repo), context);
}
void LCRepositoryDeleteCache(LCRepositoryRef repo, LCContextRef context) {
objectDeleteCache(LCRepositoryHead(repo), context);
}
static LCStageRef repositoryCreateMergedStage(LCCommitRef base, LCCommitRef local, LCCommitRef foreign,
void *cookie, createResolvedData strategy) {
LCArrayRef diffLocal = LCCommitDiff(base, local);
LCArrayRef diffForeign = LCCommitDiff(base, foreign);
LCMutableDictionaryRef diffMerged = LCMutableDictionaryCreate(LCArrayObjects(diffLocal), LCArrayLength(diffLocal));
LCKeyValueRef *diffForeignRef = LCArrayObjects(diffForeign);
for (LCInteger i=0; i<LCArrayLength(diffForeign); i++) {
LCStringRef path = LCKeyValueKey(diffForeignRef[i]);
LCObjectRef valueLocal = LCMutableDictionaryValueForKey(diffMerged, path);
if (valueLocal) {
LCObjectRef valueForeign = LCKeyValueValue(diffForeignRef[i]);
LCArrayRef resolved = strategy(cookie, path, valueLocal, valueForeign);
LCMutableDictionaryAddEntries(diffMerged, LCArrayObjects(resolved), LCArrayLength(resolved));
objectRelease(resolved);
} else {
LCMutableDictionaryAddEntry(diffMerged, diffForeignRef[i]);
}
}
LCStageRef mergeStage = LCStageCreate();
LCStageAddKeyValues(mergeStage, LCMutableDictionaryEntries(diffMerged), LCMutableDictionaryLength(diffMerged));
objectRelease(diffMerged);
objectRelease(diffLocal);
objectRelease(diffForeign);
return mergeStage;
}
void LCRepositoryMerge(LCRepositoryRef localRepo, LCRepositoryRef foreignRepo, void *cookie, createResolvedData conflictStrategy) {
LCCommitRef localHead = LCRepositoryHead(localRepo);
LCCommitRef foreignHead = LCRepositoryHead(foreignRepo);
if (!localHead) {
repositorySetHead(localRepo, foreignHead);
return;
}
if (!foreignHead) {
return;
}
LCCommitRef heads[] = {localHead, foreignHead};
LCCommitRef commonCommit = LCCommitFindCommonParent(heads, 2);
if (objectHashEqual(foreignHead, commonCommit)) {
return;
}
if (objectHashEqual(localHead, commonCommit)) {
repositorySetHead(localRepo, foreignHead);
} else {
LCStageRef merged = repositoryCreateMergedStage(commonCommit, localHead, foreignHead, cookie, conflictStrategy);
LCTreeRef newTree = LCTreeCreateTreeUpdatingData(LCCommitTree(localHead), LCStageUpdates(merged),
LCStageUpdatesLength(merged));
LCCommitRef parents[] = {localHead, foreignHead};
LCCommitRef newHead = LCCommitCreate(newTree, parents, 2);
repositorySetHead(localRepo, newHead);
objectRelease(newHead);
objectRelease(merged);
}
}
void repositoryDealloc(LCObjectRef object) {
objectRelease(LCRepositoryHead(object));
}
void repositorySetHead(LCRepositoryRef store, LCCommitRef newHead) {
repositoryDataRef data = objectData(store);
if (newHead != data->head) {
objectRelease(data->head);
}
data->head = objectRetain(newHead);
}
static LCTypeRef livelyStoreStringToType(char *typeString) {
LCTypeRef types[] = {LCTypeRepository, LCTypeStage, LCTypeCommit, LCTypeTree};
for (LCInteger i=0; i<4; i++) {
if (strcmp(typeString, typeName(types[i]))==0) {
return types[i];
}
}
return NULL;
}
LCContextRef createRepositoryContext(LCStoreRef store) {
stringToType funs[] = {livelyStoreStringToType, coreStringToType};
return contextCreate(store, funs, 2);
}
LCArrayRef conflictStrategyKeepLocal(void *cookie, LCStringRef path, LCObjectRef localData, LCObjectRef foreignData) {
LCKeyValueRef resolved = LCKeyValueCreate(path, localData);
LCArrayRef array = LCArrayCreate(&resolved, 1);
objectRelease(resolved);
return array;
}
LCArrayRef conflictStrategyKeepForeign(void *cookie, LCStringRef path, LCObjectRef localData, LCObjectRef foreignData) {
LCKeyValueRef resolved = LCKeyValueCreate(path, foreignData);
LCArrayRef array = LCArrayCreate(&resolved, 1);
objectRelease(resolved);
return array;
}
LCArrayRef conflictStrategyKeepBoth(void *cookie, LCStringRef path, LCObjectRef localData, LCObjectRef foreignData) {
char foreignHash[HASH_LENGTH];
objectHash(foreignData, foreignHash);
char conflictPath[LCStringLength(path)+1+HASH_LENGTH+1];
strcpy(conflictPath, LCStringChars(path));
strcat(conflictPath, "_");
strcat(conflictPath, foreignHash);
LCStringRef conflictPathObj = LCStringCreate(conflictPath);
LCKeyValueRef conflictKeyValue = LCKeyValueCreate(conflictPathObj, foreignData);
LCKeyValueRef localKeyValue = LCKeyValueCreate(path, localData);
LCKeyValueRef both[] = {localKeyValue, conflictKeyValue};
LCArrayRef array = LCArrayCreate(both, 2);
objectRelease(conflictPathObj);
objectRelease(conflictKeyValue);
objectRelease(localKeyValue);
return array;
}