/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=2 et sw=2 tw=80: */ /* Copyright 2013-2021 The Libfossil Authors, see LICENSES/BSD-2-Clause.txt SPDX-License-Identifier: BSD-2-Clause-FreeBSD SPDX-FileCopyrightText: 2021 The Libfossil Authors SPDX-ArtifactOfProjectName: Libfossil SPDX-FileType: Code */ /** This is a test app demonstrating creating a repository and checking in files without an associated checkout. */ #ifdef NDEBUG /* Force assert() to always be in effect. */ #undef NDEBUG #endif #include "libfossil.h" // Only for testing/debugging.. #define MARKER(pfexp) \ do{ printf("MARKER: %s:%d:%s():\t",__FILE__,__LINE__,__func__); \ printf pfexp; \ } while(0) // Global app state. struct App_ { char const * repoDbName; bool addEmptyCommit; bool addRCard; } App = { "_ciwoco.f", true/*addEmptyCommit*/, true/*addRCard*/ }; static int repo_create(bool addEgg){ int rc; fsl_cx * const f = fcli_cx(); fsl_repo_create_opt cOpt = fsl_repo_create_opt_empty; if(addEgg){ cOpt.commitMessage = "This is a repo. There are many like it " "but this one is mine."; }else{ cOpt.commitMessage = NULL; } cOpt.filename = App.repoDbName; fsl_file_unlink(cOpt.filename); rc = fsl_repo_create(f, &cOpt); assert(0==rc); assert(fsl_cx_db_repo(f)); return rc; } static int setup_deck(fsl_deck *d, char const * msg){ double const julian = true ? fsl_db_julian_now(fsl_cx_db_repo(d->f)) /* ^^^^ ms precision */ : fsl_julian_now() /* seconds precision */; int rc = fsl_deck_D_set(d, julian); assert(0==rc); rc = fsl_deck_C_set(d, msg, -1); assert(0==rc); rc = fsl_deck_U_set(d, "ciwoco"); assert(0==rc); return rc; } static int do_demo(void){ int rc = 0; fsl_cx * const f = fcli_cx(); fsl_buffer content = fsl_buffer_empty; fsl_deck d = fsl_deck_empty; char const *fname = 0; rc = repo_create(App.addEmptyCommit); if(rc) goto end; f_out("(Re)created repo: %s\n", fsl_cx_db_file_repo(f, NULL)); if(App.addEmptyCommit){ f_out("Empty initial commit was added.\n"); }else{ f_out("No empty initial commit was created.\n"); } assert(fsl_needs_repo(f)); fsl_cx_flag_set(f, FSL_CX_F_CALC_R_CARD, App.addRCard); f_out("R-card generation is %s\n", App.addRCard ? "ON" : "OFF"); rc = fsl_cx_transaction_begin(f); if(rc) goto end; ////////////////////////////////////////////////////////////// // Step 1: initialize our deck... if(App.addEmptyCommit){ rc = fsl_deck_load_sym(f, &d, "trunk", FSL_SATYPE_CHECKIN); assert(0==rc); assert(f==d.f); f_out("Deriving from initial trunk checkin #%"FSL_ID_T_PFMT"\n", d.rid); rc = fsl_deck_derive(&d); assert(0==rc); }else{ f_out("Creating initial commit with files.\n"); fsl_deck_init(f, &d, FSL_SATYPE_CHECKIN); /* If we don't set a branch, we cannot resolve the checkins via a branch name! */ rc = fsl_deck_branch_set(&d, "trunk"); assert(0==rc); } ////////////////////////////////////////////////////////////// // Step 2: set up some commonly-required cards... rc = setup_deck(&d, "Files added w/o checkout."); assert(0==rc); ////////////////////////////////////////////////////////////// // Step 3: add some files... char const * fnames[] = { "f-test-ciwoco.c", "GNUmakefile", NULL }; for( int i = 0; (fname = fnames[i]); ++i ){ rc = fsl_buffer_fill_from_filename(&content, fname); assert(0==rc); rc = fsl_deck_F_set_content(&d, fname, &content, FSL_FILE_PERM_REGULAR, NULL); assert(0==rc); f_out("Added file: %s\n", fname); } ////////////////////////////////////////////////////////////// // Step 4: save the deck... rc = fsl_deck_save(&d, false); assert(0==rc); f_out("Saved checkin #%"FSL_ID_T_PFMT"\n", d.rid); ////////////////////////////////////////////////////////////// // Step 5: ... f_out("Now we'll try again so we can ensure that deltaing " "of parent file content works.\n"); fsl_deck_derive(&d); setup_deck(&d, "Modified GNUmakefile."); fname = "GNUmakefile"; rc = fsl_buffer_fill_from_filename(&content, fname); assert(0==rc); rc = fsl_buffer_append(&content, "\n# This is an edit. There are many " "like it, but this one is mine.\n", -1); assert(0==rc); rc = fsl_deck_F_set_content(&d, fname, &content, FSL_FILE_PERM_REGULAR, NULL); assert(0==rc); f_out("Added file: %s\n", fname); rc = fsl_deck_save(&d, false); assert(0==rc); f_out("Saved checkin #%"FSL_ID_T_PFMT"\n", d.rid); f_out("You can confirm that the previous file version is delta'd " "by running:\n f-acat -R %s --raw rid:X\n" "where X is the lowest-numbered entry in this list:\n", App.repoDbName); fsl_db_each( fsl_cx_db(f), fsl_stmt_each_f_dump, NULL, "SELECT m.fid FROM mlink m, filename f " "WHERE m.fnid=f.fnid and f.name=%Q", fname); assert(fsl_cx_transaction_level(f)>0); rc = fsl_cx_transaction_end(f, false); end: if(fsl_cx_transaction_level(f)){ fsl_cx_transaction_end(f, true); } fsl_deck_finalize(&d); fsl_buffer_clear(&content); if(!rc){ f_out("Results are in repo file %s\n", App.repoDbName); } return rc; } int main(int argc, const char * const * argv ){ /** Set up flag handling, which is used for processing basic CLI flags and generating --help text output. */ const fcli_cliflag FCliFlags[] = { FCLI_FLAG_BOOL_INVERT("e", "empty", &App.addEmptyCommit, "If set, do not create the initial " "empty checkin."), FCLI_FLAG_BOOL_INVERT("r", "no-r-card", &App.addRCard, "If set, do not add an R-card to commits."), fcli_cliflag_empty_m // list MUST end with this (or equivalent) }; const fcli_help_info FCliHelp = { "A demo of creating a new repo and checking in files " "without a checkout.", NULL, // very brief usage text, e.g. "file1 [...fileN]" NULL // optional callback which outputs app-specific help }; fcli.clientFlags.checkoutDir = NULL; // same effect as global -C flag. int rc = fcli_setup_v2(argc, argv, FCliFlags, &FCliHelp); if(rc) goto end; else if((rc=fcli_has_unused_args(false))) goto end; else if(fsl_cx_db_repo(fcli_cx())){ rc = fcli_err_set(FSL_RC_MISUSE, "This app must be started WITHOUT a repo/checkout."); goto end; } rc = do_demo(); end: return fcli_end_of_main(rc); }