/* -*- 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/
*****************************************************************************
A simple tool for dumping blobs to stdout.
*/
#include "fossil-scm/fossil-cli.h"
#include "fossil-scm/fossil-internal.h"
static const fcli_help_arg_t fcliHelpArgs_Status[] = {
/*{flagShort, flagLong, valType, callback, brief}*/
{"f","no-files",0, 0,
"Disables the status check for local files."},
{0,"utc",0, 0,
"Enables UTC timestamps (default is local time)."},
{"v", "version", "VERSION", 0,
"Specifies the checkin version to show "
"(default is checkout version)."},
{0, "no-reset", 0, 0,
"Is an internal debugging flag which tells this app "
"not to re-set the vfile contents to be those of the current checkout. "
"For compatibility with fossil(1), do not use it. It is only for testing."
},
{0,0,0,0,0}/*end-of-list marker*/
};
static const fcli_help_t fcliHelp_Status = {
"Outputs status info for the current checkout.",
"[options]",
fcliHelpArgs_Status,
0
};
static char const * fsl_ckout_change_label(fsl_checkout_change_t change){
switch(change){
case FSL_CKOUT_CHANGE_NONE: return NULL;
case FSL_CKOUT_CHANGE_MOD: return "MODIFIED";
case FSL_CKOUT_CHANGE_MERGE_MOD: return "MERGE-MOD";
case FSL_CKOUT_CHANGE_MERGE_ADD: return "MERGE-ADD";
case FSL_CKOUT_CHANGE_INTEGRATE_MOD: return "INTEGRATE-MOD";
case FSL_CKOUT_CHANGE_INTEGRATE_ADD: return "INTEGRATE-ADD";
case FSL_CKOUT_CHANGE_ADDED: return "ADDED";
case FSL_CKOUT_CHANGE_REMOVED: return "REMOVED";
case FSL_CKOUT_CHANGE_MISSING: return "MISSING";
case FSL_CKOUT_CHANGE_RENAMED: return "RENAMED";
case FSL_CKOUT_CHANGE_CONFLICT: return "CONFLICT";
case FSL_CKOUT_CHANGE_NOT_A_FILE: return "NOT-A-FILE";
case FSL_CKOUT_CHANGE_CHERRYPICK: return "CHERRYPICK";
case FSL_CKOUT_CHANGE_BACKOUT: return "BACKOUT";
default:
return "?!NO WAY!?";
}
}
/**
A fsl_checkout_changes_f() impl which outputs change status to f_out().
*/
static int fsl_checkout_changes_fapp(void * state, fsl_checkout_change_t change,
char const * filename, char const * origName){
if(1==++*((int*)state)){
f_out("\nLocal changes compared to this version:\n\n");
}
f_out("%-15s", fsl_ckout_change_label(change));
if(FSL_CKOUT_CHANGE_RENAMED==change){
assert(origName && *origName);
f_out("%s\n%14s %s\n", origName, "to", filename);
}else{
f_out("%s\n", filename);
}
return 0;
}
static int fapp_show_local_changes(fsl_cx * f, fsl_id_t vid, char doScan){
int rc = 0;
if(doScan) rc = fsl_checkout_changes_scan(f);
if(!rc){
int counter = 0;
rc = fsl_checkout_changes_visit(f, vid, 0,
fsl_checkout_changes_fapp, &counter);
if(!rc){
f_out("%s\n", counter ? "" : "\nNo local changes.");
}
}
return rc;
}
/**
I
*/
static int fapp_show_info(fsl_cx * f, fsl_id_t rid, char useUtc){
int rc = 0;
fsl_stmt st = fsl_stmt_empty;
fsl_db * dbR = fsl_cx_db_repo(f);
fsl_db * dbC = fsl_cx_db_checkout(f);
int lblWidth = -20;
assert(dbR);
assert(dbC);
{
fsl_id_t ckoutRid = 0;
fsl_uuid_cstr ckoutUuid = NULL;
fsl_checkout_version_info(f, &ckoutRid, &ckoutUuid);
assert((ckoutUuid && (ckoutRid>0)) || (!ckoutUuid && (0==ckoutRid)));
}
f_out("%*s %s\n", lblWidth, "repository-db:",
fsl_cx_db_file_repo(f, NULL));
f_out("%*s %s\n", lblWidth, "checkout-root:",
fsl_cx_checkout_dir_name(f, NULL));
rc = fsl_db_prepare(dbR, &st, "SELECT "
/*0*/"datetime(event.mtime%s) AS timestampString, "
/*1*/"coalesce(euser, user) AS user, "
/*2*/"(SELECT group_concat(substr(tagname,5), ', ') FROM tag, tagxref "
"WHERE tagname GLOB 'sym-*' AND tag.tagid=tagxref.tagid "
"AND tagxref.rid=blob.rid AND tagxref.tagtype>0) as tags, "
/*3*/"coalesce(ecomment, comment) AS comment, "
/*4*/"uuid AS uuid "
"FROM event JOIN blob "
"WHERE "
"event.type='ci' "
"AND blob.rid=%"FSL_ID_T_PFMT" "
"AND blob.rid=event.objid "
"ORDER BY event.mtime DESC",
useUtc ? "" : ", 'localtime'",
(fsl_id_t)rid);
if(rc) goto dberr;
if( FSL_RC_STEP_ROW != fsl_stmt_step(&st)){
/* fcli_err_set(FSL_RC_ERROR, "Event data for checkout not found."); */
f_out("\nNo 'event' data found. This is only normal for an empty repo.\n");
goto end;
}
f_out("%*s %s %s %s (RID %"FSL_ID_T_PFMT")\n",
lblWidth, "checkout-version:",
fsl_stmt_g_text(&st, 4, NULL),
fsl_stmt_g_text(&st, 0, NULL),
useUtc ? "UTC" : "local",
(fsl_id_t)rid );
{
/* list parent(s) */
fsl_stmt stP = fsl_stmt_empty;
rc = fsl_db_prepare(dbR, &stP, "SELECT "
"uuid, pid, isprim "
"FROM plink JOIN blob ON pid=rid "
"WHERE cid=%"FSL_ID_T_PFMT" "
"ORDER BY isprim DESC, mtime DESC /*sort*/",
(fsl_id_t)rid);
if(rc) goto dberr;
while( FSL_RC_STEP_ROW == fsl_stmt_step(&stP) ){
char const * zLabel = fsl_stmt_g_int32(&stP,2)
? "parent:" : "merged-from:";
f_out("%*s %s\n", lblWidth, zLabel,
fsl_stmt_g_text(&stP, 0, NULL));
}
fsl_stmt_finalize(&stP);
}
{
/* list children */
fsl_stmt stC = fsl_stmt_empty;
rc = fsl_db_prepare(dbR, &stC, "SELECT "
"uuid, cid, isprim "
"FROM plink JOIN blob ON cid=rid "
"WHERE pid=%"FSL_ID_T_PFMT" "
"ORDER BY isprim DESC, mtime DESC /*sort*/",
(fsl_id_t)rid);
if(rc) goto dberr;
while( FSL_RC_STEP_ROW == fsl_stmt_step(&stC) ){
char const * zLabel = fsl_stmt_g_int32(&stC,2)
? "child:" : "merged-into:";
f_out("%*s %s\n", lblWidth, zLabel,
fsl_stmt_g_text(&stC, 0, NULL));
}
fsl_stmt_finalize(&stC);
}
f_out("%*s %s\n", lblWidth, "user:",
fsl_stmt_g_text(&st, 1, NULL));
f_out("%*s %s\n", lblWidth, "tags:",
fsl_stmt_g_text(&st, 2, NULL));
f_out("%*s %s\n", lblWidth, "comment:",
fsl_stmt_g_text(&st, 3, NULL));
dberr:
if(rc){
fsl_cx_uplift_db_error(f, dbR);
}
end:
fsl_stmt_finalize(&st);
return rc;
}
int main(int argc, char * const * argv ){
int rc = 0;
fsl_cx * f;
fsl_db * db;
fsl_id_t rid = 0;
char skipFiles;
char useUtc;
char * zVersion = NULL;
fsl_id_t ckoutRid = 0;
char resetVfile;
fcli.appHelp2 = &fcliHelp_Status;
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 = fcli_err_set( FSL_RC_NOT_A_CHECKOUT,
"This app requires a checkout db.");
goto end;
}
skipFiles = fcli_flag2("f","no-files", NULL);
useUtc = fcli_flag("utc", NULL);
fcli_flag2("v","version", &zVersion);
resetVfile = !fcli_flag("no-reset",NULL);
if(fcli_has_unused_flags(0)) goto end;
fsl_checkout_version_info(f, &ckoutRid, NULL);
if(zVersion){
rc = fsl_sym_to_rid(f, zVersion, FSL_CATYPE_CHECKIN, &rid);
if(rc) goto end;
}
if(!rid){
rid = ckoutRid;
}
if( !fsl_rid_is_a_checkin(f, rid) ) {
rc = fcli_err_set(FSL_RC_RANGE,
"%s does not refer to a checkin version.",
zVersion ? zVersion : "RID");
goto end;
}
rc = fapp_show_info(f, rid, useUtc);
if(!rc && !skipFiles){
rc = fapp_show_local_changes(f, rid, resetVfile);
}
end:
fsl_free(zVersion);
return fcli_end_of_main(rc);
}