Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/editor/version_control/editor_vcs_interface.cpp
9903 views
1
/**************************************************************************/
2
/* editor_vcs_interface.cpp */
3
/**************************************************************************/
4
/* This file is part of: */
5
/* GODOT ENGINE */
6
/* https://godotengine.org */
7
/**************************************************************************/
8
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
9
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
10
/* */
11
/* Permission is hereby granted, free of charge, to any person obtaining */
12
/* a copy of this software and associated documentation files (the */
13
/* "Software"), to deal in the Software without restriction, including */
14
/* without limitation the rights to use, copy, modify, merge, publish, */
15
/* distribute, sublicense, and/or sell copies of the Software, and to */
16
/* permit persons to whom the Software is furnished to do so, subject to */
17
/* the following conditions: */
18
/* */
19
/* The above copyright notice and this permission notice shall be */
20
/* included in all copies or substantial portions of the Software. */
21
/* */
22
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
23
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
24
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
25
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
26
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
27
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
28
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
29
/**************************************************************************/
30
31
#include "editor_vcs_interface.h"
32
33
#include "editor/editor_node.h"
34
35
EditorVCSInterface *EditorVCSInterface::singleton = nullptr;
36
37
void EditorVCSInterface::popup_error(const String &p_msg) {
38
// TRANSLATORS: %s refers to the name of a version control system (e.g. "Git").
39
EditorNode::get_singleton()->show_warning(p_msg.strip_edges(), vformat(TTR("%s Error"), get_vcs_name()));
40
}
41
42
bool EditorVCSInterface::initialize(const String &p_project_path) {
43
bool result = false;
44
GDVIRTUAL_CALL(_initialize, p_project_path, result);
45
return result;
46
}
47
48
void EditorVCSInterface::set_credentials(const String &p_username, const String &p_password, const String &p_ssh_public_key, const String &p_ssh_private_key, const String &p_ssh_passphrase) {
49
GDVIRTUAL_CALL(_set_credentials, p_username, p_password, p_ssh_public_key, p_ssh_private_key, p_ssh_passphrase);
50
}
51
52
List<String> EditorVCSInterface::get_remotes() {
53
TypedArray<String> result;
54
if (!GDVIRTUAL_CALL(_get_remotes, result)) {
55
return {};
56
}
57
58
List<String> remotes;
59
for (int i = 0; i < result.size(); i++) {
60
remotes.push_back(result[i]);
61
}
62
return remotes;
63
}
64
65
List<EditorVCSInterface::StatusFile> EditorVCSInterface::get_modified_files_data() {
66
TypedArray<Dictionary> result;
67
if (!GDVIRTUAL_CALL(_get_modified_files_data, result)) {
68
return {};
69
}
70
71
List<EditorVCSInterface::StatusFile> status_files;
72
for (int i = 0; i < result.size(); i++) {
73
status_files.push_back(_convert_status_file(result[i]));
74
}
75
return status_files;
76
}
77
78
void EditorVCSInterface::stage_file(const String &p_file_path) {
79
GDVIRTUAL_CALL(_stage_file, p_file_path);
80
}
81
82
void EditorVCSInterface::unstage_file(const String &p_file_path) {
83
GDVIRTUAL_CALL(_unstage_file, p_file_path);
84
}
85
86
void EditorVCSInterface::discard_file(const String &p_file_path) {
87
GDVIRTUAL_CALL(_discard_file, p_file_path);
88
}
89
90
void EditorVCSInterface::commit(const String &p_msg) {
91
GDVIRTUAL_CALL(_commit, p_msg);
92
}
93
94
List<EditorVCSInterface::DiffFile> EditorVCSInterface::get_diff(const String &p_identifier, TreeArea p_area) {
95
TypedArray<Dictionary> result;
96
if (!GDVIRTUAL_CALL(_get_diff, p_identifier, int(p_area), result)) {
97
return {};
98
}
99
100
List<DiffFile> diff_files;
101
for (int i = 0; i < result.size(); i++) {
102
diff_files.push_back(_convert_diff_file(result[i]));
103
}
104
return diff_files;
105
}
106
107
List<EditorVCSInterface::Commit> EditorVCSInterface::get_previous_commits(int p_max_commits) {
108
TypedArray<Dictionary> result;
109
if (!GDVIRTUAL_CALL(_get_previous_commits, p_max_commits, result)) {
110
return {};
111
}
112
113
List<EditorVCSInterface::Commit> commits;
114
for (int i = 0; i < result.size(); i++) {
115
commits.push_back(_convert_commit(result[i]));
116
}
117
return commits;
118
}
119
120
List<String> EditorVCSInterface::get_branch_list() {
121
TypedArray<String> result;
122
if (!GDVIRTUAL_CALL(_get_branch_list, result)) {
123
return {};
124
}
125
126
List<String> branch_list;
127
for (int i = 0; i < result.size(); i++) {
128
branch_list.push_back(result[i]);
129
}
130
return branch_list;
131
}
132
133
void EditorVCSInterface::create_branch(const String &p_branch_name) {
134
GDVIRTUAL_CALL(_create_branch, p_branch_name);
135
}
136
137
void EditorVCSInterface::create_remote(const String &p_remote_name, const String &p_remote_url) {
138
GDVIRTUAL_CALL(_create_remote, p_remote_name, p_remote_url);
139
}
140
141
void EditorVCSInterface::remove_branch(const String &p_branch_name) {
142
GDVIRTUAL_CALL(_remove_branch, p_branch_name);
143
}
144
145
void EditorVCSInterface::remove_remote(const String &p_remote_name) {
146
GDVIRTUAL_CALL(_remove_remote, p_remote_name);
147
}
148
149
String EditorVCSInterface::get_current_branch_name() {
150
String result;
151
GDVIRTUAL_CALL(_get_current_branch_name, result);
152
return result;
153
}
154
155
bool EditorVCSInterface::checkout_branch(const String &p_branch_name) {
156
bool result = false;
157
GDVIRTUAL_CALL(_checkout_branch, p_branch_name, result);
158
return result;
159
}
160
161
void EditorVCSInterface::pull(const String &p_remote) {
162
GDVIRTUAL_CALL(_pull, p_remote);
163
}
164
165
void EditorVCSInterface::push(const String &p_remote, bool p_force) {
166
GDVIRTUAL_CALL(_push, p_remote, p_force);
167
}
168
169
void EditorVCSInterface::fetch(const String &p_remote) {
170
GDVIRTUAL_CALL(_fetch, p_remote);
171
}
172
173
List<EditorVCSInterface::DiffHunk> EditorVCSInterface::get_line_diff(const String &p_file_path, const String &p_text) {
174
TypedArray<Dictionary> result;
175
if (!GDVIRTUAL_CALL(_get_line_diff, p_file_path, p_text, result)) {
176
return {};
177
}
178
179
List<DiffHunk> diff_hunks;
180
for (int i = 0; i < result.size(); i++) {
181
diff_hunks.push_back(_convert_diff_hunk(result[i]));
182
}
183
return diff_hunks;
184
}
185
186
bool EditorVCSInterface::shut_down() {
187
bool result = false;
188
GDVIRTUAL_CALL(_shut_down, result);
189
return result;
190
}
191
192
String EditorVCSInterface::get_vcs_name() {
193
String result;
194
GDVIRTUAL_CALL(_get_vcs_name, result);
195
return result;
196
}
197
198
Dictionary EditorVCSInterface::create_diff_line(int p_new_line_no, int p_old_line_no, const String &p_content, const String &p_status) {
199
Dictionary diff_line;
200
diff_line["new_line_no"] = p_new_line_no;
201
diff_line["old_line_no"] = p_old_line_no;
202
diff_line["content"] = p_content;
203
diff_line["status"] = p_status;
204
205
return diff_line;
206
}
207
208
Dictionary EditorVCSInterface::create_diff_hunk(int p_old_start, int p_new_start, int p_old_lines, int p_new_lines) {
209
Dictionary diff_hunk;
210
diff_hunk["new_lines"] = p_new_lines;
211
diff_hunk["old_lines"] = p_old_lines;
212
diff_hunk["new_start"] = p_new_start;
213
diff_hunk["old_start"] = p_old_start;
214
diff_hunk["diff_lines"] = TypedArray<Dictionary>();
215
return diff_hunk;
216
}
217
218
Dictionary EditorVCSInterface::add_line_diffs_into_diff_hunk(Dictionary p_diff_hunk, TypedArray<Dictionary> p_line_diffs) {
219
p_diff_hunk["diff_lines"] = p_line_diffs;
220
return p_diff_hunk;
221
}
222
223
Dictionary EditorVCSInterface::create_diff_file(const String &p_new_file, const String &p_old_file) {
224
Dictionary file_diff;
225
file_diff["new_file"] = p_new_file;
226
file_diff["old_file"] = p_old_file;
227
file_diff["diff_hunks"] = TypedArray<Dictionary>();
228
return file_diff;
229
}
230
231
Dictionary EditorVCSInterface::create_commit(const String &p_msg, const String &p_author, const String &p_id, int64_t p_unix_timestamp, int64_t p_offset_minutes) {
232
Dictionary commit_info;
233
commit_info["message"] = p_msg;
234
commit_info["author"] = p_author;
235
commit_info["unix_timestamp"] = p_unix_timestamp;
236
commit_info["offset_minutes"] = p_offset_minutes;
237
commit_info["id"] = p_id;
238
return commit_info;
239
}
240
241
Dictionary EditorVCSInterface::add_diff_hunks_into_diff_file(Dictionary p_diff_file, TypedArray<Dictionary> p_diff_hunks) {
242
p_diff_file["diff_hunks"] = p_diff_hunks;
243
return p_diff_file;
244
}
245
246
Dictionary EditorVCSInterface::create_status_file(const String &p_file_path, ChangeType p_change, TreeArea p_area) {
247
Dictionary sf;
248
sf["file_path"] = p_file_path;
249
sf["change_type"] = p_change;
250
sf["area"] = p_area;
251
return sf;
252
}
253
254
EditorVCSInterface::DiffLine EditorVCSInterface::_convert_diff_line(const Dictionary &p_diff_line) {
255
DiffLine d;
256
d.new_line_no = p_diff_line["new_line_no"];
257
d.old_line_no = p_diff_line["old_line_no"];
258
d.content = p_diff_line["content"];
259
d.status = p_diff_line["status"];
260
return d;
261
}
262
263
EditorVCSInterface::DiffHunk EditorVCSInterface::_convert_diff_hunk(const Dictionary &p_diff_hunk) {
264
DiffHunk dh;
265
dh.new_lines = p_diff_hunk["new_lines"];
266
dh.old_lines = p_diff_hunk["old_lines"];
267
dh.new_start = p_diff_hunk["new_start"];
268
dh.old_start = p_diff_hunk["old_start"];
269
TypedArray<Dictionary> diff_lines = p_diff_hunk["diff_lines"];
270
for (int i = 0; i < diff_lines.size(); i++) {
271
DiffLine dl = _convert_diff_line(diff_lines[i]);
272
dh.diff_lines.push_back(dl);
273
}
274
return dh;
275
}
276
277
EditorVCSInterface::DiffFile EditorVCSInterface::_convert_diff_file(const Dictionary &p_diff_file) {
278
DiffFile df;
279
df.new_file = p_diff_file["new_file"];
280
df.old_file = p_diff_file["old_file"];
281
TypedArray<Dictionary> diff_hunks = p_diff_file["diff_hunks"];
282
for (int i = 0; i < diff_hunks.size(); i++) {
283
DiffHunk dh = _convert_diff_hunk(diff_hunks[i]);
284
df.diff_hunks.push_back(dh);
285
}
286
return df;
287
}
288
289
EditorVCSInterface::Commit EditorVCSInterface::_convert_commit(const Dictionary &p_commit) {
290
EditorVCSInterface::Commit c;
291
c.msg = p_commit["message"];
292
c.author = p_commit["author"];
293
c.unix_timestamp = p_commit["unix_timestamp"];
294
c.offset_minutes = p_commit["offset_minutes"];
295
c.id = p_commit["id"];
296
return c;
297
}
298
299
EditorVCSInterface::StatusFile EditorVCSInterface::_convert_status_file(const Dictionary &p_status_file) {
300
StatusFile sf;
301
sf.file_path = p_status_file["file_path"];
302
sf.change_type = (ChangeType)(int)p_status_file["change_type"];
303
sf.area = (TreeArea)(int)p_status_file["area"];
304
return sf;
305
}
306
307
void EditorVCSInterface::_bind_methods() {
308
// Proxy end points that implement the VCS specific operations that the editor demands.
309
GDVIRTUAL_BIND(_initialize, "project_path");
310
GDVIRTUAL_BIND(_set_credentials, "username", "password", "ssh_public_key_path", "ssh_private_key_path", "ssh_passphrase");
311
GDVIRTUAL_BIND(_get_modified_files_data);
312
GDVIRTUAL_BIND(_stage_file, "file_path");
313
GDVIRTUAL_BIND(_unstage_file, "file_path");
314
GDVIRTUAL_BIND(_discard_file, "file_path");
315
GDVIRTUAL_BIND(_commit, "msg");
316
GDVIRTUAL_BIND(_get_diff, "identifier", "area");
317
GDVIRTUAL_BIND(_shut_down);
318
GDVIRTUAL_BIND(_get_vcs_name);
319
GDVIRTUAL_BIND(_get_previous_commits, "max_commits");
320
GDVIRTUAL_BIND(_get_branch_list);
321
GDVIRTUAL_BIND(_get_remotes);
322
GDVIRTUAL_BIND(_create_branch, "branch_name");
323
GDVIRTUAL_BIND(_remove_branch, "branch_name");
324
GDVIRTUAL_BIND(_create_remote, "remote_name", "remote_url");
325
GDVIRTUAL_BIND(_remove_remote, "remote_name");
326
GDVIRTUAL_BIND(_get_current_branch_name);
327
GDVIRTUAL_BIND(_checkout_branch, "branch_name");
328
GDVIRTUAL_BIND(_pull, "remote");
329
GDVIRTUAL_BIND(_push, "remote", "force");
330
GDVIRTUAL_BIND(_fetch, "remote");
331
GDVIRTUAL_BIND(_get_line_diff, "file_path", "text");
332
333
ClassDB::bind_method(D_METHOD("create_diff_line", "new_line_no", "old_line_no", "content", "status"), &EditorVCSInterface::create_diff_line);
334
ClassDB::bind_method(D_METHOD("create_diff_hunk", "old_start", "new_start", "old_lines", "new_lines"), &EditorVCSInterface::create_diff_hunk);
335
ClassDB::bind_method(D_METHOD("create_diff_file", "new_file", "old_file"), &EditorVCSInterface::create_diff_file);
336
ClassDB::bind_method(D_METHOD("create_commit", "msg", "author", "id", "unix_timestamp", "offset_minutes"), &EditorVCSInterface::create_commit);
337
ClassDB::bind_method(D_METHOD("create_status_file", "file_path", "change_type", "area"), &EditorVCSInterface::create_status_file);
338
ClassDB::bind_method(D_METHOD("add_diff_hunks_into_diff_file", "diff_file", "diff_hunks"), &EditorVCSInterface::add_diff_hunks_into_diff_file);
339
ClassDB::bind_method(D_METHOD("add_line_diffs_into_diff_hunk", "diff_hunk", "line_diffs"), &EditorVCSInterface::add_line_diffs_into_diff_hunk);
340
ClassDB::bind_method(D_METHOD("popup_error", "msg"), &EditorVCSInterface::popup_error);
341
342
BIND_ENUM_CONSTANT(CHANGE_TYPE_NEW);
343
BIND_ENUM_CONSTANT(CHANGE_TYPE_MODIFIED);
344
BIND_ENUM_CONSTANT(CHANGE_TYPE_RENAMED);
345
BIND_ENUM_CONSTANT(CHANGE_TYPE_DELETED);
346
BIND_ENUM_CONSTANT(CHANGE_TYPE_TYPECHANGE);
347
BIND_ENUM_CONSTANT(CHANGE_TYPE_UNMERGED);
348
349
BIND_ENUM_CONSTANT(TREE_AREA_COMMIT);
350
BIND_ENUM_CONSTANT(TREE_AREA_STAGED);
351
BIND_ENUM_CONSTANT(TREE_AREA_UNSTAGED);
352
}
353
354
EditorVCSInterface *EditorVCSInterface::get_singleton() {
355
return singleton;
356
}
357
358
void EditorVCSInterface::set_singleton(EditorVCSInterface *p_singleton) {
359
singleton = p_singleton;
360
}
361
362
void EditorVCSInterface::create_vcs_metadata_files(VCSMetadata p_vcs_metadata_type, String &p_dir) {
363
if (p_vcs_metadata_type == VCSMetadata::GIT) {
364
Ref<FileAccess> f = FileAccess::open(p_dir.path_join(".gitignore"), FileAccess::WRITE);
365
if (f.is_null()) {
366
ERR_FAIL_MSG("Couldn't create .gitignore in project path.");
367
} else {
368
f->store_line("# Godot 4+ specific ignores");
369
f->store_line(".godot/");
370
f->store_line("/android/");
371
}
372
f = FileAccess::open(p_dir.path_join(".gitattributes"), FileAccess::WRITE);
373
if (f.is_null()) {
374
ERR_FAIL_MSG("Couldn't create .gitattributes in project path.");
375
} else {
376
f->store_line("# Normalize EOL for all files that Git considers text files.");
377
f->store_line("* text=auto eol=lf");
378
}
379
}
380
}
381
382