Skip to content

Instantly share code, notes, and snippets.

@brvier
Last active January 14, 2018 08:35
Show Gist options
  • Save brvier/cd7ea4a28e80190c9632 to your computer and use it in GitHub Desktop.
Save brvier/cd7ea4a28e80190c9632 to your computer and use it in GitHub Desktop.
NotesModel.cpp wrong use of libgit2
#include "notesmodel.h"
#include "QDir"
#include "QDateTime"
#include "QStandardPaths"
#include "QTextStream"
#include "QDebug"
#include "QSettings"
#include "git2.h"
Note::Note(const QString &path)
: m_path(path)
{
}
QString NotesModel::createNote()
{
QDir notefolder = notesFolder();
QString path = notefolder.absoluteFilePath("Untitled");
int idx = 1;
while (notefolder.exists(path + " " + QString::number(idx) + ".md")) {
idx++;
}
QFile fnote(path + " " + QString::number(idx) + ".md");
if (fnote.open(QIODevice::WriteOnly | QIODevice::Text)) {
QTextStream stream(&fnote);
stream << "Untitled";
}
else{
return QString();
}
return path + " " + QString::number(idx) + ".md";
}
/* typedef struct { } match_data;
int match_cb(const char *path, const char *spec, void *payload)
{
match_data *d = (match_data*)payload;
// return 0 to add/remove this path,
// a positive number to skip this path,
// or a negative number to abort the operation.
}*/
bool NotesModel::checkGitErr(int gerr) {
if (gerr < 0) {
const git_error *e = giterr_last();
if (e) {
qDebug() << "Libgit error : " << e->message;
emit error(QString().fromLatin1(e->message));
return true;
}
else
{ return false; }
}
return false;
}
int cred_acquire_cb(git_cred **out,
const char * UNUSED(url),
const char * UNUSED(username_from_url),
unsigned int UNUSED(allowed_types),
void * UNUSED(payload))
{
return git_cred_ssh_key_new(out,
UNUSED_username_from_url,
QSettings().value("pubKeyPath").toString().toUtf8().constData(),
QSettings().value("keyPath").toString().toUtf8().constData(),
"");
}
static int status_cb(
const char *ref,
const char *msg,
void *data)
{
fprintf(stderr, "status ref: %s\t\tmsg: %s\n", ref, msg);
return 0;
}
//BOUHHHHOUUUH
void NotesModel::pullMergePush() {
git_repository *repo = NULL;
git_strarray remotes = {0};
git_remote *remote = NULL;
git_index *idx = NULL;
git_remote_callbacks callbacks = GIT_REMOTE_CALLBACKS_INIT;
git_checkout_options checkout_opts = GIT_CHECKOUT_OPTIONS_INIT;
git_clone_options clone_opts = GIT_CLONE_OPTIONS_INIT;
//Open Repo
int err = git_repository_open(&repo, notesFolder().absolutePath().toUtf8().constData());
if (!checkGitErr(err)) {
if (!checkGitErr(err)) {
//List repository and add one from Settings if none exists or doesn t have same URI than settings
err = git_remote_list(&remotes, repo);
if (remotes.count >= 1) {
for (size_t i = 0; i < remotes.count; i++) {
err = git_remote_load(&remote, repo, remotes.strings[i]);
if (!checkGitErr(err)) {
qDebug() << git_remote_url(remote);
if (QSettings().value("gitRemoteUrl").isValid() && git_remote_url(remote)) {
if (strcmp(git_remote_url(remote),QSettings().value("gitRemoteUrl").toString().toUtf8().constData()) != 0) {
err = git_remote_delete(remote);
checkGitErr(err);
qDebug() << "Delete then create repo : " << QSettings().value("gitRemoteUrl").toString().toUtf8().constData();
err = git_remote_create(&remote, repo, "upstream",
QSettings().value("gitRemoteUrl").toString().toUtf8().constData());
}
}
}
}
} else if (remotes.count == 0) {
err = git_remote_create(&remote, repo, "upstream",
QSettings().value("gitRemoteUrl").toString().toUtf8().constData());
callbacks.credentials = cred_acquire_cb;
}
//Where magic failed
err = git_remote_load(&remote, repo, "upstream");
if (!checkGitErr(err)) {
callbacks.credentials = cred_acquire_cb;
git_remote_set_callbacks(remote, &callbacks);
// Connect to remote
err = git_remote_connect(remote, GIT_DIRECTION_FETCH);
if (!checkGitErr(err)) {
//fetch
err = git_remote_fetch(remote, NULL, NULL);
if (!checkGitErr(err)) {
err = git_repository_index(&idx, repo);
//Try to merges upstream master in local master
git_merge_head *newHead_ref;
git_oid lastHeadCommit_oid;
err = git_reference_name_to_id(&lastHeadCommit_oid, repo, "HEAD");
checkGitErr(err);
err = git_merge_head_from_fetchhead(&newHead_ref,
repo,
"upstream/master",
QSettings().value("gitRemoteUrl").toString().toUtf8().constData(),
(const git_oid*)&lastHeadCommit_oid);
checkGitErr(err);
git_merge_head *their_heads[1] = { newHead_ref };
git_merge_options merge_opts = GIT_MERGE_OPTIONS_INIT;
merge_opts.file_favor = GIT_MERGE_FILE_FAVOR_UNION;
err = git_merge(repo, (const git_merge_head **)their_heads, 1, &merge_opts, &checkout_opts);
checkGitErr(err);
git_merge_head_free(newHead_ref);
//Try to commit the merge
/* Create signature */
git_signature *me = NULL;
err = git_signature_now(&me, "sparkleNotes", "sparklenotes@khertan.net");
/* Get Tree */
git_tree *tree = NULL;
git_oid *treeoid;
git_object *obj = NULL;
err = git_revparse_single(&obj, repo, "HEAD^{tree}");
checkGitErr(err);
if (obj) {
err = git_tree_lookup(&tree, repo, git_object_id(obj));
checkGitErr(err);
}
/* Get parent commit */
const git_oid *parentCommitId;
git_commit *hparent;
git_commit *parent;
int nparents;
if (err == -3) {
nparents = 0;
}
else {
err = git_commit_lookup( &parent, repo, (const git_oid*)&lastHeadCommit_oid );
checkGitErr(err);
err = git_reference_name_to_id(&lastHeadCommit_oid, repo, "FETCH_HEAD");
checkGitErr(err);
err = git_commit_lookup( &hparent, repo, (const git_oid*)&lastHeadCommit_oid );
checkGitErr(err);
nparents = 2;
}
const git_commit *parents [2] = { hparent, parent };
/* Get root tree */
//FIXME : search tree of commit
err = git_commit_tree(&tree, parent);
checkGitErr(err);
// create the commit
if (tree) {
git_oid new_commit_id;
err = git_commit_create(
&new_commit_id,
repo,
"HEAD", /* name of ref to update */
me, /* author */
me, /* committer */
"UTF-8", /* message encoding */
"Merge of upstream/master", /* message */
tree, /* root tree */
nparents, /* parent count */
parents); /* parents */
checkGitErr(err);
if(nparents > 0)
git_commit_free (parent);
err = git_checkout_head(repo, NULL);
checkGitErr(err);
}
//PUSH
git_push *push;
err = git_push_new(&push, remote);
if (!checkGitErr(err)) {
callbacks.credentials = cred_acquire_cb;
//git_push_set_callbacks()
//git_push_set_callbacks(push, &callbacks);
checkGitErr(err);
err = git_push_add_refspec(push, "refs/heads/master:refs/heads/master");
checkGitErr(err);
err = git_push_finish(push);
checkGitErr(err);
err = git_push_unpack_ok(push);
checkGitErr(err);
checkGitErr(git_push_status_foreach(push, &status_cb, NULL) < 0);
git_push_free(push);
}
}
}
}
}
}
}
void NotesModel::updateGitStatus() {
git_repository *repo = NULL;
int err = git_repository_open(&repo, notesFolder().absolutePath().toUtf8().constData());
if (!checkGitErr(err)) {
/* Each repository owns an index */
git_index *idx = NULL;
err = git_repository_index(&idx, repo);
if (!checkGitErr(err)) {
/* Read index from disk : FIXME Necessary ?*/
err = git_index_read(idx, true);
if (!checkGitErr(err)) {
/* Set Option for git status */
git_status_options opts = { 1, GIT_STATUS_SHOW_INDEX_AND_WORKDIR,
GIT_STATUS_OPT_INCLUDE_UNTRACKED |
// GIT_STATUS_OPT_INCLUDE_UNMODIFIED |
GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS };
//status_data d;
int state = git_repository_state(repo);
git_status_list *statuses = NULL;
err = git_status_list_new(&statuses, repo, &opts);
if (!checkGitErr(err)) {
size_t count = git_status_list_entrycount(statuses);
if ((count == 0) && (GIT_REPOSITORY_STATE_MERGE != state)) {
return; // No modified file, exit
}
for (size_t i=0; i<count; ++i) {
const git_status_entry *entry = git_status_byindex(statuses, i);
//qDebug() << "path:" << entry->index_to_workdir->new_file.path;
//qDebug() << "status_flags:" << entry->index_to_workdir->status;
/* For each new file add it to the index */
if ((entry->status & GIT_STATUS_WT_MODIFIED) || (entry->status & GIT_STATUS_WT_RENAMED) || (entry->status & GIT_STATUS_WT_TYPECHANGE) || entry->status == GIT_STATUS_WT_NEW) {
err = git_index_add_bypath(idx, entry->index_to_workdir->new_file.path);
checkGitErr(err);
}
else if (entry->status & GIT_STATUS_WT_DELETED) {
err = git_index_remove_bypath(idx, entry->index_to_workdir->old_file.path);
checkGitErr(err);
}
}
err = git_index_write(idx);
if (!checkGitErr(err)) {
/* Write the index contents to the ODB as a tree */
git_oid new_tree_id;
err = git_index_write_tree(&new_tree_id, idx);
if (!checkGitErr(err)) {
/* Create signature */
git_signature *me = NULL;
err = git_signature_now(&me, "sparkleNotes", "sparklenotes@khertan.net");
if (!checkGitErr(err)) {
/* Get tree ref */
git_tree *tree;
err = git_tree_lookup(&tree, repo, &new_tree_id);
if (!checkGitErr(err)) {
/* Get parent commit */
git_oid parentCommitId;
git_commit *parent;
int nparents;
err = git_reference_name_to_id( &parentCommitId, repo, "HEAD" );
if (err == -3) {
nparents = 0;
}
else {
err = git_commit_lookup( &parent, repo, &parentCommitId );
nparents = 1;
}
const git_commit *parents [1] = { parent };
git_oid new_commit_id;
err = git_commit_create(
&new_commit_id,
repo,
"HEAD", /* name of ref to update */
me, /* author */
me, /* committer */
"UTF-8", /* message encoding */
"Modif on notes", /* message */
tree, /* root tree */
nparents, /* parent count */
parents); /* parents */
checkGitErr(err);
if(nparents > 0)
git_commit_free (parent);
git_checkout_index (repo, idx, NULL);
}
git_signature_free(me);
}
}
}
}
}
}
git_index_free(idx);
}
git_repository_free(repo);
}
NotesModel::NotesModel(QObject *parent)
: QAbstractListModel(parent)
{
QDir notesdir = notesFolder();
if (!(git_repository_open_ext(
NULL, notesdir.absolutePath().toUtf8().constData() , GIT_REPOSITORY_OPEN_NO_SEARCH, NULL) == 0)) {
git_repository *repo = NULL;
// Init Git Repository if not
int gerr = git_repository_init(&repo, notesdir.absolutePath().toUtf8().constData(), false);
if (gerr < 0) {
const git_error *e = giterr_last();
qDebug() << "Libgit error : %s\n" << e->message;
emit error(QString().fromLatin1(e->message));
}
git_repository_free(repo);
}
// Add already existing files in case created outside sparkleNotes
updateGitStatus();
// Check Remote
pullMergePush();
}
QString Note::title() const
{
//qDebug() << QString(m_path);
//qDebug() << QString(QFileInfo(m_path).baseName());
return QFileInfo(m_path).baseName();
}
QString Note::category() const
{
QString cat = QFileInfo(m_path).dir().dirName();
if (cat == notesFolder().dirName())
return "";
return cat;
}
uint Note::datetime() const
{
return QFileInfo(m_path).lastModified().toTime_t();
}
QString Note::path() const
{
//qDebug() << "path:" << m_path;
return m_path;
}
void NotesModel::addNote(const Note &note)
{
beginInsertRows(QModelIndex(), rowCount(), rowCount());
m_notes << note;
endInsertRows();
}
QDir notesFolder() {
QDir notesdir = QDir(QStandardPaths::writableLocation(QStandardPaths::DataLocation));
if (!notesdir.exists())
notesdir.mkpath(".");
return notesdir;
}
bool dtcomp(Note left, Note right)
{
if (left.category() == right.category()) {
return left.datetime() > right.datetime(); }
else {
return left.category() < right.category();
}
}
void NotesModel::sort() {
beginResetModel();
qSort(m_notes.begin(), m_notes.end(), dtcomp);
endResetModel();
}
void NotesModel::refresh() {
updateGitStatus();
beginResetModel();
m_notes.clear();
loadNotes(notesFolder());
endResetModel();
}
void NotesModel::loadNotes(QDir notesdir) {
QFileInfoList fileInfoList = notesdir.entryInfoList();
for (int i=0; i<fileInfoList.count(); i++) {
if ((fileInfoList[i].fileName()!=".") && (fileInfoList[i].fileName()!="..")) {
if (fileInfoList[i].isFile()) {
NotesModel::addNote(Note(fileInfoList[i].absoluteFilePath()));
}
else {
NotesModel::loadNotes(QDir(fileInfoList[i].absoluteFilePath()));
}
}
}
this->sort();
}
QString NotesModel::create() {
QString path = NotesModel::createNote();
NotesModel::addNote(Note(path));
NotesModel::sort();
return path;
}
int NotesModel::rowCount(const QModelIndex & parent) const {
return m_notes.count();
}
QString NotesModel::readNote(const QString &path) const {
qDebug() << "readNote:" << path;
QString fileName = QQmlFile::urlToLocalFileOrQrc(path);
if (QFile::exists(fileName)) {
QFile file(fileName);
if (file.open(QFile::ReadOnly)) {
QByteArray data = file.readAll();
QTextCodec *codec = QTextCodec::codecForHtml(data);
return codec->toUnicode(data);
}
}
return QString();
}
QVariant NotesModel::data(const QModelIndex & index, int role) const {
if (index.row() < 0 || index.row() >= m_notes.count())
return QVariant();
const Note &note = m_notes[index.row()];
if (role == TitleRole)
return note.title();
else if (role == CategoryRole)
return note.category();
else if (role == DatetimeRole)
return note.datetime();
else if (role == PathRole)
return note.path();
return QVariant();
}
QHash<int, QByteArray> NotesModel::roleNames() const {
QHash<int, QByteArray> roles;
roles[TitleRole] = "title";
roles[CategoryRole] = "category";
roles[DatetimeRole] = "datetime";
roles[PathRole] = "path";
return roles;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment