Fossil SCM

Mini-checkin can now create deltas from deltas, inheriting the F-cards from the delta while using the same baseline as its parent delta. Honor the new forbid-delta-manifests config setting.

stephan 2020-04-30 16:09 UTC checkin-without-checkout
Commit d8d9929c403eeed2cc14459033ad8ef84ae69878024f14a4aaec819fa5c643b2
1 file changed +171 -84
+171 -84
--- src/checkin.c
+++ src/checkin.c
@@ -2796,10 +2796,17 @@
27962796
** A "stronger hint" to checkin_mini() to prefer creation of a delta
27972797
** manifest if it at all can. It will decide not to only if creation
27982798
** of a delta is not a realistic option. For this to work, it must be
27992799
** set together with the CIMINI_PREFER_DELTA flag, but the two cannot
28002800
** be combined in this enum.
2801
+**
2802
+** This option is ONLY INTENDED FOR TESTING, used in bypassing
2803
+** heuristics which may otherwise disable generation of a delta on the
2804
+** grounds of efficiency (e.g. not generating a delta if the parent
2805
+** non-delta only has a few F-cards).
2806
+**
2807
+** The forbid-delta-manifests repo config option trumps this.
28012808
*/
28022809
CIMINI_STRONGLY_PREFER_DELTA = 1<<7,
28032810
/*
28042811
** Tells checkin_mini() to permit the addition of a new file. Normally
28052812
** this is disabled because there are many cases where it could cause
@@ -2806,109 +2813,184 @@
28062813
** the inadvertent addition of a new file when an update to an
28072814
** existing was intended, as a side-effect of name-case differences.
28082815
*/
28092816
CIMINI_ALLOW_NEW_FILE = 1<<8
28102817
};
2818
+
2819
+/*
2820
+** Internal helper which returns an F-card perms string suitable for
2821
+** writing into a manifest.
2822
+*/
2823
+static const char * mfile_permint_mstring(int perm){
2824
+ switch(perm){
2825
+ case PERM_EXE: return " x";
2826
+ case PERM_LNK: return " l";
2827
+ default: return "";
2828
+ }
2829
+}
2830
+
2831
+static const char * mfile_perm_mstring(const ManifestFile * p){
2832
+ return mfile_permint_mstring(manifest_file_mperm(p));
2833
+}
28112834
28122835
/*
28132836
** Handles the F-card parts for create_manifest_mini().
28142837
**
2815
-** If asDelta is true, pCI->pParent MUST be a baseline, else an
2816
-** assert() is triggered. Still TODO is creating a delta from
2817
-** pCI->pParent when that object is itself a delta.
2838
+** If asDelta is true, F-cards will be handled as for a delta
2839
+** manifest, and the caller MUST have added a B-card to pOut before
2840
+** calling this.
28182841
**
28192842
** Returns 1 on success, 0 on error, and writes any error message to
28202843
** pErr (if it's not NULL).
28212844
*/
28222845
static int create_manifest_mini_fcards( Blob * pOut,
28232846
CheckinMiniInfo * pCI,
28242847
int asDelta,
28252848
Blob * pErr){
2826
- ManifestFile *zFile; /* One file entry from the pCI->pParent*/
2827
- const char *zPerm = 0; /* permissions for new F-card */
2849
+ ManifestFile *zFile; /* One file entry from pCI->pParent */
28282850
const char *zFilename = 0; /* filename for new F-card */
28292851
const char *zUuid = 0; /* UUID for new F-card */
28302852
int cmp = 0; /* filename comparison result */
2853
+ int iFCursor = 0; /* Cursor into pCI->pParent->aFile if
2854
+ pCI->pParent is a delta. */
2855
+ int postProcess = -1; /* How to traverse post-pCI->zFilename
2856
+ F-cards at the end of the function:
2857
+ 0=none, 1=normal traversal,
2858
+ 2=delta-clone tranversal. */
28312859
int (*fncmp)(char const *, char const *) = /* filename comparator */
28322860
filenames_are_case_sensitive()
28332861
? fossil_strcmp
28342862
: fossil_stricmp;
28352863
#define mf_err(EXPR) if(pErr) blob_appendf EXPR; return 0
28362864
28372865
assert(pCI->filePerm!=PERM_LNK && "This should have been validated before.");
2838
- assert(pCI->filePerm>=0 && "Must have been set by the caller.");
2839
-
2840
- manifest_file_rewind(pCI->pParent);
2841
- if(asDelta){
2842
- /* Parent is a baseline and we have only 1 file to modify, so this
2843
- ** is the simplest case...
2844
- */
2845
- assert(pCI->pParent->zBaseline==0 && "Delta-from-delta is NYI.");
2846
- zFile = manifest_file_find(pCI->pParent, pCI->zFilename);
2847
- if(zFile==0){
2848
- /* New file */
2849
- zFilename = pCI->zFilename;
2850
- }else{
2851
- /* Replacement file */
2852
- if(manifest_file_mperm(zFile)==PERM_LNK){
2853
- goto err_no_symlink;
2854
- }
2855
- zFilename = zFile->zName
2856
- /* use original name in case of name-case difference */;
2857
- zFile = 0;
2858
- }
2859
- }else{
2860
- /* Non-delta: write F-cards which lexically preceed pCI->zFilename */
2861
- while((zFile = manifest_file_next(pCI->pParent, 0))
2862
- && (cmp = fncmp(zFile->zName, pCI->zFilename))<0){
2863
- blob_appendf(pOut, "F %F %s%s%s\n", zFile->zName, zFile->zUuid,
2864
- (zFile->zPerm && *zFile->zPerm) ? " " : "",
2865
- (zFile->zPerm && *zFile->zPerm) ? zFile->zPerm : "");
2866
- }
2867
- /* Figure out file perms and name to save... */
2868
- if(zFile!=0 && cmp==0){
2869
- /* Match: override this F-card */
2870
- if(manifest_file_mperm(zFile)==PERM_LNK){
2871
- goto err_no_symlink;
2872
- }
2873
- zFilename = zFile->zName
2874
- /* use original name in case of name-case difference */;
2875
- zFile = 0;
2876
- }else{
2877
- /* This is a new file. */
2878
- assert(zFile==0);
2879
- zFilename = pCI->zFilename;
2880
- }
2881
- }
2882
- if(PERM_LNK==pCI->filePerm){
2883
- goto err_no_symlink;
2884
- }else if(PERM_EXE==pCI->filePerm){
2885
- zPerm = " x";
2886
- }else{
2887
- zPerm = "";
2888
- }
2889
- zUuid = blob_str(&pCI->fileHash);
2890
- assert(zFile ? cmp>0&&asDelta==0 : 1);
2891
- assert(zFilename);
2892
- assert(zUuid);
2893
- assert(zPerm);
2894
- blob_appendf(pOut, "F %F %s%s\n", zFilename, zUuid, zPerm);
2895
- /* Non-delta: write F-cards which lexically follow pCI->zFilename */
2896
- while(zFile!=0){
2866
+ assert(pCI->filePerm==PERM_REG || pCI->filePerm==PERM_EXE);
2867
+ if(PERM_LNK==pCI->filePerm){
2868
+ goto err_no_symlink;
2869
+ }
2870
+ manifest_file_rewind(pCI->pParent);
2871
+ if(asDelta){
2872
+ if(pCI->pParent->zBaseline==0 || pCI->pParent->nFile==0 ){
2873
+ /* Parent is a baseline or a delta with no F-cards, so this is
2874
+ ** the simplest case: create a delta with a single F-card.
2875
+ */
2876
+ zFile = manifest_file_find(pCI->pParent, pCI->zFilename);
2877
+ if(zFile==0){/* New file */
2878
+ zFilename = pCI->zFilename;
2879
+ }else{/* Replacement file */
2880
+ if(manifest_file_mperm(zFile)==PERM_LNK){
2881
+ goto err_no_symlink;
2882
+ }
2883
+ zFilename = zFile->zName
2884
+ /* use original name in case of name-case difference */;
2885
+ }
2886
+ postProcess = 0;
2887
+ }else{
2888
+ /* Parent is a delta manifest with F-cards. Traversal of delta
2889
+ ** manifest file entries is normally done via
2890
+ ** manifest_file_next(), which takes into account the
2891
+ ** differences between the delta and its parent and returns
2892
+ ** F-cards from both. Each successive delta from the same
2893
+ ** baseline includes all F-card changes from the previous
2894
+ ** deltas, so we instead "clone" the parent's F-cards except for
2895
+ ** the one (if any) which matches the new file.
2896
+ */
2897
+ Manifest * p = pCI->pParent;
2898
+ cmp = -1;
2899
+ assert(p->nFile > 0);
2900
+ iFCursor = 0;
2901
+ zFile = &p->aFile[iFCursor];
2902
+ /* Write F-cards which lexically preceed pCI->zFilename */
2903
+ for( ; iFCursor<p->nFile; ){
2904
+ zFile = &p->aFile[iFCursor];
2905
+ cmp = fncmp(zFile->zName, pCI->zFilename);
2906
+ if(cmp<0){
2907
+ ++iFCursor;
2908
+ if(zFile->zUuid){
2909
+ blob_appendf(pOut, "F %F %s%s\n", zFile->zName,
2910
+ zFile->zUuid, mfile_perm_mstring(zFile));
2911
+ }else{/* File was removed in parent delta */
2912
+ blob_appendf(pOut, "F %F\n", zFile->zName);
2913
+ }
2914
+ }else{
2915
+ break;
2916
+ }
2917
+ }
2918
+ if(0==cmp){/* Match: override this F-card */
2919
+ assert(zFile);
2920
+ if(manifest_file_mperm(zFile)==PERM_LNK){
2921
+ goto err_no_symlink;
2922
+ }
2923
+ ++iFCursor;
2924
+ zFilename = zFile->zName
2925
+ /* use original name in case of name-case difference */;
2926
+ }else{/* This is a new file */
2927
+ zFilename = pCI->zFilename;
2928
+ }
2929
+ postProcess = 2;
2930
+ }
2931
+ }else{/* Non-delta: write F-cards which lexically preceed
2932
+ pCI->zFilename */
2933
+ cmp = -1;
2934
+ while((zFile = manifest_file_next(pCI->pParent, 0))
2935
+ && (cmp = fncmp(zFile->zName, pCI->zFilename))<0){
2936
+ blob_appendf(pOut, "F %F %s%s\n", zFile->zName, zFile->zUuid,
2937
+ mfile_perm_mstring(zFile));
2938
+ }
2939
+ if(cmp==0){/* Match: override this F-card*/
2940
+ if(manifest_file_mperm(zFile)==PERM_LNK){
2941
+ goto err_no_symlink;
2942
+ }
2943
+ zFilename = zFile->zName
2944
+ /* use original name in case of name-case difference */;
2945
+ }else{/* This is a new file. */
2946
+ zFilename = pCI->zFilename;
2947
+ if(zFile!=0){
2948
+ assert(cmp>0);
2949
+ assert(pCI->pParent->iFile>0);
2950
+ --pCI->pParent->iFile
2951
+ /* So that the post-processing loop picks up this file
2952
+ again.*/;
2953
+ }
2954
+ }
2955
+ postProcess = 1;
2956
+ }
2957
+ /* Finally add the new file's F-card... */
2958
+ zFile = 0;
2959
+ zUuid = blob_str(&pCI->fileHash);
2960
+ assert(zFilename);
2961
+ assert(zUuid);
2962
+ assert(postProcess==0 || postProcess==1 || postProcess==2);
2963
+ blob_appendf(pOut, "F %F %s%s\n", zFilename, zUuid,
2964
+ mfile_permint_mstring(pCI->filePerm));
2965
+ while(postProcess>0){
2966
+ /* Write F-cards which lexically follow pCI->zFilename */
2967
+ if(postProcess==1){ /* non-delta parent */
2968
+ zFile = manifest_file_next(pCI->pParent, 0);
2969
+ }else{ /* clone directly from delta parent */
2970
+ zFile = iFCursor<pCI->pParent->nFile
2971
+ ? &pCI->pParent->aFile[iFCursor++] : 0;
2972
+ }
2973
+ if(zFile==0){
2974
+ break;
2975
+ }
28972976
#ifndef NDEBUG
28982977
cmp = fncmp(zFile->zName, pCI->zFilename);
28992978
assert(cmp>0);
29002979
if(cmp<=0){
29012980
mf_err((pErr,"Internal error: mis-ordering of "
29022981
"F-cards detected."));
29032982
}
29042983
#endif
2905
- blob_appendf(pOut, "F %F %s%s%s\n",
2906
- zFile->zName, zFile->zUuid,
2907
- (zFile->zPerm && *zFile->zPerm) ? " " : "",
2908
- (zFile->zPerm && *zFile->zPerm) ? zFile->zPerm : "");
2909
- zFile = manifest_file_next(pCI->pParent, 0);
2984
+ if(zFile->zUuid){
2985
+ blob_appendf(pOut, "F %F %s%s\n", zFile->zName, zFile->zUuid,
2986
+ mfile_perm_mstring(zFile));
2987
+ }else{
2988
+ assert(postProcess==2);
2989
+ /* File was removed from parent delta. */
2990
+ blob_appendf(pOut, "F %F\n", zFile->zName);
2991
+ }
29102992
}
29112993
return 1;
29122994
err_no_symlink:
29132995
mf_err((pErr,"Cannot commit or overwrite symlinks "
29142996
"via mini-checkin."));
@@ -2951,23 +3033,21 @@
29513033
** sensible way to handle a symlink add/checkin without a
29523034
** checkout.
29533035
*/
29543036
blob_zero(pOut);
29553037
manifest_file_rewind(pCI->pParent) /* force load of baseline */;
2956
- if(((CIMINI_PREFER_DELTA & pCI->flags)
2957
- && pCI->pParent->zBaseline==0 /* parent is not a delta */
2958
- /* ^^^ TODO allow creation of a delta from a delta */
2959
- )&&(
2960
- CIMINI_STRONGLY_PREFER_DELTA & pCI->flags
2961
- || (pCI->pParent->pBaseline
2962
- ? pCI->pParent->pBaseline
2963
- : pCI->pParent)->nFile > 10
2964
- /* 10 is arbitrary: don't create a delta when there is only a
2965
- ** tiny gain for doing so. */)){
3038
+
3039
+ if((CIMINI_PREFER_DELTA & pCI->flags)
3040
+ && ((CIMINI_STRONGLY_PREFER_DELTA & pCI->flags)
3041
+ || (pCI->pParent->pBaseline
3042
+ ? pCI->pParent->pBaseline
3043
+ : pCI->pParent)->nFile > 10
3044
+ /* 10 is arbitrary: don't create a delta when there is only a
3045
+ ** tiny gain for doing so. */)
3046
+ && !db_get_boolean("forbid-delta-manifests",0)
3047
+ ){
29663048
asDelta = 1;
2967
- }
2968
- if(asDelta){
29693049
blob_appendf(pOut, "B %s\n",
29703050
pCI->pParent->zBaseline
29713051
? pCI->pParent->zBaseline
29723052
: pCI->zParentUuid);
29733053
}
@@ -3058,10 +3138,11 @@
30583138
zProjCode)){
30593139
fossil_fatal("Never, ever run this in/on the core fossil repo "
30603140
"in non-dry-run mode until it's been well-vetted. "
30613141
"Use a temp/test repo.");
30623142
}
3143
+ fossil_free(zProjCode);
30633144
}
30643145
db_begin_transaction();
30653146
30663147
if(pCI->pParent==0){
30673148
pCI->pParent = manifest_get_by_name(pCI->zParentUuid, 0);
@@ -3140,11 +3221,14 @@
31403221
** case-sensitivity mismatch between the user/repo/filesystem, or
31413222
** some such.
31423223
*/
31433224
manifest_file_rewind(pCI->pParent);
31443225
zFilePrev = manifest_file_find(pCI->pParent, pCI->zFilename);
3145
- if(!zFilePrev && !(CIMINI_ALLOW_NEW_FILE & pCI->flags)){
3226
+ if(!(CIMINI_ALLOW_NEW_FILE & pCI->flags)
3227
+ && (!zFilePrev
3228
+ || !zFilePrev->zUuid/*was removed from parent delta manifest*/)
3229
+ ){
31463230
ci_err((pErr,"File [%s] not found in manifest [%S]. "
31473231
"Adding new files is currently not permitted.",
31483232
pCI->zFilename, pCI->zParentUuid));
31493233
}else if(zFilePrev
31503234
&& manifest_file_mperm(zFilePrev)==PERM_LNK){
@@ -3210,11 +3294,12 @@
32103294
assert(blob_size(&pCI->fileHash)>0);
32113295
}
32123296
if(zFilePrev){
32133297
/* Has this file been changed since its previous commit? */
32143298
assert(blob_size(&pCI->fileHash));
3215
- if(0==fossil_strcmp(zFilePrev->zUuid, blob_str(&pCI->fileHash))){
3299
+ if(0==fossil_strcmp(zFilePrev->zUuid, blob_str(&pCI->fileHash))
3300
+ && manifest_file_mperm(zFilePrev)==pCI->filePerm){
32163301
ci_err((pErr,"File is unchanged. Not saving."));
32173302
}
32183303
}
32193304
/* Create, save, deltify, and crosslink the manifest... */
32203305
if(create_manifest_mini(&mf, pCI, pErr)==0){
@@ -3284,11 +3369,13 @@
32843369
** --convert-eol Convert EOL style of the checkin to match
32853370
** the previous version's content. Does not
32863371
** modify the input file, only the checked-in
32873372
** content.
32883373
** --delta Prefer to generate a delta manifest, if
3289
-** able.
3374
+** able. The forbid-delta-manifests repo
3375
+** config option trumps this, as do certain
3376
+** heuristics.
32903377
** --allow-new-file Allow addition of a new file this way.
32913378
** Disabled by default to avoid that case-
32923379
** sensitivity errors inadvertently lead to
32933380
** adding a new file where an update is
32943381
** intended.
32953382
--- src/checkin.c
+++ src/checkin.c
@@ -2796,10 +2796,17 @@
2796 ** A "stronger hint" to checkin_mini() to prefer creation of a delta
2797 ** manifest if it at all can. It will decide not to only if creation
2798 ** of a delta is not a realistic option. For this to work, it must be
2799 ** set together with the CIMINI_PREFER_DELTA flag, but the two cannot
2800 ** be combined in this enum.
 
 
 
 
 
 
 
2801 */
2802 CIMINI_STRONGLY_PREFER_DELTA = 1<<7,
2803 /*
2804 ** Tells checkin_mini() to permit the addition of a new file. Normally
2805 ** this is disabled because there are many cases where it could cause
@@ -2806,109 +2813,184 @@
2806 ** the inadvertent addition of a new file when an update to an
2807 ** existing was intended, as a side-effect of name-case differences.
2808 */
2809 CIMINI_ALLOW_NEW_FILE = 1<<8
2810 };
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2811
2812 /*
2813 ** Handles the F-card parts for create_manifest_mini().
2814 **
2815 ** If asDelta is true, pCI->pParent MUST be a baseline, else an
2816 ** assert() is triggered. Still TODO is creating a delta from
2817 ** pCI->pParent when that object is itself a delta.
2818 **
2819 ** Returns 1 on success, 0 on error, and writes any error message to
2820 ** pErr (if it's not NULL).
2821 */
2822 static int create_manifest_mini_fcards( Blob * pOut,
2823 CheckinMiniInfo * pCI,
2824 int asDelta,
2825 Blob * pErr){
2826 ManifestFile *zFile; /* One file entry from the pCI->pParent*/
2827 const char *zPerm = 0; /* permissions for new F-card */
2828 const char *zFilename = 0; /* filename for new F-card */
2829 const char *zUuid = 0; /* UUID for new F-card */
2830 int cmp = 0; /* filename comparison result */
 
 
 
 
 
 
2831 int (*fncmp)(char const *, char const *) = /* filename comparator */
2832 filenames_are_case_sensitive()
2833 ? fossil_strcmp
2834 : fossil_stricmp;
2835 #define mf_err(EXPR) if(pErr) blob_appendf EXPR; return 0
2836
2837 assert(pCI->filePerm!=PERM_LNK && "This should have been validated before.");
2838 assert(pCI->filePerm>=0 && "Must have been set by the caller.");
2839
2840 manifest_file_rewind(pCI->pParent);
2841 if(asDelta){
2842 /* Parent is a baseline and we have only 1 file to modify, so this
2843 ** is the simplest case...
2844 */
2845 assert(pCI->pParent->zBaseline==0 && "Delta-from-delta is NYI.");
2846 zFile = manifest_file_find(pCI->pParent, pCI->zFilename);
2847 if(zFile==0){
2848 /* New file */
2849 zFilename = pCI->zFilename;
2850 }else{
2851 /* Replacement file */
2852 if(manifest_file_mperm(zFile)==PERM_LNK){
2853 goto err_no_symlink;
2854 }
2855 zFilename = zFile->zName
2856 /* use original name in case of name-case difference */;
2857 zFile = 0;
2858 }
2859 }else{
2860 /* Non-delta: write F-cards which lexically preceed pCI->zFilename */
2861 while((zFile = manifest_file_next(pCI->pParent, 0))
2862 && (cmp = fncmp(zFile->zName, pCI->zFilename))<0){
2863 blob_appendf(pOut, "F %F %s%s%s\n", zFile->zName, zFile->zUuid,
2864 (zFile->zPerm && *zFile->zPerm) ? " " : "",
2865 (zFile->zPerm && *zFile->zPerm) ? zFile->zPerm : "");
2866 }
2867 /* Figure out file perms and name to save... */
2868 if(zFile!=0 && cmp==0){
2869 /* Match: override this F-card */
2870 if(manifest_file_mperm(zFile)==PERM_LNK){
2871 goto err_no_symlink;
2872 }
2873 zFilename = zFile->zName
2874 /* use original name in case of name-case difference */;
2875 zFile = 0;
2876 }else{
2877 /* This is a new file. */
2878 assert(zFile==0);
2879 zFilename = pCI->zFilename;
2880 }
2881 }
2882 if(PERM_LNK==pCI->filePerm){
2883 goto err_no_symlink;
2884 }else if(PERM_EXE==pCI->filePerm){
2885 zPerm = " x";
2886 }else{
2887 zPerm = "";
2888 }
2889 zUuid = blob_str(&pCI->fileHash);
2890 assert(zFile ? cmp>0&&asDelta==0 : 1);
2891 assert(zFilename);
2892 assert(zUuid);
2893 assert(zPerm);
2894 blob_appendf(pOut, "F %F %s%s\n", zFilename, zUuid, zPerm);
2895 /* Non-delta: write F-cards which lexically follow pCI->zFilename */
2896 while(zFile!=0){
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2897 #ifndef NDEBUG
2898 cmp = fncmp(zFile->zName, pCI->zFilename);
2899 assert(cmp>0);
2900 if(cmp<=0){
2901 mf_err((pErr,"Internal error: mis-ordering of "
2902 "F-cards detected."));
2903 }
2904 #endif
2905 blob_appendf(pOut, "F %F %s%s%s\n",
2906 zFile->zName, zFile->zUuid,
2907 (zFile->zPerm && *zFile->zPerm) ? " " : "",
2908 (zFile->zPerm && *zFile->zPerm) ? zFile->zPerm : "");
2909 zFile = manifest_file_next(pCI->pParent, 0);
 
 
 
2910 }
2911 return 1;
2912 err_no_symlink:
2913 mf_err((pErr,"Cannot commit or overwrite symlinks "
2914 "via mini-checkin."));
@@ -2951,23 +3033,21 @@
2951 ** sensible way to handle a symlink add/checkin without a
2952 ** checkout.
2953 */
2954 blob_zero(pOut);
2955 manifest_file_rewind(pCI->pParent) /* force load of baseline */;
2956 if(((CIMINI_PREFER_DELTA & pCI->flags)
2957 && pCI->pParent->zBaseline==0 /* parent is not a delta */
2958 /* ^^^ TODO allow creation of a delta from a delta */
2959 )&&(
2960 CIMINI_STRONGLY_PREFER_DELTA & pCI->flags
2961 || (pCI->pParent->pBaseline
2962 ? pCI->pParent->pBaseline
2963 : pCI->pParent)->nFile > 10
2964 /* 10 is arbitrary: don't create a delta when there is only a
2965 ** tiny gain for doing so. */)){
2966 asDelta = 1;
2967 }
2968 if(asDelta){
2969 blob_appendf(pOut, "B %s\n",
2970 pCI->pParent->zBaseline
2971 ? pCI->pParent->zBaseline
2972 : pCI->zParentUuid);
2973 }
@@ -3058,10 +3138,11 @@
3058 zProjCode)){
3059 fossil_fatal("Never, ever run this in/on the core fossil repo "
3060 "in non-dry-run mode until it's been well-vetted. "
3061 "Use a temp/test repo.");
3062 }
 
3063 }
3064 db_begin_transaction();
3065
3066 if(pCI->pParent==0){
3067 pCI->pParent = manifest_get_by_name(pCI->zParentUuid, 0);
@@ -3140,11 +3221,14 @@
3140 ** case-sensitivity mismatch between the user/repo/filesystem, or
3141 ** some such.
3142 */
3143 manifest_file_rewind(pCI->pParent);
3144 zFilePrev = manifest_file_find(pCI->pParent, pCI->zFilename);
3145 if(!zFilePrev && !(CIMINI_ALLOW_NEW_FILE & pCI->flags)){
 
 
 
3146 ci_err((pErr,"File [%s] not found in manifest [%S]. "
3147 "Adding new files is currently not permitted.",
3148 pCI->zFilename, pCI->zParentUuid));
3149 }else if(zFilePrev
3150 && manifest_file_mperm(zFilePrev)==PERM_LNK){
@@ -3210,11 +3294,12 @@
3210 assert(blob_size(&pCI->fileHash)>0);
3211 }
3212 if(zFilePrev){
3213 /* Has this file been changed since its previous commit? */
3214 assert(blob_size(&pCI->fileHash));
3215 if(0==fossil_strcmp(zFilePrev->zUuid, blob_str(&pCI->fileHash))){
 
3216 ci_err((pErr,"File is unchanged. Not saving."));
3217 }
3218 }
3219 /* Create, save, deltify, and crosslink the manifest... */
3220 if(create_manifest_mini(&mf, pCI, pErr)==0){
@@ -3284,11 +3369,13 @@
3284 ** --convert-eol Convert EOL style of the checkin to match
3285 ** the previous version's content. Does not
3286 ** modify the input file, only the checked-in
3287 ** content.
3288 ** --delta Prefer to generate a delta manifest, if
3289 ** able.
 
 
3290 ** --allow-new-file Allow addition of a new file this way.
3291 ** Disabled by default to avoid that case-
3292 ** sensitivity errors inadvertently lead to
3293 ** adding a new file where an update is
3294 ** intended.
3295
--- src/checkin.c
+++ src/checkin.c
@@ -2796,10 +2796,17 @@
2796 ** A "stronger hint" to checkin_mini() to prefer creation of a delta
2797 ** manifest if it at all can. It will decide not to only if creation
2798 ** of a delta is not a realistic option. For this to work, it must be
2799 ** set together with the CIMINI_PREFER_DELTA flag, but the two cannot
2800 ** be combined in this enum.
2801 **
2802 ** This option is ONLY INTENDED FOR TESTING, used in bypassing
2803 ** heuristics which may otherwise disable generation of a delta on the
2804 ** grounds of efficiency (e.g. not generating a delta if the parent
2805 ** non-delta only has a few F-cards).
2806 **
2807 ** The forbid-delta-manifests repo config option trumps this.
2808 */
2809 CIMINI_STRONGLY_PREFER_DELTA = 1<<7,
2810 /*
2811 ** Tells checkin_mini() to permit the addition of a new file. Normally
2812 ** this is disabled because there are many cases where it could cause
@@ -2806,109 +2813,184 @@
2813 ** the inadvertent addition of a new file when an update to an
2814 ** existing was intended, as a side-effect of name-case differences.
2815 */
2816 CIMINI_ALLOW_NEW_FILE = 1<<8
2817 };
2818
2819 /*
2820 ** Internal helper which returns an F-card perms string suitable for
2821 ** writing into a manifest.
2822 */
2823 static const char * mfile_permint_mstring(int perm){
2824 switch(perm){
2825 case PERM_EXE: return " x";
2826 case PERM_LNK: return " l";
2827 default: return "";
2828 }
2829 }
2830
2831 static const char * mfile_perm_mstring(const ManifestFile * p){
2832 return mfile_permint_mstring(manifest_file_mperm(p));
2833 }
2834
2835 /*
2836 ** Handles the F-card parts for create_manifest_mini().
2837 **
2838 ** If asDelta is true, F-cards will be handled as for a delta
2839 ** manifest, and the caller MUST have added a B-card to pOut before
2840 ** calling this.
2841 **
2842 ** Returns 1 on success, 0 on error, and writes any error message to
2843 ** pErr (if it's not NULL).
2844 */
2845 static int create_manifest_mini_fcards( Blob * pOut,
2846 CheckinMiniInfo * pCI,
2847 int asDelta,
2848 Blob * pErr){
2849 ManifestFile *zFile; /* One file entry from pCI->pParent */
 
2850 const char *zFilename = 0; /* filename for new F-card */
2851 const char *zUuid = 0; /* UUID for new F-card */
2852 int cmp = 0; /* filename comparison result */
2853 int iFCursor = 0; /* Cursor into pCI->pParent->aFile if
2854 pCI->pParent is a delta. */
2855 int postProcess = -1; /* How to traverse post-pCI->zFilename
2856 F-cards at the end of the function:
2857 0=none, 1=normal traversal,
2858 2=delta-clone tranversal. */
2859 int (*fncmp)(char const *, char const *) = /* filename comparator */
2860 filenames_are_case_sensitive()
2861 ? fossil_strcmp
2862 : fossil_stricmp;
2863 #define mf_err(EXPR) if(pErr) blob_appendf EXPR; return 0
2864
2865 assert(pCI->filePerm!=PERM_LNK && "This should have been validated before.");
2866 assert(pCI->filePerm==PERM_REG || pCI->filePerm==PERM_EXE);
2867 if(PERM_LNK==pCI->filePerm){
2868 goto err_no_symlink;
2869 }
2870 manifest_file_rewind(pCI->pParent);
2871 if(asDelta){
2872 if(pCI->pParent->zBaseline==0 || pCI->pParent->nFile==0 ){
2873 /* Parent is a baseline or a delta with no F-cards, so this is
2874 ** the simplest case: create a delta with a single F-card.
2875 */
2876 zFile = manifest_file_find(pCI->pParent, pCI->zFilename);
2877 if(zFile==0){/* New file */
2878 zFilename = pCI->zFilename;
2879 }else{/* Replacement file */
2880 if(manifest_file_mperm(zFile)==PERM_LNK){
2881 goto err_no_symlink;
2882 }
2883 zFilename = zFile->zName
2884 /* use original name in case of name-case difference */;
2885 }
2886 postProcess = 0;
2887 }else{
2888 /* Parent is a delta manifest with F-cards. Traversal of delta
2889 ** manifest file entries is normally done via
2890 ** manifest_file_next(), which takes into account the
2891 ** differences between the delta and its parent and returns
2892 ** F-cards from both. Each successive delta from the same
2893 ** baseline includes all F-card changes from the previous
2894 ** deltas, so we instead "clone" the parent's F-cards except for
2895 ** the one (if any) which matches the new file.
2896 */
2897 Manifest * p = pCI->pParent;
2898 cmp = -1;
2899 assert(p->nFile > 0);
2900 iFCursor = 0;
2901 zFile = &p->aFile[iFCursor];
2902 /* Write F-cards which lexically preceed pCI->zFilename */
2903 for( ; iFCursor<p->nFile; ){
2904 zFile = &p->aFile[iFCursor];
2905 cmp = fncmp(zFile->zName, pCI->zFilename);
2906 if(cmp<0){
2907 ++iFCursor;
2908 if(zFile->zUuid){
2909 blob_appendf(pOut, "F %F %s%s\n", zFile->zName,
2910 zFile->zUuid, mfile_perm_mstring(zFile));
2911 }else{/* File was removed in parent delta */
2912 blob_appendf(pOut, "F %F\n", zFile->zName);
2913 }
2914 }else{
2915 break;
2916 }
2917 }
2918 if(0==cmp){/* Match: override this F-card */
2919 assert(zFile);
2920 if(manifest_file_mperm(zFile)==PERM_LNK){
2921 goto err_no_symlink;
2922 }
2923 ++iFCursor;
2924 zFilename = zFile->zName
2925 /* use original name in case of name-case difference */;
2926 }else{/* This is a new file */
2927 zFilename = pCI->zFilename;
2928 }
2929 postProcess = 2;
2930 }
2931 }else{/* Non-delta: write F-cards which lexically preceed
2932 pCI->zFilename */
2933 cmp = -1;
2934 while((zFile = manifest_file_next(pCI->pParent, 0))
2935 && (cmp = fncmp(zFile->zName, pCI->zFilename))<0){
2936 blob_appendf(pOut, "F %F %s%s\n", zFile->zName, zFile->zUuid,
2937 mfile_perm_mstring(zFile));
2938 }
2939 if(cmp==0){/* Match: override this F-card*/
2940 if(manifest_file_mperm(zFile)==PERM_LNK){
2941 goto err_no_symlink;
2942 }
2943 zFilename = zFile->zName
2944 /* use original name in case of name-case difference */;
2945 }else{/* This is a new file. */
2946 zFilename = pCI->zFilename;
2947 if(zFile!=0){
2948 assert(cmp>0);
2949 assert(pCI->pParent->iFile>0);
2950 --pCI->pParent->iFile
2951 /* So that the post-processing loop picks up this file
2952 again.*/;
2953 }
2954 }
2955 postProcess = 1;
2956 }
2957 /* Finally add the new file's F-card... */
2958 zFile = 0;
2959 zUuid = blob_str(&pCI->fileHash);
2960 assert(zFilename);
2961 assert(zUuid);
2962 assert(postProcess==0 || postProcess==1 || postProcess==2);
2963 blob_appendf(pOut, "F %F %s%s\n", zFilename, zUuid,
2964 mfile_permint_mstring(pCI->filePerm));
2965 while(postProcess>0){
2966 /* Write F-cards which lexically follow pCI->zFilename */
2967 if(postProcess==1){ /* non-delta parent */
2968 zFile = manifest_file_next(pCI->pParent, 0);
2969 }else{ /* clone directly from delta parent */
2970 zFile = iFCursor<pCI->pParent->nFile
2971 ? &pCI->pParent->aFile[iFCursor++] : 0;
2972 }
2973 if(zFile==0){
2974 break;
2975 }
2976 #ifndef NDEBUG
2977 cmp = fncmp(zFile->zName, pCI->zFilename);
2978 assert(cmp>0);
2979 if(cmp<=0){
2980 mf_err((pErr,"Internal error: mis-ordering of "
2981 "F-cards detected."));
2982 }
2983 #endif
2984 if(zFile->zUuid){
2985 blob_appendf(pOut, "F %F %s%s\n", zFile->zName, zFile->zUuid,
2986 mfile_perm_mstring(zFile));
2987 }else{
2988 assert(postProcess==2);
2989 /* File was removed from parent delta. */
2990 blob_appendf(pOut, "F %F\n", zFile->zName);
2991 }
2992 }
2993 return 1;
2994 err_no_symlink:
2995 mf_err((pErr,"Cannot commit or overwrite symlinks "
2996 "via mini-checkin."));
@@ -2951,23 +3033,21 @@
3033 ** sensible way to handle a symlink add/checkin without a
3034 ** checkout.
3035 */
3036 blob_zero(pOut);
3037 manifest_file_rewind(pCI->pParent) /* force load of baseline */;
3038
3039 if((CIMINI_PREFER_DELTA & pCI->flags)
3040 && ((CIMINI_STRONGLY_PREFER_DELTA & pCI->flags)
3041 || (pCI->pParent->pBaseline
3042 ? pCI->pParent->pBaseline
3043 : pCI->pParent)->nFile > 10
3044 /* 10 is arbitrary: don't create a delta when there is only a
3045 ** tiny gain for doing so. */)
3046 && !db_get_boolean("forbid-delta-manifests",0)
3047 ){
3048 asDelta = 1;
 
 
3049 blob_appendf(pOut, "B %s\n",
3050 pCI->pParent->zBaseline
3051 ? pCI->pParent->zBaseline
3052 : pCI->zParentUuid);
3053 }
@@ -3058,10 +3138,11 @@
3138 zProjCode)){
3139 fossil_fatal("Never, ever run this in/on the core fossil repo "
3140 "in non-dry-run mode until it's been well-vetted. "
3141 "Use a temp/test repo.");
3142 }
3143 fossil_free(zProjCode);
3144 }
3145 db_begin_transaction();
3146
3147 if(pCI->pParent==0){
3148 pCI->pParent = manifest_get_by_name(pCI->zParentUuid, 0);
@@ -3140,11 +3221,14 @@
3221 ** case-sensitivity mismatch between the user/repo/filesystem, or
3222 ** some such.
3223 */
3224 manifest_file_rewind(pCI->pParent);
3225 zFilePrev = manifest_file_find(pCI->pParent, pCI->zFilename);
3226 if(!(CIMINI_ALLOW_NEW_FILE & pCI->flags)
3227 && (!zFilePrev
3228 || !zFilePrev->zUuid/*was removed from parent delta manifest*/)
3229 ){
3230 ci_err((pErr,"File [%s] not found in manifest [%S]. "
3231 "Adding new files is currently not permitted.",
3232 pCI->zFilename, pCI->zParentUuid));
3233 }else if(zFilePrev
3234 && manifest_file_mperm(zFilePrev)==PERM_LNK){
@@ -3210,11 +3294,12 @@
3294 assert(blob_size(&pCI->fileHash)>0);
3295 }
3296 if(zFilePrev){
3297 /* Has this file been changed since its previous commit? */
3298 assert(blob_size(&pCI->fileHash));
3299 if(0==fossil_strcmp(zFilePrev->zUuid, blob_str(&pCI->fileHash))
3300 && manifest_file_mperm(zFilePrev)==pCI->filePerm){
3301 ci_err((pErr,"File is unchanged. Not saving."));
3302 }
3303 }
3304 /* Create, save, deltify, and crosslink the manifest... */
3305 if(create_manifest_mini(&mf, pCI, pErr)==0){
@@ -3284,11 +3369,13 @@
3369 ** --convert-eol Convert EOL style of the checkin to match
3370 ** the previous version's content. Does not
3371 ** modify the input file, only the checked-in
3372 ** content.
3373 ** --delta Prefer to generate a delta manifest, if
3374 ** able. The forbid-delta-manifests repo
3375 ** config option trumps this, as do certain
3376 ** heuristics.
3377 ** --allow-new-file Allow addition of a new file this way.
3378 ** Disabled by default to avoid that case-
3379 ** sensitivity errors inadvertently lead to
3380 ** adding a new file where an update is
3381 ** intended.
3382

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button