/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=2 et sw=2 tw=80: */ /* Copyright (c) 2013 D. Richard Hipp This program is free software; you can redistribute it and/or modify it under the terms of the Simplified BSD License (also known as the "2-Clause License" or "FreeBSD License".) This program is distributed in the hope that it will be useful, but without any warranty; without even the implied warranty of merchantability or fitness for a particular purpose. Author contact information: drh@hwaci.com http://www.hwaci.com/drh/ ***************************************************************************** This file implements a basic artifact tagging [test] app using the libfossil API. */ #include "fossil-scm/fossil-cli.h" /* Fossil App mini-framework */ #include "fossil-scm/fossil-internal.h" /* only for testing/debugging */ static void fapp_local_help(){ printf("Usage:\n\t%s [options] [files...]\n\n", fcli.appName); puts("INCOMPLETE app for developing the libfossil checking features. Options:\n"); puts("\t--dry-run|-n runs the operation in a transaction and then rolls it back.\n"); puts("\t--message|-m=TEXT specifies the commit message.\n"); puts("\t--mime-type|-mt=TEXT specifies the mime-type of the commit message.\n"); puts("\t--no-r-card|-r specifies not to calculate an R-card (use with care).\n"); puts("\t--dump|-d=FILENAME dumps the generated manifest to the given file.\n"); puts("\t--bg-color|-bg=#RRGGBB one-time color for commit or propagating color of --branch\n"); puts("\t--branch|-b=NAME Creates a new branch. ONLYL LIGHTLY TESTED!\n"); puts("\t--integrate|-i specifies to close all merged-in branches, not just " "integrate-merges.\n"); puts("\t--baseline forces generation of a baseline manifest. By default " "a delta is generated if possible.\n"); puts("KNOWN BUGS:\n"); puts("- Leaf status in the timeline is not updated properly in the local " "repo, but is okay on the remote after push or a local rebuild " "(so the metadata is okay).\n"); } /** A fsl_stmt_each_f() impl which simply outputs row data in tabular form via f_out(). If state is not NULL then it must be a (fsl_size_t*), to which the row count is written. */ static int stmt_each_dump( fsl_stmt * stmt, void * state ){ int i; char const * sep = "\t"; fsl_size_t * counter = (fsl_size_t*)state; if(1==stmt->rowCount){ for( i = 0; i < stmt->colCount; ++i ){ f_out("%s%s", fsl_stmt_col_name(stmt, i), (i==stmt->colCount-1) ? "" : sep); } f_out("\n"); } for( i = 0; i < stmt->colCount; ++i ){ char const * val = fsl_stmt_g_text(stmt, i, NULL); f_out("%s%s", val ? val : "NULL", (i==stmt->colCount-1) ? "" : sep); } f_out("\n"); if(counter) *counter = stmt->rowCount; return 0; } /** Just experimenting with fsl_xlink_listener() and friends. */ static int my_xlink_f(fsl_cx * f, fsl_deck const * d, void * state){ FCLI_V(("Crosslink callback for %s artifact [%.12s] (RID %"FSL_ID_T_PFMT")\n", fsl_catype_cstr(d->type), d->uuid, d->rid)); return 0; } /* Reminder: name of a file by its content RID: SELECT fn.name FROM filename fn, mlink ml WHERE fn.fnid=ml.fnid AND ml.fid=$contentRid */ int main(int argc, char * const * argv ){ int rc = 0; fsl_cx * f; char inTrans = 0; fsl_db * db; char dryRun = 0; fsl_id_t ckoutId = 0; char * cMsg = NULL; char * cBranch = NULL; char * fname = NULL; char * cMimeType = NULL; char * cDumpMf = NULL; char * cColor = NULL; fsl_checkin_opt opt = fsl_checkin_opt_empty; fsl_size_t counter = 0; fsl_id_t newRid = 0; fsl_uuid_str newUuid = NULL; fcli.appHelp = fapp_local_help; rc = fcli_setup(argc, argv); if(FSL_RC_BREAK==rc) /* --help */ return 0; else if(rc) goto end; f = fcli.f; db = fsl_cx_db_checkout(f); if(!db){ rc = fsl_cx_err_set(f, FSL_RC_NOT_A_CHECKOUT, "This app requires a checkout db."); goto end; } #if 1 dryRun = !fcli_flag2("y","do-it", NULL); #else dryRun = fcli.fDryRun || fcli_flag("n",NULL); #endif if(dryRun){ f_out("For safety reasons (until this code is stable), " "checking in runs in dry-run mode unless the " "-y|--do-it flag is provided.\n"); } opt.calcRCard = !fcli_flag2("r","no-r-card", NULL); fcli_flag2("m", "message", &cMsg); fcli_flag2("mt", "mime-type", &cMimeType); fcli_flag2("b", "branch", &cBranch); fcli_flag2("bg", "bg-color", &cColor); fcli_flag2("d", "dump", &cDumpMf); opt.integrate = fcli_flag2("i", "integrate", NULL); if(fcli_flag("baseline", NULL)){ opt.deltaPolicy = 0; } if(fcli_has_unused_flags(0)) goto end; if(!cMsg){ rc = fcli_err_set(FSL_RC_MISUSE, "Commit message (-m|--message MSG) is required."); goto end; } fsl_xlink_listener( f, fcli.appName, my_xlink_f, NULL ); opt.user = fsl_cx_user_get(f); if(!opt.user){ rc = fcli_err_set(FSL_RC_MISUSE, "Could not figure out user name to commit as."); goto end; } opt.message = cMsg; opt.messageMimeType = cMimeType; opt.dumpManifestFile = cDumpMf; opt.branch = cBranch; opt.bgColor = cColor; rc = fsl_db_transaction_begin(db); if(rc){ fsl_cx_uplift_db_error(f, db); goto end; } inTrans = 1; while((fname = fcli_next_arg(1))){ rc = fsl_checkin_file_enqueue( f, fname, 1 ); if(!rc){ ++counter; f_out("Queued for commit: %s\n", fname); } fsl_free(fname); if(rc) goto end; } fsl_checkout_version_info(f, &ckoutId, NULL); /* rc = fsl_checkout_changes_scan(f); */ /* if(rc) goto end; */ if(counter){ f_out("vfile selected contents:\n"); fsl_db_each( fsl_cx_db_checkout(f), stmt_each_dump, NULL, "SELECT vf.id, substr(b.uuid,0,12), vf.pathname " "FROM vfile vf LEFT JOIN blob b " "ON b.rid=vf.rid " "WHERE vf.vid=%"FSL_ID_T_PFMT" " "AND fsl_is_enqueued(vf.id) " "ORDER BY vf.id", (fsl_id_t)ckoutId); f_out("f->ckin.selectedIds count: %d\n", (int)f->ckin.selectedIds.entryCount); } rc = fsl_checkin_commit(f, &opt, &newRid, &newUuid); if(rc){ f_out("rc=%s\n", fsl_rc_cstr(rc)); goto end; } f_out("New version: %s (%"FSL_ID_T_PFMT")\n", newUuid, (fsl_id_t)newRid); if(1){ f_out("vfile contents:\n"); fsl_db_each( fsl_cx_db_checkout(f), stmt_each_dump, NULL, "SELECT * from vfile " "WHERE vid=%"FSL_ID_T_PFMT " AND (chnged OR deleted " " OR (origname IS NOT NULL AND origname<>pathname)" " )" " ORDER BY pathname", (fsl_id_t)newRid ); } if(dryRun){ f_out("Dry-run mode. Rolling back transaction.\n"); fsl_db_transaction_rollback(db); }else{ rc = fsl_db_transaction_end(db, 0); f_out("Note that libfossil currently has no remote sync support, " "so to push your changes you will need to learn the Fossil " "sync protocol and speak it to the remote server of your " "choice over a telnet connection." "\nOr you can just use " "fossil(1)'s \"push\" feature.\n"); } inTrans = 0; end: if(inTrans){ fsl_db_transaction_rollback(db); } fsl_free(cMsg); fsl_free(cBranch); fsl_free(cMimeType); fsl_free(cDumpMf); fsl_free(cColor); fsl_free(newUuid); return fcli_end_of_main(rc); }