Fossil SCM

File perms are now always taken from the local file. Added flag to allow addition of a new file (default is not to, for reasons explained in the comments).

stephan 2020-04-30 13:20 UTC checkin-without-checkout
Commit c281a179c0da28fc6cc6d64bd09d7bcf09d29ebfcfd91608de69e18492161ce4
1 file changed +124 -98
+124 -98
--- src/checkin.c
+++ src/checkin.c
@@ -2692,35 +2692,43 @@
26922692
**
26932693
** Memory for all non-const (char *) members is owned by the
26942694
** CheckinMiniInfo instance.
26952695
*/
26962696
struct CheckinMiniInfo {
2697
- Manifest * pParent; /* parent checkin */
2698
- char *zParentUuid; /* UUID of pParent */
2699
- Blob comment; /* Check-in comment text */
2700
- char *zMimetype; /* Mimetype of check-in command. May be NULL */
2701
- char *zUser; /* User name */
2702
- char *zDate; /* Optionally force this date string
2703
- (anything supported by
2704
- date_in_standard_format()).
2705
- Maybe be NULL. */
2706
- char *zFilename; /* Name of single file to commit. Must be
2707
- relative to the top of the repo. */
2708
- Blob fileContent; /* Content of file referred to by zFilename. */
2709
- Blob fileHash; /* Hash of this->fileContent, using the
2710
- repo's preferred hash method. */
2711
- int flags; /* Bitmask of fossil_cimini_flags for
2712
- communication from checkin_mini() to
2713
- create_manifest_mini(). */
2697
+ Manifest * pParent; /* parent checkin */
2698
+ char *zParentUuid; /* UUID of pParent */
2699
+ Blob comment; /* Check-in comment text */
2700
+ char *zMimetype; /* Mimetype of check-in command. May be NULL */
2701
+ char *zUser; /* User name */
2702
+ char *zDate; /* Optionally force this date string (anything
2703
+ supported by date_in_standard_format()).
2704
+ Maybe be NULL. */
2705
+ char *zFilename; /* Name of single file to commit. Must be
2706
+ relative to the top of the repo. */
2707
+ Blob fileContent; /* Content of file referred to by zFilename. */
2708
+ Blob fileHash; /* Hash of this->fileContent, using the repo's
2709
+ preferred hash method. */
2710
+ int filePerm; /* Permissions (via file_perm()) of file. We
2711
+ need to store this before calling
2712
+ checkin_mini() because the real input file
2713
+ name may differ from this->zFilename and
2714
+ checkin_mini() requires the permissions of
2715
+ the original file. For web commits, set this
2716
+ to PERM_REG before calling
2717
+ checkin_mini(). */
2718
+ int flags; /* Bitmask of fossil_cimini_flags for
2719
+ communication from checkin_mini() to
2720
+ create_manifest_mini(). */
27142721
};
27152722
typedef struct CheckinMiniInfo CheckinMiniInfo;
27162723
/*
27172724
** Initializes p to a known-valid default state.
27182725
*/
27192726
static void CheckinMiniInfo_init( CheckinMiniInfo * p ){
27202727
memset(p, 0, sizeof(CheckinMiniInfo));
27212728
p->flags = 0;
2729
+ p->filePerm = -1;
27222730
p->comment = p->fileContent = p->fileHash = empty_blob;
27232731
}
27242732
27252733
/*
27262734
** Frees all memory owned by p, but does not free p.
@@ -2784,13 +2792,16 @@
27842792
** A hint to checkin_mini() to prefer creation of a delta manifest.
27852793
*/
27862794
CIMINI_PREFER_DELTA = 1<<6,
27872795
27882796
/*
2789
-** NOT YET IMPLEMENTED.
2797
+** Tells checkin_mini() to permit the addition of a new file. Normally
2798
+** this is disabled because there are many cases where it could cause
2799
+** the inadvertent addition of a new file when an update to an
2800
+** existing was intended, as a side-effect of name-case differences.
27902801
*/
2791
-CIMINI_ALLOW_CLOSED_LEAF = 1<<7
2802
+CIMINI_ALLOW_NEW_FILE = 1<<7
27922803
};
27932804
27942805
/*
27952806
** Handles the F-card parts for create_manifest_mini().
27962807
**
@@ -2804,11 +2815,10 @@
28042815
static int create_manifest_mini_fcards( Blob * pOut,
28052816
CheckinMiniInfo * pCI,
28062817
int asDelta,
28072818
Blob * pErr){
28082819
ManifestFile *zFile; /* One file entry from the pCI->pParent*/
2809
- int fperm = 0; /* file permissions */
28102820
const char *zPerm = 0; /* permissions for new F-card */
28112821
const char *zFilename = 0; /* filename for new F-card */
28122822
const char *zUuid = 0; /* UUID for new F-card */
28132823
int cmp = 0; /* filename comparison result */
28142824
int (*fncmp)(char const *, char const *) = /* filename comparator */
@@ -2815,17 +2825,12 @@
28152825
filenames_are_case_sensitive()
28162826
? fossil_strcmp
28172827
: fossil_stricmp;
28182828
#define mf_err(EXPR) if(pErr) blob_appendf EXPR; return 0
28192829
2820
- /* Potential TODOs:
2821
- **
2822
- ** - When updating a file and the new one has the +x bit, add that
2823
- ** bit if needed. We also need that logic in the upstream "has
2824
- ** this file changed?" check. We currently always inherit the old
2825
- ** perms.
2826
- */
2830
+ assert(pCI->filePerm!=PERM_LNK && "This should have been validated before.");
2831
+ assert(pCI->filePerm>=0 && "Must have been set by the caller.");
28272832
28282833
manifest_file_rewind(pCI->pParent);
28292834
if(asDelta){
28302835
/* Parent is a baseline and we have only 1 file to modify, so this
28312836
** is the simplest case...
@@ -2832,54 +2837,52 @@
28322837
*/
28332838
assert(pCI->pParent->zBaseline==0 && "Delta-from-delta is NYI.");
28342839
zFile = manifest_file_seek(pCI->pParent, pCI->zFilename,0);
28352840
if(zFile==0){
28362841
/* New file */
2837
- fperm = file_perm(pCI->zFilename, ExtFILE);
28382842
zFilename = pCI->zFilename;
28392843
}else{
28402844
/* Replacement file */
2841
- fperm = manifest_file_mperm(zFile);
2845
+ if(manifest_file_mperm(zFile)==PERM_LNK){
2846
+ goto err_no_symlink;
2847
+ }
28422848
zFilename = zFile->zName
28432849
/* use original name in case of name-case difference */;
28442850
zFile = 0;
28452851
}
28462852
}else{
28472853
/* Non-delta: write F-cards which lexically preceed pCI->zFilename */
2848
- while((zFile = manifest_file_next(pCI->pParent, 0))){
2849
- cmp = fncmp(zFile->zName, pCI->zFilename);
2850
- if(cmp<0){
2851
- blob_appendf(pOut, "F %F %s%s%s\n", zFile->zName, zFile->zUuid,
2852
- (zFile->zPerm && *zFile->zPerm) ? " " : "",
2853
- (zFile->zPerm && *zFile->zPerm) ? zFile->zPerm : "");
2854
- }else{
2855
- break;
2856
- }
2854
+ while((zFile = manifest_file_next(pCI->pParent, 0))
2855
+ && (cmp = fncmp(zFile->zName, pCI->zFilename))<0){
2856
+ blob_appendf(pOut, "F %F %s%s%s\n", zFile->zName, zFile->zUuid,
2857
+ (zFile->zPerm && *zFile->zPerm) ? " " : "",
2858
+ (zFile->zPerm && *zFile->zPerm) ? zFile->zPerm : "");
28572859
}
28582860
/* Figure out file perms and name to save... */
2859
- if(cmp==0){
2861
+ if(zFile!=0 && cmp==0){
28602862
/* Match: override this F-card */
2861
- fperm = manifest_file_mperm(zFile);
2863
+ if(manifest_file_mperm(zFile)==PERM_LNK){
2864
+ goto err_no_symlink;
2865
+ }
28622866
zFilename = zFile->zName
28632867
/* use original name in case of name-case difference */;
28642868
zFile = 0;
28652869
}else{
28662870
/* This is a new file. */
2867
- fperm = file_perm(pCI->zFilename, ExtFILE);
2871
+ assert(zFile==0);
28682872
zFilename = pCI->zFilename;
28692873
}
28702874
}
2871
- assert(fperm!=PERM_LNK && "This should have been validated before.");
2872
- if(PERM_LNK==fperm){
2873
- mf_err((pErr,"Cannot commit symlinks via mini-checkin."));
2874
- }else if(PERM_EXE==fperm){
2875
+ if(PERM_LNK==pCI->filePerm){
2876
+ goto err_no_symlink;
2877
+ }else if(PERM_EXE==pCI->filePerm){
28752878
zPerm = " x";
28762879
}else{
28772880
zPerm = "";
28782881
}
28792882
zUuid = blob_str(&pCI->fileHash);
2880
- assert(zFile ? cmp>0&&asDelta==0 : asDelta!=0||cmp==0);
2883
+ assert(zFile ? cmp>0&&asDelta==0 : 1);
28812884
assert(zFilename);
28822885
assert(zUuid);
28832886
assert(zPerm);
28842887
blob_appendf(pOut, "F %F %s%s\n", zFilename, zUuid, zPerm);
28852888
/* Non-delta: write F-cards which lexically follow pCI->zFilename */
@@ -2896,12 +2899,16 @@
28962899
zFile->zName, zFile->zUuid,
28972900
(zFile->zPerm && *zFile->zPerm) ? " " : "",
28982901
(zFile->zPerm && *zFile->zPerm) ? zFile->zPerm : "");
28992902
zFile = manifest_file_next(pCI->pParent, 0);
29002903
}
2901
-#undef mf_err
29022904
return 1;
2905
+err_no_symlink:
2906
+ mf_err((pErr,"Cannot commit or overwrite symlinks "
2907
+ "via mini-checkin."));
2908
+ return 0;
2909
+#undef mf_err
29032910
}
29042911
29052912
29062913
/*
29072914
** Creates a manifest file, written to pOut, from the state in the
@@ -2982,11 +2989,14 @@
29822989
** This routine uses the state from the given fully-populated pCI
29832990
** argument to add pCI->fileContent to the database, and create and
29842991
** save a manifest for that change. Ownership of pCI and its contents
29852992
** are unchanged.
29862993
**
2987
-** pCI may be modified as follows:
2994
+** This function may may modify pCI as follows:
2995
+**
2996
+** - If Manifest pCI->pParent is NULL then it will be loaded
2997
+** using pCI->zParentUuid. pCI->zParentUuid may not be NULL.
29882998
**
29892999
** - pCI->zDate is normalized to/replaced with a valid date/time
29903000
** string. If its original value cannot be validated then
29913001
** this function fails. If pCI->zDate is NULL, the current time
29923002
** is used.
@@ -3014,29 +3024,49 @@
30143024
** enum for the docs for each flag.
30153025
*/
30163026
static int checkin_mini(CheckinMiniInfo * pCI, int *pRid, Blob * pErr){
30173027
Blob mf = empty_blob; /* output manifest */
30183028
int rid = 0, frid = 0; /* various RIDs */
3019
- const int isPrivate = content_is_private(pCI->pParent->rid);
3029
+ int isPrivate; /* whether this is private content
3030
+ or not */
30203031
ManifestFile * zFilePrev; /* file entry from pCI->pParent */
30213032
int prevFRid = 0; /* RID of file's prev. version */
3022
-
30233033
#define ci_err(EXPR) if(pErr!=0){blob_appendf EXPR;} goto ci_error
30243034
3035
+ if(!(pCI->flags & CIMINI_DRY_RUN)){
3036
+ /* Until this feature is fully vetted, disallow it in the main
3037
+ ** fossil repo unless dry-run mode is being used. */
3038
+ char * zProjCode = db_get("project-code",0);
3039
+ assert(zProjCode);
3040
+ if(0==fossil_stricmp("CE59BB9F186226D80E49D1FA2DB29F935CCA0333",
3041
+ zProjCode)){
3042
+ fossil_fatal("Never, ever run this in/on the core fossil repo "
3043
+ "in non-dry-run mode until it's been well-vetted. "
3044
+ "Use a temp/test repo.");
3045
+ }
3046
+ }
30253047
db_begin_transaction();
3026
- if( !db_exists("SELECT 1 FROM user WHERE login=%Q", pCI->zUser) ){
3027
- ci_err((pErr,"No such user: %s", pCI->zUser));
3048
+
3049
+ if(pCI->pParent==0){
3050
+ pCI->pParent = manifest_get_by_name(pCI->zParentUuid, 0);
3051
+ if(pCI->pParent==0){
3052
+ ci_err((pErr,"Cannot load manifest for [%S].", pCI->zParentUuid));
3053
+ }
30283054
}
3055
+
30293056
assert(pCI->pParent->rid>0);
30303057
if(leaf_is_closed(pCI->pParent->rid)){
30313058
ci_err((pErr,"Cannot commit to a closed leaf."));
30323059
/* Remember that in order to override this we'd also need to
3033
- ** cancel TAG_CLOSED on pCI->pParent. There would seem to be
3034
- ** no reason we can't do that via the generated manifest,
3035
- ** but the commit command does not offer that option, so
3036
- ** we won't, either.
3060
+ ** cancel TAG_CLOSED on pCI->pParent. There would seem to be no
3061
+ ** reason we can't do that via the generated manifest, but the
3062
+ ** commit command does not offer that option, so mini-checkin
3063
+ ** probably shouldn't, either.
30373064
*/
3065
+ }
3066
+ if( !db_exists("SELECT 1 FROM user WHERE login=%Q", pCI->zUser) ){
3067
+ ci_err((pErr,"No such user: %s", pCI->zUser));
30383068
}
30393069
if(!(CIMINI_ALLOW_FORK & pCI->flags)
30403070
&& !is_a_leaf(pCI->pParent->rid)){
30413071
ci_err((pErr,"Parent [%S] is not a leaf and forking is disabled.",
30423072
pCI->zParentUuid));
@@ -3047,36 +3077,34 @@
30473077
}
30483078
if(!file_is_simple_pathname(pCI->zFilename, 1)){
30493079
ci_err((pErr,"Invalid filename for use in a repository: %s",
30503080
pCI->zFilename));
30513081
}
3052
-
3053
- {
3054
- /*
3055
- ** Normalize the timestamp. We don't use date_in_standard_format()
3056
- ** because that has side-effects we don't want to trigger here.
3057
- */
3058
- char * zDVal = db_text(
3059
- 0, "SELECT strftime('%%Y-%%m-%%dT%%H:%%M:%%f',%Q)",
3060
- pCI->zDate ? pCI->zDate : "now");
3061
- if(zDVal[0]==0){
3062
- fossil_free(zDVal);
3063
- ci_err((pErr,"Invalid timestamp string: %s", pCI->zDate));
3064
- }
3065
- fossil_free(pCI->zDate);
3066
- pCI->zDate = zDVal;
3067
- }
30683082
if(!(CIMINI_ALLOW_OLDER & pCI->flags)
30693083
&& !checkin_is_younger(pCI->pParent->rid, pCI->zDate)){
30703084
ci_err((pErr,"Checkin time (%s) may not be older "
30713085
"than its parent (%z).",
30723086
pCI->zDate,
30733087
db_text(0, "SELECT strftime('%%Y-%%m-%%dT%%H:%%M:%%f',%lf)",
30743088
pCI->pParent->rDate)
30753089
));
30763090
}
3077
-
3091
+ {
3092
+ /*
3093
+ ** Normalize the timestamp. We don't use date_in_standard_format()
3094
+ ** because that has side-effects we don't want to trigger here.
3095
+ */
3096
+ char * zDVal = db_text(
3097
+ 0, "SELECT strftime('%%Y-%%m-%%dT%%H:%%M:%%f',%Q)",
3098
+ pCI->zDate ? pCI->zDate : "now");
3099
+ if(zDVal==0 || zDVal[0]==0){
3100
+ fossil_free(zDVal);
3101
+ ci_err((pErr,"Invalid timestamp string: %s", pCI->zDate));
3102
+ }
3103
+ fossil_free(pCI->zDate);
3104
+ pCI->zDate = zDVal;
3105
+ }
30783106
/* Potential TODOs include:
30793107
**
30803108
** - Commit allows an empty checkin only with a flag, but we
30813109
** currently disallow it entirely. Conform with commit?
30823110
**
@@ -3086,25 +3114,24 @@
30863114
** code cannot do if it's going to be run via a web page.
30873115
*/
30883116
30893117
/*
30903118
** Confirm that pCI->zFilename can be found in pCI->pParent. If
3091
- ** not, fail. This is admittedly an artificial limitation, not
3092
- ** strictly necessary. We do it to hopefully reduce the chance of an
3093
- ** "oops" where file X/Y/z gets committed as X/Y/Z due to a typo or
3119
+ ** not, fail unless the CIMINI_ALLOW_NEW_FILE flag is set. This is
3120
+ ** admittedly an artificial limitation, not strictly necessary. We
3121
+ ** do it to hopefully reduce the chance of an "oops" where file
3122
+ ** X/Y/z gets committed as X/Y/Z or X/y/z due to a typo or
30943123
** case-sensitivity mismatch between the user/repo/filesystem, or
3095
- ** some such. That said, the remainder of this function is written
3096
- ** as if this check did not exist, so enabling it "should" just be a
3097
- ** matter of removing this check or guarding it with a flag.
3124
+ ** some such.
30983125
*/
30993126
manifest_file_rewind(pCI->pParent);
31003127
zFilePrev = manifest_file_seek(pCI->pParent, pCI->zFilename, 0);
3101
- if(!zFilePrev){
3128
+ if(!zFilePrev && !(CIMINI_ALLOW_NEW_FILE & pCI->flags)){
31023129
ci_err((pErr,"File [%s] not found in manifest [%S]. "
31033130
"Adding new files is currently not permitted.",
31043131
pCI->zFilename, pCI->zParentUuid));
3105
- }else if(zFilePrev->zPerm
3132
+ }else if(zFilePrev
31063133
&& manifest_file_mperm(zFilePrev)==PERM_LNK){
31073134
ci_err((pErr,"Cannot save a symlink via a mini-checkin."));
31083135
}
31093136
if(zFilePrev){
31103137
prevFRid = fast_uuid_to_rid(zFilePrev->zUuid);
@@ -3174,10 +3201,11 @@
31743201
}
31753202
/* Create, save, deltify, and crosslink the manifest... */
31763203
if(create_manifest_mini(&mf, pCI, pErr)==0){
31773204
return 0;
31783205
}
3206
+ isPrivate = content_is_private(pCI->pParent->rid);
31793207
rid = content_put_ex(&mf, 0, 0, 0, isPrivate);
31803208
content_deltify(rid, &pCI->pParent->rid, 1, 0);
31813209
if(pCI->flags & CIMINI_DUMP_MANIFEST){
31823210
fossil_print("Manifest %z:\n%b", rid_to_uuid(rid), &mf);
31833211
}
@@ -3237,10 +3265,15 @@
32373265
** the previous version's content. Does not
32383266
** modify the original file, only the
32393267
** checked-in content.
32403268
** --delta Prefer to generate a delta manifest, if
32413269
** able.
3270
+** --allow-new-file Allow addition of a new file this way.
3271
+** Disabled by default to avoid that case-
3272
+** sensitivity errors inadvertently lead to
3273
+** adding a new file where an update is
3274
+** intended.
32423275
** --dump-manifest|-d Dumps the generated manifest to stdout.
32433276
** --wet-run Disables the default dry-run mode.
32443277
**
32453278
** Example:
32463279
**
@@ -3256,10 +3289,16 @@
32563289
const char * zAsFilename; /* --as filename */
32573290
const char * zRevision; /* --revision|-r [=trunk|checkout] */
32583291
const char * zUser; /* --user-override */
32593292
const char * zDate; /* --date-override */
32603293
3294
+ /* This function should perform only the minimal "business logic" it
3295
+ ** needs in order to fully/properly populate the CheckinMiniInfo and
3296
+ ** then pass it on to checkin_mini() to do most of the validation
3297
+ ** and work. The point of this is to avoid duplicate code when a web
3298
+ ** front-end is added for checkin_mini().
3299
+ */
32613300
CheckinMiniInfo_init(&cinf);
32623301
zComment = find_option("comment","m",1);
32633302
zCommentFile = find_option("comment-file","M",1);
32643303
zAsFilename = find_option("as",0,1);
32653304
zRevision = find_option("revision","r",1);
@@ -3284,30 +3323,19 @@
32843323
cinf.flags |= CIMINI_CONVERT_EOL;
32853324
}
32863325
if(find_option("delta",0,0)!=0){
32873326
cinf.flags |= CIMINI_PREFER_DELTA;
32883327
}
3328
+ if(find_option("allow-new-file",0,0)!=0){
3329
+ cinf.flags |= CIMINI_ALLOW_NEW_FILE;
3330
+ }
32893331
db_find_and_open_repository(0, 0);
32903332
verify_all_options();
32913333
user_select();
32923334
if(g.argc!=3){
32933335
usage("INFILE");
32943336
}
3295
-
3296
- if(!(cinf.flags & CIMINI_DRY_RUN)){
3297
- /* Until this feature is fully vetted, disallow it in the main
3298
- ** fossil repo unless dry-run mode is being used. */
3299
- char * zProjCode = db_get("project-code",0);
3300
- assert(zProjCode);
3301
- if(0==fossil_stricmp("CE59BB9F186226D80E49D1FA2DB29F935CCA0333",
3302
- zProjCode)){
3303
- fossil_fatal("Never, ever run this in/on the core fossil repo "
3304
- "until it's been well-vetted. Use a temp/test "
3305
- "repo.");
3306
- }
3307
- }
3308
-
33093337
if(zComment && zCommentFile){
33103338
fossil_fatal("Only one of -m or -M, not both, may be used.");
33113339
}else{
33123340
if(zCommentFile && *zCommentFile){
33133341
blob_read_from_file(&cinf.comment, zCommentFile, ExtFILE);
@@ -3316,13 +3344,13 @@
33163344
}
33173345
if(!blob_size(&cinf.comment)){
33183346
fossil_fatal("Non-empty checkin comment is required.");
33193347
}
33203348
}
3321
-
33223349
zFilename = g.argv[2];
33233350
cinf.zFilename = mprintf("%/", zAsFilename ? zAsFilename : zFilename);
3351
+ cinf.filePerm = file_perm(zFilename, ExtFILE);
33243352
cinf.zUser = mprintf("%s", zUser ? zUser : login_name());
33253353
if(zDate){
33263354
cinf.zDate = mprintf("%s", zDate);
33273355
}
33283356
if(zRevision==0 || zRevision[0]==0){
@@ -3334,12 +3362,10 @@
33343362
}
33353363
name_to_uuid2(zRevision, "ci", &cinf.zParentUuid);
33363364
if(cinf.zParentUuid==0){
33373365
fossil_fatal("Cannot determine version to commit to.");
33383366
}
3339
- cinf.pParent = manifest_get_by_name(cinf.zParentUuid, 0);
3340
- assert(cinf.pParent!=0);
33413367
blob_read_from_file(&cinf.fileContent, zFilename,
33423368
ExtFILE/*may want to reconsider*/);
33433369
{
33443370
Blob errMsg = empty_blob;
33453371
const int rc = checkin_mini(&cinf, &newRid, &errMsg);
33463372
--- src/checkin.c
+++ src/checkin.c
@@ -2692,35 +2692,43 @@
2692 **
2693 ** Memory for all non-const (char *) members is owned by the
2694 ** CheckinMiniInfo instance.
2695 */
2696 struct CheckinMiniInfo {
2697 Manifest * pParent; /* parent checkin */
2698 char *zParentUuid; /* UUID of pParent */
2699 Blob comment; /* Check-in comment text */
2700 char *zMimetype; /* Mimetype of check-in command. May be NULL */
2701 char *zUser; /* User name */
2702 char *zDate; /* Optionally force this date string
2703 (anything supported by
2704 date_in_standard_format()).
2705 Maybe be NULL. */
2706 char *zFilename; /* Name of single file to commit. Must be
2707 relative to the top of the repo. */
2708 Blob fileContent; /* Content of file referred to by zFilename. */
2709 Blob fileHash; /* Hash of this->fileContent, using the
2710 repo's preferred hash method. */
2711 int flags; /* Bitmask of fossil_cimini_flags for
2712 communication from checkin_mini() to
2713 create_manifest_mini(). */
 
 
 
 
 
 
 
2714 };
2715 typedef struct CheckinMiniInfo CheckinMiniInfo;
2716 /*
2717 ** Initializes p to a known-valid default state.
2718 */
2719 static void CheckinMiniInfo_init( CheckinMiniInfo * p ){
2720 memset(p, 0, sizeof(CheckinMiniInfo));
2721 p->flags = 0;
 
2722 p->comment = p->fileContent = p->fileHash = empty_blob;
2723 }
2724
2725 /*
2726 ** Frees all memory owned by p, but does not free p.
@@ -2784,13 +2792,16 @@
2784 ** A hint to checkin_mini() to prefer creation of a delta manifest.
2785 */
2786 CIMINI_PREFER_DELTA = 1<<6,
2787
2788 /*
2789 ** NOT YET IMPLEMENTED.
 
 
 
2790 */
2791 CIMINI_ALLOW_CLOSED_LEAF = 1<<7
2792 };
2793
2794 /*
2795 ** Handles the F-card parts for create_manifest_mini().
2796 **
@@ -2804,11 +2815,10 @@
2804 static int create_manifest_mini_fcards( Blob * pOut,
2805 CheckinMiniInfo * pCI,
2806 int asDelta,
2807 Blob * pErr){
2808 ManifestFile *zFile; /* One file entry from the pCI->pParent*/
2809 int fperm = 0; /* file permissions */
2810 const char *zPerm = 0; /* permissions for new F-card */
2811 const char *zFilename = 0; /* filename for new F-card */
2812 const char *zUuid = 0; /* UUID for new F-card */
2813 int cmp = 0; /* filename comparison result */
2814 int (*fncmp)(char const *, char const *) = /* filename comparator */
@@ -2815,17 +2825,12 @@
2815 filenames_are_case_sensitive()
2816 ? fossil_strcmp
2817 : fossil_stricmp;
2818 #define mf_err(EXPR) if(pErr) blob_appendf EXPR; return 0
2819
2820 /* Potential TODOs:
2821 **
2822 ** - When updating a file and the new one has the +x bit, add that
2823 ** bit if needed. We also need that logic in the upstream "has
2824 ** this file changed?" check. We currently always inherit the old
2825 ** perms.
2826 */
2827
2828 manifest_file_rewind(pCI->pParent);
2829 if(asDelta){
2830 /* Parent is a baseline and we have only 1 file to modify, so this
2831 ** is the simplest case...
@@ -2832,54 +2837,52 @@
2832 */
2833 assert(pCI->pParent->zBaseline==0 && "Delta-from-delta is NYI.");
2834 zFile = manifest_file_seek(pCI->pParent, pCI->zFilename,0);
2835 if(zFile==0){
2836 /* New file */
2837 fperm = file_perm(pCI->zFilename, ExtFILE);
2838 zFilename = pCI->zFilename;
2839 }else{
2840 /* Replacement file */
2841 fperm = manifest_file_mperm(zFile);
 
 
2842 zFilename = zFile->zName
2843 /* use original name in case of name-case difference */;
2844 zFile = 0;
2845 }
2846 }else{
2847 /* Non-delta: write F-cards which lexically preceed pCI->zFilename */
2848 while((zFile = manifest_file_next(pCI->pParent, 0))){
2849 cmp = fncmp(zFile->zName, pCI->zFilename);
2850 if(cmp<0){
2851 blob_appendf(pOut, "F %F %s%s%s\n", zFile->zName, zFile->zUuid,
2852 (zFile->zPerm && *zFile->zPerm) ? " " : "",
2853 (zFile->zPerm && *zFile->zPerm) ? zFile->zPerm : "");
2854 }else{
2855 break;
2856 }
2857 }
2858 /* Figure out file perms and name to save... */
2859 if(cmp==0){
2860 /* Match: override this F-card */
2861 fperm = manifest_file_mperm(zFile);
 
 
2862 zFilename = zFile->zName
2863 /* use original name in case of name-case difference */;
2864 zFile = 0;
2865 }else{
2866 /* This is a new file. */
2867 fperm = file_perm(pCI->zFilename, ExtFILE);
2868 zFilename = pCI->zFilename;
2869 }
2870 }
2871 assert(fperm!=PERM_LNK && "This should have been validated before.");
2872 if(PERM_LNK==fperm){
2873 mf_err((pErr,"Cannot commit symlinks via mini-checkin."));
2874 }else if(PERM_EXE==fperm){
2875 zPerm = " x";
2876 }else{
2877 zPerm = "";
2878 }
2879 zUuid = blob_str(&pCI->fileHash);
2880 assert(zFile ? cmp>0&&asDelta==0 : asDelta!=0||cmp==0);
2881 assert(zFilename);
2882 assert(zUuid);
2883 assert(zPerm);
2884 blob_appendf(pOut, "F %F %s%s\n", zFilename, zUuid, zPerm);
2885 /* Non-delta: write F-cards which lexically follow pCI->zFilename */
@@ -2896,12 +2899,16 @@
2896 zFile->zName, zFile->zUuid,
2897 (zFile->zPerm && *zFile->zPerm) ? " " : "",
2898 (zFile->zPerm && *zFile->zPerm) ? zFile->zPerm : "");
2899 zFile = manifest_file_next(pCI->pParent, 0);
2900 }
2901 #undef mf_err
2902 return 1;
 
 
 
 
 
