/* -*- 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 Stephan Beal (https://wanderinghorse.net).
Derived heavily from previous work:
Copyright (c) 2013 D. Richard Hipp (https://www.hwaci.com/drh/)
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.
*****************************************************************************
This file implements the code to checkout a Fossil repository.
*/
#include "fossil-scm/fossil-cli.h" /* Fossil App mini-framework */
#include "fossil-scm/fossil-core.h"
#include "fossil-scm/fossil-db.h"
#include "fossil-scm/fossil-internal.h"
#include "fossil-scm/fossil-util.h"
#include <errno.h>
#include <sys/stat.h>
#define MARKER(pfexp) \
do{ printf("MARKER: %s:%d:%s():\t",__FILE__,__LINE__,__func__); \
printf pfexp; \
} while(0)
static int verbose = 0;
typedef struct {
int extracted;
int kept;
bool quiet;
} extract_state;
static int
fsl_repo_checkout_f_my(fsl_repo_checkout_state const *coState)
{
enum {DATE_SHORT = 16};
fsl_repo_extract_state const * xs = coState->xState;
extract_state *state = (extract_state *)coState->callbackState;
static char tbuf[DATE_SHORT];
char mode = '?';
if(coState->fileWasWritten) {
mode = '+';
++state->extracted;
}else{
mode = '!';
++state->kept;
}
if(!state->quiet){
fsl_strftime_unix(tbuf, DATE_SHORT, "%d %b %Y", coState->mtime, 1);
f_out("[%c] %8"FSL_SIZE_T_PFMT" %s %s\n",
mode, coState->size, tbuf, xs->fc->name);
}
return 0;
}
int
main(int argc, char const * const *argv)
{
fsl_db *db = 0;
extract_state ex = {0,0,0};
fsl_cx *f = NULL;
fsl_id_t prevId = 0, rid = 0;
const char *sym = NULL;
bool force = false, keep = false, manifest = false,
mtime = false, q = false, doRm = true;
int rc = 0;
fcli_cliflag fcli_flags[] = {
FCLI_FLAG_BOOL("f", "force", &force, "Continue with the checkout "
"irrespective of any unsaved changes in the current checkout."),
FCLI_FLAG_BOOL(0, "keep", &keep, "Only checkout files from the requested "
"<version> that do not have a file of the same name already present on "
"disk. Files with the same name as those from the requested <version> will"
" remain unmodified irrespective of whether their content is consistent "
"with that of the requested <version>. In such a case, the checkout will "
"immediately be in a changed state, which 'f-status' will report."),
FCLI_FLAG_BOOL(0, "manifest", &manifest,
"Only modify the manifest and manifest.uuid files."),
FCLI_FLAG_BOOL("q", "quiet", &q,
"Suppress non-error output unless --verbose is used."),
FCLI_FLAG_BOOL(0, "setmtime", &mtime, "Set timestamps of all files to that "
"of the last check-in in which they were modified (i.e., manifest time)."),
FCLI_FLAG_BOOL_INVERT(0,"no-rm", &doRm,
"Do not delete files which were removed between the "
"original and new checkout versions."),
fcli_cliflag_empty_m
};
fcli_help_info fcli_help = {
"Change the current checkout to the requested <version> or to the tip of "
"the trunk if no <version> is specified.\n",
"[<version>]\n",
NULL
};
fcli.cliFlags = fcli_flags;
fcli.appHelp = &fcli_help;
rc = fcli_setup(argc, argv);
if (rc == FCLI_RC_HELP)
return 0;
else if (rc)
goto end;
f = fcli.f;
db = fsl_needs_checkout(f);
if(!db){
goto end;
}
verbose = fcli_is_verbose();
if(!verbose){
verbose = !q;
}
if(fcli_has_unused_flags(0)){
goto end;
}
rc = fsl_cx_transaction_begin(f);
if(rc) goto end;
fsl_checkout_version_info(f, &prevId, NULL);
fsl_checkout_changes_scan(f);
if(!force && fsl_checkout_has_changes(f)) {
fcli_err_set(FSL_RC_ALREADY_EXISTS,
"The current checkout contains unsaved changes.");
goto end;
}
if(force){
fsl_checkout_clear_db(f);
}
if(!(sym = fcli_next_arg(1))){
char * mainBranch = fsl_config_get_text(f, FSL_CONFDB_REPO,
"main-branch", 0);
if(mainBranch){
fcli_fax(mainBranch);
sym = mainBranch;
}else{
sym = "trunk";
}
}
rc = fsl_sym_to_rid(f, sym, FSL_SATYPE_CHECKIN, &rid);
if(rc) goto end;
if(prevId == rid){
f_out("Same version - nothing to do.\n");
goto end;
}
ex.extracted = 0;
ex.kept = 0;
ex.quiet = q;
fsl_repo_checkout_opt cOpt = fsl_repo_checkout_opt_empty;
cOpt.dryRun = fcli.fDryRun;
cOpt.vid = rid;
cOpt.callback = fsl_repo_checkout_f_my;
cOpt.callbackState = &ex;
cOpt.fileOverwritePolicy = keep ? FSL_OVERWRITE_NEVER : FSL_OVERWRITE_ALWAYS;
cOpt.rmMissingFiles = doRm;
rc = fsl_repo_checkout(f, &cOpt);
if(rc) goto end;
f_out("\nChecked out %d file(s) from %s [RID: %"FSL_ID_T_PFMT"].\n",
ex.extracted, sym, rid);
if(ex.kept){
f_out("%d file(s) left unchanged on disk\n", ex.kept);
}
if(cOpt.dryRun){
f_out("Dry-run mode. Rolling back transaction.\n");
rc = fsl_cx_transaction_end(f, true);
}
end:
if(fsl_cx_transaction_level(f)){
int const rc2 = fsl_cx_transaction_end(f, !!rc);
rc = rc ? rc : fsl_cx_uplift_db_error2(f, db, rc2);
}
return fcli_end_of_main(rc);
}