dbhub: Allow checking out different commits and different branches

After opening a clone of a remote database, allow checking out the
different branches and commits of this database in the Current Database
section. This way you access all branches and see their history in DB4S.
This commit is contained in:
Martin Kleusberg
2020-07-22 14:21:35 +02:00
parent f67d72df17
commit 065adabe75
5 changed files with 66 additions and 53 deletions

View File

@@ -824,10 +824,6 @@ QString RemoteDatabase::localAdd(QString filename, QString identity, const QUrl&
QString RemoteDatabase::localExists(const QUrl& url, QString identity, const std::string& branch)
{
// This function checks if there already is a clone for the given combination of url and identity. It returns the filename
// of this clone if there is or a null string if there isn't a clone yet. The identity needs to be part of this check because
// with the url alone there could be corner cases where different versions or whatever may not be accessible for all users.
localAssureOpened();
// Extract commit id from url and remove query part afterwards
@@ -875,51 +871,14 @@ QString RemoteDatabase::localExists(const QUrl& url, QString identity, const std
// There are three possibilities now: either we didn't get any commit id in the URL in which case we just return the file we got, no matter what.
// Or the requested commit id is the same as the local commit id in which case we return the file we got as well.
// Or the requested commit id differ in which case we ask the user what to do.
// Or the requested commit id differ in which case we return no match.
if(url_commit_id.isNull() || local_commit_id == url_commit_id)
{
// Both commit ids are the same. That's the perfect match, so we can download the local file if it still exists
// Both commit ids are the same. That's the perfect match, so we can open the local file if it still exists
return localCheckFile(local_file);
} else {
// The commit ids differ. That means we have another version locally checked out than we're trying to download. Because the commit ids are
// only calculated on the server side and we're currently always checking out the latest version this can only mean that the remote version has
// been updated, i.e. is newer than the local version.
// TODO Support multiple checkouts of the same database at different versions at the same time. For this we need to be more intelligent with
// comparing the commit ids.
// Ask the user what to do: open the local version or updating to the new remote version
if(QMessageBox::question(nullptr, qApp->applicationName(),
tr("The remote database has been updated since the last checkout. Do you want to update the local database to the newest version? Note "
"that this discards any changes you have made locally! If you don't want to lose local changes, click No to open the local version."),
QMessageBox::Yes | QMessageBox::No, QMessageBox::No) == QMessageBox::Yes)
{
// User wants to download the newest version. So delete the entry from the clones database and delete the local database copy and return an empty
// string to indicate a redownload request.
// Build full path to database file and delete it
QFile::remove(Settings::getValue("remote", "clonedirectory").toString() + "/" + local_file);
// Remove the old entry from the local clones database to enforce a redownload. The file column is unique for the entire table because the
// files are all in the same directory and their names need to be unique because of this.
QString delete_sql = QString("DELETE FROM local WHERE file=?");
sqlite3_stmt* delete_stmt;
if(sqlite3_prepare_v2(m_dbLocal, delete_sql.toUtf8(), -1, &delete_stmt, nullptr) != SQLITE_OK)
return QString();
if(sqlite3_bind_text(delete_stmt, 1, local_file.toUtf8(), local_file.toUtf8().length(), SQLITE_TRANSIENT))
{
sqlite3_finalize(delete_stmt);
return QString();
}
sqlite3_step(delete_stmt);
sqlite3_finalize(delete_stmt);
// Return an empty string to indicate a redownload request
return QString();
} else {
// User wants to open the local version. So build the full path and return it if the file still exists.
return localCheckFile(local_file);
}
// The commit ids differ. This means we have no match
return QString();
}
}

View File

@@ -138,6 +138,12 @@ public:
// Delete a local database clone
void localDeleteFile(QString filename);
// This function checks if there already is a clone for the given combination of url and identity. It returns the filename
// of this clone if there is or a null string if there isn't a clone yet. The identity needs to be part of this check because
// with the url alone there could be corner cases where different versions or whatever may not be accessible for all users.
// If the URL contains a commit id (optional), this commit id is part of the check.
QString localExists(const QUrl& url, QString identity, const std::string& branch);
signals:
// As soon as you can safely open a network connection, this signal is emitted. This can be used to delay early network requests
// which might otherwise fail.
@@ -171,7 +177,6 @@ private:
// Helper functions for managing the list of locally available databases
void localAssureOpened();
QString localAdd(QString filename, QString identity, const QUrl& url, const std::string& new_commit_id, const std::string& branch);
QString localExists(const QUrl& url, QString identity, const std::string& branch);
QString localCheckFile(const QString& local_file);
std::string localLastCommitId(QString clientCert, const QUrl& url, const std::string& branch);

View File

@@ -192,10 +192,45 @@ void RemoteDock::fetchDatabase(QString url_string)
return;
}
// Check if we already have a clone of this database branch. In so, show a warning because there might
// be unpushed changes. For this we don't care about the currently checked out commit id because for
// any commit local changes could be lost.
// TODO Detect local changes and don't warn when no changes were made
QUrl url_without_commit_id(url);
QUrlQuery url_without_commit_id_query(url_without_commit_id);
url_without_commit_id_query.removeQueryItem("commit");
url_without_commit_id.setQuery(url_without_commit_id_query);
if(!remoteDatabase.localExists(url_without_commit_id, remoteModel->currentClientCertificate(), QUrlQuery(url).queryItemValue("branch").toStdString()).isEmpty())
{
if(QMessageBox::warning(this,
QApplication::applicationName(),
tr("Fetching this commit might override local changes when you have not pushed them yet.\n"
"Are you sure you want to fetch it?"),
QMessageBox::Yes | QMessageBox::Cancel,
QMessageBox::Cancel) == QMessageBox::Cancel)
{
return;
}
}
// Clone the database
remoteDatabase.fetch(url.toString(), RemoteDatabase::RequestTypeDatabase, remoteModel->currentClientCertificate());
}
void RemoteDock::fetchCommit(const QModelIndex& idx)
{
// Fetch selected commit
QUrl url(QString::fromStdString(currently_opened_file_info.url));
QUrlQuery query({
{"branch", ui->comboDatabaseBranch->currentText()},
{"commit", idx.sibling(idx.row(), RemoteCommitsModel::ColumnCommitId).data().toString()}
});
url.setQuery(query);
fetchDatabase(url.toString());
}
void RemoteDock::enableButtons()
{
bool db_opened = mainWindow->getDb().isOpen() && mainWindow->getDb().currentFile() != ":memory:";
@@ -362,7 +397,7 @@ void RemoteDock::refreshMetadata(const QString& username, const QString& dbname)
}
void RemoteDock::showMetadata(const std::vector<RemoteMetadataBranchInfo>& branches, const std::string& commits,
const std::vector<RemoteMetadataReleaseInfo>& releases, const std::vector<RemoteMetadataReleaseInfo>& tags,
const std::vector<RemoteMetadataReleaseInfo>& /*releases*/, const std::vector<RemoteMetadataReleaseInfo>& /*tags*/,
const std::string& /*default_branch*/, const std::string& web_page)
{
// Store all the commit information as-is
@@ -375,10 +410,6 @@ void RemoteDock::showMetadata(const std::vector<RemoteMetadataBranchInfo>& branc
ui->comboDatabaseBranch->clear();
for(const auto& branch : branches)
ui->comboDatabaseBranch->addItem(QString::fromStdString(branch.name), QString::fromStdString(branch.commit_id));
for(const auto& release : releases)
ui->comboDatabaseBranch->addItem(QString::fromStdString(release.name) + " (" + tr("release") + ")", QString::fromStdString(release.commit_id));
for(const auto& tag : tags)
ui->comboDatabaseBranch->addItem(QString::fromStdString(tag.name) + " (" + tr("tag") + ")", QString::fromStdString(tag.commit_id));
ui->comboDatabaseBranch->setCurrentIndex(ui->comboDatabaseBranch->findText(ui->editDatabaseBranch->text()));
}

View File

@@ -38,6 +38,7 @@ private slots:
void setNewIdentity(const QString& identity);
void fetchDatabase(const QModelIndex& idx);
void fetchDatabase(QString url = QString());
void fetchCommit(const QModelIndex& idx);
void pushDatabase();
void newDirectoryNode(const QModelIndex& parent);
void switchToMainView();

View File

@@ -585,8 +585,24 @@
<slot>refresh()</slot>
<hints>
<hint type="sourcelabel">
<x>51</x>
<y>21</y>
<x>-1</x>
<y>-1</y>
</hint>
<hint type="destinationlabel">
<x>266</x>
<y>193</y>
</hint>
</hints>
</connection>
<connection>
<sender>treeDatabaseCommits</sender>
<signal>doubleClicked(QModelIndex)</signal>
<receiver>RemoteDock</receiver>
<slot>fetchCommit(QModelIndex)</slot>
<hints>
<hint type="sourcelabel">
<x>266</x>
<y>337</y>
</hint>
<hint type="destinationlabel">
<x>266</x>
@@ -604,5 +620,6 @@
<slot>fetchDatabase()</slot>
<slot>openCurrentDatabaseInBrowser()</slot>
<slot>refresh()</slot>
<slot>fetchCommit(QModelIndex)</slot>
</slots>
</ui>