Fossil SCM

Now disallow mini-checkin if the datestamp is older than the parent unless --allow-older is used.

stephan 2020-04-29 12:19 checkin-without-checkout
Commit 0a0d96dd42db6dde58d1f65ea94467f51b2f24eb7ac39d468a5ee4e5a51c952d
1 file changed +96 -40
+96 -40
--- src/checkin.c
+++ src/checkin.c
@@ -1399,10 +1399,28 @@
13991399
g.aCommitFile[jj] = 0;
14001400
bag_clear(&toCommit);
14011401
}
14021402
return result;
14031403
}
1404
+
1405
+/*
1406
+** Returns true if the checkin identified by the first parameter is
1407
+** older than the given (valid) date/time string, else returns false.
1408
+** Also returns true if rid does not refer to a checkin, but it is not
1409
+** intended to be used for that case.
1410
+*/
1411
+static int checkin_is_younger(
1412
+ int rid, /* The record ID of the ancestor */
1413
+ const char *zDate /* Date & time of the current check-in */
1414
+){
1415
+ return db_exists(
1416
+ "SELECT 1 FROM event"
1417
+ " WHERE datetime(mtime)>=%Q"
1418
+ " AND type='ci' AND objid=%d",
1419
+ zDate, rid
1420
+ ) ? 0 : 1;
1421
+}
14041422
14051423
/*
14061424
** Make sure the current check-in with timestamp zDate is younger than its
14071425
** ancestor identified rid and zUuid. Throw a fatal error if not.
14081426
*/
@@ -1410,23 +1428,18 @@
14101428
int rid, /* The record ID of the ancestor */
14111429
const char *zUuid, /* The artifact ID of the ancestor */
14121430
const char *zDate /* Date & time of the current check-in */
14131431
){
14141432
#ifndef FOSSIL_ALLOW_OUT_OF_ORDER_DATES
1415
- int b;
1416
- b = db_exists(
1417
- "SELECT 1 FROM event"
1418
- " WHERE datetime(mtime)>=%Q"
1419
- " AND type='ci' AND objid=%d",
1420
- zDate, rid
1421
- );
1422
- if( b ){
1433
+ if(checkin_is_younger(rid,zDate)==0){
14231434
fossil_fatal("ancestor check-in [%S] (%s) is not older (clock skew?)"
14241435
" Use --allow-older to override.", zUuid, zDate);
14251436
}
14261437
#endif
14271438
}
1439
+
1440
+
14281441
14291442
/*
14301443
** zDate should be a valid date string. Convert this string into the
14311444
** format YYYY-MM-DDTHH:MM:SS. If the string is not a valid date,
14321445
** print a fatal error and quit.
@@ -2745,13 +2758,26 @@
27452758
** marker is not permitted. This flag relaxes that requirement.
27462759
*/
27472760
CIMINI_ALLOW_MERGE_MARKER = 1<<3,
27482761
27492762
/*
2763
+** By default mini-checkins are not allowed to be "older"
2764
+** than their parent. i.e. they may not have a timestamp
2765
+** which predates their parent. This flag bypasses that
2766
+** check.
2767
+*/
2768
+CIMINI_ALLOW_OLDER = 1<<4,
2769
+
2770
+/*
2771
+** NOT YET IMPLEMENTED. A hint to checkin_mini() to prefer
2772
+** creation of a delta manifest.
2773
+*/
2774
+CIMINI_PREFER_DELTA = 1<<5,
2775
+/*
27502776
** NOT YET IMPLEMENTED.
27512777
*/
2752
-CIMINI_ALLOW_CLOSED_LEAF = 1<<4
2778
+CIMINI_ALLOW_CLOSED_LEAF = 1<<6
27532779
};
27542780
27552781
/*
27562782
** Creates a manifest file, written to pOut, from the state in the
27572783
** fully-populated pCI argument. pCI is not *semantically* modified
@@ -2777,10 +2803,11 @@
27772803
27782804
assert(blob_str(&pCI->fileHash));
27792805
assert(pCI->pParent);
27802806
assert(pCI->zFilename);
27812807
assert(pCI->zUser);
2808
+ assert(pCI->zDate);
27822809
27832810
#define mf_err(EXPR) if(pErr) blob_appendf EXPR; return 0
27842811
/* Potential TODOs include...
27852812
** - Create a delta manifest, if possible, rather than a baseline.
27862813
** - Maybe add support for tags. Those can be edited via /info page.
@@ -2794,29 +2821,11 @@
27942821
if(blob_size(&pCI->comment)!=0){
27952822
blob_appendf(pOut, "C %F\n", blob_str(&pCI->comment));
27962823
}else{
27972824
blob_append(pOut, "C (no\\scomment)\n", 16);
27982825
}
2799
- {
2800
- /*
2801
- ** We don't use date_in_standard_format() because that has
2802
- ** side-effects we don't want to trigger here.
2803
- */
2804
- char * zDVal = db_text(
2805
- 0, "SELECT strftime('%%Y-%%m-%%dT%%H:%%M:%%f',%Q)",
2806
- pCI->zDate
2807
- ? pCI->zDate
2808
- : "now");
2809
- if(zDVal[0]==0){
2810
- fossil_free(zDVal);
2811
- mf_err((pErr,
2812
- "Invalid date format (%s): use \"YYYY-MM-DD HH:MM:SS.SSS\"",
2813
- pCI->zDate));
2814
- }
2815
- blob_appendf(pOut, "D %z\n", zDVal);
2816
- }
2817
-
2826
+ blob_appendf(pOut, "D %z\n", pCI->zDate);
28182827
manifest_file_rewind(pCI->pParent);
28192828
while((zFile = manifest_file_next(pCI->pParent, 0))){
28202829
cmp = fncmp(zFile->zName, pCI->zFilename);
28212830
if(cmp<0){
28222831
blob_appendf(pOut, "F %F %s%s%s\n", zFile->zName, zFile->zUuid,
@@ -2893,13 +2902,21 @@
28932902
** This routine uses the state from the given fully-populated pCI
28942903
** argument to add pCI->fileContent to the database, and create and
28952904
** save a manifest for that change. Ownership of pCI and its contents
28962905
** are unchanged.
28972906
**
2898
-** If pCI->fileHash is empty, this routine populates it with the
2899
-** repository's preferred hash algorithm. pCI is not otherwise
2900
-** modified, nor is its ownership modified.
2907
+** pCI may be modified as follows:
2908
+**
2909
+** - If pCI->fileHash is empty, this routine populates it with the
2910
+** repository's preferred hash algorithm.
2911
+**
2912
+** - pCI->zDate is normalized to/replaced with a valid date/time
2913
+** string. If its original value cannot be validated then
2914
+** this function fails. If pCI->zDate is NULL, the current time
2915
+** is used.
2916
+**
2917
+** pCI's ownership is not modified.
29012918
**
29022919
** This function validates several of the inputs and fails if any
29032920
** validation fails.
29042921
**
29052922
** On error, returns false (0) and, if pErr is not NULL, writes a
@@ -2918,11 +2935,13 @@
29182935
int rid = 0, frid = 0; /* various RIDs */
29192936
const int isPrivate = content_is_private(pCI->pParent->rid);
29202937
ManifestFile * zFile; /* file from pCI->pParent */;
29212938
const char * zFilePrevUuid = 0; /* UUID of previous version of
29222939
the file */
2940
+
29232941
#define ci_err(EXPR) if(pErr!=0){blob_appendf EXPR;} goto ci_error
2942
+
29242943
db_begin_transaction();
29252944
if( !db_exists("SELECT 1 FROM user WHERE login=%Q", pCI->zUser) ){
29262945
ci_err((pErr,"No such user: %s", pCI->zUser));
29272946
}
29282947
assert(pCI->pParent->rid>0);
@@ -2942,14 +2961,42 @@
29422961
}
29432962
if(!(CIMINI_ALLOW_MERGE_MARKER & ciminiFlags)
29442963
&& contains_merge_marker(&pCI->fileContent)){
29452964
ci_err((pErr,"Content appears to contain a merge conflict marker."));
29462965
}
2966
+
2967
+ {
2968
+ /*
2969
+ ** Normalize the timestamp. We don't use date_in_standard_format()
2970
+ ** because that has side-effects we don't want to trigger here.
2971
+ */
2972
+ char * zDVal = db_text(
2973
+ 0, "SELECT strftime('%%Y-%%m-%%dT%%H:%%M:%%f',%Q)",
2974
+ pCI->zDate ? pCI->zDate : "now");
2975
+ if(zDVal[0]==0){
2976
+ fossil_free(zDVal);
2977
+ ci_err((pErr,"Invalid timestamp string: %s", pCI->zDate));
2978
+ }
2979
+ fossil_free(pCI->zDate);
2980
+ pCI->zDate = zDVal;
2981
+ }
2982
+ if(!(CIMINI_ALLOW_OLDER & ciminiFlags)
2983
+ && !checkin_is_younger(pCI->pParent->rid, pCI->zDate)){
2984
+ ci_err((pErr,"Checkin time (%s) may not be older "
2985
+ "than its parent (%z).",
2986
+ pCI->zDate,
2987
+ db_text(0, "SELECT strftime('%%Y-%%m-%%dT%%H:%%M:%%f',%lf)",
2988
+ pCI->pParent->rDate)
2989
+ ));
2990
+ }
2991
+
29472992
/* Potential TODOs include:
29482993
**
29492994
** - Commit allows an empty checkin only with a flag, but we
29502995
** currently disallow it entirely. Conform with commit?
2996
+ **
2997
+ ** Non-TODOs:
29512998
**
29522999
** - Check for a commit lock would require auto-sync, which this
29533000
** code cannot do if it's going to be run via a web page.
29543001
*/
29553002
@@ -2969,20 +3016,24 @@
29693016
ci_err((pErr,"File [%s] not found in manifest [%S]. "
29703017
"Adding new files is currently not allowed.",
29713018
pCI->zFilename, pCI->zParentUuid));
29723019
}else if(zFile->zPerm && strstr(zFile->zPerm, "l")){
29733020
ci_err((pErr,"Cannot save a symlink this way."));
2974
- }else{
3021
+ }
3022
+ if(blob_size(&pCI->fileHash)==0){
3023
+ /* Hash the content if it's not done already... */
3024
+ hname_hash(&pCI->fileContent, 0, &pCI->fileHash);
3025
+ assert(blob_size(&pCI->fileHash)>0);
3026
+ }
3027
+ if(zFile){
3028
+ /* Has this file been changed since its previous commit? */
3029
+ assert(blob_size(&pCI->fileHash));
29753030
if(0==fossil_strcmp(zFile->zUuid, blob_str(&pCI->fileHash))){
29763031
ci_err((pErr,"File is unchanged. Not saving."));
29773032
}
29783033
zFilePrevUuid = zFile->zUuid;
29793034
}
2980
- if(blob_size(&pCI->fileHash)==0){
2981
- hname_hash(&pCI->fileContent, 0, &pCI->fileHash);
2982
- assert(blob_size(&pCI->fileHash)>0);
2983
- }
29843035
/* Create the manifest... */
29853036
if(create_manifest_mini(&mf, pCI, pErr)==0){
29863037
return 0;
29873038
}
29883039
/* Save and deltify the file content... */
@@ -2994,19 +3045,19 @@
29943045
content_deltify(frid, &prevFRid, 1, 0);
29953046
}
29963047
/* Save, deltify, and crosslink the manifest... */
29973048
rid = content_put_ex(&mf, 0, 0, 0, isPrivate);
29983049
content_deltify(rid, &pCI->pParent->rid, 1, 0);
2999
- if(pRid!=0){
3000
- *pRid = rid;
3001
- }
30023050
if(ciminiFlags & CIMINI_DUMP_MANIFEST){
3003
- fossil_print("Manifest: %z\n%b", rid_to_uuid(rid), &mf);
3051
+ fossil_print("Manifest %z:\n%b", rid_to_uuid(rid), &mf);
30043052
}
30053053
manifest_crosslink(rid, &mf, 0);
30063054
blob_reset(&mf);
30073055
db_end_transaction((CIMINI_DRY_RUN & ciminiFlags) ? 1 : 0);
3056
+ if(pRid!=0){
3057
+ *pRid = rid;
3058
+ }
30083059
return 1;
30093060
ci_error:
30103061
assert(db_transaction_nesting_depth()>0);
30113062
db_end_transaction(1);
30123063
return 0;
@@ -3040,10 +3091,12 @@
30403091
** is performed beforehand.
30413092
** --allow-merge-conflict Allows checkin of a file even if it appears
30423093
** to contain a fossil merge conflict marker.
30433094
** --user-override USER USER to use instead of the current default.
30443095
** --date-override DATETIME DATE to use instead of 'now'.
3096
+** --allow-older Allow a commit to be older than its
3097
+** ancestor.
30453098
** --dump-manifest|-d Dumps the generated manifest to stdout.
30463099
** --wet-run Disables the default dry-run mode.
30473100
**
30483101
** Example:
30493102
**
@@ -3078,10 +3131,13 @@
30783131
if(find_option("dump-manifest","d",0)!=0){
30793132
ciminiFlags |= CIMINI_DUMP_MANIFEST;
30803133
}
30813134
if(find_option("allow-merge-conflict",0,0)!=0){
30823135
ciminiFlags |= CIMINI_ALLOW_MERGE_MARKER;
3136
+ }
3137
+ if(find_option("allow-older",0,0)!=0){
3138
+ ciminiFlags |= CIMINI_ALLOW_OLDER;
30833139
}
30843140
30853141
db_find_and_open_repository(0, 0);
30863142
verify_all_options();
30873143
user_select();
30883144
--- src/checkin.c
+++ src/checkin.c
@@ -1399,10 +1399,28 @@
1399 g.aCommitFile[jj] = 0;
1400 bag_clear(&toCommit);
1401 }
1402 return result;
1403 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1404
1405 /*
1406 ** Make sure the current check-in with timestamp zDate is younger than its
1407 ** ancestor identified rid and zUuid. Throw a fatal error if not.
1408 */
@@ -1410,23 +1428,18 @@
1410 int rid, /* The record ID of the ancestor */
1411 const char *zUuid, /* The artifact ID of the ancestor */
1412 const char *zDate /* Date & time of the current check-in */
1413 ){
1414 #ifndef FOSSIL_ALLOW_OUT_OF_ORDER_DATES
1415 int b;
1416 b = db_exists(
1417 "SELECT 1 FROM event"
1418 " WHERE datetime(mtime)>=%Q"
1419 " AND type='ci' AND objid=%d",
1420 zDate, rid
1421 );
1422 if( b ){
1423 fossil_fatal("ancestor check-in [%S] (%s) is not older (clock skew?)"
1424 " Use --allow-older to override.", zUuid, zDate);
1425 }
1426 #endif
1427 }
 
 
1428
1429 /*
1430 ** zDate should be a valid date string. Convert this string into the
1431 ** format YYYY-MM-DDTHH:MM:SS. If the string is not a valid date,
1432 ** print a fatal error and quit.
@@ -2745,13 +2758,26 @@
2745 ** marker is not permitted. This flag relaxes that requirement.
2746 */
2747 CIMINI_ALLOW_MERGE_MARKER = 1<<3,
2748
2749 /*
 
 
 
 
 
 
 
 
 
 
 
 
 
2750 ** NOT YET IMPLEMENTED.
2751 */
2752 CIMINI_ALLOW_CLOSED_LEAF = 1<<4
2753 };
2754
2755 /*
2756 ** Creates a manifest file, written to pOut, from the state in the
2757 ** fully-populated pCI argument. pCI is not *semantically* modified
@@ -2777,10 +2803,11 @@
2777
2778 assert(blob_str(&pCI->fileHash));
2779 assert(pCI->pParent);
2780 assert(pCI->zFilename);
2781 assert(pCI->zUser);
 
2782
2783 #define mf_err(EXPR) if(pErr) blob_appendf EXPR; return 0
2784 /* Potential TODOs include...
2785 ** - Create a delta manifest, if possible, rather than a baseline.
2786 ** - Maybe add support for tags. Those can be edited via /info page.
@@ -2794,29 +2821,11 @@
2794 if(blob_size(&pCI->comment)!=0){
2795 blob_appendf(pOut, "C %F\n", blob_str(&pCI->comment));
2796 }else{
2797 blob_append(pOut, "C (no\\scomment)\n", 16);
2798 }
2799 {
2800 /*
2801 ** We don't use date_in_standard_format() because that has
2802 ** side-effects we don't want to trigger here.
2803 */
2804 char * zDVal = db_text(
2805 0, "SELECT strftime('%%Y-%%m-%%dT%%H:%%M:%%f',%Q)",
2806 pCI->zDate
2807 ? pCI->zDate
2808 : "now");
2809 if(zDVal[0]==0){
2810 fossil_free(zDVal);
2811 mf_err((pErr,
2812 "Invalid date format (%s): use \"YYYY-MM-DD HH:MM:SS.SSS\"",
2813 pCI->zDate));
2814 }
2815 blob_appendf(pOut, "D %z\n", zDVal);
2816 }
2817
2818 manifest_file_rewind(pCI->pParent);
2819 while((zFile = manifest_file_next(pCI->pParent, 0))){
2820 cmp = fncmp(zFile->zName, pCI->zFilename);
2821 if(cmp<0){
2822 blob_appendf(pOut, "F %F %s%s%s\n", zFile->zName, zFile->zUuid,
@@ -2893,13 +2902,21 @@
2893 ** This routine uses the state from the given fully-populated pCI
2894 ** argument to add pCI->fileContent to the database, and create and
2895 ** save a manifest for that change. Ownership of pCI and its contents
2896 ** are unchanged.
2897 **
2898 ** If pCI->fileHash is empty, this routine populates it with the
2899 ** repository's preferred hash algorithm. pCI is not otherwise
2900 ** modified, nor is its ownership modified.
 
 
 
 
 
 
 
 
2901 **
2902 ** This function validates several of the inputs and fails if any
2903 ** validation fails.
2904 **
2905 ** On error, returns false (0) and, if pErr is not NULL, writes a
@@ -2918,11 +2935,13 @@
2918 int rid = 0, frid = 0; /* various RIDs */
2919 const int isPrivate = content_is_private(pCI->pParent->rid);
2920 ManifestFile * zFile; /* file from pCI->pParent */;
2921 const char * zFilePrevUuid = 0; /* UUID of previous version of
2922 the file */
 
2923 #define ci_err(EXPR) if(pErr!=0){blob_appendf EXPR;} goto ci_error
 
2924 db_begin_transaction();
2925 if( !db_exists("SELECT 1 FROM user WHERE login=%Q", pCI->zUser) ){
2926 ci_err((pErr,"No such user: %s", pCI->zUser));
2927 }
2928 assert(pCI->pParent->rid>0);
@@ -2942,14 +2961,42 @@
2942 }
2943 if(!(CIMINI_ALLOW_MERGE_MARKER & ciminiFlags)
2944 && contains_merge_marker(&pCI->fileContent)){
2945 ci_err((pErr,"Content appears to contain a merge conflict marker."));
2946 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2947 /* Potential TODOs include:
2948 **
2949 ** - Commit allows an empty checkin only with a flag, but we
2950 ** currently disallow it entirely. Conform with commit?
 
 
2951 **
2952 ** - Check for a commit lock would require auto-sync, which this
2953 ** code cannot do if it's going to be run via a web page.
2954 */
2955
@@ -2969,20 +3016,24 @@
2969 ci_err((pErr,"File [%s] not found in manifest [%S]. "
2970 "Adding new files is currently not allowed.",
2971 pCI->zFilename, pCI->zParentUuid));
2972 }else if(zFile->zPerm && strstr(zFile->zPerm, "l")){
2973 ci_err((pErr,"Cannot save a symlink this way."));
2974 }else{
 
 
 
 
 
 
 
 
2975 if(0==fossil_strcmp(zFile->zUuid, blob_str(&pCI->fileHash))){
2976 ci_err((pErr,"File is unchanged. Not saving."));
2977 }
2978 zFilePrevUuid = zFile->zUuid;
2979 }
2980 if(blob_size(&pCI->fileHash)==0){
2981 hname_hash(&pCI->fileContent, 0, &pCI->fileHash);
2982 assert(blob_size(&pCI->fileHash)>0);
2983 }
2984 /* Create the manifest... */
2985 if(create_manifest_mini(&mf, pCI, pErr)==0){
2986 return 0;
2987 }
2988 /* Save and deltify the file content... */
@@ -2994,19 +3045,19 @@
2994 content_deltify(frid, &prevFRid, 1, 0);
2995 }
2996 /* Save, deltify, and crosslink the manifest... */
2997 rid = content_put_ex(&mf, 0, 0, 0, isPrivate);
2998 content_deltify(rid, &pCI->pParent->rid, 1, 0);
2999 if(pRid!=0){
3000 *pRid = rid;
3001 }
3002 if(ciminiFlags & CIMINI_DUMP_MANIFEST){
3003 fossil_print("Manifest: %z\n%b", rid_to_uuid(rid), &mf);
3004 }
3005 manifest_crosslink(rid, &mf, 0);
3006 blob_reset(&mf);
3007 db_end_transaction((CIMINI_DRY_RUN & ciminiFlags) ? 1 : 0);
 
 
 
3008 return 1;
3009 ci_error:
3010 assert(db_transaction_nesting_depth()>0);
3011 db_end_transaction(1);
3012 return 0;
@@ -3040,10 +3091,12 @@
3040 ** is performed beforehand.
3041 ** --allow-merge-conflict Allows checkin of a file even if it appears
3042 ** to contain a fossil merge conflict marker.
3043 ** --user-override USER USER to use instead of the current default.
3044 ** --date-override DATETIME DATE to use instead of 'now'.
 
 
3045 ** --dump-manifest|-d Dumps the generated manifest to stdout.
3046 ** --wet-run Disables the default dry-run mode.
3047 **
3048 ** Example:
3049 **
@@ -3078,10 +3131,13 @@
3078 if(find_option("dump-manifest","d",0)!=0){
3079 ciminiFlags |= CIMINI_DUMP_MANIFEST;
3080 }
3081 if(find_option("allow-merge-conflict",0,0)!=0){
3082 ciminiFlags |= CIMINI_ALLOW_MERGE_MARKER;
 
 
 
3083 }
3084
3085 db_find_and_open_repository(0, 0);
3086 verify_all_options();
3087 user_select();
3088
--- src/checkin.c
+++ src/checkin.c
@@ -1399,10 +1399,28 @@
1399 g.aCommitFile[jj] = 0;
1400 bag_clear(&toCommit);
1401 }
1402 return result;
1403 }
1404
1405 /*
1406 ** Returns true if the checkin identified by the first parameter is
1407 ** older than the given (valid) date/time string, else returns false.
1408 ** Also returns true if rid does not refer to a checkin, but it is not
1409 ** intended to be used for that case.
1410 */
1411 static int checkin_is_younger(
1412 int rid, /* The record ID of the ancestor */
1413 const char *zDate /* Date & time of the current check-in */
1414 ){
1415 return db_exists(
1416 "SELECT 1 FROM event"
1417 " WHERE datetime(mtime)>=%Q"
1418 " AND type='ci' AND objid=%d",
1419 zDate, rid
1420 ) ? 0 : 1;
1421 }
1422
1423 /*
1424 ** Make sure the current check-in with timestamp zDate is younger than its
1425 ** ancestor identified rid and zUuid. Throw a fatal error if not.
1426 */
@@ -1410,23 +1428,18 @@
1428 int rid, /* The record ID of the ancestor */
1429 const char *zUuid, /* The artifact ID of the ancestor */
1430 const char *zDate /* Date & time of the current check-in */
1431 ){
1432 #ifndef FOSSIL_ALLOW_OUT_OF_ORDER_DATES
1433 if(checkin_is_younger(rid,zDate)==0){
 
 
 
 
 
 
 
1434 fossil_fatal("ancestor check-in [%S] (%s) is not older (clock skew?)"
1435 " Use --allow-older to override.", zUuid, zDate);
1436 }
1437 #endif
1438 }
1439
1440
1441
1442 /*
1443 ** zDate should be a valid date string. Convert this string into the
1444 ** format YYYY-MM-DDTHH:MM:SS. If the string is not a valid date,
1445 ** print a fatal error and quit.
@@ -2745,13 +2758,26 @@
2758 ** marker is not permitted. This flag relaxes that requirement.
2759 */
2760 CIMINI_ALLOW_MERGE_MARKER = 1<<3,
2761
2762 /*
2763 ** By default mini-checkins are not allowed to be "older"
2764 ** than their parent. i.e. they may not have a timestamp
2765 ** which predates their parent. This flag bypasses that
2766 ** check.
2767 */
2768 CIMINI_ALLOW_OLDER = 1<<4,
2769
2770 /*
2771 ** NOT YET IMPLEMENTED. A hint to checkin_mini() to prefer
2772 ** creation of a delta manifest.
2773 */
2774 CIMINI_PREFER_DELTA = 1<<5,
2775 /*
2776 ** NOT YET IMPLEMENTED.
2777 */
2778 CIMINI_ALLOW_CLOSED_LEAF = 1<<6
2779 };
2780
2781 /*
2782 ** Creates a manifest file, written to pOut, from the state in the
2783 ** fully-populated pCI argument. pCI is not *semantically* modified
@@ -2777,10 +2803,11 @@
2803
2804 assert(blob_str(&pCI->fileHash));
2805 assert(pCI->pParent);
2806 assert(pCI->zFilename);
2807 assert(pCI->zUser);
2808 assert(pCI->zDate);
2809
2810 #define mf_err(EXPR) if(pErr) blob_appendf EXPR; return 0
2811 /* Potential TODOs include...
2812 ** - Create a delta manifest, if possible, rather than a baseline.
2813 ** - Maybe add support for tags. Those can be edited via /info page.
@@ -2794,29 +2821,11 @@
2821 if(blob_size(&pCI->comment)!=0){
2822 blob_appendf(pOut, "C %F\n", blob_str(&pCI->comment));
2823 }else{
2824 blob_append(pOut, "C (no\\scomment)\n", 16);
2825 }
2826 blob_appendf(pOut, "D %z\n", pCI->zDate);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2827 manifest_file_rewind(pCI->pParent);
2828 while((zFile = manifest_file_next(pCI->pParent, 0))){
2829 cmp = fncmp(zFile->zName, pCI->zFilename);
2830 if(cmp<0){
2831 blob_appendf(pOut, "F %F %s%s%s\n", zFile->zName, zFile->zUuid,
@@ -2893,13 +2902,21 @@
2902 ** This routine uses the state from the given fully-populated pCI
2903 ** argument to add pCI->fileContent to the database, and create and
2904 ** save a manifest for that change. Ownership of pCI and its contents
2905 ** are unchanged.
2906 **
2907 ** pCI may be modified as follows:
2908 **
2909 ** - If pCI->fileHash is empty, this routine populates it with the
2910 ** repository's preferred hash algorithm.
2911 **
2912 ** - pCI->zDate is normalized to/replaced with a valid date/time
2913 ** string. If its original value cannot be validated then
2914 ** this function fails. If pCI->zDate is NULL, the current time
2915 ** is used.
2916 **
2917 ** pCI's ownership is not modified.
2918 **
2919 ** This function validates several of the inputs and fails if any
2920 ** validation fails.
2921 **
2922 ** On error, returns false (0) and, if pErr is not NULL, writes a
@@ -2918,11 +2935,13 @@
2935 int rid = 0, frid = 0; /* various RIDs */
2936 const int isPrivate = content_is_private(pCI->pParent->rid);
2937 ManifestFile * zFile; /* file from pCI->pParent */;
2938 const char * zFilePrevUuid = 0; /* UUID of previous version of
2939 the file */
2940
2941 #define ci_err(EXPR) if(pErr!=0){blob_appendf EXPR;} goto ci_error
2942
2943 db_begin_transaction();
2944 if( !db_exists("SELECT 1 FROM user WHERE login=%Q", pCI->zUser) ){
2945 ci_err((pErr,"No such user: %s", pCI->zUser));
2946 }
2947 assert(pCI->pParent->rid>0);
@@ -2942,14 +2961,42 @@
2961 }
2962 if(!(CIMINI_ALLOW_MERGE_MARKER & ciminiFlags)
2963 && contains_merge_marker(&pCI->fileContent)){
2964 ci_err((pErr,"Content appears to contain a merge conflict marker."));
2965 }
2966
2967 {
2968 /*
2969 ** Normalize the timestamp. We don't use date_in_standard_format()
2970 ** because that has side-effects we don't want to trigger here.
2971 */
2972 char * zDVal = db_text(
2973 0, "SELECT strftime('%%Y-%%m-%%dT%%H:%%M:%%f',%Q)",
2974 pCI->zDate ? pCI->zDate : "now");
2975 if(zDVal[0]==0){
2976 fossil_free(zDVal);
2977 ci_err((pErr,"Invalid timestamp string: %s", pCI->zDate));
2978 }
2979 fossil_free(pCI->zDate);
2980 pCI->zDate = zDVal;
2981 }
2982 if(!(CIMINI_ALLOW_OLDER & ciminiFlags)
2983 && !checkin_is_younger(pCI->pParent->rid, pCI->zDate)){
2984 ci_err((pErr,"Checkin time (%s) may not be older "
2985 "than its parent (%z).",
2986 pCI->zDate,
2987 db_text(0, "SELECT strftime('%%Y-%%m-%%dT%%H:%%M:%%f',%lf)",
2988 pCI->pParent->rDate)
2989 ));
2990 }
2991
2992 /* Potential TODOs include:
2993 **
2994 ** - Commit allows an empty checkin only with a flag, but we
2995 ** currently disallow it entirely. Conform with commit?
2996 **
2997 ** Non-TODOs:
2998 **
2999 ** - Check for a commit lock would require auto-sync, which this
3000 ** code cannot do if it's going to be run via a web page.
3001 */
3002
@@ -2969,20 +3016,24 @@
3016 ci_err((pErr,"File [%s] not found in manifest [%S]. "
3017 "Adding new files is currently not allowed.",
3018 pCI->zFilename, pCI->zParentUuid));
3019 }else if(zFile->zPerm && strstr(zFile->zPerm, "l")){
3020 ci_err((pErr,"Cannot save a symlink this way."));
3021 }
3022 if(blob_size(&pCI->fileHash)==0){
3023 /* Hash the content if it's not done already... */
3024 hname_hash(&pCI->fileContent, 0, &pCI->fileHash);
3025 assert(blob_size(&pCI->fileHash)>0);
3026 }
3027 if(zFile){
3028 /* Has this file been changed since its previous commit? */
3029 assert(blob_size(&pCI->fileHash));
3030 if(0==fossil_strcmp(zFile->zUuid, blob_str(&pCI->fileHash))){
3031 ci_err((pErr,"File is unchanged. Not saving."));
3032 }
3033 zFilePrevUuid = zFile->zUuid;
3034 }
 
 
 
 
3035 /* Create the manifest... */
3036 if(create_manifest_mini(&mf, pCI, pErr)==0){
3037 return 0;
3038 }
3039 /* Save and deltify the file content... */
@@ -2994,19 +3045,19 @@
3045 content_deltify(frid, &prevFRid, 1, 0);
3046 }
3047 /* Save, deltify, and crosslink the manifest... */
3048 rid = content_put_ex(&mf, 0, 0, 0, isPrivate);
3049 content_deltify(rid, &pCI->pParent->rid, 1, 0);
 
 
 