2903 }
2904
2905
2906 /*
2907 ** Creates a manifest file, written to pOut, from the state in the
@@ -2982,11 +2989,14 @@
2982 ** This routine uses the state from the given fully-populated pCI
2983 ** argument to add pCI->fileContent to the database, and create and
2984 ** save a manifest for that change. Ownership of pCI and its contents
2985 ** are unchanged.
2986 **
2987 ** pCI may be modified as follows:
 
 
 
2988 **
2989 ** - pCI->zDate is normalized to/replaced with a valid date/time
2990 ** string. If its original value cannot be validated then
2991 ** this function fails. If pCI->zDate is NULL, the current time
2992 ** is used.
@@ -3014,29 +3024,49 @@
3014 ** enum for the docs for each flag.
3015 */
3016 static int checkin_mini(CheckinMiniInfo * pCI, int *pRid, Blob * pErr){
3017 Blob mf = empty_blob; /* output manifest */
3018 int rid = 0, frid = 0; /* various RIDs */
3019 const int isPrivate = content_is_private(pCI->pParent->rid);
 
3020 ManifestFile * zFilePrev; /* file entry from pCI->pParent */
3021 int prevFRid = 0; /* RID of file's prev. version */
3022
3023 #define ci_err(EXPR) if(pErr!=0){blob_appendf EXPR;} goto ci_error
3024
 
 
 
 
 
 
 
 
 
 
 
 
3025 db_begin_transaction();
3026 if( !db_exists("SELECT 1 FROM user WHERE login=%Q", pCI->zUser) ){
3027 ci_err((pErr,"No such user: %s", pCI->zUser));
 
 
 
 
3028 }
 
3029 assert(pCI->pParent->rid>0);
3030 if(leaf_is_closed(pCI->pParent->rid)){
3031 ci_err((pErr,"Cannot commit to a closed leaf."));
3032 /* Remember that in order to override this we'd also need to
3033 ** cancel TAG_CLOSED on pCI->pParent. There would seem to be
3034 ** no reason we can't do that via the generated manifest,
3035 ** but the commit command does not offer that option, so
3036 ** we won't, either.
3037 */
 
 
 
3038 }
3039 if(!(CIMINI_ALLOW_FORK & pCI->flags)
3040 && !is_a_leaf(pCI->pParent->rid)){
3041 ci_err((pErr,"Parent [%S] is not a leaf and forking is disabled.",
3042 pCI->zParentUuid));
@@ -3047,36 +3077,34 @@
3047 }
3048 if(!file_is_simple_pathname(pCI->zFilename, 1)){
3049 ci_err((pErr,"Invalid filename for use in a repository: %s",
3050 pCI->zFilename));
3051 }
3052
3053 {
3054 /*
3055 ** Normalize the timestamp. We don't use date_in_standard_format()
3056 ** because that has side-effects we don't want to trigger here.
3057 */
3058 char * zDVal = db_text(
3059 0, "SELECT strftime('%%Y-%%m-%%dT%%H:%%M:%%f',%Q)",
3060 pCI->zDate ? pCI->zDate : "now");
3061 if(zDVal[0]==0){
3062 fossil_free(zDVal);
3063 ci_err((pErr,"Invalid timestamp string: %s", pCI->zDate));
3064 }
3065 fossil_free(pCI->zDate);
3066 pCI->zDate = zDVal;
3067 }
3068 if(!(CIMINI_ALLOW_OLDER & pCI->flags)
3069 && !checkin_is_younger(pCI->pParent->rid, pCI->zDate)){
3070 ci_err((pErr,"Checkin time (%s) may not be older "
3071 "than its parent (%z).",
3072 pCI->zDate,
3073 db_text(0, "SELECT strftime('%%Y-%%m-%%dT%%H:%%M:%%f',%lf)",
3074 pCI->pParent->rDate)
3075 ));
3076 }
3077
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3078 /* Potential TODOs include:
3079 **
3080 ** - Commit allows an empty checkin only with a flag, but we
3081 ** currently disallow it entirely. Conform with commit?
3082 **
@@ -3086,25 +3114,24 @@
3086 ** code cannot do if it's going to be run via a web page.
3087 */
3088
3089 /*
3090 ** Confirm that pCI->zFilename can be found in pCI->pParent. If
3091 ** not, fail. This is admittedly an artificial limitation, not
3092 ** strictly necessary. We do it to hopefully reduce the chance of an
3093 ** "oops" where file X/Y/z gets committed as X/Y/Z due to a typo or
 
3094 ** case-sensitivity mismatch between the user/repo/filesystem, or
3095 ** some such. That said, the remainder of this function is written
3096 ** as if this check did not exist, so enabling it "should" just be a
3097 ** matter of removing this check or guarding it with a flag.
3098 */
3099 manifest_file_rewind(pCI->pParent);
3100 zFilePrev = manifest_file_seek(pCI->pParent, pCI->zFilename, 0);
3101 if(!zFilePrev){
3102 ci_err((pErr,"File [%s] not found in manifest [%S]. "
3103 "Adding new files is currently not permitted.",
3104 pCI->zFilename, pCI->zParentUuid));
3105 }else if(zFilePrev->zPerm
3106 && manifest_file_mperm(zFilePrev)==PERM_LNK){
3107 ci_err((pErr,"Cannot save a symlink via a mini-checkin."));
3108 }
3109 if(zFilePrev){
3110 prevFRid = fast_uuid_to_rid(zFilePrev->zUuid);
@@ -3174,10 +3201,11 @@
3174 }
3175 /* Create, save, deltify, and crosslink the manifest... */
3176 if(create_manifest_mini(&mf, pCI, pErr)==0){
3177 return 0;
3178 }
 
3179 rid = content_put_ex(&mf, 0, 0, 0, isPrivate);
3180 content_deltify(rid, &pCI->pParent->rid, 1, 0);
3181 if(pCI->flags & CIMINI_DUMP_MANIFEST){
3182 fossil_print("Manifest %z:\n%b", rid_to_uuid(rid), &mf);
3183 }
@@ -3237,10 +3265,15 @@
3237 ** the previous version's content. Does not
3238 ** modify the original file, only the
3239 ** checked-in content.
3240 ** --delta Prefer to generate a delta manifest, if
3241 ** able.
 
 
 
 
 
3242 ** --dump-manifest|-d Dumps the generated manifest to stdout.
3243 ** --wet-run Disables the default dry-run mode.
3244 **
3245 ** Example:
3246 **
@@ -3256,10 +3289,16 @@
3256 const char * zAsFilename; /* --as filename */
3257 const char * zRevision; /* --revision|-r [=trunk|checkout] */
3258 const char * zUser; /* --user-override */
3259 const char * zDate; /* --date-override */
3260
 
 
 
 
 
 
3261 CheckinMiniInfo_init(&cinf);
3262 zComment = find_option("comment","m",1);
3263 zCommentFile = find_option("comment-file","M",1);
3264 zAsFilename = find_option("as",0,1);
3265 zRevision = find_option("revision","r",1);
@@ -3284,30 +3323,19 @@
3284 cinf.flags |= CIMINI_CONVERT_EOL;
3285 }
3286 if(find_option("delta",0,0)!=0){
3287 cinf.flags |= CIMINI_PREFER_DELTA;
3288 }
 
 
 
