Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Changes In Branch port-annotate Excluding Merge-Ins
This is equivalent to a diff from 32ba71065d to e456c5ab39
2021-10-07
| ||
03:34 | Merged in annotate feature branch and fixed horrible diff calculation bug. check-in: e3d91a7fed user: stephan tags: trunk | |
03:28 | Remove all but -R and -V short form global options. To avoid collisions with app code, restrict global options to long-form, with the exception of the abovementioned two common options as discussed in /chat. check-in: 6d2c463307 user: mark tags: trunk | |
03:27 | Resolved the mind-numbing diff problem, caused by having used an (unsigned int) where a uint64_t was needed for the fsl_dline::h member (hash value overflow led to mismatches between this impl and fossil's). Annotate, at least in its basic form, now works (testing with ignoring whitespace and such is pending). Closed-Leaf check-in: e456c5ab39 user: stephan tags: port-annotate | |
02:02 | Eliminated some extraneous diff-internal code. check-in: 32ba71065d user: stephan tags: trunk | |
01:55 | Merged inadvertent fork. check-in: 81a7b3af8a user: stephan tags: trunk | |
2021-10-06
| ||
23:21 | Have narrowed down the source of the annotation difference (as it were) to the lower-level diff code, but _why_ libf is getting different results from fossil there (unless we force _both_ to do optimal matching on all diffs) is still a complete mystery. check-in: 0e55962dc9 user: stephan tags: port-annotate | |
Changes to f-apps/Makefile.in.
︙ | ︙ | |||
37 38 39 40 41 42 43 44 45 46 47 48 49 50 | endef F_BINS := \ f-_scratchpad \ f-_template \ f-acat \ f-add \ f-adiff \ f-aparse \ f-ci \ f-co \ f-config \ f-delta \ f-event \ f-ls \ | > | 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | endef F_BINS := \ f-_scratchpad \ f-_template \ f-acat \ f-add \ f-adiff \ f-annotate \ f-aparse \ f-ci \ f-co \ f-config \ f-delta \ f-event \ f-ls \ |
︙ | ︙ |
Added f-apps/f-annotate.c.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 | /* -*- 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 template application for libfossil fcli client apps, with commentary explaining how to do various common things with the API. Copy/paste this and modify it to suit. */ #include "fossil-scm/fossil-cli.h" #include "fossil-scm/fossil-internal.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_ { fsl_annotate_opt opt; } App = { fsl_annotate_opt_empty_m }; int main(int argc, const char * const * argv ){ bool ignoreAllSpace = false; bool ignoreEOLSpace = false; char const * zRevision = 0; char const * zOrigin = 0; fsl_buffer fnamebuf = fsl_buffer_empty; /** Set up flag handling, which is used for processing basic CLI flags and generating --help text output. */ const fcli_cliflag FCliFlags[] = { // FCLI_FLAG_xxx macros are convenience forms for initializing // these members... FCLI_FLAG_BOOL("p","praise",&App.opt.praise, "Use praise/blame mode."), FCLI_FLAG_BOOL("f","file-versions",&App.opt.fileVersions, "List file blob versions instead of checkin versions."), FCLI_FLAG_BOOL("w","ignore-all-space",&ignoreAllSpace, "Ignore all whitespace changes."), FCLI_FLAG_BOOL("Z","ignore-trailing-space",&ignoreEOLSpace, "Ignore end-of-line whitespace."), FCLI_FLAG("r","revision", "string",&zRevision, "Checkin containing input file (default=checkout)"), FCLI_FLAG("o","origin", "string", &zOrigin, "Origin Starting checkin version (default root of " "the tree)"), FCLI_FLAG("f","file", "filename",&App.opt.filename, "Repo-relative file to annote. May be provided as the " "first non-flag argument."), FCLI_FLAG_I32("n","limit", "int>=0",(int32_t*)&App.opt.limit, "Checkin containing input file (default=checkout)"), FCLI_FLAG_BOOL(NULL,"versions", &App.opt.dumpVersions, "Start output with a list of all analyzed versions."), fcli_cliflag_empty_m // list MUST end with this (or equivalent) }; const fcli_help_info FCliHelp = { "Outputs an annoted listing of a file's contents.", "[options] FILENAME", NULL // optional callback which outputs app-specific help }; fcli.cliFlags = FCliFlags; fcli.appHelp = &FCliHelp; int rc = fcli_setup(argc, argv); if(rc) goto end; fsl_cx * const f = fcli_cx(); if(!App.opt.filename){ App.opt.filename = fcli_next_arg(true); if(!App.opt.filename){ rc = fcli_err_set(FSL_RC_MISUSE, "Missing required filename argument. Try --help."); goto end; } } if(fsl_cx_db_ckout(f) && 0==fsl_stat(App.opt.filename, NULL, false)){ // We're in a checkout and the file exists. Canonicalize // the filename relative to the repo root... rc = fsl_ckout_filename_check(f, true, App.opt.filename, &fnamebuf); if(rc) goto end; App.opt.filename = fsl_buffer_cstr(&fnamebuf); } if(zRevision){ rc = fsl_sym_to_rid(f, zRevision, FSL_SATYPE_CHECKIN, &App.opt.versionRid); if(rc) goto end; }else{ fsl_ckout_version_info(f, &App.opt.versionRid, &zRevision); if(!zRevision){ rc = fcli_err_set(FSL_RC_MISUSE, "Cannot determine --revision value."); goto end; } } if(zOrigin){ rc = fsl_sym_to_rid(f, zOrigin, FSL_SATYPE_CHECKIN, &App.opt.originRid); if(rc) goto end; } if((rc=fcli_has_unused_args(false))) goto end; if(App.opt.limit<0) App.opt.limit = 0; if(ignoreAllSpace) App.opt.spacePolicy = 1; else if(ignoreEOLSpace) App.opt.spacePolicy = -1; App.opt.out = fsl_output_f_FILE; App.opt.outState = stdout; rc = fsl_annotate(f, &App.opt); end: fsl_buffer_clear(&fnamebuf); return fcli_end_of_main(rc) /* Will report any pending error state and return either EXIT_SUCCESS or EXIT_FAILURE. */; } |
Changes to include/fossil-scm/fossil-checkout.h.
︙ | ︙ | |||
44 45 46 47 48 49 50 | later. Corner case: a new repo with no checkins has an RID of 0 and a UUID of NULL. That does not happen with fossil-generated repositories, as those always "seed" the database with an initial commit artifact containing no files. */ | | | | 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 | later. Corner case: a new repo with no checkins has an RID of 0 and a UUID of NULL. That does not happen with fossil-generated repositories, as those always "seed" the database with an initial commit artifact containing no files. */ FSL_EXPORT void fsl_ckout_version_info(fsl_cx * const f, fsl_id_t * const rid, fsl_uuid_cstr * const uuid ); /** Given a fsl_cx with an opened checkout, and a filename, this function canonicalizes zOrigName to a form suitable for use as an in-repo filename, _appending_ the results to pOut. If pOut is NULL, it performs its normal checking but does not write a result, other than to return 0 for success. |
︙ | ︙ | |||
245 246 247 248 249 250 251 | Files queued for addition this way can be unqueued before they are committed using fsl_ckout_unmanage(). @see fsl_ckout_unmanage() @see fsl_reserved_fn_check() */ | | | | 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 | Files queued for addition this way can be unqueued before they are committed using fsl_ckout_unmanage(). @see fsl_ckout_unmanage() @see fsl_reserved_fn_check() */ FSL_EXPORT int fsl_ckout_manage( fsl_cx * const f, fsl_ckout_manage_opt * const opt ); /** Callback type for use with fsl_ckout_unmanage(). It is called by the removal process, immediately after a file is "removed" from SCM management (a.k.a. when the file becomes "unmanaged"). |
︙ | ︙ |
Changes to include/fossil-scm/fossil-core.h.
︙ | ︙ | |||
1792 1793 1794 1795 1796 1797 1798 | /** Returns the same as passing fsl_cx_db() to fsl_db_transaction_level(), or 0 if f has no db opened. @see fsl_cx_db() */ | | | | | 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 | /** Returns the same as passing fsl_cx_db() to fsl_db_transaction_level(), or 0 if f has no db opened. @see fsl_cx_db() */ FSL_EXPORT int fsl_cx_transaction_level(fsl_cx * const f); /** Returns the same as passing fsl_cx_db() to fsl_db_transaction_begin(). */ FSL_EXPORT int fsl_cx_transaction_begin(fsl_cx * const f); /** Returns the same as passing fsl_cx_db() to fsl_db_transaction_end(). */ FSL_EXPORT int fsl_cx_transaction_end(fsl_cx * const f, bool doRollback); /** Installs or (if f is NULL) uninstalls a confirmation callback for use by operations on f which require user confirmation. The exact implications of *not* installing a confirmer depend on the operation in question: see fsl_cx_confirm(). |
︙ | ︙ |
Changes to include/fossil-scm/fossil-repo.h.
︙ | ︙ | |||
1819 1820 1821 1822 1823 1824 1825 | - FSL_RC_OOM if an allocation fails. @see fsl_content_blob() @see fsl_content_size() */ | | > | > | 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 | - FSL_RC_OOM if an allocation fails. @see fsl_content_blob() @see fsl_content_size() */ FSL_EXPORT int fsl_content_get( fsl_cx * const f, fsl_id_t blobRid, fsl_buffer * const tgt ); /** Uses fsl_sym_to_rid() to convert sym to a record ID, then passes that to fsl_content_get(). Returns 0 on success. */ FSL_EXPORT int fsl_content_get_sym( fsl_cx * const f, char const * sym, fsl_buffer * const tgt ); /** Returns true if the given rid is marked as PRIVATE in f's current repository. Returns false (0) on error or if the content is not marked as private. */ FSL_EXPORT bool fsl_content_is_private(fsl_cx * f, fsl_id_t rid); |
︙ | ︙ | |||
2899 2900 2901 2902 2903 2904 2905 | If type is not FSL_SATYPE_ANY then it will only match artifacts of the specified type. In order to resolve arbitrary UUIDs, e.g. those of arbitrary blob content, type needs to be FSL_SATYPE_ANY. */ | | | | 2901 2902 2903 2904 2905 2906 2907 2908 2909 2910 2911 2912 2913 2914 2915 2916 | If type is not FSL_SATYPE_ANY then it will only match artifacts of the specified type. In order to resolve arbitrary UUIDs, e.g. those of arbitrary blob content, type needs to be FSL_SATYPE_ANY. */ FSL_EXPORT int fsl_sym_to_rid( fsl_cx * const f, char const * sym, fsl_satype_e type, fsl_id_t * rv ); /** Similar to fsl_sym_to_rid() but on success it returns a UUID string by assigning it to *rv (if rv is not NULL). If rid is not NULL then on success the db record ID corresponding to the returned UUID is assigned to *rid. The caller must eventually free the returned string memory by passing it to fsl_free(). Returns 0 if it finds a |
︙ | ︙ | |||
3256 3257 3258 3259 3260 3261 3262 | FSL_EXPORT int fsl_repo_manifest_write(fsl_cx *f, fsl_id_t manifestRid, fsl_buffer * const manifest, fsl_buffer * const manifestUuid, fsl_buffer * const manifestTags ); /** | | > > > > > > | 3258 3259 3260 3261 3262 3263 3264 3265 3266 3267 3268 3269 3270 3271 3272 3273 3274 3275 3276 3277 3278 | FSL_EXPORT int fsl_repo_manifest_write(fsl_cx *f, fsl_id_t manifestRid, fsl_buffer * const manifest, fsl_buffer * const manifestUuid, fsl_buffer * const manifestTags ); /** Configuration for use with fsl_annotate(). This structure holds options for the "annotate" operation and its close cousin, "blame" a.k.a. "praise." Annotation takes a given file version and builds a line-by-line history, showing when each line was last modified. The "blame" a.k.a. "praise" option includes *who* modified that line. */ struct fsl_annotate_opt { /** The repository-root-relative NUL-terminated filename to annotate. */ char const * filename; /** |
︙ | ︙ | |||
3279 3280 3281 3282 3283 3284 3285 3286 | TODO: figure out and explain the difference between versionRid and originRid. */ fsl_id_t originRid; /** The maximum number of versions to search through. */ | > > > > | | | | | > | > > > > > > > > > | > | | > > > > | 3287 3288 3289 3290 3291 3292 3293 3294 3295 3296 3297 3298 3299 3300 3301 3302 3303 3304 3305 3306 3307 3308 3309 3310 3311 3312 3313 3314 3315 3316 3317 3318 3319 3320 3321 3322 3323 3324 3325 3326 3327 3328 3329 3330 3331 3332 3333 3334 3335 3336 3337 3338 3339 3340 3341 3342 3343 3344 3345 3346 3347 3348 3349 3350 3351 3352 3353 3354 3355 3356 3357 3358 3359 3360 3361 3362 3363 3364 3365 3366 3367 3368 3369 3370 3371 3372 3373 3374 3375 3376 3377 3378 3379 3380 3381 3382 3383 3384 3385 3386 3387 3388 3389 | TODO: figure out and explain the difference between versionRid and originRid. */ fsl_id_t originRid; /** The maximum number of versions to search through. Note that fossil(1) offers the ability to limit the calculation based on processing time, e.g. to 1500ms. We may or may not add that in this library. */ uint32_t limit; /** - 0 = do not ignore any spaces. - <0 = ignore trailing end-of-line spaces. - >1 = ignore all spaces */ int16_t spacePolicy; /** If true, include the name of the user for which each change is attributed (noting that merges show whoever merged the change, which may differ from the original committer, and amended user names will be used over those in the initial commit). If false, show only version information. This option is alternately known as "blame". For reasons lost to history, blame/praise mode does not include line numbers. That may change in the future. */ bool praise; /** Output file blob versions, instead of checkin versions. */ bool fileVersions; /** If true, annotation output will start with a list of all versions analyzed by the annotation process. */ bool dumpVersions; /** The output channel for the resulting annotation. */ fsl_output_f out; /** State for passing as the first argument to this->out(). */ void * outState; }; /** Convenience typedef. */ typedef struct fsl_annotate_opt fsl_annotate_opt; /** Initialized-with-defaults fsl_annotate_opt structure, intended for const-copy initialization. */ #define fsl_annotate_opt_empty_m {\ NULL/*filename*/, \ 0/*versionRid*/,0/*originRid*/, \ 0U/*limit*/, 0/*spacePolicy*/, \ false/*praise*/, false/*fileVersions*/, \ false/*dumpVersions*/, \ NULL/*out*/, NULL/*outState*/ \ } /** Initialized-with-defaults fsl_annotate_opt structure, intended for non-const copy initialization. */ extern const fsl_annotate_opt fsl_annotate_opt_empty; /** UNDER CONSTRUCTION. Not yet known to be fully functional or bug-free. Runs an "annotation" of an SCM-controled file and sends the results to opt->out(). Returns 0 on success. On error, returns one of: - FSL_RC_OOM on OOM - FSL_RC_NOT_A_CKOUT if opt->versionRid<=0 and f has no opened checkout. - FSL_RC_NOT_FOUND if the given filename cannot be found in the repository OR a given version ID does not resolve to a blob. (Sorry about this ambiguity!) - FSL_RC_PHANTOM if a phantom blob is encountered while trying to annotate. opt->out() may return arbitrary non-0 result codes, in which case the returned code is propagated to the caller of this function. Results are undefined if either argument is invalid or opt->out is NULL. */ FSL_EXPORT int fsl_annotate( fsl_cx * const f, fsl_annotate_opt const * const opt ); #if defined(__cplusplus) } /*extern "C"*/ #endif #endif /* ORG_FOSSIL_SCM_FSL_REPO_H_INCLUDED */ |
Changes to include/fossil-scm/fossil-util.h.
︙ | ︙ | |||
3321 3322 3323 3324 3325 3326 3327 | This type is only in the public API for use by the fsl_diff_builder interface. It is not otherwise intended for public use. */ struct fsl_dline { /** The text of the line. Owned by higher-level code. */ const char *z; /** Hash of the line. Lower X bits are the length. */ | | | 3321 3322 3323 3324 3325 3326 3327 3328 3329 3330 3331 3332 3333 3334 3335 | This type is only in the public API for use by the fsl_diff_builder interface. It is not otherwise intended for public use. */ struct fsl_dline { /** The text of the line. Owned by higher-level code. */ const char *z; /** Hash of the line. Lower X bits are the length. */ uint64_t h; /** Indent of the line. Only !=0 with certain options */ unsigned short indent; /** number of bytes */ unsigned short n; /** 1+(Index of next line with same the same hash) */ unsigned int iNext; /** |
︙ | ︙ |
Changes to include/fossil-scm/fossil-vpath.h.
︙ | ︙ | |||
202 203 204 205 206 207 208 209 210 211 212 213 214 215 | compared to fossil(1)'s version! */ FSL_EXPORT int fsl_vpath_shortest_store_in_ancestor(fsl_cx * const f, fsl_id_t iFrom, fsl_id_t iTo, uint32_t *pSteps); /** Reconstructs path from path->pStart to path->pEnd, reversing its order by fiddling with the u->pTo fields. Unfortunately does not reverse after the initial creation/reversal :/. | > > > > > > > > > > > > > > | 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 | compared to fossil(1)'s version! */ FSL_EXPORT int fsl_vpath_shortest_store_in_ancestor(fsl_cx * const f, fsl_id_t iFrom, fsl_id_t iTo, uint32_t *pSteps); /** Computes a list of direct (non-merge) ancestors of the given checkin RID and stores it in the TEMP table [ancestor], which it creates if needed or clears if it currently exists. The [ancestor] schema is described in fsl_vpath_shortest_store_in_ancestor(). The [generation] value of the record corresponding to rid is 1, increasing by 1 for each generation back in the history. Returns 0 on success, FSL_RC_NOT_A_REPO if f has no repo db opened, and any number of lower-level result codes if something goes wrong. */ FSL_EXPORT int fsl_compute_direct_ancestors(fsl_cx * const f, fsl_id_t rid); /** Reconstructs path from path->pStart to path->pEnd, reversing its order by fiddling with the u->pTo fields. Unfortunately does not reverse after the initial creation/reversal :/. |
︙ | ︙ |
Changes to src/annotate.c.
︙ | ︙ | |||
11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | Heavily indebted to the Fossil SCM project (https://fossil-scm.org). */ /************************************************************************ This file implements the annoate/blame/praise-related APIs. */ #include "fossil-scm/fossil-internal.h" #include "fossil-scm/fossil-vpath.h" #include <assert.h> /* Only for debugging */ #include <stdio.h> #define MARKER(pfexp) \ do{ printf("MARKER: %s:%d:%s():\t",__FILE__,__LINE__,__func__); \ printf pfexp; \ } while(0) const fsl_annotate_opt fsl_annotate_opt_empty = fsl_annotate_opt_empty_m; #define TO_BE_STATIC /* ** The status of an annotation operation is recorded by an instance ** of the following structure. */ typedef struct Annotator Annotator; struct Annotator { fsl_diff_cx c; /* The diff-engine context */ struct AnnLine { /* Lines of the original files... */ | > > | > | | | | | | < | < | | > | | | | | > > > > > > > | > > | > | | > | | | > < > | | | | < | < | | | > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | < | > | > > > > > > > > > > > | | | > > > > > > | < < > > > > > > > > > | > > > > > > > > > > > > > > > | 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 | Heavily indebted to the Fossil SCM project (https://fossil-scm.org). */ /************************************************************************ This file implements the annoate/blame/praise-related APIs. */ #include "fossil-scm/fossil-internal.h" #include "fossil-scm/fossil-vpath.h" #include "fossil-scm/fossil-checkout.h" #include <assert.h> /* Only for debugging */ #include <stdio.h> #define MARKER(pfexp) \ do{ printf("MARKER: %s:%d:%s():\t",__FILE__,__LINE__,__func__); \ printf pfexp; \ } while(0) const fsl_annotate_opt fsl_annotate_opt_empty = fsl_annotate_opt_empty_m; #define TO_BE_STATIC /* ** The status of an annotation operation is recorded by an instance ** of the following structure. */ typedef struct Annotator Annotator; struct Annotator { fsl_diff_cx c; /* The diff-engine context */ fsl_buffer headVersion;/*starting version of the content*/ struct AnnLine { /* Lines of the original files... */ const char *z; /* The text of the line. Points into this->headVersion. */ short int n; /* Number of bytes (omitting trailing \n) */ short int iVers; /* Level at which tag was set */ } *aOrig; unsigned int nOrig;/* Number of elements in aOrig[] */ unsigned int nVers;/* Number of versions analyzed */ bool bMoreToDo; /* True if the limit was reached */ fsl_id_t origId; /* RID for the zOrigin version */ fsl_id_t showId; /* RID for the version being analyzed */ struct AnnVers { char *zFUuid; /* File being analyzed */ char *zMUuid; /* Check-in containing the file */ char *zDate; /* Date of the check-in */ char *zUser; /* Name of user who did the check-in */ } *aVers; /* For each check-in analyzed */ unsigned int naVers; /* # of entries allocated in this->aVers */ }; static const Annotator Annotator_empty = { fsl_diff_cx_empty_m, fsl_buffer_empty_m/*headVersion*/, NULL/*aOrig*/, 0U/*nOrig*/, 0U/*nVers*/, false/*bMoreToDo*/, 0/*origId*/, 0/*showId*/, NULL/*aVers*/, 0U/*naVerse*/ }; static void fsl__annotator_clean(Annotator * const a){ unsigned i; fsl__diff_cx_clean(&a->c); for(i = 0; i < a->nVers; ++i){ fsl_free(a->aVers[i].zFUuid); fsl_free(a->aVers[i].zMUuid); fsl_free(a->aVers[i].zDate); fsl_free(a->aVers[i].zUser); } fsl_free(a->aVers); fsl_free(a->aOrig); fsl_buffer_clear(&a->headVersion); } static uint64_t fsl__annotate_opt_difflags(fsl_annotate_opt const * const opt){ uint64_t diffFlags = FSL_DIFF2_STRIP_EOLCR; if(opt->spacePolicy>0) diffFlags |= FSL_DIFF2_IGNORE_ALLWS; else if(opt->spacePolicy<0) diffFlags |= FSL_DIFF2_IGNORE_EOLWS; return diffFlags; } /** Initializes the annocation process by populating `a` from a->toAnnote, which must have been previously populated. `a` must have already been cleanly initialized via copying from Annotator_empty and a->headVersion populated. Returns 0 on success, else: - FSL_RC_RANGE if pInput is empty. - FSL_RC_OOM on OOM. Regardless of success or failure, `a` must eventually be passed to fsl__annotator_clean() to free up any resources. */ static int fsl__annotation_start(Annotator * const a, fsl_annotate_opt const * const opt){ int rc; uint64_t const diffFlags = fsl__annotate_opt_difflags(opt); if(opt->spacePolicy>0){ a->c.cmpLine = fsl_dline_cmp_ignore_ws; }else{ assert(fsl_dline_cmp == a->c.cmpLine); } rc = fsl_break_into_dlines(fsl_buffer_cstr(&a->headVersion), (fsl_int_t)a->headVersion.used, (uint32_t*)&a->c.nTo, &a->c.aTo, diffFlags); if(rc) goto end; if(!a->c.nTo){ rc = FSL_RC_RANGE; goto end; } a->aOrig = fsl_malloc( (fsl_size_t)(sizeof(a->aOrig[0]) * a->c.nTo) ); if(!a->aOrig){ rc = FSL_RC_OOM; goto end; } for(int i = 0; i < a->c.nTo; ++i){ a->aOrig[i].z = a->c.aTo[i].z; a->aOrig[i].n = a->c.aTo[i].n; a->aOrig[i].iVers = -1; } a->nOrig = (unsigned)a->c.nTo; end: return rc; } /** The input pParent is the next most recent ancestor of the file being annotated. Do another step of the annotation. On success return 0 and, if additional annotation is required, assign *doMore (if not NULL) to true. */ static int fsl__annotation_step( Annotator * const a, fsl_buffer const *pParent, int iVers, fsl_annotate_opt const * const opt ){ int i, j, rc; int lnTo; uint64_t const diffFlags = fsl__annotate_opt_difflags(opt); /* Prepare the parent file to be diffed */ rc = fsl_break_into_dlines(fsl_buffer_cstr(pParent), (fsl_int_t)pParent->used, (uint32_t*)&a->c.nFrom, &a->c.aFrom, diffFlags); if(rc) goto end; else if( a->c.aFrom==0 ){ return 0; } //MARKER(("Line #1: %.*s\n", (int)a->c.aFrom[0].n, a->c.aFrom[0].z)); /* Compute the differences going from pParent to the file being ** annotated. */ rc = fsl__diff_all(&a->c); if(rc) goto end; /* Where new lines are inserted on this difference, record the ** iVers as the source of the new line. */ for(i=lnTo=0; i<a->c.nEdit; i+=3){ int const nCopy = a->c.aEdit[i]; int const nIns = a->c.aEdit[i+2]; lnTo += nCopy; for(j=0; j<nIns; ++j, ++lnTo){ if( a->aOrig[lnTo].iVers<0 ){ a->aOrig[lnTo].iVers = iVers; } } } /* Clear out the diff results except for c.aTo, as that's pointed to by a->aOrig.*/ fsl_free(a->c.aEdit); a->c.aEdit = 0; a->c.nEdit = 0; a->c.nEditAlloc = 0; /* Clear out the from file */ fsl_free(a->c.aFrom); a->c.aFrom = 0; a->c.nFrom = 0; end: return rc; } /* MISSING(?) fossil(1) converts the diff inputs into utf8 with no BOM. Whether we really want to do that here or rely on the caller to is up for debate. If we do it here, we have to make the inputs non-const, which seems "wrong" for a library API. */ #define blob_to_utf8_no_bom(A,B) (void)0 static int fsl__annotate_file(fsl_cx * const f, Annotator * const a, fsl_annotate_opt const * const opt){ int rc = FSL_RC_NYI; fsl_buffer step = fsl_buffer_empty /*previous revision*/; fsl_id_t cid = 0, fnid = 0; // , rid = 0; fsl_stmt q = fsl_stmt_empty; bool openedTransaction = false; fsl_db * const db = fsl_needs_repo(f); if(!db) return FSL_RC_NOT_A_REPO; rc = fsl_cx_transaction_begin(f); if(rc) goto dberr; openedTransaction = true; fnid = fsl_db_g_id(db, 0, "SELECT fnid FROM filename WHERE name=%Q %s", opt->filename, fsl_cx_filename_collation(f)); if(0==fnid){ rc = fsl_cx_err_set(f, FSL_RC_NOT_FOUND, "File not found in repository: %s", opt->filename); goto end; } if(opt->versionRid>0){ cid = opt->versionRid; }else{ fsl_ckout_version_info(f, &cid, NULL); if(cid<=0){ rc = fsl_cx_err_set(f, FSL_RC_NOT_A_CKOUT, "Cannot determine version RID to " "annotate from."); goto end; } } if(opt->originRid>0){ rc = fsl_vpath_shortest_store_in_ancestor(f, cid, opt->originRid, NULL); }else{ rc = fsl_compute_direct_ancestors(f, cid); } if(rc) goto end; rc = fsl_db_prepare(db, &q, "SELECT DISTINCT" " (SELECT uuid FROM blob WHERE rid=mlink.fid)," " (SELECT uuid FROM blob WHERE rid=mlink.mid)," " date(event.mtime)," " coalesce(event.euser,event.user)," " mlink.fid" " FROM mlink, event, ancestor" " WHERE mlink.fnid=%" FSL_ID_T_PFMT " AND ancestor.rid=mlink.mid" " AND event.objid=mlink.mid" " AND mlink.mid!=mlink.pid" " ORDER BY ancestor.generation;", fnid ); if(rc) goto dberr; while(FSL_RC_STEP_ROW==fsl_stmt_step(&q)){ if(a->nVers>=3){ /*Process at least 3 rows before imposing any limit. Note that we do not impose a time-based limit here like fossil does, but may want to add that at some point.*/ if(opt->limit>0 && a->nVers>=opt->limit){ a->bMoreToDo = true; break; } } char * zTmp = 0; char const * zCol = 0; fsl_size_t nCol = 0; fsl_id_t const rid = fsl_stmt_g_id(&q, 4); if(0==a->nVers){ rc = fsl_content_get(f, rid, &a->headVersion); if(rc) goto end; blob_to_utf8_no_bom(&a->headVersion,0); rc = fsl__annotation_start(a, opt); if(rc) goto end; a->bMoreToDo = opt->originRid>0; a->origId = opt->originRid; a->showId = cid; assert(0==a->nVers); assert(NULL==a->aVers); } if(a->naVers==a->nVers){ unsigned int const n = a->naVers ? a->naVers*3/2 : 10; void * const x = fsl_realloc(a->aVers, n*sizeof(a->aVers[0])); if(NULL==x){ rc = FSL_RC_OOM; goto end; } a->aVers = x; a->naVers = n; } #define AnnStr(COL,FLD) zCol = fsl_stmt_g_text(&q, COL, &nCol); \ zTmp = fsl_strndup(zCol, nCol); \ if(!zTmp){ rc = FSL_RC_OOM; goto end; } \ a->aVers[a->nVers].FLD = zTmp AnnStr(0,zFUuid); AnnStr(1,zMUuid); AnnStr(2,zDate); AnnStr(3,zUser); #undef AnnStr if( a->nVers>0 ){ rc = fsl_content_get(f, rid, &step); if(!rc){ rc = fsl__annotation_step(a, &step, a->nVers-1, opt); } fsl_buffer_reuse(&step); if(rc) goto end; } ++a->nVers; } assert(0==rc); if(0==a->nVers){ if(opt->versionRid>0){ rc = fsl_cx_err_set(f, FSL_RC_NOT_FOUND, "File [%s] does not exist " "in checkin RID %" FSL_ID_T_PFMT, opt->filename, opt->versionRid); }else{ rc = fsl_cx_err_set(f, FSL_RC_NOT_FOUND, "No history found for file: %s", opt->filename); } } end: fsl_buffer_clear(&step); fsl_stmt_finalize(&q); if(openedTransaction) fsl_cx_transaction_end(f, rc!=0); return rc; dberr: assert(openedTransaction); assert(rc!=0); fsl_stmt_finalize(&q); fsl_buffer_clear(&step); rc = fsl_cx_uplift_db_error2(f, db, rc); if(openedTransaction) fsl_cx_transaction_end(f, rc!=0); return rc; } /*TO_BE_STATIC int fann__out(fsl_annotate_opt const *const o, char const *z, fsl_size_t n){ return o->out(o->outState, z, n); }*/ static int fann__outf(fsl_annotate_opt const * const o, char const *fmt, ...){ int rc = 0; //MARKER(("fmt=%s\n", fmt)); va_list va; va_start(va,fmt); rc = fsl_appendfv(o->out, o->outState, fmt, va); va_end(va); //MARKER(("after appendfv\n")); return rc; } int fsl_annotate( fsl_cx * const f, fsl_annotate_opt const * const opt ){ int rc; Annotator ann = Annotator_empty; unsigned int i; assert(opt->out); rc = fsl__annotate_file(f, &ann, opt); if(rc) goto end; int const szHash = 10 /*# of hash bytes to show. TODO: move this into fsl_annotate_opt. */; if(opt->dumpVersions){ struct AnnVers *av; for(av = ann.aVers, i = 0; 0==rc && i < ann.nVers; ++i, ++av){ rc = fann__outf(opt, "version %3u: %s %.*s file %.*s\n", i+1, av->zDate, szHash, av->zMUuid, szHash, av->zFUuid); } if(!rc) rc = fann__outf(opt, "%.*c\n", 60, '-'); if(rc) goto end; } for(i = 0; 0==rc && i<ann.nOrig; ++i){ short iVers = ann.aOrig[i].iVers; char const * z = ann.aOrig[i].z; int const n = ann.aOrig[i].n; if(iVers<0 && !ann.bMoreToDo){ iVers = ann.nVers-1; } if(opt->praise){ if(iVers>=0){ struct AnnVers * const av = &ann.aVers[iVers]; rc = fann__outf(opt, "%.*s %s %13.13s: %.*s\n", szHash, opt->fileVersions ? av->zFUuid : av->zMUuid, av->zDate, av->zUser, n, z); }else{ rc = fann__outf(opt, "%*s %.*s\n", szHash+26, "", n, z); } }else{ if(iVers>=0){ struct AnnVers * const av = &ann.aVers[iVers]; rc = fann__outf(opt, "%.*s %s %5u: %.*s\n", szHash, opt->fileVersions ? av->zFUuid : av->zMUuid, av->zDate, i+1, n, z); }else{ rc = fann__outf(opt, "%*s %5u: %.*s\n", szHash+11, "", i+1, n, z); } } } end: fsl__annotator_clean(&ann); return rc; } #undef MARKER #undef TO_BE_STATIC #undef blob_to_utf8_no_bom |
Changes to src/checkout.c.
︙ | ︙ | |||
414 415 416 417 418 419 420 | CoAddState * cas = (CoAddState*)dst->callbackState; int rc = fsl_buffer_appendf(fsl_buffer_reuse(cas->absBuf), "%s/%s", dst->absoluteDir, dst->entryName); if(!rc) rc = co_add_one(cas, true); return rc; } | | | 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 | CoAddState * cas = (CoAddState*)dst->callbackState; int rc = fsl_buffer_appendf(fsl_buffer_reuse(cas->absBuf), "%s/%s", dst->absoluteDir, dst->entryName); if(!rc) rc = co_add_one(cas, true); return rc; } int fsl_ckout_manage( fsl_cx * const f, fsl_ckout_manage_opt * const opt_ ){ int rc = 0; CoAddState cas = CoAddState_empty; fsl_ckout_manage_opt opt; if(!f) return FSL_RC_MISUSE; else if(!fsl_needs_ckout(f)) return FSL_RC_NOT_A_CKOUT; assert(f->ckout.rid>=0); opt = *opt_ |
︙ | ︙ |
Changes to src/content.c.
︙ | ︙ | |||
145 146 147 148 149 150 151 | if(!rc) rc = fsl_stmt_step(s1); fsl_stmt_cached_yield(s1); } return rc==FSL_RC_STEP_ROW ? true : false; } | | > | | 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 | if(!rc) rc = fsl_stmt_step(s1); fsl_stmt_cached_yield(s1); } return rc==FSL_RC_STEP_ROW ? true : false; } int fsl_content_get( fsl_cx * const f, fsl_id_t rid, fsl_buffer * const tgt ){ fsl_db * const db = fsl_cx_db_repo(f); if(!tgt) return FSL_RC_MISUSE; else if(rid<=0){ return fsl_cx_err_set(f, FSL_RC_RANGE, "RID %"FSL_ID_T_PFMT" is out of range.", rid); } else if(!db){ |
︙ | ︙ | |||
311 312 313 314 315 316 317 | : &f->cache.arty.missing, rid); } return rc; } } | | > | 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 | : &f->cache.arty.missing, rid); } return rc; } } int fsl_content_get_sym( fsl_cx * const f, char const * sym, fsl_buffer * const tgt ){ int rc; fsl_db * db = f ? fsl_needs_repo(f) : NULL; fsl_id_t rid = 0; if(!f || !sym || !tgt) return FSL_RC_MISUSE; else if(!db) return FSL_RC_NOT_A_REPO; rc = fsl_sym_to_rid(f, sym, FSL_SATYPE_ANY, &rid); return rc ? rc : fsl_content_get(f, rid, tgt); |
︙ | ︙ |
Changes to src/cx.c.
︙ | ︙ | |||
1279 1280 1281 1282 1283 1284 1285 | if(!rc){ int const mode = vid ? -1 : 0; rc = fsl_ckout_manifest_write(f, mode, mode, mode, 0); } return rc; } | | > | 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 | if(!rc){ int const mode = vid ? -1 : 0; rc = fsl_ckout_manifest_write(f, mode, mode, mode, 0); } return rc; } void fsl_ckout_version_info(fsl_cx * const f, fsl_id_t * const rid, fsl_uuid_cstr * const uuid ){ if(uuid) *uuid = f->ckout.uuid; if(rid) *rid = f->ckout.rid>=0 ? f->ckout.rid : 0; } int fsl_ckout_db_search( char const * dirName, bool checkParentDirs, fsl_buffer * pOut ){ int rc; |
︙ | ︙ | |||
1809 1810 1811 1812 1813 1814 1815 | return old; } fsl_hashpolicy_e fsl_cx_hash_policy_get(fsl_cx const*f){ return f->cxConfig.hashPolicy; } | | | | > | | > | 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 | return old; } fsl_hashpolicy_e fsl_cx_hash_policy_get(fsl_cx const*f){ return f->cxConfig.hashPolicy; } int fsl_cx_transaction_level(fsl_cx * const f){ return f->dbMain ? fsl_db_transaction_level(f->dbMain) : 0; } int fsl_cx_transaction_begin(fsl_cx * const f){ int const rc = fsl_db_transaction_begin(f->dbMain); return rc ? fsl_cx_uplift_db_error2(f, f->dbMain, rc) : 0; } int fsl_cx_transaction_end(fsl_cx * const f, bool doRollback){ int const rc = fsl_db_transaction_end(f->dbMain, doRollback); return rc ? fsl_cx_uplift_db_error2(f, f->dbMain, rc) : 0; } void fsl_cx_confirmer(fsl_cx * f, fsl_confirmer const * newConfirmer, fsl_confirmer * prevConfirmer){ if(prevConfirmer) *prevConfirmer = f->confirmer; f->confirmer = newConfirmer ? *newConfirmer : fsl_confirmer_empty; |
︙ | ︙ |
Changes to src/diff.c.
︙ | ︙ | |||
50 51 52 53 54 55 56 | /* Annotation flags (any DIFF flag can be used as Annotation flag as well) */ #define ANN_FILE_VERS (((u64)0x20)<<32) /* Show file vers rather than commit vers */ #define ANN_FILE_ANCEST (((u64)0x40)<<32) /* Prefer check-ins in the ANCESTOR table */ /** Maximum length of a line in a text file, in bytes. (2**13 = 8192 bytes) */ | | | 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 | /* Annotation flags (any DIFF flag can be used as Annotation flag as well) */ #define ANN_FILE_VERS (((u64)0x20)<<32) /* Show file vers rather than commit vers */ #define ANN_FILE_ANCEST (((u64)0x40)<<32) /* Prefer check-ins in the ANCESTOR table */ /** Maximum length of a line in a text file, in bytes. (2**13 = 8192 bytes) */ #define LENGTH_MASK_SZ 15 #define LENGTH_MASK ((1<<LENGTH_MASK_SZ)-1) /* ANSI escape codes: https://en.wikipedia.org/wiki/ANSI_escape_code */ #define ANSI_COLOR_BLACK(BOLD) ((BOLD) ? "\x1b[30m" : "\x1b[30m") |
︙ | ︙ | |||
269 270 271 272 273 274 275 | return rc; } /* Minimum of two values */ | < | | 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 | return rc; } /* Minimum of two values */ static int minInt(int a, int b){ return a<b ? a : b; } /** Compute the optimal longest common subsequence (LCS) using an exhaustive search. This version of the LCS is only used for shorter input strings since runtime is O(N*N) where N is the input string length. */ |
︙ | ︙ | |||
348 349 350 351 352 353 354 | fsl_dline *pA, *pB; /* Pointers to lines */ int iSX, iSY, iEX, iEY; /* Current match */ int skew = 0; /* How lopsided is the match */ int dist = 0; /* Distance of match from center */ int mid; /* Center of the chng */ int iSXb, iSYb, iEXb, iEYb; /* Best match so far */ int iSXp, iSYp, iEXp, iEYp; /* Previous match */ | | | | 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 | fsl_dline *pA, *pB; /* Pointers to lines */ int iSX, iSY, iEX, iEY; /* Current match */ int skew = 0; /* How lopsided is the match */ int dist = 0; /* Distance of match from center */ int mid; /* Center of the chng */ int iSXb, iSYb, iEXb, iEYb; /* Best match so far */ int iSXp, iSYp, iEXp, iEYp; /* Previous match */ sqlite3_int64 bestScore; /* Best score so far */ sqlite3_int64 score; /* Score for current candidate LCS */ int span; /* combined width of the input sequences */ span = (iE1 - iS1) + (iE2 - iS2); bestScore = -10000; score = 0; iSXb = iSXp = iS1; iEXb = iEXp = iS1; |
︙ | ︙ | |||
396 397 398 399 400 401 402 | for(k=0; k<n && p->cmpLine(pA,pB)==0; k++, pA++, pB++){} iEX += k; iEY += k; skew = (iSX-iS1) - (iSY-iS2); if( skew<0 ) skew = -skew; dist = (iSX+iEX)/2 - mid; if( dist<0 ) dist = -dist; | | > > > > > > > > | > > < | | 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 | for(k=0; k<n && p->cmpLine(pA,pB)==0; k++, pA++, pB++){} iEX += k; iEY += k; skew = (iSX-iS1) - (iSY-iS2); if( skew<0 ) skew = -skew; dist = (iSX+iEX)/2 - mid; if( dist<0 ) dist = -dist; score = (iEX - iSX)*(sqlite3_int64)span - (skew + dist); if( score>bestScore ){ bestScore = score; iSXb = iSX; iSYb = iSY; iEXb = iEX; iEYb = iEY; }else if( iEX>iEXp ){ iSXp = iSX; iSYp = iSY; iEXp = iEX; iEYp = iEY; } } if( #if 0 1 /* CANNOT EXPLAIN why we get different diff results than fossil unless we use fsl_diff_optimal_lcs() despite using, insofar as i can tell, the same inputs. There is some aspect i'm overlooking. */ #else iSXb==iEXb && (sqlite3_int64)(iE1-iS1)*(iE2-iS2)<400 #endif ){ /* If no common sequence is found using the hashing heuristic and ** the input is not too big, use the expensive exact solution */ fsl__diff_optimal_lcs(p, iS1, iE1, iS2, iE2, piSX, piEX, piSY, piEY); }else{ *piSX = iSXb; *piSY = iSYb; *piEX = iEXb; *piEY = iEYb; } } void fsl__dump_triples(fsl_diff_cx const * const p, char const * zFile, int ln ){ // Compare this with (fossil xdiff --raw) on the same inputs fprintf(stderr,"%s:%d: Compare this with (fossil xdiff --raw) on the same inputs:\n", zFile, ln); for(int i = 0; p->aEdit[i] || p->aEdit[i+1] || p->aEdit[i+2]; i+=3){ printf(" copy %6d delete %6d insert %6d\n", p->aEdit[i], p->aEdit[i+1], p->aEdit[i+2]); } } |
︙ | ︙ | |||
514 515 516 517 518 519 520 | if( iE2<=iS2 ){ /* The second segment is empty */ return appendTriple(p, 0, iE1-iS1, 0); } /* Find the longest matching segment between the two sequences */ fsl__diff_lcs(p, iS1, iE1, iS2, iE2, &iSX, &iEX, &iSY, &iEY); | < < | 522 523 524 525 526 527 528 529 530 531 532 533 534 535 | if( iE2<=iS2 ){ /* The second segment is empty */ return appendTriple(p, 0, iE1-iS1, 0); } /* Find the longest matching segment between the two sequences */ fsl__diff_lcs(p, iS1, iE1, iS2, iE2, &iSX, &iEX, &iSY, &iEY); if( iEX>iSX ){ /* A common segment has been found. Recursively diff either side of the matching segment */ rc = diff_step(p, iS1, iSX, iS2, iSY); if(!rc){ if(iEX>iSX){ rc = appendTriple(p, iEX - iSX, 0, 0); |
︙ | ︙ | |||
540 541 542 543 544 545 546 | int fsl__diff_all(fsl_diff_cx * const p){ int mnE, iS, iE1, iE2; int rc = 0; /* Carve off the common header and footer */ iE1 = p->nFrom; iE2 = p->nTo; | < < < < < < < | 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 | int fsl__diff_all(fsl_diff_cx * const p){ int mnE, iS, iE1, iE2; int rc = 0; /* Carve off the common header and footer */ iE1 = p->nFrom; iE2 = p->nTo; while( iE1>0 && iE2>0 && p->cmpLine(&p->aFrom[iE1-1], &p->aTo[iE2-1])==0 ){ iE1--; iE2--; } mnE = iE1<iE2 ? iE1 : iE2; for(iS=0; iS<mnE && p->cmpLine(&p->aFrom[iS],&p->aTo[iS])==0; iS++){} /* do the difference */ if( iS>0 ){ rc = appendTriple(p, iS, 0, 0); if(rc) return rc; } rc = diff_step(p, iS, iE1, iS, iE2); //fsl__dump_triples(p, __FILE__, __LINE__); if(rc) return rc; else if( iE1<p->nFrom ){ rc = appendTriple(p, p->nFrom - iE1, 0, 0); if(rc) return rc; } /* Terminate the COPY/DELETE/INSERT triples with three zeros */ rc = fsl__diff_expand_edit(p, p->nEdit+3); if(0==rc){ if(p->aEdit ){ p->aEdit[p->nEdit++] = 0; p->aEdit[p->nEdit++] = 0; p->aEdit[p->nEdit++] = 0; //fsl__dump_triples(p, __FILE__, __LINE__); } } return rc; } /* Attempt to shift insertion or deletion blocks so that they begin and end on lines that are pure whitespace. In other words, try to transform |
︙ | ︙ | |||
1860 1861 1862 1863 1864 1865 1866 | for( i = 0; i < (int)(sizeof(aCols)/sizeof(aCols[0])); ++i ){ fsl_buffer_clear(&aCols[i]); } fsl_buffer_clear(&unesc); return rc; } | < | 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 | for( i = 0; i < (int)(sizeof(aCols)/sizeof(aCols[0])); ++i ){ fsl_buffer_clear(&aCols[i]); } fsl_buffer_clear(&unesc); return rc; } /** @internal Performs a text diff on two buffers, either streaming the output to the 3rd argument or returning the results as an array of copy/delete/insert triples via the final argument. ONE of the 3rd or final arguments must be set and the other |
︙ | ︙ | |||
2030 2031 2032 2033 2034 2035 2036 | #undef DIFF_HTML #undef DIFF_LINENO #undef DIFF_NOOPT #undef DIFF_INVERT #undef DIFF_CONTEXT_EX #undef DIFF_NOTTOOBIG #undef DIFF_STRIP_EOLCR | < | 2028 2029 2030 2031 2032 2033 2034 2035 2036 2037 2038 2039 2040 2041 | #undef DIFF_HTML #undef DIFF_LINENO #undef DIFF_NOOPT #undef DIFF_INVERT #undef DIFF_CONTEXT_EX #undef DIFF_NOTTOOBIG #undef DIFF_STRIP_EOLCR #undef SBS_LNA #undef SBS_TXTA #undef SBS_MKR #undef SBS_LNB #undef SBS_TXTB #undef ANN_FILE_VERS #undef ANN_FILE_ANCEST |
︙ | ︙ |
Changes to src/diff2.c.
︙ | ︙ | |||
163 164 165 166 167 168 169 | *pnLine = nLine; *pOut = a; return 0; } int fsl_dline_cmp(const fsl_dline * const pA, | | | | 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 | *pnLine = nLine; *pOut = a; return 0; } int fsl_dline_cmp(const fsl_dline * const pA, const fsl_dline * const pB){ if( pA->h!=pB->h ) return 1; return memcmp(pA->z,pB->z, pA->h&LENGTH_MASK); } int fsl_dline_cmp_ignore_ws(const fsl_dline * const pA, const fsl_dline * const pB){ unsigned short a = pA->indent, b = pB->indent; if( pA->h==pB->h ){ while( a<pA->n || b<pB->n ){ if( a<pA->n && b<pB->n && pA->z[a++] != pB->z[b++] ) return 1; while( a<pA->n && fsl_isspace(pA->z[a])) ++a; while( b<pB->n && fsl_isspace(pB->z[b])) ++b; } |
︙ | ︙ | |||
509 510 511 512 513 514 515 | } return n; } /* ** Minimum of two values */ | | | 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 | } return n; } /* ** Minimum of two values */ static int minInt2(int a, int b){ return a<b ? a : b; } /****************************************************************************/ /* ** Return the number between 0 and 100 that is smaller the closer pA and ** pB match. Return 0 for a perfect match. Return 100 if pA and pB are ** completely different. ** |
︙ | ︙ | |||
573 574 575 576 577 578 579 | c = (unsigned char)zB[i]; aNext[i] = aFirst[c]; aFirst[c] = i; } for(i=1; i<=nA-best; i++){ c = (unsigned char)zA[i]; for(j=aFirst[c]; j<nB-best && memcmp(&zA[i],&zB[j],best)==0; j = aNext[j]){ | | | 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 | c = (unsigned char)zB[i]; aNext[i] = aFirst[c]; aFirst[c] = i; } for(i=1; i<=nA-best; i++){ c = (unsigned char)zA[i]; for(j=aFirst[c]; j<nB-best && memcmp(&zA[i],&zB[j],best)==0; j = aNext[j]){ int limit = minInt2(nA-i, nB-j); for(k=best; k<=limit && zA[k+i]==zB[k+j]; k++){} if( k>best ) best = k; } } score = (best>=avg) ? 0 : (avg - best)*100/avg; #if 0 |
︙ | ︙ | |||
1118 1119 1120 1121 1122 1123 1124 | rc = FSL_RC_DIFF_BINARY; goto end; } /* Compute the difference */ rc = fsl__diff_all(&c); if(rc) goto end; | < | 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 | rc = FSL_RC_DIFF_BINARY; goto end; } /* Compute the difference */ rc = fsl__diff_all(&c); if(rc) goto end; if( ignoreWs && c.nEdit==6 && c.aEdit[1]==0 && c.aEdit[2]==0 ){ rc = FSL_RC_DIFF_WS_ONLY; goto end; } if( (cfg->diffFlags & FSL_DIFF2_NOTTOOBIG)!=0 ){ int i, m, n; int const * const a = c.aEdit; |
︙ | ︙ | |||
1199 1200 1201 1202 1203 1204 1205 | if(extra){ rc->pimpl = ((unsigned char *)rc)+sizeof(fsl_diff_builder); } } return rc; } | < < < < < < | | 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 | if(extra){ rc->pimpl = ((unsigned char *)rc)+sizeof(fsl_diff_builder); } } return rc; } static int fdb__out(fsl_diff_builder *const b, char const *z, fsl_size_t n){ return b->cfg->out(b->cfg->outState, z, n); } static int fdb__outf(fsl_diff_builder * const b, char const *fmt, ...){ int rc = 0; va_list va; assert(b->cfg->out); va_start(va,fmt); rc = fsl_appendfv(b->cfg->out, b->cfg->outState, fmt, va); va_end(va); return rc; } static int fdb__debug_start(fsl_diff_builder * const b){ int rc = fdb__outf(b, "DEBUG builder starting up.\n"); |
︙ | ︙ |
Changes to src/repo.c.
︙ | ︙ | |||
221 222 223 224 225 226 227 | } return ans; oom: fsl_cx_err_set(f, FSL_RC_OOM, NULL); return -1; } | | | | 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 | } return ans; oom: fsl_cx_err_set(f, FSL_RC_OOM, NULL); return -1; } int fsl_sym_to_rid( fsl_cx * const f, char const * sym, fsl_satype_e type, fsl_id_t * rv ){ fsl_id_t rid = 0; fsl_id_t vid; fsl_size_t symLen; /* fsl_int_t i; */ fsl_db * dbR = fsl_cx_db_repo(f); fsl_db * dbC = fsl_cx_db_ckout(f); bool startOfBranch = 0; |
︙ | ︙ |
Changes to src/vpath.c.
︙ | ︙ | |||
197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 | } } end: fsl_stmt_finalize(&s); fsl_vpath_clear(path); return rc; } int fsl_vpath_shortest_store_in_ancestor(fsl_cx * const f, fsl_id_t iFrom, fsl_id_t iTo, uint32_t *pSteps){ int rc; fsl_vpath path = fsl_vpath_empty; fsl_stmt ins = fsl_stmt_empty; fsl_db * const db = fsl_needs_repo(f); fsl_vpath_node * node; int32_t gen = 0; if(!db) return FSL_RC_NOT_A_REPO; rc = fsl_vpath_shortest(f, &path, iFrom, iTo, true, false); if(rc) goto end; | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | < < < < < | | 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 | } } end: fsl_stmt_finalize(&s); fsl_vpath_clear(path); return rc; } /** Creates, if needed, the [ancestor] table, else clears its contents. Returns */ static int fsl__init_ancestor(fsl_cx * const f){ fsl_db * const db = fsl_cx_db_repo(f); int rc; if(db){ rc = fsl_db_exec_multi(db, "CREATE TEMP TABLE IF NOT EXISTS ancestor(" " rid INT UNIQUE," " generation INTEGER PRIMARY KEY" ");" "DELETE FROM TEMP.ancestor;"); }else{ rc = fsl_cx_err_set(f, FSL_RC_NOT_A_REPO, "Cannot compute ancestors without an " "opened repository."); } return rc ? fsl_cx_uplift_db_error2(f, db, rc) : 0; } int fsl_compute_direct_ancestors(fsl_cx * const f, fsl_id_t rid){ int rc = fsl__init_ancestor(f); fsl_db * const db = rc ? NULL : fsl_needs_repo(f); if(rc) return rc; assert(db); return fsl_db_exec_multi(db, "WITH RECURSIVE g(x,i) AS (" " VALUES(%" FSL_ID_T_PFMT ",1)" " UNION ALL" " SELECT plink.pid, g.i+1 FROM plink, g" " WHERE plink.cid=g.x AND plink.isprim)" "INSERT INTO ancestor(rid,generation) SELECT x,i FROM g;", rid ); } int fsl_vpath_shortest_store_in_ancestor(fsl_cx * const f, fsl_id_t iFrom, fsl_id_t iTo, uint32_t *pSteps){ int rc; fsl_vpath path = fsl_vpath_empty; fsl_stmt ins = fsl_stmt_empty; fsl_db * const db = fsl_needs_repo(f); fsl_vpath_node * node; int32_t gen = 0; if(!db) return FSL_RC_NOT_A_REPO; rc = fsl_vpath_shortest(f, &path, iFrom, iTo, true, false); if(rc) goto end; rc = fsl__init_ancestor(f); if(rc) goto end; rc = fsl_db_prepare(db, &ins, "INSERT INTO TEMP.ancestor(rid, generation) " "VALUES(?,?)"); if(rc) goto dberr; for(node = fsl_vpath_first(&path); node; node = fsl_vpath_next(node)){ rc = fsl_stmt_bind_step(&ins, "Ri", node->rid, ++gen); |
︙ | ︙ |