File indexing completed on 2024-04-14 15:50:56
0001 /** 0002 * SPDX-FileCopyrightText: (C) 2014 Narfinger <Narfinger@users.noreply.github.com> 0003 * SPDX-FileCopyrightText: (C) 2014 Gleb Baryshev <gleb.baryshev@gmail.com> 0004 * 0005 * SPDX-License-Identifier: GPL-2.0-or-later 0006 */ 0007 #include <QDateTime> 0008 #include <QDebug> 0009 #include <QDir> 0010 #include <QDirIterator> 0011 #include <QMutexLocker> 0012 0013 #include "basketscene.h" 0014 #include "gitwrapper.h" 0015 #include "settings.h" 0016 0017 #ifdef WITH_LIBGIT2 0018 0019 extern "C" { 0020 #include <git2.h> 0021 } 0022 0023 #include "global.h" 0024 0025 #define GIT_RETURN_IF_DISABLED() \ 0026 if (!Settings::versionSyncEnabled()) \ 0027 return; 0028 0029 QMutex GitWrapper::gitMutex; 0030 0031 void GitWrapper::initializeGitRepository(QString folder) 0032 { 0033 GIT_RETURN_IF_DISABLED() 0034 QMutexLocker l(&gitMutex); 0035 // this is not thread safe, we use locking elsewhere 0036 git_repository *repo = nullptr; 0037 QByteArray ba = folder.toUtf8(); 0038 0039 const char *cString = ba.data(); 0040 0041 int error = git_repository_init(&repo, cString, false); 0042 if (error < 0) { 0043 const git_error *e = giterr_last(); 0044 qDebug() << e->message; 0045 } 0046 0047 git_signature *sig = nullptr; 0048 git_index *index = nullptr; 0049 git_oid tree_id; 0050 git_oid commit_id; 0051 git_tree *tree = nullptr; 0052 0053 // no error handling at the moment 0054 git_signature_now(&sig, "AutoGit", "auto@localhost"); 0055 git_repository_index(&index, repo); 0056 git_index_write_tree(&tree_id, index); 0057 git_tree_lookup(&tree, repo, &tree_id); 0058 git_commit_create_v(&commit_id, repo, "HEAD", sig, sig, nullptr, "Initial commit", tree, 0); 0059 0060 git_signature_free(sig); 0061 git_index_free(index); 0062 git_tree_free(tree); 0063 0064 // first commit 0065 commitPattern(repo, "*", "Initial full commit"); 0066 git_repository_free(repo); 0067 } 0068 0069 void GitWrapper::commitBasketView() 0070 { 0071 GIT_RETURN_IF_DISABLED() 0072 QMutexLocker l(&gitMutex); 0073 0074 git_repository *repo = openRepository(); 0075 if (repo == nullptr) 0076 return; 0077 0078 const QDateTime gitdate = getLastCommitDate(repo); 0079 0080 const QString pathtosave = Global::savesFolder(); 0081 QDir basketdir(pathtosave + "baskets/"); 0082 bool changed = false; 0083 basketdir.setFilter(QDir::Dirs | QDir::NoDotAndDotDot); // this automatically skips baskets.xml file 0084 QDirIterator it(basketdir); 0085 while (!changed && it.hasNext()) { 0086 const QFileInfo file(it.next()); 0087 if (file.lastModified() > gitdate) 0088 changed = true; 0089 } 0090 0091 if (changed) { 0092 commitPattern(repo, "*"); 0093 } 0094 0095 git_repository_free(repo); 0096 } 0097 0098 void GitWrapper::commitCreateBasket() 0099 { 0100 GIT_RETURN_IF_DISABLED() 0101 QMutexLocker l(&gitMutex); 0102 git_repository *repo = openRepository(); 0103 if (repo == nullptr) 0104 return; 0105 0106 const QDateTime gitdate = getLastCommitDate(repo); 0107 0108 const QString basketxml = Global::savesFolder() + "baskets/baskets.xml"; 0109 const QFileInfo basketxmlinfo(basketxml); 0110 0111 if (gitdate <= basketxmlinfo.lastModified()) { 0112 git_index *index = nullptr; 0113 int error = git_repository_index(&index, repo); 0114 if (error < 0) { 0115 gitErrorHandling(); 0116 return; 0117 } 0118 // this is kind of hacky because somebody could come in between and we still have stuff open 0119 // change basket.xml 0120 const QString basketxml("baskets/baskets.xml"); 0121 QByteArray basketxmlba = basketxml.toUtf8(); 0122 char *basketxmlCString = basketxmlba.data(); 0123 error = git_index_add_bypath(index, basketxmlCString); 0124 if (error < 0) { 0125 gitErrorHandling(); 0126 return; 0127 } 0128 0129 bool result = commitIndex(repo, index); 0130 git_index_free(index); 0131 } 0132 0133 git_repository_free(repo); 0134 } 0135 0136 void GitWrapper::commitTagsXml() 0137 { 0138 GIT_RETURN_IF_DISABLED() 0139 QMutexLocker l(&gitMutex); 0140 git_repository *repo = openRepository(); 0141 if (repo == nullptr) 0142 return; 0143 0144 git_index *index = nullptr; 0145 int error = git_repository_index(&index, repo); 0146 if (error < 0) { 0147 gitErrorHandling(); 0148 return; 0149 } 0150 0151 const QString tagsxml("tags.xml"); 0152 QByteArray tagsxmlba = tagsxml.toUtf8(); 0153 char *tagsxmlCString = tagsxmlba.data(); 0154 error = git_index_add_bypath(index, tagsxmlCString); 0155 0156 bool result = commitIndex(repo, index); 0157 git_index_free(index); 0158 0159 git_repository_free(repo); 0160 } 0161 0162 void GitWrapper::commitDeleteBasket(QString basketFolderName) 0163 { 0164 GIT_RETURN_IF_DISABLED() 0165 QMutexLocker l(&gitMutex); 0166 0167 git_index *index = nullptr; 0168 git_repository *repo = openRepository(); 0169 if (repo == nullptr) 0170 return; 0171 0172 int error = git_repository_index(&index, repo); 0173 if (error < 0) { 0174 gitErrorHandling(); 0175 return; 0176 } 0177 0178 // remove the directory 0179 const QString dir("baskets/" + basketFolderName); 0180 const QByteArray dirba = dir.toUtf8(); 0181 const char *dirCString = dirba.data(); 0182 error = git_index_remove_directory(index, dirCString, 0); 0183 if (error < 0) { 0184 gitErrorHandling(); 0185 return; 0186 } 0187 0188 // change basket.xml 0189 const QString basketxml("baskets/baskets.xml"); 0190 QByteArray basketxmlba = basketxml.toUtf8(); 0191 char *basketxmlCString = basketxmlba.data(); 0192 error = git_index_add_bypath(index, basketxmlCString); 0193 if (error < 0) { 0194 gitErrorHandling(); 0195 return; 0196 } 0197 removeDeletedFromIndex(repo, index); 0198 0199 bool result = commitIndex(repo, index); 0200 0201 git_index_free(index); 0202 git_repository_free(repo); 0203 } 0204 0205 void GitWrapper::commitBasket(BasketScene *basket) 0206 { 0207 GIT_RETURN_IF_DISABLED() 0208 QMutexLocker l(&gitMutex); 0209 git_repository *repo = openRepository(); 0210 if (repo == nullptr) 0211 return; 0212 0213 const QDateTime gitdate = getLastCommitDate(repo); 0214 0215 const QString fullpath = basket->fullPath(); 0216 const QDir basketdir(fullpath); 0217 bool changed = false; 0218 QDirIterator it(basketdir); 0219 while (!changed && it.hasNext()) { 0220 const QFileInfo file(it.next()); 0221 if (file.fileName() != ".basket") { 0222 if (file.lastModified() >= gitdate) 0223 changed = true; 0224 } 0225 } 0226 0227 if (changed) { 0228 git_index *index = nullptr; 0229 0230 int error = git_repository_index(&index, repo); 0231 if (error < 0) { 0232 gitErrorHandling(); 0233 return; 0234 } 0235 0236 const QString pattern("baskets/" + basket->folderName() + '*'); 0237 0238 QByteArray patternba = pattern.toUtf8(); 0239 char *patternCString = patternba.data(); 0240 git_strarray arr = {&patternCString, 1}; 0241 error = git_index_add_all(index, &arr, 0, nullptr, nullptr); 0242 if (error < 0) { 0243 gitErrorHandling(); 0244 return; 0245 } 0246 const QString basketxml("baskets/baskets.xml"); 0247 QByteArray basketxmlba = basketxml.toUtf8(); 0248 char *basketxmlCString = basketxmlba.data(); 0249 error = git_index_add_bypath(index, basketxmlCString); 0250 if (error < 0) { 0251 gitErrorHandling(); 0252 return; 0253 } 0254 0255 removeDeletedFromIndex(repo, index); 0256 0257 bool result = commitIndex(repo, index); 0258 0259 git_index_free(index); 0260 } 0261 0262 git_repository_free(repo); 0263 } 0264 0265 bool GitWrapper::commitPattern(git_repository *repo, QString pattern, QString message) 0266 { 0267 git_index *index = nullptr; 0268 int error = git_repository_index(&index, repo); 0269 if (error < 0) { 0270 gitErrorHandling(); 0271 return false; 0272 } 0273 0274 QByteArray patternba = pattern.toUtf8(); 0275 char *patternCString = patternba.data(); 0276 git_strarray arr = {&patternCString, 1}; 0277 error = git_index_add_all(index, &arr, 0, nullptr, nullptr); 0278 if (error < 0) { 0279 gitErrorHandling(); 0280 return false; 0281 } 0282 0283 bool result = commitIndex(repo, index, message); 0284 0285 git_index_free(index); 0286 0287 return true; 0288 } 0289 0290 bool GitWrapper::commitIndex(git_repository *repo, git_index *index, QString message) 0291 { 0292 // write git index 0293 git_signature *sig = nullptr; 0294 git_oid tree_id; 0295 git_oid commit_id; 0296 git_tree *tree = nullptr; 0297 0298 int error = git_signature_now(&sig, "AutoGit", "auto@localhost"); 0299 if (error < 0) { 0300 gitErrorHandling(); 0301 return false; 0302 } 0303 0304 error = git_repository_index(&index, repo); 0305 if (error < 0) { 0306 gitErrorHandling(); 0307 return false; 0308 } 0309 0310 git_commit *commit = nullptr; /* parent */ 0311 git_oid oid_parent_commit; /* the SHA1 for last commit */ 0312 0313 error = git_reference_name_to_id(&oid_parent_commit, repo, "HEAD"); 0314 if (error < 0) { 0315 gitErrorHandling(); 0316 return false; 0317 } 0318 0319 error = git_commit_lookup(&commit, repo, &oid_parent_commit); 0320 if (error < 0) { 0321 gitErrorHandling(); 0322 return false; 0323 } 0324 0325 error = git_index_write(index); 0326 if (error < 0) { 0327 gitErrorHandling(); 0328 return false; 0329 } 0330 0331 error = git_index_write_tree(&tree_id, index); 0332 if (error < 0) { 0333 gitErrorHandling(); 0334 return false; 0335 } 0336 0337 error = git_tree_lookup(&tree, repo, &tree_id); 0338 if (error < 0) { 0339 gitErrorHandling(); 0340 return false; 0341 } 0342 0343 const git_commit *parentarray[] = {commit}; 0344 QByteArray commitmessageba = message.toUtf8(); 0345 const char *commitmessageCString = commitmessageba.data(); 0346 error = git_commit_create(&commit_id, repo, "HEAD", sig, sig, nullptr, commitmessageCString, tree, 1, parentarray); 0347 if (error < 0) { 0348 gitErrorHandling(); 0349 return false; 0350 } 0351 0352 git_signature_free(sig); 0353 git_tree_free(tree); 0354 return true; 0355 } 0356 0357 void GitWrapper::removeDeletedFromIndex(git_repository *repo, git_index *index) 0358 { 0359 git_status_foreach( 0360 repo, 0361 [](const char *path, unsigned int status_flags, void *payload) -> int { 0362 if (status_flags & GIT_STATUS_WT_DELETED) { 0363 git_index *index = static_cast<git_index *>(payload); 0364 git_index_remove_bypath(index, path); 0365 } 0366 return 0; 0367 }, 0368 index); 0369 } 0370 0371 git_repository *GitWrapper::openRepository() 0372 { 0373 QString pathtosave = Global::savesFolder(); 0374 QByteArray pathba = pathtosave.toUtf8(); 0375 const char *pathCString = pathba.data(); 0376 git_repository *repo = nullptr; 0377 0378 int error = git_repository_open(&repo, pathCString); 0379 if (error < 0) { 0380 gitErrorHandling(); 0381 return repo; 0382 } 0383 return repo; 0384 } 0385 0386 QDateTime GitWrapper::getLastCommitDate(git_repository *repo) 0387 { 0388 if (repo == nullptr) 0389 return QDateTime(); 0390 git_oid oid_parent_commit; /* the SHA1 for last commit */ 0391 int error = git_reference_name_to_id(&oid_parent_commit, repo, "HEAD"); 0392 if (error < 0) 0393 return QDateTime(); 0394 0395 git_commit *head = nullptr; 0396 error = git_commit_lookup(&head, repo, &oid_parent_commit); 0397 if (error < 0) 0398 return QDateTime(); 0399 int64_t time = static_cast<int64_t>(git_commit_time(head)); 0400 0401 QDateTime date; 0402 date.setTime_t(time); 0403 0404 git_commit_free(head); 0405 return date; 0406 } 0407 0408 void GitWrapper::gitErrorHandling() 0409 { 0410 const git_error *e = giterr_last(); 0411 qDebug() << "Error in git (error,class,message)" << e->klass << e->message; 0412 } 0413 0414 #else 0415 // make everything noop 0416 void GitWrapper::initializeGitRepository(QString folder) 0417 { 0418 } 0419 void GitWrapper::commitBasketView() 0420 { 0421 } 0422 void GitWrapper::commitCreateBasket() 0423 { 0424 } 0425 void GitWrapper::commitDeleteBasket(QString basketFolderName) 0426 { 0427 } 0428 void GitWrapper::commitBasket(BasketScene *basket) 0429 { 0430 } 0431 void GitWrapper::commitTagsXml() 0432 { 0433 } 0434 bool GitWrapper::commitPattern(git_repository *repo, QString pattern, QString message) 0435 { 0436 return true; 0437 } 0438 bool GitWrapper::commitIndex(git_repository *repo, git_index *index, QString message) 0439 { 0440 return true; 0441 } 0442 void GitWrapper::removeDeletedFromIndex(git_repository *repo, git_index *index) 0443 { 0444 } 0445 git_repository *GitWrapper::openRepository() 0446 { 0447 return 0; 0448 } 0449 0450 QDateTime GitWrapper::getLastCommitDate(git_repository *repo) 0451 { 0452 return QDateTime(); 0453 } 0454 0455 void GitWrapper::gitErrorHandling() 0456 { 0457 } 0458 0459 #endif