Index: f-apps/f-ci.c ================================================================== --- f-apps/f-ci.c +++ f-apps/f-ci.c @@ -1,16 +1,16 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* -*- 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 - Heavily indebted to the Fossil SCM project (https://fossil-scm.org). + Heavily indebted to the Fossil SCM project (https://fossil-scm.org). ***************************************************************************** This file implements a checkin [test] app using the libfossil API. */ @@ -147,16 +147,22 @@ bool fNoRCard = false; bool fBaseline = false; bool fDryRun = false; bool fAutoSync = true; bool fAllowFork = false; + bool fCloseBranch = false; fsl_id_bag bagCheck = fsl_id_bag_empty /* to verify that we know about any given filename/wildcard */; fcli_pre_setup()/*allocator setup we need for arg processing*/; fcli_cliflag FCliFlags[] = { FCLI_FLAG("m","message","text",&cMsg, "Commit message."), FCLI_FLAG("b","branch","branch-name",&cBranch, "New branch name."), + FCLI_FLAG_BOOL(0,"close",&fCloseBranch, + "Immediately close the new branch and do not update the " + "checkout to its version."), + FCLI_FLAG(0,"close-with-comment","comment", &cOpt.closeBranch, + "Works like --close but also adds a close-tag comment."), FCLI_FLAG_BOOL("n","dry-run", &fDryRun,"Dry-run mode."), FCLI_FLAG_BOOL("i","integrate",&cOpt.integrate, "Close all merge-parent branches, not just " "integrate-merge branches."), FCLI_FLAG_BOOL(0,"fork", &fAllowFork, @@ -237,11 +243,11 @@ /* TODO: the "multi-leaf" check, fail if we find one, and add a CLI flag to permit that case to succeed. */ } rc = fsl_cx_transaction_begin(f) - /* recall that we can't sync if a transaction is open: the transaction + /* recall that we can't sync if a transaction is open: the transaction locks the sync out. */; if(rc) goto end; rc = fcli_fingerprint_check(true); if(rc) goto end; fsl_ckout_version_info(f, &ckoutId, NULL) @@ -268,10 +274,12 @@ cOpt.messageMimeType = cMimeType; cOpt.dumpManifestFile = cDumpMf; cOpt.bgColor = cColor; cOpt.scanForChanges = false /*we'll do this ourselves*/; cOpt.branch = cBranch; + if( fCloseBranch && !cOpt.closeBranch ) cOpt.closeBranch = ""; + cOpt.writeCheckoutVersion = !(fCloseBranch || !!cOpt.closeBranch); fsl_checkin_queue_opt qOpt = fsl_checkin_queue_opt_empty; qOpt.relativeToCwd = true; qOpt.callback = fsl_checkin_queue_f_my; qOpt.callbackState = &enqueuedArgCount; @@ -350,11 +358,17 @@ if(rc) goto end; rc = fsl_checkin_commit(f, &cOpt, &newRid, &newUuid); if(rc) goto end; f_out("New version: %s (%"FSL_ID_T_PFMT")\n", newUuid, newRid); - + if( !cOpt.writeCheckoutVersion ){ + f_out( + "WARNING: because of the --close flag:\n" + " - NOT updating checkout to the new version.\n" + " - Local changes are retained.\n" + ); + } if(fcli_is_verbose()>1){ f_out("Post-commit vfile changed contents:\n"); fsl_db_each( fsl_cx_db_ckout(f), fsl_stmt_each_f_dump, NULL, "SELECT vf.vid, vf.id, substr(b.uuid,0,8) hash, chnged, " "deleted, vf.pathname " Index: include/fossil-scm/checkout.h ================================================================== --- include/fossil-scm/checkout.h +++ include/fossil-scm/checkout.h @@ -747,11 +747,18 @@ effectively switching the checkout to that version. It does not do this by default because it is sometimes necessary to have two versions in the vfile table at once and the operation doing so needs to control which version number is the current checkout. */ -FSL_VFILE_CKSIG_WRITE_CKOUT_VERSION = 0x010 +FSL_VFILE_CKSIG_WRITE_CKOUT_VERSION = 0x010, + +/** + Forces a re-load of the vfile table even if the checkout's version + matches the provided version. Normally the vfile load is skipped in + that case. +*/ +FSL_VFILE_CKSIG_RELOAD = 0x020 }; /** This function populates (if needed) the vfile table of f's checkout db for the given checkin version ID then compares files @@ -1155,10 +1162,26 @@ Reminder to self: when we do this, incorporate fsl_rm_empty_dirs(). */ bool rmRemovedFiles; + /** + Normally after checking in, the local checkout is updated to + point to the new checkin version. For cases where this should be + suppressed, set writeCheckoutVersion to false. An example is when + checking in to a new branch and closing it at the same time, + where we would typically want to stay on the originating branch. + + Added 2024-03-28. + + Design note: this should arguably be called updateCheckoutVersion, + but it's not in order to avoid any confusion about the meaning + of "update" in this context (no SCM-level update is performed). + Maybe we should call it stampCheckoutVersion? + */ + bool writeCheckoutVersion; + /** Whether to allow (or try to force) a delta manifest or not. 0 means no deltas allowed - it will generate a baseline manifest. Greater than 0 forces generation of a delta if possible (if one can be readily found) even if doing so would not @@ -1248,10 +1271,11 @@ true/*calcRCard*/, \ false/*integrate*/, \ false/*allowMergeConflict*/,\ true/*scanForChanges*/,\ false/*rmRemovedFiles*/,\ + true/*writeCheckoutVersion*/,\ 0/*deltaPolicy*/, \ 0.0/*julianTime*/, \ NULL/*closeBranch*/, \ NULL/*dumpManifestFile*/ \ } Index: src/checkin.c ================================================================== --- src/checkin.c +++ src/checkin.c @@ -1175,28 +1175,38 @@ if(opt->calcRCard) f->flags |= FSL_CX_F_CALC_R_CARD; else f->flags &= ~FSL_CX_F_CALC_R_CARD; rc = fsl_deck_save( d, opt->isPrivate ); RC; assert(d->rid>0); + assert(d->f == f); /* Now get vfile back into shape. We do not do a vfile scan because that loses state like add/rm-queued files. */ - rc = fsl_db_exec_multi(dbC, - "DELETE FROM vfile WHERE vid<>" - "%" FSL_ID_T_PFMT ";" - "UPDATE vfile SET vid=%" FSL_ID_T_PFMT ";" - "DELETE FROM vfile WHERE deleted AND " - "fsl_is_enqueued(id); " - "UPDATE vfile SET rid=mrid, mhash=NULL, " - "chnged=0, deleted=0, origname=NULL " - "WHERE fsl_is_enqueued(id)", - vid, d->rid); - if(!rc) rc = fsl__ckout_version_write(f, d->rid, NULL); - RC; - assert(d->f == f); - rc = fsl_checkin_add_unsent(f, d->rid); - RC; - rc = fsl__ckout_clear_merge_state(f, true); + if(opt->writeCheckoutVersion){ + rc = fsl_db_exec_multi(dbC, + "DELETE FROM vfile WHERE vid<>" + "%" FSL_ID_T_PFMT ";" + "UPDATE vfile SET vid=%" FSL_ID_T_PFMT ";" + "DELETE FROM vfile WHERE deleted AND " + "fsl_is_enqueued(id); " + "UPDATE vfile SET rid=mrid, mhash=NULL, " + "chnged=0, deleted=0, origname=NULL " + "WHERE fsl_is_enqueued(id)", + vid, d->rid); + RC; + rc = fsl__ckout_version_write(f, d->rid, NULL); + RC; + rc = fsl__ckout_clear_merge_state(f, true); + }else{ + /* Ensure that the checkout state is still such that the + changes just checked in are _still_ listed as changes + in the current status.*/ + rc = fsl_vfile_unload_except(f, vid); + RC; + rc = fsl_vfile_changes_scan(f, vid, FSL_VFILE_CKSIG_RELOAD); + } + RC; + rc = fsl_checkin_add_unsent(f, d->rid); RC; /* todo(?) from fossil(1) follows. Most of this seems to be what the vfile handling does (above). Index: src/checkout.c ================================================================== --- src/checkout.c +++ src/checkout.c @@ -1,6 +1,6 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* -*- 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 @@ -8,11 +8,11 @@ SPDX-ArtifactOfProjectName: Libfossil SPDX-FileType: Code Heavily indebted to the Fossil SCM project (https://fossil-scm.org). */ -/* +/* ***************************************************************************** This file houses the code for checkout-level APIS. */ #include @@ -2110,11 +2110,11 @@ rc = fsl_vfile_unload_except(f, tid); } if(!rc){ rc = fsl__ckout_version_write(f, tid, 0); } - + end: /* clang bug? If we declare rc2 here, it says "expression expected". Moving the decl to the top resolves it. Wha? */ if(rec.tgtDir) fsl__cx_scratchpad_yield(f, rec.tgtDir); if(bFullPath) fsl__cx_scratchpad_yield(f, bFullPath); Index: src/vfile.c ================================================================== --- src/vfile.c +++ src/vfile.c @@ -121,11 +121,11 @@ rc = fsl_stmt_step(&qIns); if(FSL_RC_STEP_DONE!=rc) break; else rc = 0; fsl_stmt_reset(&qIns); } - + end: fsl_stmt_finalize(&qIns); fsl_stmt_finalize(&qRid); /* Update f->ckout state and some db bits we need when changing the checkout. */ @@ -218,11 +218,18 @@ rc = fsl_cx_transaction_begin(f); if(rc){ fsl__cx_scratchpad_yield(f, fileCksum); return rc; } - if(f->ckout.rid != vid){ + if( FSL_VFILE_CKSIG_RELOAD & cksigFlags ){ + /* Because fsl_vfile_load() will skip scanning if vfile + contains vid, and has no flag to force it to do so, + we need to force it by removing that vfile set... */ + rc = fsl_vfile_unload(f, vid); + } + if( !rc && (f->ckout.rid != vid + || (FSL_VFILE_CKSIG_RELOAD & cksigFlags)) ){ rc = fsl_vfile_load(f, vid, (FSL_VFILE_CKSIG_KEEP_OTHERS & cksigFlags) ? false : true, NULL); } if(rc) goto end;