Created
October 30, 2014 17:07
-
-
Save brvier/9a3a7d6ff8e068795a61 to your computer and use it in GitHub Desktop.
NotesModel.cpp MERGE_HEAD file still present after merge commit
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include "notesmodel.h" | |
#include "QDir" | |
#include "QDateTime" | |
#include "QStandardPaths" | |
#include "QTextStream" | |
#include "QDebug" | |
#include "QSettings" | |
#include "git2.h" | |
void e(int error) { | |
if (error < 0) | |
throw error; | |
} | |
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 | |
int NotesModel::push() { | |
git_push *push; | |
git_remote_callbacks callbacks = GIT_REMOTE_CALLBACKS_INIT; | |
git_repository *repo = NULL; | |
git_remote *remote = NULL; | |
git_merge_head *merge_head = NULL; | |
try { | |
//Open Repo | |
e(git_repository_open(&repo, notesFolder().absolutePath().toUtf8().constData())); | |
e(git_remote_load(&remote, repo, "upstream")); | |
callbacks.credentials = cred_acquire_cb; | |
e(git_remote_set_callbacks(remote, &callbacks)); | |
e(git_remote_connect(remote, GIT_DIRECTION_PUSH)); | |
int connected = git_remote_connected(remote); | |
if (connected) { | |
e(git_push_new(&push, remote)); | |
e(git_push_add_refspec(push, "refs/heads/master:refs/heads/master")); | |
e(git_push_finish(push)); | |
e(git_push_unpack_ok(push)); | |
e(git_push_status_foreach(push, &status_cb, NULL) < 0); | |
git_push_free(push); | |
} | |
} | |
catch (int error) { | |
const git_error *err = giterr_last(); | |
if (err != NULL) | |
qDebug() << QString::number(err->klass) + "\t" + QString(err->message); | |
giterr_clear(); | |
git_push_free(push); | |
git_remote_free(remote); | |
git_repository_free(repo); | |
} | |
return true; | |
} | |
int NotesModel::pull() { | |
/* | |
git_repository *repo = NULL; | |
git_strarray remotes = {0}; | |
git_remote *remote = NULL; | |
git_index *idx = NULL; | |
git_checkout_options checkout_opts = GIT_CHECKOUT_OPTIONS_INIT; | |
git_clone_options clone_opts = GIT_CLONE_OPTIONS_INIT;*/ | |
git_repository *repo = NULL; | |
git_remote *remote = NULL; | |
git_merge_head *merge_head = NULL; | |
try { | |
//Open Repo | |
e(git_repository_open(&repo, notesFolder().absolutePath().toUtf8().constData())); | |
//List repository and add one from Settings if none exists or doesn t have same URI than settings | |
git_strarray remotes = {0}; | |
git_remote_list(&remotes, repo); | |
if (remotes.count >= 1) { | |
for (size_t i = 0; i < remotes.count; i++) { | |
e(git_remote_load(&remote, repo, remotes.strings[i])); | |
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) { | |
e(git_remote_delete(remote)); | |
e(git_remote_create(&remote, repo, "upstream", | |
QSettings().value("gitRemoteUrl").toString().toUtf8().constData())); | |
e(git_remote_save(remote)); | |
} | |
} | |
} | |
} else if (remotes.count == 0) { | |
e(git_remote_create(&remote, repo, "upstream", | |
QSettings().value("gitRemoteUrl").toString().toUtf8().constData())); | |
e(git_remote_save(remote)); | |
} | |
e(git_remote_load(&remote, repo, "upstream")); | |
git_remote_callbacks callbacks = GIT_REMOTE_CALLBACKS_INIT; | |
callbacks.credentials = cred_acquire_cb; | |
e(git_remote_set_callbacks(remote, &callbacks)); | |
e(git_remote_connect(remote, GIT_DIRECTION_FETCH)); | |
int connected = git_remote_connected(remote); | |
if (connected) | |
e(git_remote_fetch(remote, NULL, NULL)); | |
git_checkout_options checkout_opt = GIT_CHECKOUT_OPTIONS_INIT; | |
checkout_opt.checkout_strategy = GIT_CHECKOUT_FORCE; | |
checkout_opt.file_mode = 0644; | |
git_merge_options merge_opt = GIT_MERGE_OPTIONS_INIT; | |
merge_opt.file_favor = GIT_MERGE_FILE_FAVOR_UNION; | |
const git_remote_head **head = NULL; | |
size_t size = 0; | |
e(git_remote_ls(&head, &size, remote)); | |
if (size <= 0) | |
e(-1); | |
git_oid oid = head[0]->oid; | |
e(git_merge_head_from_fetchhead(&merge_head, repo, "master", git_remote_url(remote), &oid)); | |
e(git_merge(repo, (const git_merge_head **)(&merge_head), 1, &merge_opt, &checkout_opt)); | |
//TRY TO COMMIT | |
/* Create signature */ | |
git_signature *me = NULL; | |
e(git_signature_now(&me, "sparkleNotes", "sparklenotes@khertan.net")); | |
//Tree Lookup | |
git_tree *tree; | |
git_object *tree_obj; | |
e(git_revparse_single(&tree_obj, repo, "HEAD^{tree}")); | |
// Get parent commit | |
git_oid parentCommitId; | |
git_commit *parent; | |
git_oid remoteParentCommitId; | |
git_commit *remoteParent; | |
int nparents; | |
int err; | |
e(git_reference_name_to_id( &parentCommitId, repo, "ORIG_HEAD" )); | |
e(git_commit_lookup( &parent, repo, &parentCommitId )); | |
e(git_reference_name_to_id( &remoteParentCommitId, repo, "MERGE_HEAD" )); | |
e(git_commit_lookup( &remoteParent, repo, &remoteParentCommitId )); | |
const git_commit *parents [2] = { parent, remoteParent }; | |
git_oid new_commit_id; | |
e(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 */ | |
(git_tree *) tree_obj, /* root tree */ | |
2, /* parent count */ | |
parents)); /* parents */ | |
git_merge_head_free(merge_head); | |
git_remote_free(remote); | |
git_repository_free(repo); | |
} | |
catch (int error) { | |
const git_error *err = giterr_last(); | |
if (err != NULL) | |
qDebug() << QString::number(err->klass) + "\t" + QString(err->message); | |
giterr_clear(); | |
git_merge_head_free(merge_head); | |
git_remote_free(remote); | |
git_repository_free(repo); | |
} | |
return true; | |
} | |
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)); | |
} | |
//Create Initial Commit | |
git_signature *sig; | |
git_index *index; | |
git_oid tree_id, commit_id; | |
git_tree *tree; | |
gerr = git_signature_now(&sig, "sparkleNotes", "sparklenotes@khertan.net"); | |
gerr = git_repository_index(&index, repo); | |
if (!checkGitErr(gerr)) { | |
gerr = git_index_write_tree(&tree_id, index); | |
if (!checkGitErr(gerr)) { | |
git_index_free(index); | |
gerr = git_tree_lookup(&tree, repo, &tree_id); | |
if (!checkGitErr(gerr)) { | |
gerr = git_commit_create_v( | |
&commit_id, repo, "HEAD", sig, sig, | |
NULL, "Initial commit", tree, 0); | |
} | |
git_tree_free(tree); | |
} | |
} | |
git_signature_free(sig); | |
git_repository_free(repo); | |
} | |
// Add already existing files in case created outside sparkleNotes | |
updateGitStatus(); | |
// Pull Merge | |
pull(); | |
// Push | |
push(); | |
} | |
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 ¬e) | |
{ | |
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 ¬e = 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