/* -*- 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);
}