3289 db_find_and_open_repository(0, 0);
3290 verify_all_options();
3291 user_select();
3292 if(g.argc!=3){
3293 usage("INFILE");
3294 }
3295
3296 if(!(cinf.flags & CIMINI_DRY_RUN)){
3297 /* Until this feature is fully vetted, disallow it in the main
3298 ** fossil repo unless dry-run mode is being used. */
3299 char * zProjCode = db_get("project-code",0);
3300 assert(zProjCode);
3301 if(0==fossil_stricmp("CE59BB9F186226D80E49D1FA2DB29F935CCA0333",
3302 zProjCode)){
3303 fossil_fatal("Never, ever run this in/on the core fossil repo "
3304 "until it's been well-vetted. Use a temp/test "
3305 "repo.");
3306 }
3307 }
3308
3309 if(zComment && zCommentFile){
3310 fossil_fatal("Only one of -m or -M, not both, may be used.");
3311 }else{
3312 if(zCommentFile && *zCommentFile){
3313 blob_read_from_file(&cinf.comment, zCommentFile, ExtFILE);
@@ -3316,13 +3344,13 @@
3316 }
3317 if(!blob_size(&cinf.comment)){
3318 fossil_fatal("Non-empty checkin comment is required.");
3319 }
3320 }
3321
3322 zFilename = g.argv[2];
3323 cinf.zFilename = mprintf("%/", zAsFilename ? zAsFilename : zFilename);
 
