show undo diff (#3213)

Exposes undo operations via the C API, and uses those to show a (new, differently formatted) diff before committing the undo.
This commit is contained in:
ryneeverett 2024-02-09 22:11:14 -05:00 committed by GitHub
parent 89df80c9f0
commit 34c0e67469
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
18 changed files with 646 additions and 66 deletions

View file

@ -42,6 +42,7 @@
#include <main.h>
#include <util.h>
#include "tc/Server.h"
#include "tc/util.h"
bool TDB2::debug_mode = false;
static void dependency_scan (std::vector<Task> &);
@ -207,10 +208,58 @@ void TDB2::get_changes (std::vector <Task>& changes)
////////////////////////////////////////////////////////////////////////////////
void TDB2::revert ()
{
replica.undo (NULL);
auto undo_ops = replica.get_undo_ops();
if (undo_ops.len == 0) {
std::cout << "No operations to undo.";
return;
}
if (confirm_revert(undo_ops)) {
// Has the side-effect of freeing undo_ops.
replica.commit_undo_ops(undo_ops, NULL);
} else {
replica.free_replica_ops(undo_ops);
}
replica.rebuild_working_set (false);
}
////////////////////////////////////////////////////////////////////////////////
bool TDB2::confirm_revert (struct tc::ffi::TCReplicaOpList undo_ops)
{
// TODO Use show_diff rather than this basic listing of operations, though
// this might be a worthy undo.style itself.
std::cout << "The following " << undo_ops.len << " operations would be reverted:\n";
for (size_t i = 0; i < undo_ops.len; i++) {
std::cout << "- ";
tc::ffi::TCReplicaOp op = undo_ops.items[i];
switch(op.operation_type) {
case tc::ffi::TCReplicaOpType::Create:
std::cout << "Create " << replica.get_op_uuid(op);
break;
case tc::ffi::TCReplicaOpType::Delete:
std::cout << "Delete " << replica.get_op_old_task_description(op);
break;
case tc::ffi::TCReplicaOpType::Update:
std::cout << "Update " << replica.get_op_uuid(op) << "\n";
std::cout << " " << replica.get_op_property(op) << ": " << option_string(replica.get_op_old_value(op)) << " -> " << option_string(replica.get_op_value(op));
break;
case tc::ffi::TCReplicaOpType::UndoPoint:
throw std::string ("Can't undo UndoPoint.");
break;
default:
throw std::string ("Can't undo non-operation.");
break;
}
std::cout << "\n";
}
return ! Context::getContext ().config.getBoolean ("confirmation") ||
confirm ("The undo command is not reversible. Are you sure you want to revert to the previous state?");
}
////////////////////////////////////////////////////////////////////////////////
std::string TDB2::option_string(std::string input) {
return input == "" ? "<empty>" : input;
}
////////////////////////////////////////////////////////////////////////////////
void TDB2::show_diff (
const std::string& current,

View file

@ -78,13 +78,15 @@ public:
void dump ();
void sync (tc::Server server, bool avoid_snapshots);
bool confirm_revert(struct tc::ffi::TCReplicaOpList);
private:
tc::Replica replica;
std::optional<tc::WorkingSet> _working_set;
const tc::WorkingSet &working_set ();
void show_diff (const std::string&, const std::string&, const std::string&);
static std::string option_string (std::string input);
static void show_diff (const std::string&, const std::string&, const std::string&);
};
#endif

View file

@ -31,6 +31,7 @@
#include "tc/Server.h"
#include "tc/WorkingSet.h"
#include "tc/util.h"
#include <iostream>
using namespace tc::ffi;
@ -154,14 +155,68 @@ void tc::Replica::sync (Server server, bool avoid_snapshots)
}
////////////////////////////////////////////////////////////////////////////////
void tc::Replica::undo (int32_t *undone_out)
TCReplicaOpList tc::Replica::get_undo_ops ()
{
auto res = tc_replica_undo (&*inner, undone_out);
return tc_replica_get_undo_ops(&*inner);
}
////////////////////////////////////////////////////////////////////////////////
void tc::Replica::commit_undo_ops (TCReplicaOpList tc_undo_ops, int32_t *undone_out)
{
auto res = tc_replica_commit_undo_ops (&*inner, tc_undo_ops, undone_out);
if (res != TC_RESULT_OK) {
throw replica_error ();
}
}
////////////////////////////////////////////////////////////////////////////////
void tc::Replica::free_replica_ops (TCReplicaOpList tc_undo_ops)
{
tc_replica_op_list_free(&tc_undo_ops);
}
////////////////////////////////////////////////////////////////////////////////
std::string tc::Replica::get_op_uuid(TCReplicaOp &tc_replica_op) const
{
TCString uuid = tc_replica_op_get_uuid(&tc_replica_op);
return tc2string(uuid);
}
////////////////////////////////////////////////////////////////////////////////
std::string tc::Replica::get_op_property(TCReplicaOp &tc_replica_op) const
{
TCString property = tc_replica_op_get_property(&tc_replica_op);
return tc2string(property);
}
////////////////////////////////////////////////////////////////////////////////
std::string tc::Replica::get_op_value(TCReplicaOp &tc_replica_op) const
{
TCString value = tc_replica_op_get_value(&tc_replica_op);
return tc2string(value);
}
////////////////////////////////////////////////////////////////////////////////
std::string tc::Replica::get_op_old_value(TCReplicaOp &tc_replica_op) const
{
TCString old_value = tc_replica_op_get_old_value(&tc_replica_op);
return tc2string(old_value);
}
////////////////////////////////////////////////////////////////////////////////
std::string tc::Replica::get_op_timestamp(TCReplicaOp &tc_replica_op) const
{
TCString timestamp = tc_replica_op_get_timestamp(&tc_replica_op);
return tc2string(timestamp);
}
////////////////////////////////////////////////////////////////////////////////
std::string tc::Replica::get_op_old_task_description(TCReplicaOp &tc_replica_op) const
{
TCString description = tc_replica_op_get_old_task_description(&tc_replica_op);
return tc2string(description);
}
////////////////////////////////////////////////////////////////////////////////
int64_t tc::Replica::num_local_operations ()
{

View file

@ -93,7 +93,15 @@ namespace tc {
tc::Task import_task_with_uuid (const std::string &uuid);
// TODO: struct TCTask *tc_replica_import_task_with_uuid(struct TCReplica *rep, struct TCUuid tcuuid);
void sync(Server server, bool avoid_snapshots);
void undo (int32_t *undone_out);
tc::ffi::TCReplicaOpList get_undo_ops ();
void commit_undo_ops (tc::ffi::TCReplicaOpList tc_undo_ops, int32_t *undone_out);
void free_replica_ops (tc::ffi::TCReplicaOpList tc_undo_ops);
std::string get_op_uuid(tc::ffi::TCReplicaOp &tc_replica_op) const;
std::string get_op_property(tc::ffi::TCReplicaOp &tc_replica_op) const;
std::string get_op_value(tc::ffi::TCReplicaOp &tc_replica_op) const;
std::string get_op_old_value(tc::ffi::TCReplicaOp &tc_replica_op) const;
std::string get_op_timestamp(tc::ffi::TCReplicaOp &tc_replica_op) const;
std::string get_op_old_task_description(tc::ffi::TCReplicaOp &tc_replica_op) const;
int64_t num_local_operations ();
int64_t num_undo_points ();
// TODO: TCResult tc_replica_add_undo_point(struct TCReplica *rep, bool force);

View file

@ -1758,9 +1758,9 @@ checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da"
[[package]]
name = "uuid"
version = "1.6.1"
version = "1.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5e395fcf16a7a3d8127ec99782007af141946b4795001f876d54fb0d55978560"
checksum = "f00cc9702ca12d3c81455259621e676d0f7251cec66a21e98fe2e9a37db93b2a"
dependencies = [
"getrandom",
"serde",