3050 if(ciminiFlags & CIMINI_DUMP_MANIFEST){
3051 fossil_print("Manifest %z:\n%b", rid_to_uuid(rid), &mf);
3052 }
3053 manifest_crosslink(rid, &mf, 0);
3054 blob_reset(&mf);
3055 db_end_transaction((CIMINI_DRY_RUN & ciminiFlags) ? 1 : 0);
3056 if(pRid!=0){
3057 *pRid = rid;
3058 }
3059 return 1;
3060 ci_error:
3061 assert(db_transaction_nesting_depth()>0);
3062 db_end_transaction(1);
3063 return 0;
@@ -3040,10 +3091,12 @@
3091 ** is performed beforehand.
3092 ** --allow-merge-conflict Allows checkin of a file even if it appears
3093 ** to contain a fossil merge conflict marker.
3094 ** --user-override USER USER to use instead of the current default.
3095 ** --date-override DATETIME DATE to use instead of 'now'.
3096 ** --allow-older Allow a commit to be older than its
3097 ** ancestor.
3098 ** --dump-manifest|-d Dumps the generated manifest to stdout.
3099 ** --wet-run Disables the default dry-run mode.
3100 **
3101 ** Example:
3102 **
@@ -3078,10 +3131,13 @@
3131 if(find_option("dump-manifest","d",0)!=0){
3132 ciminiFlags |= CIMINI_DUMP_MANIFEST;
3133 }
3134 if(find_option("allow-merge-conflict",0,0)!=0){
3135 ciminiFlags |= CIMINI_ALLOW_MERGE_MARKER;
3136 }
3137 if(find_option("allow-older",0,0)!=0){
3138 ciminiFlags |= CIMINI_ALLOW_OLDER;
3139 }
3140
3141 db_find_and_open_repository(0, 0);
3142 verify_all_options();
3143 user_select();
3144

Keyboard Shortcuts

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