3324 cinf.zUser = mprintf("%s", zUser ? zUser : login_name());
3325 if(zDate){
3326 cinf.zDate = mprintf("%s", zDate);
3327 }
3328 if(zRevision==0 || zRevision[0]==0){
@@ -3334,12 +3362,10 @@
3334 }
3335 name_to_uuid2(zRevision, "ci", &cinf.zParentUuid);
3336 if(cinf.zParentUuid==0){
3337 fossil_fatal("Cannot determine version to commit to.");
3338 }
3339 cinf.pParent = manifest_get_by_name(cinf.zParentUuid, 0);
3340 assert(cinf.pParent!=0);
3341 blob_read_from_file(&cinf.fileContent, zFilename,
3342 ExtFILE/*may want to reconsider*/);
3343 {
3344 Blob errMsg = empty_blob;
3345 const int rc = checkin_mini(&cinf, &newRid, &errMsg);
3346
--- src/checkin.c
+++ src/checkin.c
@@ -2692,35 +2692,43 @@
2692 **
2693 ** Memory for all non-const (char *) members is owned by the
2694 ** CheckinMiniInfo instance.
2695 */
2696 struct CheckinMiniInfo {
2697 Manifest * pParent; /* parent checkin */
2698 char *zParentUuid; /* UUID of pParent */
2699 Blob comment; /* Check-in comment text */
2700 char *zMimetype; /* Mimetype of check-in command. May be NULL */
2701 char *zUser; /* User name */
2702 char *zDate; /* Optionally force this date string (anything
2703 supported by date_in_standard_format()).
2704 Maybe be NULL. */
2705 char *zFilename; /* Name of single file to commit. Must be
2706 relative to the top of the repo. */
2707 Blob fileContent; /* Content of file referred to by zFilename. */
2708 Blob fileHash; /* Hash of this->fileContent, using the repo's
2709 preferred hash method. */
2710 int filePerm; /* Permissions (via file_perm()) of file. We
2711 need to store this before calling
2712 checkin_mini() because the real input file
2713 name may differ from this->zFilename and
2714 checkin_mini() requires the permissions of
2715 the original file. For web commits, set this
2716 to PERM_REG before calling
2717 checkin_mini(). */
2718 int flags; /* Bitmask of fossil_cimini_flags for
2719 communication from checkin_mini() to
2720 create_manifest_mini(). */
2721 };
2722 typedef struct CheckinMiniInfo CheckinMiniInfo;
2723 /*
2724 ** Initializes p to a known-valid default state.
2725 */
2726 static void CheckinMiniInfo_init( CheckinMiniInfo * p ){
2727 memset(p, 0, sizeof(CheckinMiniInfo));
2728 p->flags = 0;
2729 p->filePerm = -1;
2730 p->comment = p->fileContent = p->fileHash = empty_blob;
2731 }
2732
2733 /*
2734 ** Frees all memory owned by p, but does not free p.
@@ -2784,13 +2792,16 @@
2792 ** A hint to checkin_mini() to prefer creation of a delta manifest.
2793 */
2794 CIMINI_PREFER_DELTA = 1<<6,
2795
2796 /*
2797 ** Tells checkin_mini() to permit the addition of a new file. Normally
2798 ** this is disabled because there are many cases where it could cause
2799 ** the inadvertent addition of a new file when an update to an
2800 ** existing was intended, as a side-effect of name-case differences.
2801 */
2802 CIMINI_ALLOW_NEW_FILE = 1<<7
2803 };
2804
2805 /*
2806 ** Handles the F-card parts for create_manifest_mini().
2807 **
@@ -2804,11 +2815,10 @@
2815 static int create_manifest_mini_fcards( Blob * pOut,
2816 CheckinMiniInfo * pCI,
2817 int asDelta,
2818 Blob * pErr){
2819 ManifestFile *zFile; /* One file entry from the pCI->pParent*/
 
2820 const char *zPerm = 0; /* permissions for new F-card */
2821 const char *zFilename = 0; /* filename for new F-card */
2822 const char *zUuid = 0; /* UUID for new F-card */
2823 int cmp = 0; /* filename comparison result */
2824 int (*fncmp)(char const *, char const *) = /* filename comparator */
@@ -2815,17 +2825,12 @@
2825 filenames_are_case_sensitive()
2826 ? fossil_strcmp
2827 : fossil_stricmp;
2828 #define mf_err(EXPR) if(pErr) blob_appendf EXPR; return 0
2829
2830 assert(pCI->filePerm!=PERM_LNK && "This should have been validated before.");
2831 assert(pCI->filePerm>=0 && "Must have been set by the caller.");
 
 
 
 
 
2832
2833 manifest_file_rewind(pCI->pParent);
2834 if(asDelta){
2835 /* Parent is a baseline and we have only 1 file to modify, so this
2836 ** is the simplest case...
@@ -2832,54 +2837,52 @@
2837 */
2838 assert(pCI->pParent->zBaseline==0 && "Delta-from-delta is NYI.");
2839 zFile = manifest_file_seek(pCI->pParent, pCI->zFilename,0);
2840 if(zFile==0){
2841 /* New file */
 
2842 zFilename = pCI->zFilename;
2843 }else{
2844 /* Replacement file */
2845 if(manifest_file_mperm(zFile)==PERM_LNK){
2846 goto err_no_symlink;
2847 }
2848 zFilename = zFile->zName
2849 /* use original name in case of name-case difference */;
2850 zFile = 0;
2851 }
2852 }else{
2853 /* Non-delta: write F-cards which lexically preceed pCI->zFilename */
2854 while((zFile = manifest_file_next(pCI->pParent, 0))
2855 && (cmp = fncmp(zFile->zName, pCI->zFilename))<0){
2856 blob_appendf(pOut, "F %F %s%s%s\n", zFile->zName, zFile->zUuid,
2857 (zFile->zPerm && *zFile->zPerm) ? " " : "",
2858 (zFile->zPerm && *zFile->zPerm) ? zFile->zPerm : "");
 
 
 
 
2859 }
2860 /* Figure out file perms and name to save... */
2861 if(zFile!=0 && cmp==0){
2862 /* Match: override this F-card */
2863 if(manifest_file_mperm(zFile)==PERM_LNK){
2864 goto err_no_symlink;
2865 }
2866 zFilename = zFile->zName
2867 /* use original name in case of name-case difference */;
2868 zFile = 0;
2869 }else{
2870 /* This is a new file. */
2871 assert(zFile==0);
2872 zFilename = pCI->zFilename;
2873 }
2874 }
2875 if(PERM_LNK==pCI->filePerm){
2876 goto err_no_symlink;
2877 }else if(PERM_EXE==pCI->filePerm){
 
2878 zPerm = " x";
2879 }else{
2880 zPerm = "";
2881 }
2882 zUuid = blob_str(&pCI->fileHash);
2883 assert(zFile ? cmp>0&&asDelta==0 : 1);
2884 assert(zFilename);
2885 assert(zUuid);
2886 assert(zPerm);
2887 blob_appendf(pOut, "F %F %s%s\n", zFilename, zUuid, zPerm);
2888 /* Non-delta: write F-cards which lexically follow pCI->zFilename */
@@ -2896,12 +2899,16 @@
2899 zFile->zName, zFile->zUuid,
2900 (zFile->zPerm && *zFile->zPerm) ? " " : "",
2901 (zFile->zPerm && *zFile->zPerm) ? zFile->zPerm : "");
2902 zFile = manifest_file_next(pCI->pParent, 0);
2903 }
 
2904 return 1;
2905 err_no_symlink:
2906 mf_err((pErr,"Cannot commit or overwrite symlinks "
2907 "via mini-checkin."));
2908 return 0;
2909 #undef mf_err
2910 }
2911
2912
2913 /*
2914 ** Creates a manifest file, written to pOut, from the state in the
@@ -2982,11 +2989,14 @@
2989 ** This routine uses the state from the given fully-populated pCI
2990 ** argument to add pCI->fileContent to the database, and create and
2991 ** save a manifest for that change. Ownership of pCI and its contents
2992 ** are unchanged.
2993 **
2994 ** This function may may modify pCI as follows:
2995 **
2996 ** - If Manifest pCI->pParent is NULL then it will be loaded
2997 ** using pCI->zParentUuid. pCI->zParentUuid may not be NULL.
2998 **
2999 ** - pCI->zDate is normalized to/replaced with a valid date/time
3000 ** string. If its original value cannot be validated then
3001 ** this function fails. If pCI->zDate is NULL, the current time
3002 ** is used.
@@ -3014,29 +3024,49 @@
3024 ** enum for the docs for each flag.
3025 */
3026 static int checkin_mini(CheckinMiniInfo * pCI, int *pRid, Blob * pErr){
3027 Blob mf = empty_blob; /* output manifest */
3028 int rid = 0, frid = 0; /* various RIDs */
3029 int isPrivate; /* whether this is private content
3030 or not */
3031 ManifestFile * zFilePrev; /* file entry from pCI->pParent */
3032 int prevFRid = 0; /* RID of file's prev. version */
 
3033 #define ci_err(EXPR) if(pErr!=0){blob_appendf EXPR;} goto ci_error
3034
3035 if(!(pCI->flags & CIMINI_DRY_RUN)){
3036 /* Until this feature is fully vetted, disallow it in the main
3037 ** fossil repo unless dry-run mode is being used. */
3038 char * zProjCode = db_get("project-code",0);
3039 assert(zProjCode);
3040 if(0==fossil_stricmp("CE59BB9F186226D80E49D1FA2DB29F935CCA0333",
3041 zProjCode)){
3042 fossil_fatal("Never, ever run this in/on the core fossil repo "
3043 "in non-dry-run mode until it's been well-vetted. "
3044 "Use a temp/test repo.");
3045 }
3046 }
3047 db_begin_transaction();
3048
3049 if(pCI->pParent==0){
3050 pCI->pParent = manifest_get_by_name(pCI->zParentUuid, 0);
3051 if(pCI->pParent==0){
3052 ci_err((pErr,"Cannot load manifest for [%S].", pCI->zParentUuid));
3053 }
3054 }
3055
3056 assert(pCI->pParent->rid>0);
3057 if(leaf_is_closed(pCI->pParent->rid)){
3058 ci_err((pErr,"Cannot commit to a closed leaf."));
3059 /* Remember that in order to override this we'd also need to
3060 ** cancel TAG_CLOSED on pCI->pParent. There would seem to be no
3061 ** reason we can't do that via the generated manifest, but the
3062 ** commit command does not offer that option, so mini-checkin
3063 ** probably shouldn't, either.
3064 */
3065 }
3066 if( !db_exists("SELECT 1 FROM user WHERE login=%Q", pCI->zUser) ){
3067 ci_err((pErr,"No such user: %s", pCI->zUser));
3068 }
3069 if(!(CIMINI_ALLOW_FORK & pCI->flags)
3070 && !is_a_leaf(pCI->pParent->rid)){
3071 ci_err((pErr,"Parent [%S] is not a leaf and forking is disabled.",
3072 pCI->zParentUuid));
@@ -3047,36 +3077,34 @@
3077 }
3078 if(!file_is_simple_pathname(pCI->zFilename, 1)){
3079 ci_err((pErr,"Invalid filename for use in a repository: %s",
3080 pCI->zFilename));
3081 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3082 if(!(CIMINI_ALLOW_OLDER & pCI->flags)
3083 && !checkin_is_younger(pCI->pParent->rid, pCI->zDate)){
3084 ci_err((pErr,"Checkin time (%s) may not be older "
3085 "than its parent (%z).",
3086 pCI->zDate,
3087 db_text(0, "SELECT strftime('%%Y-%%m-%%dT%%H:%%M:%%f',%lf)",
3088 pCI->pParent->rDate)
3089 ));
3090 }
3091 {
3092 /*
3093 ** Normalize the timestamp. We don't use date_in_standard_format()
3094 ** because that has side-effects we don't want to trigger here.
3095 */
3096 char * zDVal = db_text(
3097 0, "SELECT strftime('%%Y-%%m-%%dT%%H:%%M:%%f',%Q)",
3098 pCI->zDate ? pCI->zDate : "now");
3099 if(zDVal==0 || zDVal[0]==0){
3100 fossil_free(zDVal);
3101 ci_err((pErr,"Invalid timestamp string: %s", pCI->zDate));
3102 }
3103 fossil_free(pCI->zDate);
3104 pCI->zDate = zDVal;
3105 }
3106 /* Potential TODOs include:
3107 **
3108 ** - Commit allows an empty checkin only with a flag, but we
3109 ** currently disallow it entirely. Conform with commit?
3110 **
@@ -3086,25 +3114,24 @@
3114 ** code cannot do if it's going to be run via a web page.
3115 */
3116
3117 /*
3118 ** Confirm that pCI->zFilename can be found in pCI->pParent. If
3119 ** not, fail unless the CIMINI_ALLOW_NEW_FILE flag is set. This is
3120 ** admittedly an artificial limitation, not strictly necessary. We
3121 ** do it to hopefully reduce the chance of an "oops" where file
3122 ** X/Y/z gets committed as X/Y/Z or X/y/z due to a typo or
3123 ** case-sensitivity mismatch between the user/repo/filesystem, or
3124 ** some such.
 
 
3125 */
3126 manifest_file_rewind(pCI->pParent);
3127 zFilePrev = manifest_file_seek(pCI->pParent, pCI->zFilename, 0);
3128 if(!zFilePrev && !(CIMINI_ALLOW_NEW_FILE & pCI->flags)){
3129 ci_err((pErr,"File [%s] not found in manifest [%S]. "
3130 "Adding new files is currently not permitted.",
3131 pCI->zFilename, pCI->zParentUuid));
3132 }else if(zFilePrev
3133 && manifest_file_mperm(zFilePrev)==PERM_LNK){
3134 ci_err((pErr,"Cannot save a symlink via a mini-checkin."));
3135 }
3136 if(zFilePrev){
3137 prevFRid = fast_uuid_to_rid(zFilePrev->zUuid);
@@ -3174,10 +3201,11 @@
3201 }
3202 /* Create, save, deltify, and crosslink the manifest... */
3203 if(create_manifest_mini(&mf, pCI, pErr)==0){
3204 return 0;
3205 }
3206 isPrivate = content_is_private(pCI->pParent->rid);
3207 rid = content_put_ex(&mf, 0, 0, 0, isPrivate);
3208 content_deltify(rid, &pCI->pParent->rid, 1, 0);
3209 if(pCI->flags & CIMINI_DUMP_MANIFEST){
3210 fossil_print("Manifest %z:\n%b", rid_to_uuid(rid), &mf);
3211 }
@@ -3237,10 +3265,15 @@
3265 ** the previous version's content. Does not
3266 ** modify the original file, only the
3267 ** checked-in content.
3268 ** --delta Prefer to generate a delta manifest, if
3269 ** able.
3270 ** --allow-new-file Allow addition of a new file this way.
3271 ** Disabled by default to avoid that case-
3272 ** sensitivity errors inadvertently lead to
3273 ** adding a new file where an update is
3274 ** intended.
3275 ** --dump-manifest|-d Dumps the generated manifest to stdout.
3276 ** --wet-run Disables the default dry-run mode.
3277 **
3278 ** Example:
3279 **
@@ -3256,10 +3289,16 @@
3289 const char * zAsFilename; /* --as filename */
3290 const char * zRevision; /* --revision|-r [=trunk|checkout] */
3291 const char * zUser; /* --user-override */
3292 const char * zDate; /* --date-override */
3293
3294 /* This function should perform only the minimal "business logic" it
3295 ** needs in order to fully/properly populate the CheckinMiniInfo and
3296 ** then pass it on to checkin_mini() to do most of the validation
3297 ** and work. The point of this is to avoid duplicate code when a web
3298 ** front-end is added for checkin_mini().
3299 */
3300 CheckinMiniInfo_init(&cinf);
3301 zComment = find_option("comment","m",1);
3302 zCommentFile = find_option("comment-file","M",1);
3303 zAsFilename = find_option("as",0,1);
3304 zRevision = find_option("revision","r",1);
@@ -3284,30 +3323,19 @@
3323 cinf.flags |= CIMINI_CONVERT_EOL;
3324 }
3325 if(find_option("delta",0,0)!=0){
3326 cinf.flags |= CIMINI_PREFER_DELTA;
3327 }
3328 if(find_option("allow-new-file",0,0)!=0){
3329 cinf.flags |= CIMINI_ALLOW_NEW_FILE;
3330 }
3331 db_find_and_open_repository(0, 0);
3332 verify_all_options();
3333 user_select();
3334 if(g.argc!=3){
3335 usage("INFILE");
3336 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3337 if(zComment && zCommentFile){
3338 fossil_fatal("Only one of -m or -M, not both, may be used.");
3339 }else{
3340 if(zCommentFile && *zCommentFile){
3341 blob_read_from_file(&cinf.comment, zCommentFile, ExtFILE);
@@ -3316,13 +3344,13 @@
3344 }
3345 if(!blob_size(&cinf.comment)){
3346 fossil_fatal("Non-empty checkin comment is required.");
3347 }
3348 }
 
3349 zFilename = g.argv[2];
3350 cinf.zFilename = mprintf("%/", zAsFilename ? zAsFilename : zFilename);
3351 cinf.filePerm = file_perm(zFilename, ExtFILE);
3352 cinf.zUser = mprintf("%s", zUser ? zUser : login_name());
3353 if(zDate){
3354 cinf.zDate = mprintf("%s", zDate);
3355 }
3356 if(zRevision==0 || zRevision[0]==0){
@@ -3334,12 +3362,10 @@
3362 }
3363 name_to_uuid2(zRevision, "ci", &cinf.zParentUuid);
3364 if(cinf.zParentUuid==0){
3365 fossil_fatal("Cannot determine version to commit to.");
3366 }
 
 
3367 blob_read_from_file(&cinf.fileContent, zFilename,
3368 ExtFILE/*may want to reconsider*/);
3369 {
3370 Blob errMsg = empty_blob;
3371 const int rc = checkin_mini(&cinf, &newRid, &errMsg);
3372

Keyboard Shortcuts

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